Module: Authorization::AuthorizationInController::ClassMethods

Defined in:
lib/declarative_authorization/in_controller.rb

Instance Method Summary collapse

Instance Method Details

#all_filter_access_permissions ⇒ Object

Collecting all the ControllerPermission objects from the controller hierarchy. Permissions for actions are overwritten by calls to filter_access_to in child controllers with the same action.



261
262
263
264
265
266
267
268
269
270
271
272
# File 'lib/declarative_authorization/in_controller.rb', line 261

def all_filter_access_permissions # :nodoc:
  ancestors.inject([]) do |perms, mod|
    if mod.respond_to?(:filter_access_permissions)
      perms + 
        mod.filter_access_permissions.collect do |p1| 
          p1.clone.remove_actions(perms.inject(Set.new) {|actions, p2| actions + p2.actions})
        end
    else
      perms
    end
  end
end

#filter_access_to(*args, &filter_block) ⇒ Object

Defines a filter to be applied according to the authorization of the current user. Requires at least one symbol corresponding to an action as parameter. The special symbol :+all+ refers to all action. The all :+all+ statement is only employed if no specific statement is present. class UserController < ApplicationController filter_access_to :index filter_access_to :new, :edit filter_access_to :all ... end

The default is to allow access unconditionally if no rule matches. Thus, including the filter_access_to :+all+ statement is a good idea, implementing a default-deny policy.

When the access is denied, the method permission_denied is called on the current controller, if defined. Else, a simple "you are not allowed" string is output. Log.info is given more information on the reasons of denial.

def permission_denied
flash[:error] = 'Sorry, you are not allowed to the requested page.'
respond_to do |format|
  format.html { redirect_to(:back) rescue redirect_to('/') }
  format.xml  { head :unauthorized }
  format.js   { head :unauthorized }
end
end

By default, required privileges are infered from the action name and the controller name. Thus, in UserController :+edit+ requires :+edit+ users. To specify required privilege, use the option :+require+ filter_access_to :new, :create, :require => :create, :context => :users

Without the :+attribute_check+ option, no constraints from the authorization rules are enforced because for some actions (collections, new, create), there is no object to evaluate conditions against. To allow attribute checks on all actions, it is a common pattern to provide custom objects through before_filters:

class BranchesController < ApplicationController
before_filter :load_company
before_filter :new_branch_from_company_and_params,
  :only => [:index, :new, :create]
filter_access_to :all, :attribute_check => true

protected
def new_branch_from_company_and_params
  @branch = @company.branches.new(params[:branch])
end
end

NOTE: before_filters need to be defined before the first filter_access_to call.

For further customization, a custom filter expression may be formulated in a block, which is then evaluated in the context of the controller on a matching request. That is, for checking two objects, use the following:

filter_access_to :merge do
permitted_to!(:update, User.find(params[:original_id])) and
  permitted_to!(:delete, User.find(params[:id]))
end

The block should raise a Authorization::AuthorizationError or return false if the access is to be denied.

Later calls to filter_access_to with overlapping actions overwrite previous ones for that action.

All options: [:+require+] Privilege required; defaults to action_name [:+context+] The privilege's context, defaults to controller_name, pluralized. [:+namespace+] Prefix the default controller context with.

  • true: the model namespace(s) separated with underscores,
  • Symbol or String: the given symbol or string
  • else: no prefix Example: filter_access_to :show, :namespace => true filter_access_to :delete, :namespace => :foo [:+attribute_check+] Enables the check of attributes defined in the authorization rules. Defaults to false. If enabled, filter_access_to will use a context object from one of the following sources (in that order):
  • the method from the :+load_method+ option,
  • an instance variable named after the singular of the context (by default from the controller name, e.g. @post for PostsController),
  • a find on the context model, using +params+[:id] as id value. Any of these methods will only be employed if :+attribute_check+ is enabled. [:+model+] The data model to load a context object from. Defaults to the context, singularized. [:+load_method+] Specify a method by symbol or a Proc object which should be used to load the object. Both should return the loaded object. If a Proc object is given, e.g. by way of lambda, it is called in the instance of the controller.
    Example demonstrating the default behaviour: filter_access_to :show, :attribute_check => true, :load_method => lambda { User.find(params) }


227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
# File 'lib/declarative_authorization/in_controller.rb', line 227

def filter_access_to (*args, &filter_block)
  options = args.last.is_a?(Hash) ? args.pop : {}
  options = {
    :require => nil,
    :context => nil,
    :namespace => nil,
    :attribute_check => false,
    :model => nil,
    :load_method => nil
  }.merge!(options)
  privilege = options[:require]
  context = options[:context]
  actions = args.flatten

  # collect permits in controller array for use in one before_filter
  unless filter_chain.any? {|filter| filter.method == :filter_access_filter}
    before_filter :filter_access_filter
  end
  
  filter_access_permissions.each do |perm|
    perm.remove_actions(actions)
  end
  filter_access_permissions << 
    ControllerPermission.new(actions, privilege, context,
                             options[:namespace],
                             options[:attribute_check],
                             options[:model],
                             options[:load_method],
                             filter_block)
end