MySQL - Structure for Permissions to Objects - sql

What would be an ideal structure for users > permissions of objects.
I've seen many related posts for general permissions, or what sections a user can access, which consists of a users, userGroups and userGroupRelations or something of that nature.
In my system there are many different objects that can get created, and each one has to be able to be turned on or off. For instance, take a password manager that has groups and sub groups.
Group 1
Group 2
Group 3
Group 4
Group 5
Group 6
Group 7
Group 8
Group 9
Group 10
Each group can contain a set of passwords. A user can be given read, write, edit and delete permissions to any group. More groups can get created at any point in time.
If someone has permission to a group, I should be able to make him have permissions to all sub groups OR restrict it to just that group.
My current thought is to have a users table, and then a permissions table with columns like:
permission_id (int) PRIMARY_KEY
user_id (int) INDEX
object_id (int) INDEX
type (varchar) INDEX
admin (bool)
read (bool)
write (bool)
edit (bool)
delete (bool)
This has worked in the past, but the new system I'm building needs to be able to scale rapidly, and I am unsure if this is the best structure. It also makes the idea of having someone with all subgroup permissions of a group more difficult.
There will be a separate table for roles of users/admins, which means they can change the permissions on users below groups they can control.
So, as a question, should I use the above structure? Or can someone point me in the direction of a better one?
EDIT
Alternative is to create a permission table for every type of object.

I suggest you add a "last_update" timestamp and a "last_updated_by_user" column so you have some hope of tracking changes to this table in your running system.
You could consider adding a permission -- grant. A user having the grant permission for an object would be able to grant access to other users to the object in question.
Be careful with "needs to scale rapidly." It's hard to guess without real-world production experience what a scaled-up system really needs.
Also, be careful not to over-complicate a permissions system, because an overly complex system will be hard to verify and therefore easier to crack. A simple system will be much easier to refactor for scaleup than a more complex one.
Your schema seems to relate users to objects. Do you want your primary key and your unique index to be (user_id, object_id)? That is, do you want each user to have either zero or one permission entry for each object? If so, use the primary key to enforce that, rather than using the surrogate permission_id key you propose.
For your objects that exist in hierarchies, you should make one of two choices systemwide:
a grant to an object with subobjects
implicitly grants access to only the
object, or...
it also grants access
to all subobjects.
The second choice reduces the burden of explicit permission granting when new subobjects are created. The first choice is more secure.
The second choice makes it harder to determine whether a user has access to a particular object, because you have to walk the object hierarchy toward the root of the tree looking for access grants on parent objects when verifying whether a user has access. That performance issue should dominate your decision making. Will your users create a few objects and access them often? Or will they create many objects and subobjects and access them rarely? If access is more frequent than creation, you want the first choice. Take the permission-granting overhead hit at object creation time, rather than a permission-searching hit at object access time.
I think the first choice is probably superior. I suggest this table layout:
user_id (int)
object_id (int)
type (varchar) (not sure what you have this column for)
admin (bool)
read (bool)
write (bool)
edit (bool)
grant (bool)
delete (bool)
last_update (timestamp)
last_updated_by_user_id (int)
primary key = user_id, object_id.
You could also use this table layout, and have a row in the table for each distinct permission granted to each user for each object. This one scales up more easily if you add more types of permissions.
user_id (int)
object_id (int)
permission_enum (admin/read/write/edit/grant/delete)
type (varchar) (not sure what you have this column for)
last_update (timestamp)
last_updated_by_user_id (int)
primary key = user_id, object_id, permission_enum

Related

Ensure data can only be changed by its actual owner in a database

Background
We are building a new project and are currently
at the database design. We
stumbled upon a problem which
we cannot solve, we believe we
have some solutions to it but we are not sure.
The problem seems a tad ungoogleable,
probably because we don’t know enough
keywords on this topic.
Problem
In our case we are building a rest-api
where a user authenticates with a token
to do CRUD-operations to the endpoints.
Thats alright, you need to provide a
valid token to access or modify a resource.
Standard stuff.
However, being authenticated does not prevent you from modifying rows
that doesn’t belong to you.
Thus a user could possibly change other users data,
since there is no logic that handles that.
In the case a table is located far between each other, you cannot rely on foreign key or primary key constraints - unless see Solution 2.
Solution 1
Add a field to every table that is either just a value holder or an actual fk.
That field references the primary key of the “owner” or user in our case.
Solution 2
Take the performance hit and actually traverse
all the way back up to the "owner" of the
object/row.
Solution 3
Implement a RLS (row level security) solution but
that does not seem to be the usecase for RLS or at least feels
a bit to advanced for our usecase.
Question
So the question is what is the most performant while still solving
the security issue? are there other solutions to this?
Solution 2 example:
Tables
user
id - pk
…
company
id - pk
…
user_id - fk
note
id - pk
…
user_id
Session
user_session
user_id
…
Data user can change:
SELECT
*
FROM
note
WHERE
user_id = :session.user_id
When user wants to update the data:
UPDATE note
SET text = “foobar”
WHERE user_id = :session.user_id AND id=payload.id
If there exists a row with user_id that corresponds
to the requesting user, and if the supplied primary key of the note
exists.
References
Constraint to check values from a remotely related table
Designing the most performant row level ..
Application users vs. ro level security
First, you need to decide your tenant strategy ("multi-tenancy" is your keyword). You can put user's data in different clusters, databases, schemas, or rows
Tenant-per-database is my preferred solution
does not seem to be the usecase for RLS
This is absolutely a usecase for RLS
You'll want an "owner_id" column in each "secured" table. Ensure the current_user is that owner. Cascading foreign keys to update owner_id if it changes
You could use WITH CHECK OPTION views instead of RLS, but RLS is still simpler

