Let's assume I have a table of items:
CREATE TABLE items
(
item serial PRIMARY KEY,
...
);
Now I want to introduce the concept of "permissions" for each item (note, I'm not talking about database access permissions here, but business logic permissions for that item). Each item has default permissions and also per-user permissions that may override default permissions.
I tried to think of ways to implement this and came up with the following solutions:
Use a boolean column for each permission:
CREATE TABLE items
(
item serial PRIMARY KEY,
can_change_description boolean NOT NULL,
can_change_price boolean NOT NULL,
can_delete_item_from_store boolean NOT NULL,
...
);
CREATE TABLE item_per_user_permissions
(
item int NOT NULL REFERENCES items(item),
user int NOT NULL REFERENCES users(user),
PRIMARY KEY(item, user),
can_change_description boolean NOT NULL,
can_change_price boolean NOT NULL,
can_delete_item_from_store boolean NOT NULL,
...
);
Advantages: Each permission is named.
Disadvantages: There are dozens of permissions which increases the number of columns significantly and you have to define them twice (once in each table).
Use an integer and treat it as a bitfield (i.e. bit 0 is for can_change_description
, bit 1 is for can_change_price
, and so on, and use bitwise operations to set or read permissions).
CREATE DOMAIN permissions AS integer;
Advantages: very fast.
Disadvantages: You have to keep track of which bit stands for which permission in both the database and the front-end interface.
Same as 2), but use bit(n)
. Most likely the same advantages and disadvantages, maybe slightly slower.
Use an enum type for the permissions:
CREATE TYPE permission AS ENUM ('can_change_description', 'can_change_price', .....);
and then create an extra table for default permissions:
CREATE TABLE item_default_permissions
(
item int NOT NULL REFERENCES items(item),
perm permission NOT NULL,
PRIMARY KEY(item, perm)
);
and change the per-user definition table to:
CREATE TABLE item_per_user_permissions
(
item int NOT NULL REFERENCES items(item),
user int NOT NULL REFERENCES users(user),
perm permission NOT NULL,
PRIMARY KEY(item, user, perm)
);
Advantages: Easy to name individual permissions (you don't have to handle bit positions).
Disadvantages: Even when just retrieving the default permissions, it requires accessing two additional tables: first, the default permissions table, and second, the system catalog storing the enum values.
Especially because the default permissions must be retrieved for every single page view of that item, the performance impact of the last alternative might be significant.
Can you think of other alternatives?
Which approach should be taken?
Please note: this question has been reposted on DBA.
It really will depend on what level you need to go to for permissions
Role Based - Not User
User Based - (harder to maintain)
or Combination of the two
User and Roles...
The following on our systems User Table -> contains UserNames etc
Roles => list of Roles such as Office Support, Mechanic
Security Level -> List of Levels e.g. Read Only, Administrator
Application Function Table -> contains functions that can have permissions applied e.g. Can_Edit_Users
Application Security Table -> Username + Application Function Id , Role and Either bit for yes/no permission - or an integer for a more complex Role Level using the Security Level.
This gives the most flexibility but can be difficult to build the UI for.
In the application we get the users permissions once, and do a function to check if they have permissions
e.g. CurrentUser.HasPermissions("Can_Edit_Users")
public bool HasPermissions(string Permission)
{
return this.Permissions.Any(p=>p.FunctionName==Permission && IsAllowed==true);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With