authenticates_access

authenticates_access is a Rails plugin for doing model-based access control. It should really be called authorizes_access but the name seems to have stuck. Head on over to github and check it out.

Accessors

These are models which act as security “principals” - they request access to an object, and the object has rules and methods to determine whether the accessor should be allowed a certain type of access. Examples of possible accessors include users, groups, or API keys. They needn't be derived from ActiveRecord and could be a custom class.

Installing

ruby script/plugin install git://github.com/asquared/authenticates_access.git

Controller Setup

In your ApplicationController, load up an accessor before actions take place:

class ApplicationController < ActionController::Base   
  ...   
  before_filter :set_up_accessor   
  ...   
  protected   
  def set_up_accessor     
    ActiveRecord::Base.accessor = yourAccessor
  end 
end

Model Setup

In your models, set up some access rules using class methods. The rules of precedence are as follows.

  1. Rules of the same type are evaluated in the order they appear in the model.
  2. If any rule in a list passes, access is allowed. All rules must fail for access to be denied.
  3. If no rules of a given type are defined, access is always allowed.
  4. Writes to attributes are automatically denied if saves are being denied for the current accessor.
  5. Be aware, there is some magic for the predefined allow_owner test for a new record. See below.

authenticates_access - defines some magical instance methods for views and controllers to determine what is currently allowed. These include allowed_to_save, allowed_to_destroy, and allowed_to_write(attr). This is automatically done by any of the other methods so it is unlikely you need to do this explicitly.

authenticates_saves :with_accessor_method => :is_admin - allows the record to be saved (or destroyed) if the current accessor is an admin.

has_owner :user - following belongs_to :user, declares that the user is an owner of the object. Creates instance methods owner, owner_id, owner_id=, allow_owner.

authenticates_saves :with => :allow_owner - allows the record to be saved by its owner. This will also allow new records to be saved anonymously. Note that allow_owner is an instance method that will be added by has_owner.

authenticates_creation :with_accessor_method => :is_registered - restricts creation to only valid accessors for which is_registered returns true. Adds class method allowed_to_create.

autosets_owner_on_create - sets the owner ID to the accessor's ID, if the accessor is present. Be careful that the accessor is always of the same type as the owner!

authenticates_writes_to :something, :with_accessor_method => :is_admin - allows writes to the something attribute only if the accessor's is_admin method returns true.

Here's an example model from the RPI Electronics Club website (which is still under development:

class Comment < ActiveRecord::Base   
  belongs_to :member   
  belongs_to :meeting   
  belongs_to :photo   
  has_owner :member  
  autosets_owner_on_create  
  # this will allow a newly created object to be saved anonymously, since we 
  # don't authenticate_creation. But updates will be disallowed. 
  authenticates_saves :with => :allow_owner  
  authenticates_saves :with_accessor_method => :is_admin 
  authenticates_saves :with_accessor_method => :is_officer  
  authenticates_writes_to :member_id, :with_accessor_method => :is_admin  
  sanitizes_html :body, :allowed_tags => [ 'p', 'em', 'strong', 'span '], :safe_attributes => { 'span' => [ 'style' ] }   
  validates_presence_of :subject 
  validates_presence_of :body    
  ...
end

Views

The instance and class methods defined by the different tests can be used to hide otherwise-unusable links in views. Here are a few examples from the electronics club website:

<% if OpenLab.allowed_to_create %> 
<%= link_to 'Add lab hours', new_open_lab_path %> 
<% end %> 

<td>
  <% if open_lab.allowed_to_save %>
    <%= link_to 'Edit', edit_open_lab_path(open_lab) %>
  <% end %> 
</td> 
<td>
  <% if open_lab.allowed_to_destroy %>
    <%= link_to 'Remove', open_lab, :confirm => 'Are you sure?', :method => :delete %>
  <% end %> 
</td>   

<% if @open_lab.allowed_to_write(:member_id) %> 
  <p>    
    <%= f.label :member_id %><br />
    <%= f.collection_select(:member_id, Member.find(:all), :id, :name) %>
  </p> 
<% end %> 

Error Handling

Saves will fail if the access tests don't pass, but currently, there's no message added to the record if it does. If you protect your links and form fields with the allowed_to_ methods, it shouldn't be an issue. This should be addressed in an upcoming release.

Post Comment

(something to call you since you're not logged in)



Copyright © 2009 Andrew Armenia. All rights reserved.