Clean, simple and flexible model based authorization designed to work with AuthLogic and InheritedResources.¶ ↑
If these docs looks all wanky, then be sure you’re viewing them on the rdoc.info site: rdoc.info/projects/tsaleh/master-may-i¶ ↑
{MasterMayI Master May I} is not an Access Control List system. In fact, it has absolutely no opinions about how you structure your underlying business rules. All it does is gives you a consistent and flexible pattern for accessing those rules.
There are two main components:
{MasterMayI Master May I} adds query methods to all your models, which can be used to determine who’s allowed to do what. {MasterMayI::ActiveRecordExtensions Learn more.}
-
{MasterMayI::ActiveRecordExtensions::ClassMethods#creatable_by? +Model.creatable_by?(user)+} and {MasterMayI::ActiveRecordExtensions::ClassMethods#creatable?
Model.creatable?
} -
{MasterMayI::ActiveRecordExtensions::InstanceMethods#readable_by? +@model.readable_by?(user)+} and {MasterMayI::ActiveRecordExtensions::InstanceMethods#readable? +@model.readable?+}
-
{MasterMayI::ActiveRecordExtensions::InstanceMethods#editable_by? +@model.editable_by?(user)+} and {MasterMayI::ActiveRecordExtensions::InstanceMethods#editable? +@model.editable?+}
-
{MasterMayI::ActiveRecordExtensions::InstanceMethods#destroyable_by? +@model.destroyable_by?(user)+} and {MasterMayI::ActiveRecordExtensions::InstanceMethods#destroyable? +@model.destroyable?+}
These are just methods (that default to always returning true), so customizing the rules is as easy as overriding the methods:
class Note < ActiveRecord::Base def self.creatable_by?(user) user and user.administrator? end end
{MasterMayI Master May I} also adds the {MasterMayI::ActiveRecordExtensions::ClassMethods#listable_by +Model.listable_by(user)+} and {MasterMayI::ActiveRecordExtensions::ClassMethods#listable Model.listable
} methods, which return a named scope limiting the returned records to those viewable by the given user. Again, by default, it returns all records, and this should be redefined by each individual model.
If you call {MasterMayI::ActiveRecordExtensions::ClassMethods#records_creating_user records_creating_user
} in your model, then the user from the Authlogic UserSession is automatically stored whenever a record is created. {MasterMayI::ActiveRecordExtensions::ClassMethods#records_creating_user Learn more.}
class Note < ActiveRecord::Base records_creating_user def self.creatable_by?(user) user and created_by?(user) end end
If you include {MasterMayI::ControllerExtensions} in your ApplicationController, MasterMayI adds a few utility methods to your ApplicationController:
-
{MasterMayI::ControllerExtensions#current_user
current_user
} -
{MasterMayI::ControllerExtensions#logged_in?
logged_in?
} -
{MasterMayI::ControllerExtensions#require_user
require_user
} -
{MasterMayI::ControllerExtensions#deny_access
deny_access
} -
{MasterMayI::ControllerExtensions#store_location
store_location
} -
{MasterMayI::ControllerExtensions#redirect_back_or +redirect_back_or url+}
{MasterMayI Master May I} will also include the {MasterMayI::ControllerExtensions::ClassMethods#protects_restful_actions protects_restful_actions
} controller class method, which integrates with InheritedResources in order to automatically check with the model in a set of before filters. In addition, it will override the InheritedResources#collection method to filter by the listable scope. {MasterMayI::ControllerExtensions::ClassMethods#protects_restful_actions Learn more.}
class ApplicationController < ActionController::Base include MasterMayI::ControllerExtensions ... end class NotesController < InheritedResources::Base protects_restful_actions end
{MasterMayI::ControllerExtensions Learn more.}
{MasterMayI Master May I} comes with a strong set of Shoulda macros:
…for your {ActiveSupport::TestCase unit tests}:
class NoteTest < ActiveSupport::TestCase setup :activate_authlogic should_record_creating_user :as => "user" should_be_creatable_by("boy named sue-create") { Factory(:user, :username => "sue-create") } should_not_be_creatable_by("everyone") { nil } context "a note" do setup { @note = Factory(:note) } subject { @note } should_be_readable_by("boy named sue-read") { Factory(:user, :username => "sue-read") } should_be_editable_by("boy named sue-edit") { Factory(:user, :username => "sue-edit") } should_be_destroyable_by("boy named sue-destroy") { Factory(:user, :username => "sue-destroy") } should_not_be_readable_by( "everyone") { nil } should_not_be_editable_by( "everyone") { nil } should_not_be_destroyable_by("everyone") { nil } end end
…and your {ActionController::TestCase functional tests}:
class NotesControllerTest < ActionController::TestCase setup :activate_authlogic as_a_logged_in_user do who_can_manage :notes do context "on GET to /notes/new" do setup { get :new } should_not_set_the_flash should_render_template :new end ...
{MasterMayI Master May I} is hosted on gemcutter, so installation is as easy as:
sudo gem install master_may_i
It relies heavily on Authlogic, and the controller module uses the resource
and resource_class
methods from InheritedResources. Finally, the before filters expect there to be the a url helper named login_url:
map.login "/login", :controller => :user_session, :action => :new
Copyright © 2009 Tammer Saleh. See LICENSE for details.