Question

I'm building a community-based site in Rails for the members of a real-world organization. I'm trying to adhere to the best practices of RESTful design, and most of it is more or less by-the-book. The issue that's making my brain run in neat RESTful circles is that of authorization. Authentication is an easy, long-solved problem with widely-accepted RESTful solutions, but RESTful authorization seems to be a bit of a black art. I'm trying to find the approach that will provide the most general and flexible framework for controlling access to resources while being as simple as possible, all while conforming to a RESTful architecture. (Also, a pony.)

Considerations:

  1. I need to control access to a variety of resources, such as Users, Pages, Posts, et cetera.
  2. Authorization for a given resource must be finer-grained than simple CRUD.
  3. I wish to allow myself and others to edit the authorization rules from within the application.
  4. Authorization rules should be allowed to depend on predicates, such as (conceptually) Owner(User, Resource) or Locked(Topic)

Consideration (2) is the one that concerns me the most. There seems to be an impedance mismatch between my conception of permissions and the RESTful conception of actions. For example, take Posts (as in a message board). REST dictates the existence of four operations on the Post resource: Create, Read, Update, and Delete. It's simple to say that a user should be able to Update his own Posts, but only certain users (or roles, or groups) should be permitted to Lock them. The traditional way to represent locking is within the state of the Post, but that leads to the smell that a User under the same conditions may or may not be able to Update a Post depending on the (completely valid) values he supplies. It seems clear to me that there are really two different actions to change the state of the Post, and to shoehorn them is merely to disguise a violation of RESTful principles.

(I should note that this problem is quite distinct from the problem of an Update failing due to invalid or inconsistent data—a lock request from an unprivileged user is in principle quite valid, simply disallowed.)

Isn't decomposition another word for rot?

This may be overcome by decomposing the Post: a Lock is a subresource of a particular post, and to Create or Destroy one may then have separate permissions. This solution has the ring of REST to it, but is brings with it both theoretical and practical difficulties. If I factor out locks, then what about other attributes? Suppose I decide, in a fit of caprice, that only a member of Administrator should be allowed to modify the Post's title? A simple change in authorization would then require a restructuring of the database to accommodate it! This is not much of a solution. To allow for this kind of flexibility under a strategy of decomposition would require that every attribute be a resource. This presents a bit of a dilemma. My implicit assumption has been that a resource is represented in the database as a table. Under this assumption, a resource for every attribute means a table for every attribute. Clearly, this is not practical. However, to remove this assumption presents an impedance mismatch between tables and resources, which could open up its own can of worms. To use this approach would require far more in-depth consideration than I have given it. For one thing, users reasonably expect to be able to edit multiple attributes at once. Where does the request go? To the smallest resource that contains all attributes? To each individual resource in parallel? To the moon?

Some of these things are not like the others…

Suppose then that I do not decompose attributes. The alternative then seems to be defining a set of privileges for each resource. At this point, however, the homogeneity of REST is lost. In order to define access rules for a resource, the system must have specific knowledge of that resource's capabilities. Furthermore, it is now impossible to generically propagate permissions to descendant resources—even if a child resource had a privilege of the same name, there's no inherent semantic connection between the privileges. Defining a REST-like set of standard privileges seems to me to be the worst of both worlds, so I's be stuck with a separate permissions hierarchy for each type of resource.

Well, we did do the nose. And the hat. But it's a resource!

One suggestion I've seen that mitigates some of the disadvantages of the above approach is to define permissions as create/delete on resources and read/write on attributes. This system is a compromise between attributes-as-resources and privileges-per-resource: one is still left with only CRUD, but for the purposes of authorization, Read and Update pertain to attributes, which could be thought of as pseudo-resources. This provides many of the practical benefits of the attributes-as-resources approach, although the conceptual integrity is, to a certain extent, compromised. Permissions could still propagate from resource to resource and from resource to pseudo-resource, but never from a pseudo-resource. I have not fully explored the ramifications of this strategy, but it seems as though it may be promising. It occurs to me that such a system would best function as an integral part of the Model. In Rails, for example, it could be a retrofit of ActiveRecord. This seems rather drastic to me, but authorization is such a fundamental cross-cutting concern that this may be justified.

