Jimmy Bonney Home About Articles

Home

About

Articles

Link Object and Subject with ACL9 without Database View

By Jimmy Bonney | May 5, 2013

Link

Authentication and authorization are two important aspects of any web application. Users should be able to log into the application (this is where the authentication part kicks in) and then view / update / create resources based on their access rights (this is for the authorization).

There are plenty of gems allowing to implement (more or less) quickly those two concepts in a Rails application. A lot of developments have happened in this field in the last few years and to get an overview of the different tools available, the easiest is probably to look at The Ruby Toolbox in the authentication and authorization sections.

Some time ago, most of the authorization gems allowed to define global roles in an application. This means that a user could be assigned an admin role (for instance) that was valid on all components of the application. This type of behavior works well for a forum for instance – where users either have a role to manage the different posts or not – but this isn’t so convenient in an application where more granularity is needed. In a project base application, users could be assigned a manager role on some projects but a viewer role on others and therefore have local roles (or scoped roles). ACL9 was one of the first gem (as far as I know) to offer such behavior and allow to define granular roles on some resources. The remaining of this post focuses on ACL9 and more precisely on how to link / access resources based on the user role.

Introduction

For this article, let’s consider a simple blog application in which users might have different access rights on the different posts. In this case, posts might for instance accept the following roles:

  • admin: the user has all possible rights on the post (create, read, update, delete, …)
  • editor: the user is allowed to create, edit and update the post
  • reviewer: the user is allowed to read and update the post
  • reader: the user is allowed to read the post

Users can then be assigned any of those roles for each post instance. As one can imagine, being reviewer on one post, does not mean that a user should be reviewer on every single post. ACL9 allows to define such granularity easily through its has_role!() method. For more information about how this is done, have a look at the documentation.

Models Overview

Based on the project page and the documentation, one can set up the following interactions between the different models that are defined.

Class Diagram Overview

The diagram illustrates clearly how the three components (subject, role and object) are linked together. In this case, the role is linked to the object directly (in the background, it is actually a polymorphic association so that one role table can handle more than one object) and a user is linked to a role through an enrollment table. What might not be obvious from this diagram, are actually two things:

  1. How does one access the different Post objects on which the user has some rights (with an elegant my_user.posts for instance)?
  2. How to identify the different users that have a specific role on a given object (through something like my_post.editors for instance)?

The next section will provide an answer to those questions.

Models Implementation

ACL9 wiki offers two ways to deal with the first question:

  1. Without using database views
  2. With database views

For a while, only the second solution was available. While this works well and is pretty straightforward to set up, it might be a pain later when one implements tests or wants to backup / restore the database. Indeed, by default views are not included in the DB schema.

The first option (i.e. without using database view) was introduced some time later by Enrique Vidal. However, while it sets the foundation right, the actual link between object and subject are not detailed in his solution. So let’s have a look at how to define these associations in the different models.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class User < ActiveRecord::Base
    acts_as_authorization_subject  :association_name => :roles, :join_table_name => :enrollments
    has_many :enrollments
    # Note: ACL9 adds automatically a `has_many :roles` associations.
    # Trying to use this association instead of defining a new `post_roles` association to access the object
    # (i.e. the post) might break other associations (if you have roles on other objects).
    has_many :post_roles, :through => :enrollments, :source => :role
    has_many :posts, :through => :post_roles, :source => :authorizable, :source_type => 'Post'
end

class Role < ActiveRecord::Base
    acts_as_authorization_role :join_table_name => :enrollments
    has_many :enrollments
    has_many :users, :through => :enrollments
end

class Enrollment < ActiveRecord::Base
    belongs_to :user
    belongs_to :role

    validates :user, :role, :presence => true
end

class Post < ActiveRecord::Base
    acts_as_authorization_object
    has_many :user_roles, :as => :authorizable, :class_name => 'Role'
    has_many :enrollments, :through => :user_roles
    has_many :users, :source => :user, :through => :enrollments
    has_many :admins, :source => :user, :through => :enrollments, :conditions => proc { "roles.name = 'admin'" }
    has_many :editors, :source => :user, :through => :enrollments, :conditions => proc { "roles.name = 'editor'" }
    has_many :reviewers, :source => :user, :through => :enrollments, :conditions => proc { "roles.name = 'reviewer'" }
    has_many :readers, :source => :user, :through => :enrollments, :conditions => proc { "roles.name = 'reader'" }
end

Once this is in place, you will be able to access all posts on which a user has a roles with a “usual” my_user.posts. On the other side, to get the list of users that are reviewers on a given post, a simple my_post.reviewers will suffice.

Alternative Options

While it used to be complicated to implement local roles / scoped roles in order to provide different kinds of access to a given resource, the situation has evolved lately and a few other alternatives are now available. If you do not feel like using ACL9, you can give Rolify a try.

While I haven’t tested it extensively, it should interact well with Cancan to define scoped roles on resources.


Credits Image

eee36 by krosseel


Related Articles

It seems that other articles are somewhat related to this one. Similar articles are identified through a set of keywords so this might not be 100% accurate, but I hope this can interest you.

Run Rails 2 Application on Shared Hosting

Published on December 7, 2013

RSS icon Subscribe to RSS feed
Feedly icon Subscribe in feedly
Somewhat related articles
Run Rails 2 Application on Shared Hosting

Published on December 7, 2013


For the time being, comments are managed by Disqus, a third-party library. I will eventually replace it with another solution, but the timeline is unclear. Considering the amount of data being loaded, if you would like to view comments or post a comment, click on the button below. For more information about why you see this button, take a look at the following article.

External Links

LinkedIn icon LinkedIn

Keybase icon Keybase

Bitbucket icon Bitbucket

GitHub icon GitHub

Website built upon

Nanoc

Mini.CSS


Website edited by Jimmy Bonney. All rights reserved.