-
Notifications
You must be signed in to change notification settings - Fork 20
Defining permissions with resources
Instead of defining permission actions one-by-one you can define permissions through resources. Permission resources work similiar to resources in the Rails router. Permission resources are very expressive and compact to write and can be used to protect your controllers automatically.
A resource can be defined like this:
class Permissions < Aegis::permissions role :user resources :notes do allow :user end end
This generates the following actions for you:
user.may_show_note?(@note) user.may_index_notes? user.may_update_note?(@note) user.may_create_note? user.may_destroy_note?(@note)
You can control which of these five default actions are generated:
resources :notes, :only => [:index, :create] resources :notes, :except => :destroy
Any allow
or deny
directive inside the resource affects all actions of that resource. You can configure individual actions like this:
resources :notes do allow :user action :create do deny :user allow :admin end end
You can give a resource additional actions which are not CRUD verbs:
resources :apartments do action :rate end
By default a resource action expects an object:
user.may_rate_apartment(@apartment)
If you want to define an action that refers to the entire resource as a collection, write this:
resources :apartments do action :map, :collection => true end
A collection action takes no argument:
user.may_map_apartments?
As seen above, some resource actions (create
, update
, destroy
and show
) expect the object to be checked as first argument. If you need to refer to this object in a permission block, use the implicit object
. The following would allow only apartment owners to update their own apartments:
resources :apartments do action :update do allow :user do object.owner == user end end end
Additional arguments are given as block arguments. Say you would like to parametrize a relocate
action, so apartments may only be relocated within the same city:
resources :apartments do action :relocate do allow do |new_location| object.location.city == new_location.city end end end
This permission would be checked like this:
apartment = Apartment.find(...) new_location = Location.new(...) @user.may_relocate_apartment?(apartment, new_location)
By writing resource
(singular) instead of resources
, you can create a singleton resource:
resource :profile
Singleton resources take no object as argument and have no index action:
user.may_update_profile?
Resources can be nested:
resources :posts do resources :comments do action :update, :destroy do allow :admin end action :create, :index, :show do allow :everyone end end end
This generates the following actions:
user.may_show_post_comment?(post, comment) user.may_update_post_comment?(post, comment) user.may_create_post_comment?(post) user.may_destroy_post_comment?(post_comment) user.may_index_post_comments?(post)
Nested resources have their own actions and allow
/deny
directives. They inherit nothing from their parent resources.
Note how permission checks on nested resources take up to two arguments in the example above. The first argument is the parent object that provides the context inside the parent resource. If you need to refer to that object inside a permission block, write parent_object
:
resources :posts do resources :comments do action :destroy do allow :admin do parent_object.author == user end end end end
You can namespace your resources like this:
namespace :admin do resources :users end
This generates the following actions:
user.may_show_admin_user?(@user) user.may_index_admin_users? user.may_update_admin_user?(@user) user.may_create_admin_user? user.may_destroy_admin_user?(@user)
Namespaces are useful to group resources together as your permissions grow. They only change the names of generated actions, but don’t alter behavior otherwise.
Your permission resources do not necessarily need to be as complex and fine-grained as your routes/controllers. So don’t drive yourself crazy keeping those two in sync. It is more important to keep your permissions at a manageable size and complexity.