views:

371

answers:

4

Publishing and/or collaborative applications often involve the sharing of access to resources. In a portal a user may be granted access to certain content as a member of a group or because of explicit access. The complete set of content could include public content, group membership content, and private user content. Or, with collaborative applications, we may want to pass along resources as part of a workflow or share custody of a document for editing purposes.

Since most applications store these resources in a database you typically create queries like 'Get all the documents that I can edit' or 'Get all the content I can see'. Where 'can edit' and 'can see' are the user's privileges.

I have two questions:

  1. It's quite easy to authorize the user once you've retrieved a resource, but how do you efficiently perform authorization on the list of available resources? And,

  2. Can this kind of authorization be separated from the core of the application? Perhaps into a separate service? Once separated, how could you filter queries like 'Get me all the documents I can see with title like [SomeSearchTerm]'? It seems to me your separate system would have to copy over a lot of reference data.

A: 
Sklivvz
A: 

Though your question is quite elaborated, there is actually some missing context.
What defines the documents a user has privileges for? Can a user only edit his "own" files? Is there a role-based mechanism here? Is it more MAC-oriented (i.e. a user can see a level of security)? Is there a defining attribute to the data (e.g. department)?

All these questions together may give a better picture, and allow a more specific answer.

If documents are "owned" by a specific user, its pretty straightforward - have an "owner" field for the document. Then, query for all documents owned by the user.
Similarly, if you can pre-define the list of named users (or roles) that have access, you can query for a joined list between the documents, the list of authorized users/roles, and the user (or his roles).
If a user gets permissions according to his department (or other similar document's attribute), you can query on that.
Similarly, you can query for all documents with a level of security equal or lower to that of the user's privileges. If this is dynamic workflow, each document should typically be marked with its current state, and/or next expected step; which users can perform that next step is simply another privilege/role (treat the same as above).
Then, of course, you'll want to do a UNION ALL between all those and public documents...

Btw, if I have not made it clear, this is not a simple issue - do yourself a favor and find pre-built infrastructure to do it for you. Even at the cost of simplifying your authorization schemata.

AviD
A: 

Thanks for the comments (and apologies for the delayed response). Although I like the simplicity of using a view against a mapping table, I don't think it's sufficient to achieve real separation of concerns. I'm trying to envision an authorization framework that could really be separately maintained.

No, documents wouldn't be 'owned' by a particular user. At least, ownership should be transferable. But the concept of ownership loses its meaning when you frame the problem as one of permissions. I'm trying to devise an authorization process where all the rules and the actual authorization are independent of the application itself. Ideally, it should be like Authentication. Obviously, permissions are going to be particular to the application functionality, but what I want to avoid is for access rules and auth checks to be part of the application code.

One also has to deal with visibility of users. E.g., get me all the users who 'can edit documents' to whom I can 'assign edit task'. Here I think the first condition is a role (DocumentEditors) and the second is what? I want to know "who can I give this work to?"

+3  A: 

You may be interested in reading this article by Steffen Bartsch. It summarizes all authorization plugins for Ruby on Rails, and I am sure it will help you find your solution (although this article is about Rails plugins, the concepts are easily exportable outside Rails).

Steffen also built his own plugin, called "Declarative Authorization" which seems to match your needs, IMHO:

  • on the one hand, you define roles (such as "visitor", "admin"...). Your users are associated to these roles (in a many-to-many relationship). You map these roles to privileges (again in a many-to-many relationship). Each privilege is linked to a given context. For example, the role "visitor" may have privilege "read documents". In this example, "read" is the privilege, and it is applied to the "documents" context.
    • Note: in Steffen's plugin, you can define a hierarchy of roles. For example you might want to have the "global_admin" role include the "document_admin" role, as well as the "comment_admin" role, etc.
    • You can also defines hierarchies of privileges: for example, the "manage" privilege could include the "read", "update", "add" and "delete" privileges.
  • on the other hand, you code your application thinking in terms of privileges and contexts, not in terms of roles. For example, the action to display a document should only check whether the user has the privilege to "read" in the "documents" context (no need to check whether the user has the "visitor" role or any other role). This greatly simplifies your code, since most of the authorization logic is extracted elsewhere (and perhaps even defined by someone else).

This separation between the definition of the user roles and the definition of the application-level privileges guarantees that your code will not change every time you define a new role. For example, here is how simple the access-control would look like in a controller :

class DocumentController [...]
  filter_access_to :display, :require => :read
  def display
    ...
  end
end

And inside a view:

<html> [...]
  <% permitted_to?(:create, :documents) do %>
  <%= link_to 'New', new_document_path %>
  <% end %>
</html>

Steffen's plugin also allows for object-level (ie. row-level) access-control. For example, you might want to define a role such as "document_author" and give it "manage" privilege on "documents", but only if the user is the author of the document. The declaration of this rule would probably look like this:

role :document_author do
  has_permission.on :documents do
    to :manage
    if_attribute :author => is {user}
  end
end

That's all there is to it! You can now get all the documents that the user is allowed to update like this:

Document.with_permissions_to(:update)

Since the "manage" privilege includes the "update" privilege, this will return the list of documents whose author is the current user.

Of course, not every application will need this level of flexibility... but yours might.

MiniQuark