Module: Authorization::AuthorizationInController::ClassMethods
- Defined in:
- lib/declarative_authorization/in_controller.rb
Instance Method Summary collapse
-
#all_filter_access_permissions ⇒ Object
Collecting all the ControllerPermission objects from the controller hierarchy.
-
#filter_access_to(*args, &filter_block) ⇒ Object
Defines a filter to be applied according to the authorization of the current user.
-
#filter_resource_access(options = {}) ⇒ Object
To DRY up the filter_access_to statements in restful controllers, filter_resource_access combines typical filter_access_to and before_filter calls, which set up the instance variables.
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.
280 281 282 283 284 285 286 287 288 289 290 291 |
# File 'lib/declarative_authorization/in_controller.rb', line 280 def # :nodoc: ancestors.inject([]) do |perms, mod| if mod.respond_to?(:filter_access_permissions) perms + mod..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
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.
- :
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[:id]) }
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 |
# File 'lib/declarative_authorization/in_controller.rb', line 248 def filter_access_to (*args, &filter_block) = args.last.is_a?(Hash) ? args.pop : {} = { :require => nil, :context => nil, :attribute_check => false, :model => nil, :load_method => nil }.merge!() privilege = [:require] context = [: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 .each do |perm| perm.remove_actions(actions) end << ControllerPermission.new(actions, privilege, context, [:attribute_check], [:model], [:load_method], filter_block) end |
#filter_resource_access(options = {}) ⇒ Object
To DRY up the filter_access_to statements in restful controllers, filter_resource_access combines typical filter_access_to and before_filter calls, which set up the instance variables.
The simplest case are top-level resource controllers with only the seven CRUD methods, e.g.
class CompanyController < ApplicationController
filter_resource_access
def index...
end
Here, all CRUD actions are protected through a filter_access_to :all statement. :attribute_check
is enabled for all actions except for the collection action :index
. To have an object for attribute checks available, filter_resource_access will set the instance variable @company
in before filters. For the member actions (:show
, :edit
, :update
, :destroy
) @company is set to Company.find(params). For new
actions (:new
, :create
), filter_resource_access creates a new object from company parameters: Company.new(params.
For nested resources, the parent object may be loaded automatically.
class BranchController < ApplicationController
filter_resource_access :nested_in => :companies
end
Again, the CRUD actions are protected. Now, for all CRUD actions, the parent object @company is loaded from params. It is also used when creating @branch for new
actions. Here, attribute_check is enabled for the collection :index
as well, checking attributes on a You can override the default object loading by implementing any of the following instance methods on the controller. Examples are given for the BranchController (with nested_in
set to :companies
):
new_branch_from_params
-
Used for
new
actions. new_branch_for_collection
-
Used for
collection
actions if thenested_in
option is set. load_branch
-
Used for
member
actions. load_company
-
Used for all
new
,member
, andcollection
actions if thenested_in
option is set.
All options:
- :
member
-
Member methods are actions like
show
, which have an params from which to load the controller object and assign it to @controller_name, e.g. @branch
. By default, member actions are [:show
, :edit
, :update
, :destroy
]. - :
additional_member
-
Allows to add additional member actions to the default resource
member
actions. - :
collection
-
Collection actions are like :
index
, actions without any controller object to check attributes of. Ifnested_in
is given, a new object is created from the parent object, e.g. @company.branches.new. Withoutnested_in
, attribute check is deactivated for these actions. By default, collection is set to :index
. - :
additional_collection
-
Allows to add additional collaction actions to the default resource
collection
actions. - :
new
-
new
methods are actions such asnew
andcreate
, which don’t receive a params to load an object from, but a params hash with attributes for a new object. The attributes will be used here to create a new object and check the object against the authorization rules. The object is assigned to @controller_name_singular, e.g. @branch.If
nested_in
is given, the new object is created from the parent_object.controller_name proxy, e.g. company.branches.new(params). By default,new
is set to [:new, :create]. - :
additional_new
-
Allows to add additional new actions to the default resource
new
actions. - :
context
-
The context is used to determine the model to load objects from for the before_filters and the context of privileges to use in authorization checks.
- :
nested_in
-
Specifies the parent controller if the resource is nested in another one. This is used to automatically load the parent object, e.g. @
company
from params for a BranchController nested in a CompanyController. - :
no_attribute_check
-
Allows to set actions for which no attribute check should be perfomed. See filter_access_to on details. By default, with no
nested_in
,no_attribute_check
is set to all collections. Ifnested_in
is givenno_attribute_check
is empty by default.
383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 |
# File 'lib/declarative_authorization/in_controller.rb', line 383 def filter_resource_access( = {}) = { :new => [:new, :create], :additional_new => nil, :member => [:show, :edit, :update, :destroy], :additional_member => nil, :collection => [:index], :additional_collection => nil, #:new_method_for_collection => nil, # only symbol method name #:new_method => nil, # only symbol method name #:load_method => nil, # only symbol method name :no_attribute_check => nil, :context => nil, :nested_in => nil, }.merge() new_actions = actions_from_option([:new]).merge( actions_from_option([:additional_new])) members = actions_from_option([:member]).merge( actions_from_option([:additional_member])) collections = actions_from_option([:collection]).merge( actions_from_option([:additional_collection])) [:no_attribute_check] ||= collections.keys unless [:nested_in] unless [:nested_in].blank? load_method = :"load_#{[:nested_in].to_s.singularize}" before_filter do |controller| if controller.respond_to?(load_method) controller.send(load_method) else controller.send(:load_parent_controller_object, [:nested_in]) end end new_for_collection_method = :"new_#{controller_name.singularize}_for_collection" before_filter :only => collections.keys do |controller| # new_for_collection if controller.respond_to?(new_for_collection_method) controller.send(new_for_collection_method) else controller.send(:new_controller_object_for_collection, [:context] || controller_name, [:nested_in]) end end end new_from_params_method = :"new_#{controller_name.singularize}_from_params" before_filter :only => new_actions.keys do |controller| # new_from_params if controller.respond_to?(new_from_params_method) controller.send(new_from_params_method) else controller.send(:new_controller_object_from_params, [:context] || controller_name, [:nested_in]) end end load_method = :"load_#{controller_name.singularize}" before_filter :only => members.keys do |controller| # load controller object if controller.respond_to?(load_method) controller.send(load_method) else controller.send(:load_controller_object, [:context] || controller_name) end end filter_access_to :all, :attribute_check => true, :context => [:context] members.merge(new_actions).merge(collections).each do |action, privilege| if action != privilege or ([:no_attribute_check] and [:no_attribute_check].include?(action)) = { :context => [:context], :attribute_check => ![:no_attribute_check] || ![:no_attribute_check].include?(action) } [:require] = privilege if action != privilege filter_access_to(action, ) end end end |