Best way to mimic inheritance in postgresql?

For an application I am writing, there are two types of "users", those who have made accounts and those who have not, virtual_users. These two types are nearly identical, except account_users have a password, and email is required and must be unique amongst all account_users, although it can be the same as any number for virtual_users. A large number of tables have a column that references users, which should include both, and 90% of app functionality treats them as interchangeable. What is the best way of handling this? Some options I have considered:
-Put both types of users in the same table and have a complicated constraints regarding uniqueness, basically, if password is not NULL, email must be unique among all users where password is not NULL. I have no idea how I would write this constraint. On the few occasions I only want account_users query for only users who have a password. This seems like the best solution if I can figure out how to write the constraint.
-Have Account_users inherit from Virtual_usersand Virtual_users has an additional column password and unique constraints on email. From here there are two potential options:
---Have a Users table which includes two columns account_user_id and virtual_user_id one of which is NULL and one of which corresponds to the appropriate user. When other tables need to reference a user, they reference this table. Have all my queries server side for users query both tables and combine.
---When other tables need to reference they reference either table. I don't think this is possible. Have all my queries server side for users query both tables and combine.
Any advice would be appreciated.
I assume the scenario is you have a system which some parts require the user to be signed into a registered account, and others do not, but you'd still like to track users.
Postgres has table inheritance. You could use that, but I'd be concerned about the caveats.
You could put them all into one table and use some sort of flag, like Single Table Inheritance, but then you run into constraint issues. You would then enforce constraints in the model. This should be fine if you have a strong model.
You could have separate accounts and users. Rather than one being a special case of the other, they key is thinking of them as two conceptually different things. In OO terms, an account has a user.
-- Visitors to the site who haven't signed up.
create table users (
id serial,
-- The unverified email they might have given you.
email text,
-- Any other common information like a tracking token
token text
);
-- Users who have registered.
create table accounts (
id serial,
user_id int references users(id),
-- Their verified email.
email text not null,
-- Hashed, of course.
password text not null
-- any additional information only for accounts
);
I like this because there are no flags involved, users and accounts can have separate constraints, and the accounts table doesn't get bloated with users that showed up once and never came back.
You'd access complete account information with a simple join.
select accounts.*, users.token
from accounts
join users on accounts.user_id = users.id
where accounts.id = ?
If you want to delete an account you can do so without losing the underlying user information.
delete from accounts where accounts.id = ?
Systems which require an account use accounts. Systems which don't use users. Systems which behave differently for users and accounts can check if a user has an account easily.
select accounts.id
from accounts
where accounts.user_id = ?
User tracking is associated with the users table, so you have a consistent record of a user's behavior before and after they register.

Associate one column in table with two existing tables

My DB has following tables:
Resource: Some resources can be uploaded on site
Groups: Groups on site
Users: Users on site (not necessarily be part of any group but could be if they like)
Now, when some one uploads a resource then currently, ownership of that resource is given to it's uploader by default. So resource table has column OwnerID with foreign key association to User table.
But now, this has to be changed such that ownership of a resource could be given to either a user or entire group.
I'm trying to decide the migration scheme, to move this owner being user to an entity that could be either user or group. Intuition is that when someone uploads a material, he can choose its owner to be a user or entire group.
Currently my migration plans involves:
Add OwnerType (User, Group, Global) and UserOwner and GroupOwner within the Material table (probably worse normalized table).
OwnerType could be Global if owner is everyone --or-- Group if owner is group entity else user.
Then when I'm querying the Resource table, I can check the OwnerType to condionally select its Owner from either user table or group table.
I do not know if this is good way. I'm using entity framework, and things are already started to look ugly as User and Group hardly have any relationaship that would require me to make generalized entity.
Can some expert guide me on this? What is generally considered good migration plan in this case? Thanks for any help or suggestions.

