Controller integration

martin-straub edited this page Jan 28, 2011 · 11 revisions

To manually protect your controller actions with Aegis, check permissions before an action like this:

class NotesController
  def update
    @note = Note.find(params[:id])
    current_user.may_update_note!(@foo)
    @note.update_attributes!(params[:note])
  end
end

Because we used the bang variant to check permissions (may_update_note! instead of may_update_note?), Aegis raises an error if the check fails. Subsequent code (updating the note and rendering the view) will only be executed if the check passes.

Automatic permissions checks in your controllers action

Aegis offers a very comfortable mapping between your controller actions and permission rules With the automatic integration, Aegis does all permission checks in a before_filter. This way your actions do not need to concern themselves with permission checks.

Protecting a controller by a single permission

A very simple use case is that every action in a controller is protected by the same permission action. Let’s say your permissions look like this:

class Permissions < Aegis::Permissions

  role :special_agent

  action :access_internal_affairs do
    allow :special_agent
  end

end

To protected every action in SecretsController by a check on current_user.may_access_internal_affairs! include Aegis::Controller and use the permissions directive:

class SecretsController < ApplicationController
  include Aegis::Controller

  permissions :access_internal_affairs

end

This assumes that your controllers have access to a method called current_user that return the logged in user.

Integrating permission resources with RESTful controllers

If your application uses RESTful controllers you can easily map permisision resources to your controller actions. With one line of code you can make sure that an action like NotesController#edit is protected by a permission resource action like may_update_note!.

We’re making two assumptions:

  1. Your controllers have a method like current_user that return the user that is presently logged in. If you are using Clearance, Devise or authlogic, this is probably already the case.
  2. Your controllers have a method like object or load_record that loads the record to be processed. This is the default in resource_controller and also a useful pattern to keep your controllers tidy.

Example

Say you have defined the following permission resource:

class Permissions < Aegis::Permissions

  role :user

  resources :notes do
    allow :user
  end

end

A controller that is protected by those permissions automatically looks like this:

class NotesController < ApplicationController
  include Aegis::Controller

  permissions :notes

  def update
    object.update_attributes!(params[:note])
  end

  private

  def object
    @object ||= Note.find(params[:id])
  end

end

The permissions directive maps the permission resource :note to the actions of the controller. Hence the update action is protected by a before_filter that calls current_user.may_update_note!(object).

Mapping actions differently

By default each controller action maps to a permission action of the same name. The actions new and edit are aliased to create and update respectively.

If you need to map individual actions differently, you can provide an option that maps controller actions to specific permissions:

class ApartmentsController < ApplicationController
  include Aegis::Controller
  permissions :apartments, :map => { :gallery => :index_apartments }
end

Changing defaults

The permissions directive takes an optional parameter hash which you can use to configure the integration mapping, for example:

permissions :notes, :object => :set_record, :user => :signed_in_user, :except => :index

The options available are:

  • :object: The name of the method used to load the record that is to be processed by the controller. Default is :object.
  • :parent_object: The name of the method used to load the parent object when mapping to a nested permission resources. Default is :parent_object.
  • :user: The name of the method that returns the user that is currently signed in. Default is :current_user.
  • :permissions: The name of your permissions class as string or Class object. Default is 'Permissions'.
  • :except: The name of an action (or an array of actions) for which the automatic controller integration should be disabled.
  • :only: The name of an action (or an array of actions) for which the automatic controller integration should be enabled, leaving all other actions unprotected.

Forcing controllers to check permissions

You might want to force yourself to add a permission directive to every controller you write, so no controller is left unprotected. For this, add the following line to your ApplicationController:

class ApplicationController < ActionController::Base
  include Aegis::Controller
  require_permissions
end

Now Aegis will raise Aegis::UncheckedPermissions whenever a controller forgets to guard itself with a permissions directive.

To skip the forced permissions check, use skip_permissions:

class SessionController < ActionController::Base
  include Aegis::Controller
  skip_permissions :only => [:new, :create]
end

Options :only and :except are available and behave like for before_filter.