How to implement a hybrid role-based access control model

access-controlpatterns-and-practices

I am writing an enterprise web-forms-frontend application for in-house use. It has Direct access control (DAC) masquerading as Role-based access control (RBAC).

For anonymization purposes, let's call the main unit of information stored in my application a Document, and the roles in the company a Boss, a Grunt and a C-level executive. There are also Externals (e.g. somebody working for a business partner of ours). The general guideline is that everybody but externals should be able to read all documents, C-level execs can write everything, Bosses can write the Documents belonging to their own department, and Grunts can only write Documents personally assigned to them. Fairly standard thus far.

But what users actually want is that they can make arbitrary exceptions to the above. They want that any person in the system can be granted write access to any Document, and that Externals can be granted Read access to any document. (Actually, it is more complicated than that, with more roles and finer granularity of permissions, including management of who can propagate which permission to others, but the above description illustrates the core of the problem well enough). So what I have is basically permissions on a personal level, and the roles are only a convenient way of having default settings for the personal-level permissions when a user or a Document is added to the system, instead of having somebody fill out a whole row or column in an access control matrix by hand.

Now I have already designed a Permissions table in my database, which has a User FK, Document FK and columns for the different types of permissions. What I am not sure is what I should save in the table.

  • Alternative 1: I save all permissions in this table (pure DAC) and have the logic tier mimic a RBAC. E.g. when a new Boss is added, a row for each Document in the system is added to the DB, with Read permissions for all documents and Write for the Documents of her department.
  • Alternative 2: I save the deviations from the role guidelines only. So when a new Boss is added, nothing is written to the permissions table. But when an executive gives a Boss the rights to write to a Document from a different department, then a single row is added to reflect that information.

I am not sure which alternative would be better. The first one feels closer to textbook implementation, and if a principle has made it into a textbook, then there is normally good reason to use it. But in my case, it also hurts the DRY principle – after all, if the information that a C-level exec can write to Document X is derivable from his role, writing a row with this information in the DB is redundant.

What are the advantages and disadvantages of each approach in terms of a) application performance and b) complexity of implementation? What headaches can I expect from each?

Keep in mind that 1) I don't plan to implement a full logic tier. The whole application is practically a convenient CRUD frontend to a database, so I will be doing DB queries for each page view instead of keeping a collection of Document objects in memory. (I know the advantages of a MVC pattern, but it was decided that it will be overkill for this project). 2) I am programming this in ASP .NET 4.5, so the closer I stay to roles, the more I can let the framework do the heavy lifting for me. 3) I have thought of implementing groups orthogonal to the roles to manage access, but it doesn't make sense in my case.

Best Answer

User permissions

If you had full permissions information for each user entry in your database, you'd have the advantage of simplicity. You want to know the permissions of user X, load permissions for user X. The advantage is in the work you'd have to do. It's also true that it would use more database space, but I consider that a minor disadvantage that isn't really worth mentioning.

If your client was happy micromanaging the permissions of each individual user, this is ideal. You yourself do not have to manage the permissions, and at the same time, you allow full control to your client.

The problem comes in when the client starts complaining about having too many users to change. The sensible solution at this point is to give users group permissions, but modifying your current model in order to allow for groups is somewhat of a nightmare. Not to mention that updating the permissions of a group means batch updating all users with that group. It quickly becomes unmanageable and there is no easy way to switch permissions model.

Role permissions

I would advise you to use a role-based system. Instead of holding all permissions for every user, have a user record point to a permissions group or NULL. If it is NULL, have a default (undeletable) permission group that a user will inherit from if none is specified. Clients modify a permission group directly (including the default permissions group) or the permission group that a user points to, but they remain separate entities. In this way, they have all the individual control they had before, creating permission groups for single users if they desire it, but they can also make blanket permissions and simply assign users to it.

You could argue that it is slower to load, but if you make a point to cache permissions, it will only be slow in the first load. The only true disadvantage to this approach is that it is a little trickier to implement with respect to the first approach, but considering the headaches you'd have trying to implement the second approach after already having used the first approach makes it worthwhile in my humble opinion.

Related Topic