Oh, and don't forget about the pony

All of this ignores the issue of predicative permissions. Obviously, a User should be able to edit his own Posts, but no one else's. Equally obviously, the admin-written permissions table should not have separate records for each user. This is hardly an uncommon requirement—the trick is making it generic. I think that all of the functionality I need could be gained by making only the rules predicative, so that the applicability of the rule could be decided quickly and immediately. A rule "allow User write Post where Author(User, Post)" would translate to "for all User, Post such that Author(User, Post), allow User write Post", and "deny all write Post where Locked(Post)" to "for all Post such that Locked(Post), deny all write Post". (It would be grand if all such predicates could be expressed in terms of one User and one Resource.) The conceptually resultant "final" rules would be non-predicative. This raises the question of how to implement such a system. The predicates should be members of the Model classes, but I'm not sure how one could refer to them gracefully in the context of the rules. To do so safely would require some sort of reflection. Here again I have a feeling that this would require a retrofit of the Model implementation.

How do you spell that again?

The final question is how to best represent these authorization rules as data. A database table might do the trick, with enum columns for allow/deny and C/R/U/D (or perhaps CRUD bits? or perhaps {C, R, U, D} × {allow, deny, inherit}?), and a resource column with a path. Perhaps, as a convenience, an "inherit" bit. I'm at a loss as far as predicates. Separate table? Certainly lots of caching to prevent it from being too ungodly slow.


I guess that this is a lot to ask for. I tried to do my homework before asking the question, but at this point I really need an outside perspective. I'd appreciate any input that any of y'all might have on the problem.

Was it helpful?

Solution

Sorry I don't have time to do this question justice, but it is nice to see some well thought out questions on SO. Here are some comments:

Don't fall into the trap of mapping the HTTP verbs to CRUD. Yes GET and DELETE map pretty cleanly, but PUT can do Create and Update (but complete replacement only) and POST is a wildcard verb. POST is really to handle everything that does not fit into GET, PUT and DELETE.

Using attributes to represent an object's status is only one approach to state management. I am guessing you can imagine what the following request might do:

POST /LockedPosts?url=/Post/2010

A subresource is also a valid approach to manage the current state of a resource. I would not feel obliged to treat a resource's "state" attributes and its "data" attributes in a consistent manner.

Attempting to map resources directly to tables is going to seriously constrain you. Don't forget that when you follow the REST constraints you suddenly are very limited in the verbs you have available. You need to be able to make up for that in being creative in the resources that you use. Limiting yourself to one resource equals one table will severely limit the functionality of your end application.

We regularly see Rails, ASP.NET MVC and WCF Rest users posting questions here on StackOverflow about how do certain things within the constraints of REST. The problem is often not a constraint of REST but in the limitations of the framework in its support for RESTful applications. I think it is essential to first find a RESTful solution to a problem and then see if that can be mapped back to your framework of choice.

As far as creating a permissions model that exists at a finer grain than the resource itself. Remember that one of the key REST constrains is hypermedia. Hypermedia can be used for more than just finding related entities, it can also be used to represent valid/permitted state transitions. If you return a representation than contains embedded links, conditionally based on permissions, then you can control what actions can be performed by who. i.e. If a user has permissions to unlock POST 342 then you could return the following link embedded in the representation:

<Link href="/UnlockedPosts?url=/Post/342" method="POST"/>

If they don't have that permission, then don't return the link.

I think one of your difficulties here is that you are trying to chew too big of a problem at once. I think you need to look at the RESTful interface that you are trying to expose to the client as a distinct problem from how you are going to manage the permissions and predicates in order to manage authorization in your domain model.

I realize that I haven't directly answered any of your questions, but hopefully I've provided some viewpoints that may help in some way.

OTHER TIPS

I've recently discovered an authentication solution that seems to address most of my concerns. If you favorite'd this question, it might be of interest to you:

https://github.com/stffn/declarative_authorization

As Darrel pointed out - REST isn't CRUD. If you find that your identified resources are too coarse grained that the uniform interface does not provide enough control then split your resource up into sub-resources and use the original resource as a 'collection' of hyperlinks to its components.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top