Relationship redundant?

I'm designing a database and I have a user table with users, and a group table with user's group.
These groups will have a owner (a user that has created it), and a set of users that are part of the group (like a Whatsapp group).
To represent this I have this design:
Do you think the owner column on Group table is necessary? Maybe I can add an owner column on Group table I can know easily the group's owner.
If you don't add the owner in the group then where are you going to add it? The only way I see apart from this is adding a boolean isowner to the usergroup. Anyway, this would not make sense if there will only be 1 owner. If there can be N owners then that would be the way to go.
You are on the right track, but you'll need one more step to ensure an owner must actually belong to the group she owns:
There is a FOREIGN KEY in Group {groupID, owner} that references UserGroup {groupID, userID}.
If your DBMS supports deferred foreign keys, you can make owner NOT NULL, to ensure a group cannot be owner-less. Otherwise you can leave it NULL-able (and you will still be able to break the "chicken-and-egg" problem with circular references if your DBMS supports MATCH SIMPLE FKs - almost all do).
You need 4 tables:
User
UserGroup
Group
UserRole (associated with UserGroup) - Shows the role of a user in a group (admin/owner, etc.) - If your roles are Admin and Ordinary user, you could use a Binary column on UserGroup instead.
I know a solution has already been proposed, but I am so convinced there is a better one ...
At the higher level, the concept of owner can be seen as a property of the relation existing between users and groups. It should then ideally be set as a field in the UserGroup table.
It could be either a boolean field or, even better, a generalized userGroupNatureOfRelation field, that could hold values such as 'owner', 'participant', 'user', or whatever could be the status.
Of course, such a solution allows you to implement any specific business rule, like 'there is only one owner per group'. It will let you implement any other more sophisticated business rule when needed, and it will even allow you to add a level of complexity by adding fields such as:
userGroupRelationStartDate
userGroupRelationEndDate
where you'll be able to follow the nature of the relation between a group and a person through time ...
Of course you could say 'I don't need it'. But implementing such an 'open' model does not cost anything more than what you are thinking of now. Then, if for any reason, in a near or far future, business rules have to be changed or improved, your model stays valid and efficient ...
This said, building views and manipulating data should be a lot easier with this model. So this a good and immediate reason to adopt it!

Data Design debate

I am having a tough time with this design problem and would appreciate any insight.
I have a doctors office that is provided certain privileges currently there are only 5 privileges but more could be added. Each of these privileges has a status of Yes or No, but there could be a finer grained status in the future and each of these privileges are related to a location(er,inpatient, outpatient) and they too could expand in future.
So currently I have the tables OfficePrivileges, PrivilegeLocation, PrivilegeType, PrivilegeStatus.
OfficePrivilege is a Joining table between PrivilegeLocation and DoctorOffice. It has a double primary key of OfficeID and PrivilegeLocationID.
At one time I had Type and status joined to the OfficePrivileges Table then switched to have the type table be a child of Location and status be a child of type. They are all single primary key tables.
if you were designing this set of tables how would you do it? I am thinking that this is almost a hierarchy problem..and I hate them. I would like to lay out the edit screen as crosstab table having Location across the top, Type down the side and details being the statuses. That is how it is currently in the system I am trying to integrate to, but its a Cobol backend and handles hierarchies better than relational db....
EDIT:To Help clear up confusion:
For my example there are Admit Privileges, Attending Privileges,Consulting privileges and Surgery Privilges. And the locations are Inpatient, ER, OutPatient, and OP/Surgery. Currently the statuses are only Yes or NO. But they could change in the future depending on client need.
This information is stored in tables in my database.
I think the big thing here it to try to be as flexible as possible, since you're aware of "possible" changes, but you don't really want to code to those, yet.
Something like what you've got is really probably pretty good: I'd go this way: you'll actually have a 3-way relationship between Offices, PrivilegeLocations, and PrivilegeTypes
I would say your OfficePrivaleges table should have the following 6 columns: Id (its own PK), OfficeId, LocationId, PTypeId, StartDate, EndDate When a new Privilege is granted for an Office, you'll add a record to this table linking the three and adding the first date the office has the privilege in StartDate. If the Privilege is revoked, add that date to EndDate. If it's renabled, either add a new row, or reset the StartDate.
I would avoid making PrivilegeType a child of PrivilegeLocation, because then you either have to store every location n times (n = different privilege combinations). This way, you're only storing Offices, Locations, and Types once each.
So, for instance, Doctor A could have Inpatient Privileges at Location X, but only DX Imaging privileges at Location Y, while Doctor B could have Inpatient privileges at both Locations X and Y.