Permit
A flexible controller authorization tool for Ruby on Rails.
Source: http://github.com/dnd/permit
Issues: http://github.com/dnd/permit/issues
Docs: http://yardoc.org/docs/dnd-permit
Author: Steve Valaitis
Copyright: 2010
License: MIT License
- Description
- Installation & Setup
- Usage
- Specs & Coverage
- Problems
How does it work?
Permit works by allowing you to define a series of allow or deny rules to
authorize a person. The rules that apply for the action of the current request
are then evaluated against the current person. Rule evaluation stops as soon as
a match is found. If a deny rule matches, #access_denied
will be called, and
the person will be prevented from accessing the action. If an allow rule
matches, the person will be directed to the action as normal. If no rules
match, the person will be denied access. This can be overridden either at the
global or controller level by setting the default_access
option to :allow
.
Keep in mind that deny rules are always run first.
There are three different types of authorizations that you can use with Permit. They are as follows:
Static Authorizations
These are the most basic forms of authorization and allow you to use one of these roles by itself to authorize someone.
-
:everyone
- Exactly what it says, an authorization that applies to everyone. -
:guest
- Indicates a guest to the application, and only matches ifcurrent_person#guest?
returnstrue
. -
:person
- Indicates an authorized person, and only matches ifcurrent_person#guest?
returnsfalse
.
Example:
allow :guest, :to => :index
Dynamic Authorization
When using the :person
role, you can additionally specify the :who
/:that
and
:of
/:on
options. This will cause the current person object to be sent as an
argument to the method indicated by :who
/:that
on the target resource
indicated by :of
/:on
. If the method call returns true
then the rule will be
a match.
If the symbol given to :who
/:that
is prefixed with is_
some special sugar will be
applied, causing Permit to try and use various methods on the resource. You can
see these methods in the documentation for
PermitRule#initialize
A dynamic authorization might look like this:
allow :person, :who => :is_owner, :of => :project, :to => :write
Named Authorizations
These are authorizations using custom roles that you define in the database and are mapped to a person in an authorizations table. A person may be granted a role for a given resource, more than one resource, or no resource at all(depending on what the role definition allows).
Some named authorizations might look like:
allow :admin, :of => :team, :to => :show
allow [:project_owner, :project_manager], :of => :project, :to => :all
How do I get it?
Installation
You can install Permit as a gem(make sure to add "config.gem 'permit'
" to your
config/environment.rb
file):
sudo gem install permit
or as a plugin:
script/plugin install git://github.com/dnd/permit.git
as a gem from source:
git clone git://github.com/dnd/permit.git
sudo rake install
Setup
Pre-requisites
You must have a Person
model, or some other model that represents an
authorized user of the system that responds to #guest?
. Permit will not create
this model for you. You can get as fancy as you want with the guest?
method,
but a simple example would be:
def guest?
new_record?
end
Generation
If you are not going to use named authorizations, run:
script/generate permit [Person] --init-only
If you are going to use named authorizations you can run:
script/generate permit [Person [Authorization [Role]]]
You are not required to pass in any arguments to the generator. The arguments
above are optional, and reflect the default names that Permit uses. These only
need to be specified if you want to use different class name(s). So if you
wanted to use an existing Employee
class for authorization instead of the
default Person
, run:
script/generate permit Employee
For full details on the generator take a look at the --help
Run the migration for the roles and authorizations:
rake db:migrate
Controller
Include Permit in your ApplicationController
:
include Permit::ControllerExtensions
Create a method that returns the current authorization subject. This will by
default be inferred from the class name given to Permit for initialization, and
takes the form of current_*
. So if the class was Person
, Permit would look
for current_person
. For User
it would be current_user
, etc... If the
method you want to use doesn't follow this convention it can be overridden in
the initializer.
Permit::Config.controller_subject_method = :logged_user
How do I use it?
Controller
After you have "included" Permit into your controller, it is still not active.
For that you must define a block of rules by calling
permit
in your controller. If you want to by default protect all of your controllers,
you can just make an empty permit call in your base controller class(such as
ApplicationController
).
Something to keep in mind is that when permit
is called, a before filter is
set to check the authorizations. Any setup that you need to do for setting
the current subject, or the resource to be used in :of
/:on
criteria needs to
be done through before filters set prior to this call.
The rules defined in permit
blocks are not additive. When a new permit
call
is made, it wipes out any previously set rules. It also resets the before filter
position for checking the authorizations thus allowing you to add any other
before filters you may need in your implementing controller.
You can create "allow" and "deny" rules by passing at minimum, a role, and one
or more actions that the rule applies to. The actions will be expanded using the
aliases defined in
Permit::Config.action_aliases,
and are expanded in a non-recursive fashion. You can optionally pass :all
for
the action, which will cause the rule to be tested for all actions. "allow"
rules accept the :to
key for actions, and "deny" rules accept the :from
key.
For the full documentation and description of options you can use for creating rules see the documentation for PermitRule#initialize
permit do
deny :person, :from => [:write, :destroy], :if => Proc.new {|person, context| person.status == :on_leave}
allow :person, :who => :has_commented?, :on => :article, :to => :show
allow :person, :who => :is_author, :of => :article, :to => [:read, :write]
allow :admin, :to => :all
end
Deny a person from new, create, edit, update, delete, and destroy if they are on leave.
Allow a person who has commented on the article to show.
@article.has_commented?(current_person)
Allow person who is the author of the article to index, show, new, create, edit, and update.
@article.author == current_person
Allow a person that has the admin role for no resource to access any action. `current_person.authorized?(:admin, nil)
Helpers
The following helpers are included for use in your views, or for one off operations in your controllers.
-
allowed?
- Returnstrue
if the person matches the rule criteria -
denied?
- Returnstrue
if the person does not match the rule criteria -
authorized?
- Callscurrent_person.authorized?
See Permit::ControllerExtensions::InstanceMethods for the full documentation on these methods.
Models
This aspect of Permit only applies if you are using named authorizations.
Named authorizations are setup based on the call to
Permit::Config.set_core_models
in your initializer. This call sets up your
authorization, person, and role models by calling permit_authorization
,
permit_person
, and permit_role
on them respectively.
The extensions for authorization, and role setup some basic validations to ensure the integrity of the models.
Resources
To setup a model to be used as a resource for authorization, call
permit_authorizable
inside of it.
Associations
The person, role, and resource models are setup with a has_many :authorizations
association. This association is extended with a few methods that are documented
in AssociationExtensions
Person
The person model is extended with a few methods to simplify authorizing, and revoking roles, as well as checking if the person is authorized on a given set of roles for a resource. These methods are documented in PersonInstanceMethods.
Specs & Coverage
Permit currently has fairly high test coverage(>95%). To run the specs for Permit, the plugin will most likely need to be inside of an existing Rails application.
Problems?
Please use the GitHub issue tracker for any bugs, problems, or unexpected behavior you run across while using Permit.