Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: 59fa1dc30c
Fetching contributors…

Cannot retrieve contributors at this time

253 lines (177 sloc) 7.537 kb

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 ActiveModel-compliant classes.

It was inspired by Ryan Bates' fantastic Rails authorization plugin cancan and, unlike most other gems of its kind, is not bound to a specific framework.

A simple Example:

p = Allowance.define do |can|
  # Allow logging in
  can.login!

  # Allow creating new Article instances
  can.create! Article

  # Allow the user to edit Article instances that belong to him
  can.edit! Article, :author_id => current_user.id

  # Allow viewing all Article instances that are published or the user's
  can.read! Article, ['published = ? OR author_id = ?', true, current_user.id]
end

Allowance parses these permission definitions and stores them in the object the Allowance.define call returns. It is now up to you to query that object where necessary. Some examples:

p.login?            # true
p.create? Article   # true
p.read? @article    # true or false, depending on state of @article

You can use the same object to provide you with correctly scoped models, too:

p.scoped_model(:view, Article).all
# -> Article.where(['published = ? OR author_id = ?', true, current_user.id]).all

Installation

Requirements

Allowance should work fine with Ruby 1.8.7, 1.9.2, 1.9.3 and respective JRuby versions. Please consult Allowance's Travis status page for details.

Build Status

Installing through Bundler

Well, you've done this before, haven't you? Just add the allowance gem to your project's Gemfile:

gem 'allowance'

Installing without Bundler

Install using RubyGems:

gem install allowance

Then require it in your code:

require 'rubygems'
require 'allowance'

Usage

Defining permissions

Use Allowance.define to create a new permissions object, then use its allow! or can! methods to add permissions:

p = Allowance.define
p.allow! :sing

Instead of using allow! or can!, you can just name the permission directly:

p.sing!

You can also specify permissions as a block:

p = Allowance.define do |allow|
  allow.sing!
end

Querying permissions

Similar to how you define permissions, you can use the allowed? or can? methods, or query permissions directly by name. The following two lines are equivalent:

p.allowed? :sing
p.can? :sing
p.sing?

One-dimensional permissions

Allowance lets you define simple, one-dimenstional permissions like this:

p = Allowance.define do |allow|
  allow.sing!
  allow.play!
  allow.dance! if current_user.can_dance?
end

Two-dimensional permissions

Most of the time, you will be using two-dimensional permissions, consisting of a verb and an object, with the object typically being some kind of model class. For example:

p = Allowance.define do |allow|
  allow.view! Article

  if current_user.is_admin?
    allow.edit! Article
  end
end

When querying for permissions, just pass an object as an additional parameter:

p.edit? Article

When you pass a class instance (instead of a class), Allowance will check for the permission defined for its class, so the following will work, too:

p.edit? @article

Allowance will even allow you to define permissions in specific objects -- this is not recommended, though, since permission objects defined through Allowance only exist in memory; if you need to control permissions on individual model objects, you'll be better off with another authorization library, ideally one that stores its permissions in your datastore.

Defining model scopes

For classes implementing ActiveModel (eg. ActiveRecord, Mongoid and others), Allowance allows you to restrict certain permissions to specific scopes. For example, if, in a web application, a user should only be able to see articles that have been published, but admin users can see everything, you can define the permissions like this:

p = Allowance.define do |allow|
  allow.view! Article, :published => true

  if current_user.is_admin?
    allow.view! Article
  end
end

You can see here that when a specific permission (for a verb and object combination) is defined more than once, the last definition will overwrite all those that came before it.

In the example above, we defined the scope as a so-called "where conditions hash", ie. a hash passed to the model class' .where method. Since the hash notation assumes a logical AND and isn't all that flexible overall, you can also use the same string/array syntax you know from ActiveModel:

p.view! Article, ['published = ? OR user_id = ?', true, current_user.id]

Lastly, you're encouraged to re-use scopes defined on the model class. Just define your scope using a lambda:

p.view! Article, lambda { |r| r.viewable_by(current_user) }

Not only can use check permissions against those scopes, but you can also use the #scoped_model method to retreive a correctly scoped model according to its previous scope definition. For example:

@articles = p.scoped_model(:view, Article).order('created_at DESC').all

Defining contextual permissions

Since permissions are just Ruby code, you can use all your favorite language constructs when defining permissions. For example, in a web application providing a current_user method, you can do the following:

permissions = Allowance.define do |allow|
  allow.read! Article

  if current_user.is_admin?
    allow.destroy! Article
  end
end

You can then query this permission like you'd expect:

@article = Article.find(params[:id])
if permissions.destroy? @article
  @article.destroy
else
  raise "You're not allowed to do this"
end

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 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.

Jump to Line
Something went wrong with that request. Please try again.