Fine grained access control for Ruby on Rails applications
Ruby Shell
Switch branches/tags
Nothing to show
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
lib
spec
.gitignore
.rspec
.rvmrc
Gemfile
LICENSE
README.md
Rakefile
acts_as_authoritah.gemspec
sample_default.csv

README.md

ActsAsAuthoritah

Authoritah was designed to provide fine grained access for Ruby on Rails applications. It allows you to specify ACLs (Access Control List) for a complex application with users having a variety of roles, and these roles changing dynamically depending on the part/pages in the application that they are on.

Authoritah abstracts out the rules for access control and moves it away from the code, so that its not spread across multiple files, making it difficult to understand or change.

Authoritah can work with any authentication or role system that your app uses.

ACLs can be specified as a matrix stored as a CSV file. Opening the CSV file in Excel or a CSV viewer makes the ACL readable/viewable instead of being embedded within code in multiple files within the application.

Installation

Add this to your Gemfile:

gem 'acts_as_authoritah'

Basic Usage

  • Add these lines to your User model (assuming that User is your model representing users in your app)
  include ActsAsAuthoritah::Core
  acts_as_authoritah Rails.root.join('config/access_rights/')
  • Add a 'usertype' method to the User model
  def usertype(args={})
    // self.role_name - may be as simple as this or
    // something more complex depending on your app.
    // Also see advanced usage section below.
  end

This method should return the role of the the user. This is Authoritah's interface with your role system.

  • Create config/access_rights/default.csv (path can be changed in the User model)

default.csv

Last 4 columns are roles. You should replace them with roles in your app. These should exactly be the values that the User#userype method should return. You can any add any number of columns as there are roles in your app.

Row 2 means that all actions in the ForumController are accessible only for site_admin and project_admin users.

Row 3 means that all actions in the CommentsController are accessible only for registered_user.

Row 4 means that all actions in the VotesController are accessible only for registered_user and anonymous_user.

Row 5 means that all actions in all controllers that are under the Admin:: scope are accessible only to site_admin users.

Row 6 means that save_response action of the SurveyController is accessible only by project_admin and register_user.

  • Add a before_filter in your ApplicationController (or any other place that suits your app).
   before_filter :access_control
   
   def access_control
     identifier = [controller.class.to_s, controller.action_name].join('#')
     
     unless current_user.can? identifier
       deny_access
     end
   end
   
   def deny_access
     // Render an access denied page with status 401
     // or redirect to login page
     // or something else as you wish
   end
   

Advanced Usage

If your app has dynamic roles ie. role of a particular user change when he navigates from one part of the app to another, Authoritah can handle this using context.

  • We pass a context parameter to the can?.
  def access_control
    identifier = [controller.class.to_s, controller.action_name].join('#')
     
    unless current_user.can? identifier, context: current_project.state
      deny_access
    end
  end
  • We add a config/access/context-name.csv with rules that apply when we are in this particular context. This ACL will be merged with default.csv dynamically during run time. If the same rules are present in default.csv and in context-name.csv, the one in context-name.csv will take precedence.

if the possible values of project.state are 'draft', 'published' and 'archived', then you will have to add draft.csv, published.csv and archived.csv

  • All arguments passed to can? method will be passed on to the User#usertype method. This would allow parameters to be passed from controller, that might help determining the usertype at runtime, if your app has such a logic.