Allowance
Allowance is a general-use permission management library for Ruby. It's decidedly simple, highly flexible, and has out-of-the-box support for ActiveRecord models.
Key features are:
- Works great in any Ruby project, not just Rails.
- It loves ActiveRecord scopes.
- Very little code (under 100 lines).
Allowance was inspired by Ryan Bates' fantastic Rails authorization plugin CanCan.
A simple example:
class User < ActiveRecord::Base
include Allowance::Subject
def define_permissions
# every user can see all other users
allow :read, User
# every user can see all published posts
allow :read, Post, Post.published
# users can edit/delete their own posts
allow :manage, Post, user_id: id
if admin?
# admins can edit/delete all posts
allow :manage, Post
end
end
end
Installation
Simply add allowance
to your project's Gemfile
or install it through gem install allowance
.
It should work fine with Ruby 1.9.2, 1.9.3 and respective JRuby versions. Please consult Allowance's Travis status page for details.
Usage
Defining permissions
Allowance generally thinks in terms of "subject", "verb" and, optionally, "object". For example, a user (the subject) may be allowed to edit (the verb) a post (the object). The object may be optional; for example, a user may be able to login, and so on.
So, first of all, your application will need a subject. A subject can be any class that's using the Allowance::Subject
mixin. In most applications, this will be your User
class:
class User
include Allowance::Subject
end
Instances of this class can now be configured with permissions using the #allow
method:
@user.allow :read, Post
@user.allow :manage, Post, user_id: @user.id
These permissions can be queried using the #allowed?
method:
if @user.allowed?(:destroy, @post)
@post.destroy
end
Since you'll usually want permissions to be defined automatically when a subject instance is created, you can simply add a #define_permissions
method that will get executed automatically:
class User
include Allowance::Subject
def define_permissions
allow :read, Post
allow :manage, Post, user_id: id
end
end
Using Allowance with Rails
Most of you will be using Allowance within a Rails application. Allowance gives you a lot of flexibility as to how to plug it into your app, but the most straight forward way goes as follows.
First of all, add the Allowance::Subject
mixin to your User
class and create a define_permissions
method:
class User < ActiveRecord::Base
include Allowance::Subject
def define_permissions
allow :read, Post
allow :manage, Post, user_id: id
end
end
Now, assuming your application has a current_user
controller and helper method, you can use current_user.allowed?
to query the currently logged in user's permissions. The code for this could look like this:
class ApplicationController < ActionController::Base
# Set up the current user. In this example, if a currently logged in
# user could not be found, we're creating (but not saving) a new
# instance representing a "guest" user.
#
def current_user
@current_user ||= load_current_user || User.new
end
# Make it available to your views, too.
#
helper_method :current_user
# Load the currently logged in user. This is just one of the many
# ways of doing it, so this may look different in your app.
#
def load_current_user
User.find(session[:current_user_id]) if session[:current_user_id]
end
end
All this is just a suggestion -- there are many ways of setting this up. If you're used to how CanCan does it, you could set up a separate, abstract permissions class using the Allowance::Subject
mixin, create an instance in a before_filter
and use that instead.
Working with ActiveRecord scopes
When defining permissions on ActiveRecord models, you can provide an optional scope as a third parameter, either as a hash of conditions, a lambda, or an actual ActiveRecord scope. Here's a couple of examples:
# Can read all posts
allow :read, Post
# Can only read posts I've created
allow :read, Post, user_id: self.id
# Can only read published posts
allow :read, Post, Post.published
# You can also provide strings...
allow :read, Post, "published_at IS NOT NULL"
# ...or lambdas:
allow :read, Post, ->(p) { p.where("published_at < ?", 2.weeks.ago) }
When checking permissions against a model instance, Allowance will check if it's part of the allowed scope:
# This will look up whatever scope is permitted for :read actions
# on Post and will check whether the provided instance is inside
# that scope (by running `scope.exists?(instance)`).
allowed?(:read, @post)
In addition to that, the subject provides a method called #allowed_scope
that returns the scope that is allowed for a certain verb and class:
@posts = current_user.allowed_scope(Post, :read)
Allowance also add a similar class method to your ActiveRecord classes, so the following will work, too:
@posts = Post.allowed(current_user, :read)
This becomes handy in Rails controllers:
class PostsController < ApplicationController
def index
@posts = Post.allowed(current_user, :index).all
respond_with @posts
end
def show
@post = Post.allowed(current_user, :show).find(params[:id])
respond_with @post
end
# ...and so on.
end
Verb expansion
Just like its big role model CanCan, Allowance automatically expands the create
, read
, update
and manage
verbs to include the common RESTful Rails controller action names:
-
create
is expanded intocreate
andnew
-
read
is expanded intoread
,index
andshow
-
update
is expanded intoupdate
andedit
-
manage
is expanded intomanage
,index
,show
,new
,create
,edit
,update
anddestroy
Contributing
I'm looking forward to seeing your Pull Requests. However, please be aware that, like with pretty much all other projects I'm maintaining, I'm trying to keep it as sharp and small as possible, so I'm going to be somewhat picky about which pull requests to accept. If you're adding new features, I recommed you check back with me first in order to avoid disappointment.
License
Copyright (c) 2012 Hendrik Mans hendrik@mans.de
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.