Skip to content
Implements a hierarchical role based authorization system on instances of ActiveRecord models.
Pull request Compare This branch is even with mleventi:master.
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.
lib
tasks
test
MIT-LICENSE
README.rdoc
Rakefile
init.rb
install.rb
uninstall.rb

README.rdoc

ActsAsAuthorizable

Acts As Authorizable is a part solution for providing pseudo-hierarchical role based authorizations on ActiveRecord model instances using your existing database table relationships. It is designed to eliminate the need for deep nesting controllers due to authorization. That being said, it is only part of a total authorization mechanism. Essentially it takes an authorized? call and passes it to the appropriate Role.allows? methods. You still have to define filters in controllers that call ActiveRecord models authorized? method and write the allows? method in your role class.

Deep Nesting & Authorization

One reason why developers deeply nest controllers is to allow hierarchical permission. Hence you see the following…

example.com/forums/forum_id/threads/thread_id/posts/post_id/edit

With deep nesting you can check a current user's permissions against the post,thread, and forum. So a moderator for a specific form instance would be able to modify a post using the permissions present on the forum instance. Yet this is no longer possible with…

example.com/posts/post_id/edit

This is due to the fact that we don't have explicit information about where the post falls in an authorization hierarchy. This is where ActsAsAuthorizable provides an improvement. With the plugin you can specify where to go to get further authorization information on a post. So the forum moderator could access the page, even though he doesn't have direct permissions on the post.

Installation

script/plugin install git://github.com/mleventi/acts_as_authorizable.git

Your going to need ActiveRecord 2.1 or greater.

Usage

Acts As Authorizable works within your models by piggybacking on your has_many, has_one, and belongs_to associations to create a graph of model instances that represents authorization inheritance routes. For each ActiveRecord class you define which associations could yield permissions on the current class.

Prerequisites

You need a model which most likely would be a Role. It has to have an instance method “allows?” that takes in a permission object and returns a boolean. Two examples below are fine…

#Ex1
class Role < ActiveRecord::Base
  def allows?(permission)
    #do something and return true or false
  end
end

#Ex2
class Moo < ActiveRecord::Base
  def allows?(permission)
    #do something and return true or false
  end
end

The permission parameter is a black box. It can be anything because ActsAsAuthorizable doesn't use it, just passes it around. You also need a ActiveRecord model that represents a user.

Example

#ForumMembership, holds how the user is related to a forum
class ForumMemberships < ActiveRecord::Base
  acts_as_authorizable

  belongs_to :user
  belongs_to :forum
  belongs_to :role

  named_scope :with_user, lambda { |user| {:conditions => {:user_id => user}}}

  auth_belongs_to_user :user, :role_association => :role
end 

#Forum
class Forum < ActiveRecord::Base
  acts_as_authorizable

  has_many :forum_memberships
  has_many :threads

  auth_has_many_parents :forum_memberships, :user_scope => :with_user
end

#Thread
class Forum < ActiveRecord::Base
  acts_as_authorizable

  belongs_to :forum
  has_many :posts

  auth_belongs_to_parent :forum
end

#Post
class Post < ActiveRecord::Base
  acts_as_authorizable

  belongs_to :forum
  belongs_to :user

  auth_belongs_to_user :user, :role => 'Post Owner'
  auth_belongs_to_parent :thread
end

So the following models represent an authorization hierarchy. The ForumMembership model should allow authorizations for the particular Forum, Thread, & Post. Also the user association on a post, should allow some authorizations for that particular post.

Each of the four classes gets an injected authorized? instance method.

model_instance.authorized?(user_instance,permission)

Now if you want to find out if a user has a permission on a certain post you simply call the post's authorized? method. This call uses the auth_* methods to load in additional models until the permission is found or we have looked through the associations.

Note: models are searched in depth first order, with the order within a model determined lexically by how auth_* are called. Hence you almost always want to have auth_belongs_to_user calls first. Then scoped auth_has_many_parents, auth_belongs_to_parent, and finallly not-scoped, auth_belongs_to_parent.

Class Methods

acts_as_authorized

This is included in ActiveRecord models that are part of the authorization graph. If you use any of the other class methods this needs to be included in your class definition. It takes two optional parameters

  • :role_class_name => A string representing the Role class name. Defaults to 'Role'

  • :role_locate_method => A string representing a method on the Role class that is used to find roles. Defaults to 'find_by_name'.

Example:

acts_as_authorized :role_class_name => 'Role', :role_locate_method => 'find_by_name'

auth_belongs_to_user

This method takes several options…

  • association REQUIRED, What association to use to fetch the user instance, defaults to :user

  • :role_association => What association defines the role for the corresponding user instance

  • :role => What the user instance should be treated as assuming the :role_association option is not defined. Passed as a parameter to the role_locate_method.

Example:

#Association
belongs_to :user
#Auth Piggyback using a set role
auth_belongs_to_user, :user, :role => 'Grand Poo Bah'

Or:

#Association
belongs_to :user
belongs_to :role
#Auth Piggyback using an associated role
auth_belongs_to_user, :user, :role_association => :role

auth_belongs_to_parent & auth_has_one_parent

  • association REQUIRED, What association to follow.

Example:

#Association
belongs_to :forum
#Auth Piggyback
auth_belongs_to_parent :forum

auth_has_many_parents

  • association REQUIRED, What association to follow.

  • :user_scope => A symbol representing a named_scope that takes in a user object to condition the association. It is recommended that you use this for performance reasons.

Example:

#In Membership class
named_scope :with_user, lambda { |user| {:conditions => {:user_id => user}}}
#Association
has_many :memberships
#Auth Piggyback
auth_has_many_parents :memberships, :user_scope => :with_user

Instance Methods

Into every acts_as_authorizable model one instance method is injected called authorized?(user_instance,permission)

Advanced

You can have circular authorization dependencies. They will not result in infinite loops because the plugin colors the authorization graph while it searches for new permissions. Performance of the plugin is highly dependent on how you use it. Putting the obvious belongs_to_user permissions lexically first will result in better performance. Minimizing the number of auth_parents is also a good way of ensuring higher performance. The queries that result from the authorization plugins work are based on the associations passed. Hence ActiveRecords caching will work just fine as well.

Copyright © 2008 Matthew Leventi, released under the MIT license

Something went wrong with that request. Please try again.