/grant

Conscious security constraints for your ActiveRecord model objects

Primary LanguageRubyMIT LicenseMIT

Grant

Grant is a Ruby gem and Rails plugin that forces you to make explicit security decisions about the operations performed on your ActiveRecord models. It provides a declarative way to specify rules granting permission to perform CRUD operations on ActiveRecord objects.

Grant does not allow you to specify which operations are restricted. Instead, it restricts all CRUD operations unless they’re explicitly granted to the user. Only allowing operations explicitly granted forces you to make conscious security decisions. Grant will not help you make those decisions, but it won’t let you forget to.

Additional information beyond that found in this README is available on the wiki.

Installation

To install the Grant gem, simply run

gem install grant

To use it with a Rails 3 project or other project using Bundler, add the following line to your Gemfile

gem 'grant'

For your Rails 2.x project, add the following to your environment.rb file

config.gem 'grant'

Lastly, Grant can also be installed as a Rails plugin

script/plugin install git://github.com/nearinfinity/grant.git

Setup

Grant needs to know who the current user is, but with no standard for doing so you’ll have to do a little work to set things up unless your ApplicationController exposes a current_user method. If you don’t have a current_user method in ApplicationController, you simply need to set your current user model object as the Grant current user before any CRUD operations are performed. For example, in a Rails application you could add the following to your application_controller.rb

class ApplicationController < ActionController::Base
  before_filter :set_current_user

  private

  def set_current_user
    Grant::User.current_user = @current_user
  end
end

The above is essentially what Grant does if you have a current_user method in ApplicationController.

Usage

To enable grant you simply start calling the grant method. Grant will initialize itself on the first invocation of the grant method in a class. In the example below you see two grant statements. The first grants find (aka read) permission all the time. The second example grants create, update, and destroy permission when the passed block evaluates to true, which in this case happens when the model is editable by the current user. A Grant::Error is raised if any grant block evaluates to false or nil.

class Book < ActiveRecord::Base
  grant(:find) { true }
  grant(:create, :update, :destroy) { |user, model, action| model.editable_by_user? user }

  def editable_by_user? user
    user.administrator? || user.has_role?(:editor) 
  end
end

The valid actions to pass to a grant statement are :find, :create, :update, and :destroy. Each action can be passed as a Symbol or String. Any number of actions can be passed to a single grant statement, which is very useful if each of the actions share the same logic for determining access.

If you’d like to find out if a certain action is granted without attempting the action and having Grant potentially raise an error, you can use the ‘granted?` method.

book = Book.first
book.granted?(:destroy, some_user)

If you don’t pass a user as the second argument to ‘granted?`, the `Grant::User.current_user` will be assumed.

Inheritance

Subclasses inherit all the ‘grant` statements defined on their parent, but are free to override any of them individually simply by redefining them. For example:

class Book < ActiveRecord::Base
  self.abstract_class = true
  grant(:create, :find, :destroy) { true }
  grant(:update) { false }
end

class Ebook < ActiveRecord::Base
  grant(:update) { true }
  grant(:destroy) { false }
end

Instances of the parent class ‘Book` can always be created, found, and destroyed, but never updated. However, `Ebook` instances can be created, found, and updated, but never destroyed.

Integration

There may be some instances where you need to perform an action on your model object without Grant stepping in and stopping you. In those cases you can include the Grant::Status module for help.

class BooksController < ApplicationController
  include Grant::Status

  def update
    book = Book.find(params[:id])
    without_grant { book.update_attributes(params[:book]) } # Grant is disabled for the entire block
  end
end

License

Grant is released under the MIT license.

Copyright © 2011 Near Infinity. www.nearinfinity.com