Rest – Extending ACL with more sophisticated rules in web REST API

access-controlapi-designpermissionsrest

I am currently working a REST API design and I am looking for a way to add an advanced ACL management, beyond what

Let's consider for instance that I have a route which is the following :

/profile/{user}

Let's say this route might be call with GET or PUT methods. The GET method have to a controller to display the user profile,
while the PUT method should be used to update the matching user profile data.

With standard ACL, as most of the libraries provide, it is easy to define the something like :

/profile/{user} , GET -> Role::*

/profile/{user} , PUT -> Role::user

which means that the method GET for the route does not requires any role and is "public", while the method PUT method is only available if the
client has the role user.

Now, I would like to enrich this mechanism to control the exact identity of the user.
In fact, it seems logical that only the owner of an account can edit the profile, not the other users, and given the previous rules, nothing
forbids it unless a further checking is done (typically in controller).

My suggestion is to be able to write rules like this :

/profile/{user} , PUT -> Role::user, Session::user == {user}

This way, to access this route, the current user must have the user role but the user's id have to be the same than the parameter of the route.
This also could be extended more generally, for example with a post data model that would be posts in an application, in which all posts have an author (which is an user)
and only authors may edit their own posts :

/post/{post} , PUT -> Role::user, Session::user == {post}.author

I know such practices are discouraged by some people who claim that this is more business logic than simple access security logic but I don't agree.

Considering I am planning to develop a library to manage such advanced ACL, I would like to know what do you think about this way of thinking and if you know some frameworks or applications that already use such mechanism.

Best Answer

Part of the pain you feel with securing your API is that it has the wrong level of granularity. If you need to allow a user to "update user's own profile", then you should expose that specific operation on the API distinct from "update (any) user's profile". The client application should call the appropriate operation. Then you can check for permission to do that operation in a declarative way and deny it at the security border before it gets into controller code.

For example: If the client requests PUT /api/<update (any) user's profile> { profileid: ... }. You search their permissions (e.g. claims) for "update (any) user's profile". Failing to find it, they are denied at the security border. Whereas they might be allowed to "update user's own profile", so a PUT /api/<update user's own profile> {...} would be passed to the controller for that operation. You won't even have to specify a target profile id for this operation, because you can get it from their auth data.

REST is not simply exposing your database over HTTP nor mapping HTTP verbs to CRUD operations (or at least it shouldn't be). An API is there to focus clients on the higher-level operations. Looking at it that way also makes it easier to secure... you secure on the operation, not specific data changes.

Note: I intentionally didn't use specific naming conventions for the profile operations above. I'll leave it to someone else to argue the proper conventions for non-entity REST endpoints.

Here's another answer I did recently that may help you.

Related Topic