I'm trying to understand the inherent tradeoff between roles and permissions when it comes to access control (authorization).

Let's start with a given: in our system, a Permission will be a fine-grained unit of access ("Edit resource X", "Access the dashboard page", etc.). A Role will be a collection of 1+ Permissions. A User can have 1+ Roles. All these relationships (Users, Roles, Permissions) are all stored in a database and can be changed on the fly and as needed.

My concerns:

(1) What is so "bad" about checking Roles for access control? What benefits are gained by checking for permissions instead? In other words, what's the difference between these two snippets below:

if(SecurityUtils.hasRole(user)) {
    // Grant them access to a feature
}

// vs.
if(SecurityUtils.hasPermission(user)) {
    // Grant them access to a feature
}

And:

(2) In this scenario what useful value do Roles even provide? Couldn't we just assign 1+ Permissions to Users directly? What concrete value of abstraction do Roles offer (can someone give specific examples)?

  • 2
    A couple points: (1) a single user may have multiple roles, (2) you may want to look into ACL (Access Control Lists), eg. you may want to be able to grant "Access the dashboard page" to just a subset of dashboard pages (if there are several). – Matthieu M. Oct 13 '15 at 18:32
up vote 53 down vote accepted

(1) What is so "bad" about checking Roles for access control? What benefits are gained by checking for permissions instead?

At the moment of checking, the calling code only needs to know "does user X have permission to perform action Y?".
The calling code does not care about and should not be aware of relationships between roles and permissions.

The authorization layer will then check if the user has this permission, typically by checking if the user's role has this permission. This allows you to change authorization logic without updating the calling code.

If you directly check for role at the call site, you are implicitly forming role ⇄ permission relationships and injecting authorization logic into the calling code, violating separation of concerns.

Should you later decide that role foo should not have permission baz, you would have to change every code which checks if the user is a foo.

(2) In this scenario what useful value do Roles even provide? Couldn't we just assign 1+ Permissions to Users directly? What concrete value of abstraction do Roles offer (can someone give specific examples)?

Roles conceptually represent a named collection of permissions.

Let's say you are adding a new feature which allows a user to edit certain settings. This feature should be available to administrators only.

If you are storing permissions per user, you would have to find all users in your database which you somehow know are administrators (If you're not storing role information for users, how would you even know which users are administrators?), and append this permission to their list of permissions.

If you use roles, you only have to append the permission to the Administrator role, which is both easier to perform, more space efficient, and is less prone to mistakes.

  • Uh? The authentication layer will check that the user is he who claims to be; the layer that checks which functions/data can such an user access is the authorization layer – SJuan76 Oct 13 '15 at 17:57
  • 3
    This should be compulsory reading for all programmers. Excellent. – Kosta Kontos Oct 14 '15 at 19:50
  • 2
    Simple, concise, and to the point - beats an entire chapter of a book somewhere. Thanks. – Dan Nissenbaum Jan 4 '17 at 6:35
  • 2
    Comment for clarity (and please correct me if I'm mistaken): An authorization layer likely means nothing more than simply having the definition of the function (i.e.) user->hasPermission(SOME_PERMISSION) internally check the user's roles first and then check if any of the roles include/exclude the given permission. For example, the calling code might be checking to see if a certain page is visible for the user and would call user->hasPermission(VIEW_GIVEN_PAGE), and the authorization layer consists of the definition of the hasPermission function which checks the roles as above. – Dan Nissenbaum Jan 4 '17 at 6:41
  • 1
    @DanNissenbaum Yeah, sounds like you got it right, it can be as simple as just checking if the users role has this authorization. It could also be more than that. For example, maybe you have the option to temporarily suspend a user and in that case hasPermission could check usersRole.HasPermission(VIEW_GIVEN_PAGE) && !user.Suspended. The point is it's all done in one place and not in the consuming (calling) code. – Rotem Jan 4 '17 at 12:30

In response to your first question, the biggest issue with checking that a user has a role rather than a specific permission, is that permissions can be held by multiple roles. As an example to this, a developer might have access to see the developer portal on the company intranet, which is probably also a permission held by their manager. If a user is then attempting to access the developer portal, you'd have a check similar to:

if(SecurityUtils.hasRole(developer)) {
    // Grant them access to a feature
} else if(SecurityUtils.hasRole(manager)) {
    // Grant them access to a feature
} else if...

(A switch statement in your language of choice would be better, but still not particularly tidy)

The more common or widely held a permission is, the more user roles you would need to check to ensure that someone is able to access a given system. This would also lead to the issue that every time you modify permissions for a role, you would need to modify the check to reflect this. In a large system this would become very unwieldy very quickly.

If you simply check that the user has the permission that allows them access to the developer portal for example, it doesn't matter what role they hold, they will be granted access.

To answer your second question, the reason you have roles is because they act as easy to modify and distribute "packages" of permissions. If you are have a system that has hundreds of roles and thousands of permissions, adding a new user (for example a new HR manager) would require you to go through and give them every single permission that other HR managers hold. Not only would this be tedious, but it is prone to mistakes if done manually. Compare this to simply adding the "HR manager" role to a user's profile, which will grant them the same access as every other user with that role.

You could argue that you could simply clone an existing user (if your system supported this), but while this does grant the user the correct permissions for that moment in time, attempting to add or remove a permission for all users in the future may be difficult. An example scenario for this is if maybe in the past the HR staff were also in charge of payroll, but later on the company gets large enough to hire staff specifically to handle payroll. This means that HR no longer need to access the payroll system, so that permission can be removed. If you have 10 different members of HR, you'll need to manually go through and make sure you remove the correct permission which introduces the possibility for user error. The other issue with this is that it simply doesn't scale; as you gain more and more users in a given role it makes modification of a role much more difficult. Compare this to using roles, where you would only need to modify the overarching role in question to remove the permission, which would be reflected by every user that holds that role.

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.