= 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 'makandra-aegis', :lib => 'aegis', :source => 'http://gems.github.com'
Then do a
sudo rake gems:install
Alternatively, use
sudo gem sources -a http://gems.github.com
sudo gem install makandra-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.
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.