public
Description: Role-based permissions for your user models.
Homepage:
Clone URL: git://github.com/makandra/aegis.git
aegis /
name age message
file .gitignore Fri May 29 07:10:06 -0700 2009 expanded README.rdoc [makandra]
file MIT-LICENSE Thu May 28 09:18:58 -0700 2009 initial import [makandra]
file README.rdoc Wed Nov 11 01:49:03 -0800 2009 fixed typo [kratob]
file Rakefile Fri May 29 05:22:18 -0700 2009 gemification [makandra]
file VERSION Wed Nov 11 03:15:25 -0800 2009 Version bump to 1.1.6 [henning-koch]
file aegis.gemspec Wed Nov 11 03:15:45 -0800 2009 version bump [henning-koch]
directory lib/ Wed Nov 11 03:11:16 -0800 2009 models with a role now property respond_to? loo... [henning-koch]
directory test/ Wed Nov 11 03:11:16 -0800 2009 models with a role now property respond_to? loo... [henning-koch]
README.rdoc

Aegis - role-based permissions for your user models

Aegis allows you to manage fine-grained, complex permission for user accounts in a central place.

Installation

Add the following to your Initializer.run block in your environment.rb:

    config.gem 'aegis', :source => 'http://gemcutter.org'

Then do a

    sudo rake gems:install

Alternatively, use

    sudo gem sources -a http://gemcutter.org
    sudo gem install aegis

Example

First, let’s define some roles:

    # app/models/permissions.rb
    class Permissions < Aegis::Permissions

      role :guest
      role :registered_user
      role :moderator
      role :administrator, :default_permission => :allow

      permission :edit_post do |user, post|
        allow :registered_user do
          post.creator == user      # a registered_user can only edit his own posts
        end
        allow :moderator
      end

      permission :read_post do |post|
        allow :everyone
        deny :guest do
          post.private?             # guests may not read private posts
        end
      end

    end

Now we assign roles to users. For this, the users table needs to have a string column role_name.

    # app/models/user.rb
    class User
      has_role
    end

These permissions may be used in views and controllers:

    # app/views/posts/index.html.erb
    @posts.each do |post|
      <% if current_user.may_read_post? post %>
        <%= render post %>
        <% if current_user.may_edit_post? post %>
          <%= link_to 'Edit', edit_post_path(post) %>
        <% end %>
      <% end %>
    <% end %>

    # app/controllers/posts_controller.rb
    class PostsController
      # ...

      def update
        @post = Post.find(params[:id])
        current_user.may_edit_post! @post    # raises an Aegis::PermissionError for unauthorized access
        # ...
      end

    end

Details

Roles

To equip a (user) model with any permissions, you simply call has_role within the model:

    class User < ActiveRecord::Base
      has_role
    end

Aegis assumes that the corresponding database table has a string-valued column called role_name. You may override the name with the :name_accessor => :my_role_column option.

You can define a default role for a model by saying

    class User < ActiveRecord::Base
      has_role :default => :admin
    end

All this will do, is initialize the role_name with the given default when User.new is called.

The roles and permissions themselves are defined in a class inheriting from Aegis::Permissions. To define roles you create a model permissions.rb and use the role method:

    class Permissions < Aegis::Permissions
      role 'role_name'
    end

By default, users belonging to this role are not permitted anything. You may override this with :default_permission => :allow, e.g.

    role 'admin', :default_permission => :allow

Permissions

Permissions are specified with the permission method and allow and deny

    permission :do_something do
        allow :role_a, :role_b
        deny :role_c
    end

Your user model just received two methods called User#may_do_something? and User#may_do_something!. The first one with the ? returns true for users with role_a and role_b, and false for users with role_c. The second one with the ! raises an Aegis::PermissionError for role_c.

Normalization

Aegis will perform some normalization. For example, the permissions edit_something and update_something will be the same, each granting both may_edit_something? and may_update_something?. The following normalizations are active:

  • edit = update
  • show = list = view = read
  • delete = remove = destroy

Complex permissions (with parameters)

allow and deny can also take a block that may return true or false indicating if this really applies. So

    permission :pull_april_fools_prank do
      allow :everyone do
        Date.today.month == 4 and Date.today.day == 1
      end
    end

will generate a may_pull_april_fools_prank? method that only returns true on April 1.

This becomes more useful if you pass parameters to a may_…? method, which are passed through to the permission block (together with the user object). This way you can define more complex permissions like

    permission :edit_post do |current_user, post|
      allow :registered_user do
        post.owner == current_user
      end
      allow :admin
    end

which will permit admins and post owners to edit posts.

For your convenience

As a convenience, if you create a permission ending in a plural ’s’, this automatically includes the singular form. That is, after

    permission :read_posts do
      allow :everyone
    end

.may_read_post? @post will return true, as well.

If you want to grant create_something, read_something, update_something and destroy_something permissions all at once, just use

    permission :crud_something do
      allow :admin
    end

If several permission blocks (or several allow and denies) apply to a certain role, the later one always wins. That is

    permission :do_something do
      deny :everyone
      allow :admin
    end

will work as expected.

Our stance on multiple roles per user

We believe that you should only distinguish roles that have different ways of resolving their permissions. A typical set of roles would be

  • anonymous guest (has access to nothing with some exceptions)
  • signed up user (has access to some things depending on its attributes and associations)
  • administrator (has access to everything)

We don’t do multiple, parametrized roles like "leader for project #2" and "author of post #7". That would be reinventing associations. Just use a single :user role and let your permission block query regular associations and attributes.

Credits

Henning Koch, Tobias Kraze

link www.makandra.de