Handling Permissions in REST

Figuring out how to handle permission when designing a REST API can get confusing. Especially if you don’t have names to describe your permissions. I had gone through most of the permission use cases and was trying to convey the requirements to our contractors. This is when I found a great doc on authorization for Apache Shiro. It gives names to the different levels of permissions. Giving good names to the concepts I was trying to convey solidified the ideas in my head (and on paper).

Shiro described permissions in three levels:

• Resource Level – This is the broadest and easiest to build. A user can edit customer records or open doors. The resource is specified but not a specific instance of that resource.

• Instance Level – The permission specifies the instance of a resource. A user can edit the customer record for IBM or open the kitchen door.

• Attribute Level – The permission now specifies an attribute of an instance or resource. A user can edit the address on the IBM customer record.

This is a short write up on how the permission ended up looking in my design. I’ll demo it with a made up User Resource that looks like this:

{
    'firstName':'kevin',
    'age':'33',
    'hireDate':'1276304738',
    'nickname':'kevino',
    'salary':'10k'
}

There are three types of data in this object. General data like, firstName and nickname, sensitive data, like salary (I need a raise), and fixed data, hireDate.

Resource Level

Resource Level Permissions are about how to access groups of resources. The ability to view, create or delete whole resources. The following endpoint responds with the expected list of data and a _links object. The _links object indicates that the caller can create a new user, or view the detail of a user but not delete a user. Also, notice that there is no indication that they can edit a user. That information comes in the Instance Level Permissions and hinges on the ability to view a user. (It seems like the caller of this endpoint is a manager because he can see the users salaries.)

{
    '_links': {
        'self': { 'href': '/app/services/users' },
        'next': { 'href': 'app/services/users/page=2' },
        'actions.new':{'href': 'app/services/users/{id}' },
        'actions.view':{'href': 'app/services/users/{id}' }
    }
    'users': [{
    'firstName':'kevin',
    'age':'33',
    'hireDate':'1276304738',
    'nickname':'kevino',
    'salary':'10k'
    },{
    'firstName':'samuel',
    'age':'37',
    'hireDate':'998875957',
    'nickname':'sam',
    'salary':'20k'
    }]
}
Instance Level

Instance Level Permissions are seen when looking at a single resource. The example below shows that a user can edit all fields in the user object. But what if editability is limited to certain fields. (Notice that the servers response has replaced the {id} because it gives you the exact users URL for edit.)

{
    '_links': {
        'self': { 'href': '/app/services/users/5' },
        'actions.edit':{'href': 'app/services/users/5' },
    'firstName':'kevin',
    'age':'33',
    'hireDate':'1276304738',
    'nickname':'kevino',
    'salary':'10k'

}
Attribute Level

This is where I feel permission don’t fit nicely into REST principles. But because the permissions are isolated and only found at this level, it doesn’t bother me much.

There are two types of Attribute Level Permissions to take into consideration. 1) Ability to view and 2) ability to edit. Notice the permissions object show that the caller can’t edit the hireDate. Also notice that in order to prevent the caller from viewing the salary value, the server simply does not send that data back. In a lot of designs I’ve seen internally, the developer relies on the UI to block out invisible values. Even though this is “good enough” 99% of the time, it is better to set the standard that any value not viewable to a particular caller should not be sent. Key as well as value. Then you won’t accidentally send up someones salary to their co-worker.

{
    '_links': {
        'self': { 'href': '/app/services/users/6' },
        'actions.edit':{'href': 'app/services/users/6' },
    }
    'permissions':{
        'hireDate':'false'
    },
    'firstName':'samuel',
    'age':'37',
    'hireDate':'998875957',
    'nickname':'same'
    }
}

Note: I’ve trimmed the links in href but I believe that your service should return the full URL. An html page wouldn’t give you part of the URL, why should your API.

Update:
Here is a nice discussion on reddit about an alternate way of handling permissions using roles. This is a much simplified solution if you can limit the number of roles a system has to maintain.