ae_declarative_authorization 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,713 +0,0 @@
1
- # Authorization::AuthorizationInController
2
- require File.dirname(__FILE__) + '/authorization.rb'
3
-
4
- module Authorization
5
- module AuthorizationInController
6
-
7
- def self.included(base) # :nodoc:
8
- base.extend(ClassMethods)
9
- base.module_eval do
10
- before_action(:filter_access_filter) if method_defined?(:filter_access_filter)
11
- end
12
- end
13
-
14
- DEFAULT_DENY = false
15
-
16
- # If attribute_check is set for filter_access_to, decl_auth_context will try to
17
- # load the appropriate object from the current controller's model with
18
- # the id from params[:id]. If that fails, a 404 Not Found is often the
19
- # right way to handle the error. If you have additional measures in place
20
- # that restricts the find scope, handling this error as a permission denied
21
- # might be a better way. Set failed_auto_loading_is_not_found to false
22
- # for the latter behavior.
23
- @@failed_auto_loading_is_not_found = true
24
- def self.failed_auto_loading_is_not_found?
25
- @@failed_auto_loading_is_not_found
26
- end
27
- def self.failed_auto_loading_is_not_found=(new_value)
28
- @@failed_auto_loading_is_not_found = new_value
29
- end
30
-
31
- # Returns the Authorization::Engine for the current controller.
32
- def authorization_engine
33
- @authorization_engine ||= Authorization::Engine.instance
34
- end
35
-
36
- # If the current user meets the given privilege, permitted_to? returns true
37
- # and yields to the optional block. The attribute checks that are defined
38
- # in the authorization rules are only evaluated if an object is given
39
- # for context.
40
- #
41
- # See examples for Authorization::AuthorizationHelper #permitted_to?
42
- #
43
- # If no object or context is specified, the controller_name is used as
44
- # context.
45
- #
46
- def permitted_to?(privilege, object_or_sym = nil, options = {})
47
- if authorization_engine.permit!(privilege, options_for_permit(object_or_sym, options, false))
48
- yield if block_given?
49
- true
50
- else
51
- false
52
- end
53
- end
54
-
55
- # Works similar to the permitted_to? method, but
56
- # throws the authorization exceptions, just like Engine#permit!
57
- def permitted_to!(privilege, object_or_sym = nil, options = {})
58
- authorization_engine.permit!(privilege, options_for_permit(object_or_sym, options, true))
59
- end
60
-
61
- # While permitted_to? is used for authorization, in some cases
62
- # content should only be shown to some users without being concerned
63
- # with authorization. E.g. to only show the most relevant menu options
64
- # to a certain group of users. That is what has_role? should be used for.
65
- def has_role?(*roles)
66
- user_roles = authorization_engine.roles_for(current_user)
67
- result = roles.all? do |role|
68
- user_roles.include?(role)
69
- end
70
- yield if result and block_given?
71
- result
72
- end
73
-
74
- # Intended to be used where you want to allow users with any single listed role to view
75
- # the content in question
76
- def has_any_role?(*roles)
77
- user_roles = authorization_engine.roles_for(current_user)
78
- result = roles.any? do |role|
79
- user_roles.include?(role)
80
- end
81
- yield if result and block_given?
82
- result
83
- end
84
-
85
- # As has_role? except checks all roles included in the role hierarchy
86
- def has_role_with_hierarchy?(*roles)
87
- user_roles = authorization_engine.roles_with_hierarchy_for(current_user)
88
- result = roles.all? do |role|
89
- user_roles.include?(role)
90
- end
91
- yield if result and block_given?
92
- result
93
- end
94
-
95
- # As has_any_role? except checks all roles included in the role hierarchy
96
- def has_any_role_with_hierarchy?(*roles)
97
- user_roles = authorization_engine.roles_with_hierarchy_for(current_user)
98
- result = roles.any? do |role|
99
- user_roles.include?(role)
100
- end
101
- yield if result and block_given?
102
- result
103
- end
104
-
105
- protected
106
- def filter_access_filter # :nodoc:
107
- permissions = self.class.all_filter_access_permissions
108
- all_permissions = permissions.select {|p| p.actions.include?(:all)}
109
- matching_permissions = permissions.select {|p| p.matches?(action_name)}
110
- allowed = false
111
- auth_exception = nil
112
- begin
113
- allowed = if !matching_permissions.empty?
114
- matching_permissions.all? {|perm| perm.permit!(self)}
115
- elsif !all_permissions.empty?
116
- all_permissions.all? {|perm| perm.permit!(self)}
117
- else
118
- !DEFAULT_DENY
119
- end
120
- rescue NotAuthorized => e
121
- auth_exception = e
122
- end
123
-
124
- unless allowed
125
- if all_permissions.empty? and matching_permissions.empty?
126
- logger.warn "Permission denied: No matching filter access " +
127
- "rule found for #{self.class.controller_name}.#{action_name}"
128
- elsif auth_exception
129
- logger.info "Permission denied: #{auth_exception}"
130
- end
131
- if respond_to?(:permission_denied, true)
132
- # permission_denied needs to render or redirect
133
- send(:permission_denied)
134
- else
135
- send(:render, :plain => "You are not allowed to access this action.",
136
- :status => :forbidden)
137
- end
138
- end
139
- end
140
-
141
- def load_controller_object(context_without_namespace = nil, model = nil) # :nodoc:
142
- instance_var = :"@#{context_without_namespace.to_s.singularize}"
143
- model = model ? model.classify.constantize : context_without_namespace.to_s.classify.constantize
144
- instance_variable_set(instance_var, model.find(params[:id]))
145
- end
146
-
147
- def load_parent_controller_object(parent_context_without_namespace) # :nodoc:
148
- instance_var = :"@#{parent_context_without_namespace.to_s.singularize}"
149
- model = parent_context_without_namespace.to_s.classify.constantize
150
- instance_variable_set(instance_var, model.find(params[:"#{parent_context_without_namespace.to_s.singularize}_id"]))
151
- end
152
-
153
- def new_controller_object_from_params(context_without_namespace, parent_context_without_namespace, strong_params) # :nodoc:
154
- model_or_proxy = parent_context_without_namespace ?
155
- instance_variable_get(:"@#{parent_context_without_namespace.to_s.singularize}").send(context_without_namespace.to_sym) :
156
- context_without_namespace.to_s.classify.constantize
157
- instance_var = :"@#{context_without_namespace.to_s.singularize}"
158
- instance_variable_set(instance_var,
159
- model_or_proxy.new(params[context_without_namespace.to_s.singularize]))
160
- end
161
-
162
- def new_blank_controller_object(context_without_namespace, parent_context_without_namespace, strong_params, model) # :nodoc:
163
- if model
164
- model_or_proxy = model.to_s.classify.constantize
165
- else
166
- model_or_proxy = parent_context_without_namespace ?
167
- instance_variable_get(:"@#{parent_context_without_namespace.to_s.singularize}").send(context_without_namespace.to_sym) :
168
- context_without_namespace.to_s.classify.constantize
169
- end
170
- instance_var = :"@#{context_without_namespace.to_s.singularize}"
171
- instance_variable_set(instance_var,
172
- model_or_proxy.new())
173
- end
174
-
175
- def new_controller_object_for_collection(context_without_namespace, parent_context_without_namespace, strong_params) # :nodoc:
176
- model_or_proxy = parent_context_without_namespace ?
177
- instance_variable_get(:"@#{parent_context_without_namespace.to_s.singularize}").send(context_without_namespace.to_sym) :
178
- context_without_namespace.to_s.classify.constantize
179
- instance_var = :"@#{context_without_namespace.to_s.singularize}"
180
- instance_variable_set(instance_var, model_or_proxy.new)
181
- end
182
-
183
- def options_for_permit(object_or_sym = nil, options = {}, bang = true)
184
- context = object = nil
185
- if object_or_sym.nil?
186
- context = self.class.decl_auth_context
187
- elsif !Authorization.is_a_association_proxy?(object_or_sym) and object_or_sym.is_a?(Symbol)
188
- context = object_or_sym
189
- else
190
- object = object_or_sym
191
- end
192
-
193
- result = {:object => object,
194
- :context => context,
195
- :skip_attribute_test => object.nil?,
196
- :bang => bang}.merge(options)
197
- result[:user] = current_user unless result.key?(:user)
198
- result
199
- end
200
-
201
- module ClassMethods
202
- #
203
- # Defines a filter to be applied according to the authorization of the
204
- # current user. Requires at least one symbol corresponding to an
205
- # action as parameter. The special symbol :+all+ refers to all actions.
206
- # The all :+all+ statement is only employed if no specific statement is
207
- # present.
208
- # class UserController < ApplicationController
209
- # filter_access_to :index
210
- # filter_access_to :new, :edit
211
- # filter_access_to :all
212
- # ...
213
- # end
214
- #
215
- # The default is to allow access unconditionally if no rule matches.
216
- # Thus, including the +filter_access_to+ :+all+ statement is a good
217
- # idea, implementing a default-deny policy.
218
- #
219
- # When the access is denied, the method +permission_denied+ is called
220
- # on the current controller, if defined. Else, a simple "you are not
221
- # allowed" string is output. Log.info is given more information on the
222
- # reasons of denial.
223
- #
224
- # def permission_denied
225
- # flash[:error] = 'Sorry, you are not allowed to the requested page.'
226
- # respond_to do |format|
227
- # format.html { redirect_to(:back) rescue redirect_to('/') }
228
- # format.xml { head :unauthorized }
229
- # format.js { head :unauthorized }
230
- # end
231
- # end
232
- #
233
- # By default, required privileges are inferred from the action name and
234
- # the controller name. Thus, in UserController :+edit+ requires
235
- # :+edit+ +users+. To specify required privilege, use the option :+require+
236
- # filter_access_to :new, :create, :require => :create, :context => :users
237
- #
238
- # Without the :+attribute_check+ option, no constraints from the
239
- # authorization rules are enforced because for some actions (collections,
240
- # +new+, +create+), there is no object to evaluate conditions against. To
241
- # allow attribute checks on all actions, it is a common pattern to provide
242
- # custom objects through +before_actions+:
243
- # class BranchesController < ApplicationController
244
- # before_action :load_company
245
- # before_action :new_branch_from_company_and_params,
246
- # :only => [:index, :new, :create]
247
- # filter_access_to :all, :attribute_check => true
248
- #
249
- # protected
250
- # def new_branch_from_company_and_params
251
- # @branch = @company.branches.new(params[:branch])
252
- # end
253
- # end
254
- # NOTE: +before_actions+ need to be defined before the first
255
- # +filter_access_to+ call.
256
- #
257
- # For further customization, a custom filter expression may be formulated
258
- # in a block, which is then evaluated in the context of the controller
259
- # on a matching request. That is, for checking two objects, use the
260
- # following:
261
- # filter_access_to :merge do
262
- # permitted_to!(:update, User.find(params[:original_id])) and
263
- # permitted_to!(:delete, User.find(params[:id]))
264
- # end
265
- # The block should raise a Authorization::AuthorizationError or return
266
- # false if the access is to be denied.
267
- #
268
- # Later calls to filter_access_to with overlapping actions overwrite
269
- # previous ones for that action.
270
- #
271
- # All options:
272
- # [:+require+]
273
- # Privilege required; defaults to action_name
274
- # [:+context+]
275
- # The privilege's context, defaults to decl_auth_context, which consists
276
- # of controller_name, prepended by any namespaces
277
- # [:+attribute_check+]
278
- # Enables the check of attributes defined in the authorization rules.
279
- # Defaults to false. If enabled, filter_access_to will use a context
280
- # object from one of the following sources (in that order):
281
- # * the method from the :+load_method+ option,
282
- # * an instance variable named after the singular of the context
283
- # (by default from the controller name, e.g. @post for PostsController),
284
- # * a find on the context model, using +params+[:id] as id value.
285
- # Any of these methods will only be employed if :+attribute_check+
286
- # is enabled.
287
- # [:+model+]
288
- # The data model to load a context object from. Defaults to the
289
- # context, singularized.
290
- # [:+load_method+]
291
- # Specify a method by symbol or a Proc object which should be used
292
- # to load the object. Both should return the loaded object.
293
- # If a Proc object is given, e.g. by way of
294
- # +lambda+, it is called in the instance of the controller.
295
- # Example demonstrating the default behavior:
296
- # filter_access_to :show, :attribute_check => true,
297
- # :load_method => lambda { User.find(params[:id]) }
298
- #
299
-
300
- def filter_access_to(*args, &filter_block)
301
- options = args.last.is_a?(Hash) ? args.pop : {}
302
- options = {
303
- :require => nil,
304
- :context => nil,
305
- :attribute_check => false,
306
- :model => nil,
307
- :load_method => nil,
308
- :strong_parameters => nil
309
- }.merge!(options)
310
- privilege = options[:require]
311
- context = options[:context]
312
- actions = args.flatten
313
-
314
- # prevent setting filter_access_filter multiple times
315
- skip_before_action(:filter_access_filter) if method_defined?(:filter_access_filter)
316
- before_action :filter_access_filter
317
-
318
- filter_access_permissions.each do |perm|
319
- perm.remove_actions(actions)
320
- end
321
- filter_access_permissions <<
322
- ControllerPermission.new(actions, privilege, context,
323
- options[:strong_parameters],
324
- options[:attribute_check],
325
- options[:model],
326
- options[:load_method],
327
- filter_block)
328
- end
329
-
330
- # Disables authorization entirely. Requires at least one symbol corresponding
331
- # to an action as parameter. The special symbol :+all+ refers to all actions.
332
- # The all :+all+ statement is only employed if no specific statement is
333
- # present.
334
- def no_filter_access_to(*args)
335
- filter_access_to args do
336
- true
337
- end
338
- end
339
-
340
- # Collecting all the ControllerPermission objects from the controller
341
- # hierarchy. Permissions for actions are overwritten by calls to
342
- # filter_access_to in child controllers with the same action.
343
- def all_filter_access_permissions # :nodoc:
344
- ancestors.inject([]) do |perms, mod|
345
- if mod.respond_to?(:filter_access_permissions, true)
346
- perms +
347
- mod.filter_access_permissions.collect do |p1|
348
- p1.clone.remove_actions(perms.inject(Set.new) {|actions, p2| actions + p2.actions})
349
- end
350
- else
351
- perms
352
- end
353
- end
354
- end
355
-
356
- # To DRY up the filter_access_to statements in restful controllers,
357
- # filter_resource_access combines typical filter_access_to and
358
- # before_action calls, which set up the instance variables.
359
- #
360
- # The simplest case are top-level resource controllers with only the
361
- # seven CRUD methods, e.g.
362
- # class CompanyController < ApplicationController
363
- # filter_resource_access
364
- #
365
- # def index...
366
- # end
367
- # Here, all CRUD actions are protected through a filter_access_to :all
368
- # statement. :+attribute_check+ is enabled for all actions except for
369
- # the collection action :+index+. To have an object for attribute checks
370
- # available, filter_resource_access will set the instance variable
371
- # @+company+ in before filters. For the member actions (:+show+, :+edit+,
372
- # :+update+, :+destroy+) @company is set to Company.find(params[:id]).
373
- # For +new+ actions (:+new+, :+create+), filter_resource_access creates
374
- # a new object from company parameters: Company.new(params[:company].
375
- #
376
- # For nested resources, the parent object may be loaded automatically.
377
- # class BranchController < ApplicationController
378
- # filter_resource_access :nested_in => :companies
379
- # end
380
- # Again, the CRUD actions are protected. Now, for all CRUD actions,
381
- # the parent object @company is loaded from params[:company_id]. It is
382
- # also used when creating @branch for +new+ actions. Here, attribute_check
383
- # is enabled for the collection :+index+ as well, checking attributes on a
384
- # @company.branches.new method.
385
- #
386
- # In many cases, the default seven CRUD actions are not sufficient. As in
387
- # the resource definition for routing you may thus give additional member,
388
- # new and collection methods. The +options+ allow you to specify the
389
- # required privileges for each action by providing a hash or an array of
390
- # pairs. By default, for each action the action name is taken as privilege
391
- # (action search in the example below requires the privilege :index
392
- # :companies). Any controller action that is not specified and does not
393
- # belong to the seven CRUD actions is handled as a member method.
394
- # class CompanyController < ApplicationController
395
- # filter_resource_access :collection => [[:search, :index], :index],
396
- # :additional_member => {:mark_as_key_company => :update}
397
- # end
398
- # The +additional_+* options add to the respective CRUD actions,
399
- # the other options (:+member+, :+collection+, :+new+) replace their
400
- # respective CRUD actions.
401
- # filter_resource_access :member => { :toggle_open => :update }
402
- # Would declare :toggle_open as the only member action in the controller and
403
- # require that permission :update is granted for the current user.
404
- # filter_resource_access :additional_member => { :toggle_open => :update }
405
- # Would add a member action :+toggle_open+ to the default members, such as :+show+.
406
- #
407
- # If :+collection+ is an array of method names filter_resource_access will
408
- # associate a permission with the method that is the same as the method
409
- # name and no attribute checks will be performed unless
410
- # :attribute_check => true
411
- # is added in the options.
412
- #
413
- # You can override the default object loading by implementing any of the
414
- # following instance methods on the controller. Examples are given for the
415
- # BranchController (with +nested_in+ set to :+companies+):
416
- # [+new_branch_from_params+]
417
- # Used for +new+ actions.
418
- # [+new_branch_for_collection+]
419
- # Used for +collection+ actions if the +nested_in+ option is set.
420
- # [+load_branch+]
421
- # Used for +member+ actions.
422
- # [+load_company+]
423
- # Used for all +new+, +member+, and +collection+ actions if the
424
- # +nested_in+ option is set.
425
- #
426
- # All options:
427
- # [:+member+]
428
- # Member methods are actions like +show+, which have an params[:id] from
429
- # which to load the controller object and assign it to @controller_name,
430
- # e.g. @+branch+.
431
- #
432
- # By default, member actions are [:+show+, :+edit+, :+update+,
433
- # :+destroy+]. Also, any action not belonging to the seven CRUD actions
434
- # are handled as member actions.
435
- #
436
- # There are three different syntax to specify member, collection and
437
- # new actions.
438
- # * Hash: Lets you set the required privilege for each action:
439
- # {:+show+ => :+show+, :+mark_as_important+ => :+update+}
440
- # * Array of actions or pairs: [:+show+, [:+mark_as_important+, :+update+]],
441
- # with single actions requiring the privilege of the same name as the method.
442
- # * Single method symbol: :+show+
443
- # [:+additional_member+]
444
- # Allows to add additional member actions to the default resource +member+
445
- # actions.
446
- # [:+collection+]
447
- # Collection actions are like :+index+, actions without any controller object
448
- # to check attributes of. If +nested_in+ is given, a new object is
449
- # created from the parent object, e.g. @company.branches.new. Without
450
- # +nested_in+, attribute check is deactivated for these actions. By
451
- # default, collection is set to :+index+.
452
- # [:+additional_collection+]
453
- # Allows to add additional collection actions to the default resource +collection+
454
- # actions.
455
- # [:+new+]
456
- # +new+ methods are actions such as +new+ and +create+, which don't
457
- # receive a params[:id] to load an object from, but
458
- # a params[:controller_name_singular] hash with attributes for a new
459
- # object. The attributes will be used here to create a new object and
460
- # check the object against the authorization rules. The object is
461
- # assigned to @controller_name_singular, e.g. @branch.
462
- #
463
- # If +nested_in+ is given, the new object
464
- # is created from the parent_object.controller_name
465
- # proxy, e.g. company.branches.new(params[:branch]). By default,
466
- # +new+ is set to [:new, :create].
467
- # [:+additional_new+]
468
- # Allows to add additional new actions to the default resource +new+ actions.
469
- # [:+context+]
470
- # The context is used to determine the model to load objects from for the
471
- # before_actions and the context of privileges to use in authorization
472
- # checks.
473
- # [:+nested_in+]
474
- # Specifies the parent controller if the resource is nested in another
475
- # one. This is used to automatically load the parent object, e.g.
476
- # @+company+ from params[:company_id] for a BranchController nested in
477
- # a CompanyController.
478
- # [:+shallow+]
479
- # Only relevant when used in conjunction with +nested_in+. Specifies a nested resource
480
- # as being a shallow nested resource, resulting in the controller not attempting to
481
- # load a parent object for all member actions defined by +member+ and
482
- # +additional_member+ or rather the default member actions (:+show+, :+edit+,
483
- # :+update+, :+destroy+).
484
- # [:+no_attribute_check+]
485
- # Allows to set actions for which no attribute check should be performed.
486
- # See filter_access_to on details. By default, with no +nested_in+,
487
- # +no_attribute_check+ is set to all collections. If +nested_in+ is given
488
- # +no_attribute_check+ is empty by default.
489
- # [:+strong_parameters+]
490
- # If set to true, relies on controller to provide instance variable and
491
- # create new object in :create action. Set true if you use strong_params
492
- # and false if you use protected_attributes.
493
- #
494
- def filter_resource_access(options = {})
495
- options = {
496
- :new => [:new, :create],
497
- :additional_new => nil,
498
- :member => [:show, :edit, :update, :destroy],
499
- :additional_member => nil,
500
- :collection => [:index],
501
- :additional_collection => nil,
502
- #:new_method_for_collection => nil, # only symbol method name
503
- #:new_method => nil, # only symbol method name
504
- #:load_method => nil, # only symbol method name
505
- :no_attribute_check => nil,
506
- :context => nil,
507
- :model => nil,
508
- :nested_in => nil,
509
- :strong_parameters => nil
510
- }.merge(options)
511
- options.merge!({ :strong_parameters => true }) if options[:strong_parameters] == nil
512
-
513
- new_actions = actions_from_option( options[:new] ).merge(
514
- actions_from_option(options[:additional_new]) )
515
- members = actions_from_option(options[:member]).merge(
516
- actions_from_option(options[:additional_member]))
517
- collections = actions_from_option(options[:collection]).merge(
518
- actions_from_option(options[:additional_collection]))
519
-
520
- no_attribute_check_actions = options[:strong_parameters] ? actions_from_option(options[:collection]).merge(actions_from_option([:create])) : collections
521
-
522
- options[:no_attribute_check] ||= no_attribute_check_actions.keys unless options[:nested_in]
523
-
524
- unless options[:nested_in].blank?
525
- load_parent_method = :"load_#{options[:nested_in].to_s.singularize}"
526
- shallow_exceptions = options[:shallow] ? {:except => members.keys} : {}
527
- before_action shallow_exceptions do |controller|
528
- if controller.respond_to?(load_parent_method, true)
529
- controller.send(load_parent_method)
530
- else
531
- controller.send(:load_parent_controller_object, options[:nested_in])
532
- end
533
- end
534
-
535
- new_for_collection_method = :"new_#{controller_name.singularize}_for_collection"
536
- before_action :only => collections.keys do |controller|
537
- # new_for_collection
538
- if controller.respond_to?(new_for_collection_method, true)
539
- controller.send(new_for_collection_method)
540
- else
541
- controller.send(:new_controller_object_for_collection,
542
- options[:context] || controller_name, options[:nested_in], options[:strong_parameters])
543
- end
544
- end
545
- end
546
-
547
- unless options[:strong_parameters]
548
- new_from_params_method = :"new_#{controller_name.singularize}_from_params"
549
- before_action :only => new_actions.keys do |controller|
550
- # new_from_params
551
- if controller.respond_to?(new_from_params_method, true)
552
- controller.send(new_from_params_method)
553
- else
554
- controller.send(:new_controller_object_from_params,
555
- options[:context] || controller_name, options[:nested_in], options[:strong_parameters])
556
- end
557
- end
558
- else
559
- new_object_method = :"new_#{controller_name.singularize}"
560
- before_action :only => :new do |controller|
561
- # new_from_params
562
- if controller.respond_to?(new_object_method, true)
563
- controller.send(new_object_method)
564
- else
565
- controller.send(:new_blank_controller_object,
566
- options[:context] || controller_name, options[:nested_in], options[:strong_parameters], options[:model])
567
- end
568
- end
569
- end
570
-
571
- load_method = :"load_#{controller_name.singularize}"
572
- before_action :only => members.keys do |controller|
573
- # load controller object
574
- if controller.respond_to?(load_method, true)
575
- controller.send(load_method)
576
- else
577
- controller.send(:load_controller_object, options[:context] || controller_name, options[:model])
578
- end
579
- end
580
- filter_access_to :all, :attribute_check => true, :context => options[:context], :model => options[:model]
581
-
582
- members.merge(new_actions).merge(collections).each do |action, privilege|
583
- if action != privilege or (options[:no_attribute_check] and options[:no_attribute_check].include?(action))
584
- filter_options = {
585
- :strong_parameters => options[:strong_parameters],
586
- :context => options[:context],
587
- :attribute_check => !options[:no_attribute_check] || !options[:no_attribute_check].include?(action),
588
- :model => options[:model]
589
- }
590
- filter_options[:require] = privilege if action != privilege
591
- filter_access_to(action, filter_options)
592
- end
593
- end
594
- end
595
-
596
- # Returns the context for authorization checks in the current controller.
597
- # Uses the controller_name and prepends any namespaces underscored and
598
- # joined with underscores.
599
- #
600
- # E.g.
601
- # AllThosePeopleController => :all_those_people
602
- # AnyName::Space::ThingsController => :any_name_space_things
603
- #
604
- def decl_auth_context
605
- prefixes = name.split('::')[0..-2].map(&:underscore)
606
- ((prefixes + [controller_name]) * '_').to_sym
607
- end
608
-
609
- protected
610
- def filter_access_permissions # :nodoc:
611
- unless filter_access_permissions?
612
- ancestors[1..-1].reverse.each do |mod|
613
- mod.filter_access_permissions if mod.respond_to?(:filter_access_permissions, true)
614
- end
615
- end
616
- class_variable_set(:@@declarative_authorization_permissions, {}) unless filter_access_permissions?
617
- class_variable_get(:@@declarative_authorization_permissions)[self.name] ||= []
618
- end
619
-
620
- def filter_access_permissions? # :nodoc:
621
- class_variable_defined?(:@@declarative_authorization_permissions)
622
- end
623
-
624
- def actions_from_option(option) # :nodoc:
625
- case option
626
- when nil
627
- {}
628
- when Symbol, String
629
- {option.to_sym => option.to_sym}
630
- when Hash
631
- option
632
- when Enumerable
633
- option.each_with_object({}) do |action, hash|
634
- if action.is_a?(Array)
635
- raise "Unexpected option format: #{option.inspect}" if action.length != 2
636
- hash[action.first] = action.last
637
- else
638
- hash[action.to_sym] = action.to_sym
639
- end
640
- end
641
- end
642
- end
643
- end
644
- end
645
-
646
- class ControllerPermission # :nodoc:
647
- attr_reader :actions, :privilege, :context, :attribute_check, :strong_params
648
- def initialize(actions, privilege, context, strong_params, attribute_check = false,
649
- load_object_model = nil, load_object_method = nil,
650
- filter_block = nil)
651
- @actions = actions.to_set
652
- @privilege = privilege
653
- @context = context
654
- @load_object_model = load_object_model
655
- @load_object_method = load_object_method
656
- @filter_block = filter_block
657
- @attribute_check = attribute_check
658
- @strong_params = strong_params
659
- end
660
-
661
- def matches?(action_name)
662
- @actions.include?(action_name.to_sym)
663
- end
664
-
665
- def permit!(contr)
666
- if @filter_block
667
- return contr.instance_eval(&@filter_block)
668
- end
669
- object = @attribute_check ? load_object(contr) : nil
670
- privilege = @privilege || :"#{contr.action_name}"
671
-
672
- contr.authorization_engine.permit!(privilege,
673
- :user => contr.send(:current_user),
674
- :object => object,
675
- :skip_attribute_test => !@attribute_check,
676
- :context => @context || contr.class.decl_auth_context)
677
- end
678
-
679
- def remove_actions(actions)
680
- @actions -= actions
681
- self
682
- end
683
-
684
- private
685
-
686
- def load_object(contr)
687
- if @load_object_method and @load_object_method.is_a?(Symbol)
688
- contr.send(@load_object_method)
689
- elsif @load_object_method and @load_object_method.is_a?(Proc)
690
- contr.instance_eval(&@load_object_method)
691
- else
692
- load_object_model = @load_object_model ||
693
- (@context ? @context.to_s.classify.constantize : contr.class.controller_name.classify.constantize)
694
- load_object_model = load_object_model.classify.constantize if load_object_model.is_a?(String)
695
- instance_var = "@#{load_object_model.name.demodulize.underscore}"
696
- object = contr.instance_variable_get(instance_var)
697
- unless object
698
- begin
699
- object = @strong_params ? load_object_model.find_or_initialize_by(:id => contr.params[:id]) : load_object_model.find(contr.params[:id])
700
- rescue => e
701
- contr.logger.debug("filter_access_to tried to find " +
702
- "#{load_object_model} from params[:id] " +
703
- "(#{contr.params[:id].inspect}), because attribute_check is enabled " +
704
- "and #{instance_var.to_s} isn't set, but failed: #{e.class.name}: #{e}")
705
- raise if AuthorizationInController.failed_auto_loading_is_not_found?
706
- end
707
- contr.instance_variable_set(instance_var, object)
708
- end
709
- object
710
- end
711
- end
712
- end
713
- end