<?xml version="1.0" encoding="UTF-8"?>
<article>
  <body>&lt;p&gt;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 &lt;a href=&quot;http://github.com/asquared/authenticates_access&quot;&gt;github&lt;/a&gt; and check it out.&lt;/p&gt;
&lt;h3&gt;Accessors&lt;/h3&gt;
&lt;p&gt;These are models which act as security &amp;ldquo;principals&amp;rdquo; - 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.&lt;/p&gt;
&lt;h3&gt;Installing&lt;/h3&gt;
&lt;pre&gt;ruby script/plugin install git://github.com/asquared/authenticates_access.git&lt;/pre&gt;
&lt;h3&gt;Controller Setup&lt;/h3&gt;
&lt;p&gt;In your ApplicationController, load up an accessor before actions take place:&lt;/p&gt;
&lt;pre&gt;class ApplicationController &amp;lt; ActionController::Base   
  ...   
  before_filter :set_up_accessor   
  ...   
  protected   
  def set_up_accessor     
    ActiveRecord::Base.accessor = yourAccessor
  end 
end
&lt;/pre&gt;
&lt;h3&gt;Model Setup&lt;/h3&gt;
&lt;p&gt;In your models, set up some access rules using class methods. The rules of precedence are as follows.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Rules of the same type are evaluated in the order they appear in the model.&lt;/li&gt;
&lt;li&gt;If any rule in a list passes, access is allowed. All rules must fail for access to be denied.&lt;/li&gt;
&lt;li&gt;If no rules of a given type are defined, access is always allowed.&lt;/li&gt;
&lt;li&gt;Writes to attributes are automatically denied if saves are being denied for the current accessor.&lt;/li&gt;
&lt;li&gt;Be aware, there is some magic for the predefined allow_owner test for a new record. See below.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;tt&gt;authenticates_access&lt;/tt&gt; - defines some magical instance methods for views and controllers to determine what is currently allowed. These include &lt;tt&gt;allowed_to_save&lt;/tt&gt;, &lt;tt&gt;allowed_to_destroy&lt;/tt&gt;, and &lt;tt&gt;allowed_to_write(attr)&lt;/tt&gt;. This is automatically done by any of the other methods so it is unlikely you need to do this explicitly.&lt;/p&gt;
&lt;p&gt;&lt;tt&gt;authenticates_saves :with_accessor_method =&amp;gt; :is_admin&lt;/tt&gt; - allows the record to be saved (or destroyed) if the current accessor is an admin.&lt;/p&gt;
&lt;p&gt;&lt;tt&gt;has_owner :user&lt;/tt&gt; - following &lt;tt&gt;belongs_to :user&lt;/tt&gt;, declares that the user is an owner of the object. Creates instance methods &lt;tt&gt;owner&lt;/tt&gt;, &lt;tt&gt;owner_id&lt;/tt&gt;, &lt;tt&gt;owner_id=&lt;/tt&gt;, &lt;tt&gt;allow_owner&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;&lt;tt&gt;authenticates_saves :with =&amp;gt; :allow_owner&lt;/tt&gt; - allows the record to be saved by its owner. This will also allow new records to be saved anonymously. Note that &lt;tt&gt;allow_owner&lt;/tt&gt; is an instance method that will be added by &lt;tt&gt;has_owner&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;&lt;tt&gt;authenticates_creation :with_accessor_method =&amp;gt; :is_registered&lt;/tt&gt; - restricts creation to only valid accessors for which &lt;tt&gt;is_registered&lt;/tt&gt; returns true. Adds class method &lt;tt&gt;allowed_to_create&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;&lt;tt&gt;autosets_owner_on_create&lt;/tt&gt; - 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!&lt;/p&gt;
&lt;p&gt;&lt;tt&gt;authenticates_writes_to :something, :with_accessor_method =&amp;gt; :is_admin&lt;/tt&gt; - allows writes to the &lt;tt&gt;something&lt;/tt&gt; attribute only if the accessor's &lt;tt&gt;is_admin&lt;/tt&gt; method returns true.&lt;/p&gt;
&lt;p&gt;Here's an example model from the &lt;a href=&quot;http://electronics.union.rpi.edu/&quot;&gt;RPI Electronics Club&lt;/a&gt; website (which is still under development:&lt;/p&gt;
&lt;pre&gt;class Comment &amp;lt; 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 =&amp;gt; :allow_owner  
  authenticates_saves :with_accessor_method =&amp;gt; :is_admin 
  authenticates_saves :with_accessor_method =&amp;gt; :is_officer  
  authenticates_writes_to :member_id, :with_accessor_method =&amp;gt; :is_admin  
  sanitizes_html :body, :allowed_tags =&amp;gt; [ 'p', 'em', 'strong', 'span '], :safe_attributes =&amp;gt; { 'span' =&amp;gt; [ 'style' ] }   
  validates_presence_of :subject 
  validates_presence_of :body    
  ...
end&lt;/pre&gt;
&lt;h3&gt;Views&lt;/h3&gt;
&lt;p&gt;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:&lt;/p&gt;
&lt;pre&gt;&amp;lt;% if OpenLab.allowed_to_create %&amp;gt; 
&amp;lt;%= link_to 'Add lab hours', new_open_lab_path %&amp;gt; 
&amp;lt;% end %&amp;gt; 

&amp;lt;td&amp;gt;
  &amp;lt;% if open_lab.allowed_to_save %&amp;gt;
    &amp;lt;%= link_to 'Edit', edit_open_lab_path(open_lab) %&amp;gt;
  &amp;lt;% end %&amp;gt; 
&amp;lt;/td&amp;gt; 
&amp;lt;td&amp;gt;
  &amp;lt;% if open_lab.allowed_to_destroy %&amp;gt;
    &amp;lt;%= link_to 'Remove', open_lab, :confirm =&amp;gt; 'Are you sure?', :method =&amp;gt; :delete %&amp;gt;
  &amp;lt;% end %&amp;gt; 
&amp;lt;/td&amp;gt;   

&amp;lt;% if @open_lab.allowed_to_write(:member_id) %&amp;gt; 
  &amp;lt;p&amp;gt;    
    &amp;lt;%= f.label :member_id %&amp;gt;&amp;lt;br /&amp;gt;
    &amp;lt;%= f.collection_select(:member_id, Member.find(:all), :id, :name) %&amp;gt;
  &amp;lt;/p&amp;gt; 
&amp;lt;% end %&amp;gt; 
&lt;/pre&gt;
&lt;h3&gt;Error Handling&lt;/h3&gt;
&lt;p&gt;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 &lt;tt&gt;allowed_to_&lt;/tt&gt; methods, it shouldn't be an issue. This should be addressed in an upcoming release.&lt;/p&gt;</body>
  <category-id type="integer">3</category-id>
  <created-at type="datetime">2009-06-23T18:16:58-04:00</created-at>
  <id type="integer">15</id>
  <title>authenticates_access</title>
  <updated-at type="datetime">2009-06-23T19:29:07-04:00</updated-at>
</article>
