A Ruby on Rails plugin that provides fine grained access control through the MVC stack to RESTful resources in a Ruby on Rails 2.0+ application. Authorization is as simple as true or false.
This is a fork of mdarby’s version
Differences From mdarby’s version
Handling Resources Accessible As Nested And As Unnested
It handles cases where a logical parent id may not exist in the URL for a resource that is sometimes accessed via a nested route, and sometimes not.
Suppose you provide a nested and an unnested way to access a resource:
map.resources :providers do |provider| provider.resources :locations, :name_prefix => "provider_" end map.resources :locations
In this case, Location has Provider as its logical parent. However, both of these URLs are acceptable for accessing locations:
/locations (all locations) AND /providers/1/locations (locations scoped down to a particular provider).
Formerly, restful_acl would break in this instance, now, it does not – if a logical parent cannot be determined from the URL, it returns nil as the parent. You will need to check for parent == nil in your authentication methods, in your models.
Added Ability to Specify Class in View Helpers
In restful_acl, you can check for indexable and createable in your views, eg.:
= link_to "Users", if indexable
If you are in a view that corresponds with the users controller, this works fine. However, what if you wish to display a link to users somewhere else?
This change allows you to optionally specify the class you are concerned with:
= link_to "Users", if indexable(User)
What it does
RESTful_ACL is a simple Access Control Layer for Ruby on Rails. It restricts access on a fine-grained level to any RESTful MVC stack. Every application is different and everyone likes to setup their User / Account / Role resources differently; this plugin will allow you to do your thing and keep that thing locked down.
RESTful_ACL requires the super amazing RESTful_Authentication plugin.
How to Install
Install the RESTful_ACL gem:
sudo gem install adriand-restful_acl -s http://gems.github.com
Add the gem to your environment.rb file as thus:
config.gem “adriand-restful_acl”, :lib => ‘restful_acl_controller’
RESTful_ACL requires two named routes: “error” and “denied”. Add the following to your routes.rb file:
map.error ‘/error’, :controller => ‘some_controller’, :action => ‘error_action’
map.denied ‘/denied’, :controller => ‘some_controller’, :action => ‘denied_action’
How to Use
before_filter :has_permission? into any controller that you’d like to restrict access to (or application_controller.rb for your entire app).
Define a parent resource (if one exists) by using the
logical_parent method, and define the following five methods in the model of every resource you’d like to restrict access to. The five methods can contain anything you’d like so long as they return a boolean true or false. This allows you to define your User’s roles any way you wish.
class Issue < ActiveRecord::Base logical_parent :some_model_name # This method checks permissions for the :index action def self.is_indexable_by(user, parent = nil) end # This method checks permissions for the :create and :new action def self.is_creatable_by(user, parent = nil) end # This method checks permissions for the :show action def is_readable_by(user, parent = nil) end # This method checks permissions for the :update and :edit action def is_updatable_by(user, parent = nil) end # This method checks permissions for the :destroy action def is_deletable_by(user, parent = nil) end end
There are five view helpers also included in RESTful_ACL:
#deletable. These enable you to do nifty things like:
<%= link_to ‘Foo Index’, foos_path if indexable >
<= link_to ‘Edit Foo’, edit_foo_path(
foo) if updatable(foo) >
<= link_to ‘Create Foo’, new_foo_path if creatable >
<= link_to ‘View Foo’, foo_path(
foo) if readable(foo) >
<= link_to ‘Delete Foo’, foo_path(
foo) if deletable(foo), :method => :destroy %>
Huh? Here’s an example
Let’s say that you have two resources: Project and Issue. A Project has many Issues, an Issue belongs to a Project. I’d like to make sure that the current user is a member of the Project before they can create a new Issue in that Project:
class Issue < ActiveRecord::Base logical_parent :project belongs_to :author belongs_to :project def self.is_indexable_by(user, parent = nil) user.projects.include?(parent) end def self.is_creatable_by(user, parent = nil) user.projects.include?(parent) end def is_updatable_by(user, parent = nil) user == author && parent.is_active? end def is_deletable_by(user, parent = nil) user == author end def is_readable_by(user, parent = nil) user.projects.include?(parent) end end
RESTful_ACL grants global access to all actions to site administrators. To enable this, make sure that your User model defines an
is_admin? method and/or an
is_admin attribute. If the
current_user.is_admin? returns true, access will be granted automatically.
How to Test
I normally do something along these lines in RSpec:
describe “Issue” do
project = mock_model(Project) @author = mock_model(User, :projects => [project]) @issue = Issue.factory_girl(:issue, :author => @author, :project => @project) end it “should be modifiable by the author when the Project is active” do @project.stub!(:is_active? => true)
issue.is_updatable_by(author, @project).should be_true end it “should be deletable by the author” do
issue.is_deletable_by(author, @project).should be_true end it “should be readable by those assigned to the Project” do Issue.is_readable_by(@author, @project).should be_true end it “should be creatable by those assigned to the Project” do Issue.is_creatable_by(@author, @project).should be_true end end
RESTful_ACL doesn’t work with nested singleton resources. Wha? Yeah. Those are things in routes.rb like:
# Note the singular forms in 'user.resource :profile' map.resources :users do |user| user.resource :profile end
In these situations I normally skip permission checking altogether as a Profile will always be mapped to the currently logged in User, regardless of the
params[:user_id] passed in. You don’t trust those either right? Good.
Add a ticket to RESTful_ACL’s Lighthouse Account