stffn-declarative_authorization 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. data/CHANGELOG +9 -0
  2. data/README.rdoc +22 -6
  3. data/app/controllers/authorization_rules_controller.rb +135 -14
  4. data/app/helpers/authorization_rules_helper.rb +96 -13
  5. data/app/views/authorization_rules/_change.erb +49 -0
  6. data/app/views/authorization_rules/_show_graph.erb +37 -0
  7. data/app/views/authorization_rules/_suggestion.erb +9 -0
  8. data/app/views/authorization_rules/_suggestions.erb +24 -0
  9. data/app/views/authorization_rules/change.html.erb +124 -0
  10. data/app/views/authorization_rules/graph.dot.erb +23 -4
  11. data/app/views/authorization_rules/graph.html.erb +1 -0
  12. data/app/views/authorization_rules/index.html.erb +3 -2
  13. data/app/views/authorization_usages/index.html.erb +2 -11
  14. data/config/routes.rb +2 -1
  15. data/lib/declarative_authorization/authorization.rb +87 -35
  16. data/lib/declarative_authorization/development_support/analyzer.rb +252 -0
  17. data/lib/declarative_authorization/development_support/change_analyzer.rb +253 -0
  18. data/lib/declarative_authorization/development_support/change_supporter.rb +578 -0
  19. data/lib/declarative_authorization/development_support/development_support.rb +243 -0
  20. data/lib/declarative_authorization/helper.rb +6 -2
  21. data/lib/declarative_authorization/in_controller.rb +254 -26
  22. data/lib/declarative_authorization/in_model.rb +27 -2
  23. data/lib/declarative_authorization/maintenance.rb +22 -8
  24. data/lib/declarative_authorization/obligation_scope.rb +14 -9
  25. data/lib/declarative_authorization/reader.rb +10 -2
  26. data/test/authorization_test.rb +44 -0
  27. data/test/controller_filter_resource_access_test.rb +385 -0
  28. data/test/controller_test.rb +14 -6
  29. data/test/helper_test.rb +21 -0
  30. data/test/maintenance_test.rb +26 -0
  31. data/test/model_test.rb +28 -0
  32. data/test/test_helper.rb +14 -1
  33. metadata +15 -5
  34. data/lib/declarative_authorization/authorization_rules_analyzer.rb +0 -138
  35. data/test/authorization_rules_analyzer_test.rb +0 -123
@@ -0,0 +1,243 @@
1
+
2
+ module Authorization
3
+ module DevelopmentSupport
4
+ class AbstractAnalyzer
5
+ attr_reader :engine
6
+
7
+ def initialize (engine)
8
+ @engine = engine
9
+ end
10
+
11
+ def roles
12
+ AnalyzerEngine.roles(engine)
13
+ end
14
+
15
+ def rules
16
+ roles.collect {|role| role.rules }.flatten
17
+ end
18
+ end
19
+
20
+ # Groups utility methods and classes to better work with authorization object
21
+ # model.
22
+ module AnalyzerEngine
23
+
24
+ def self.roles (engine)
25
+ Role.all(engine)
26
+ end
27
+
28
+ def self.relevant_roles (engine, users)
29
+ users.collect {|user| user.role_symbols.map {|role_sym| Role.for_sym(role_sym, engine)}}.
30
+ flatten.uniq.collect {|role| [role] + role.ancestors}.flatten.uniq
31
+ end
32
+
33
+ def self.rule_for_permission (engine, privilege, context, role)
34
+ AnalyzerEngine.roles(engine).
35
+ find {|cloned_role| cloned_role.to_sym == role.to_sym}.rules.find do |rule|
36
+ rule.contexts.include?(context) and rule.privileges.include?(privilege)
37
+ end
38
+ end
39
+
40
+ def self.apply_change (engine, change)
41
+ case change[0]
42
+ when :add_role
43
+ role_symbol = change[1]
44
+ if engine.roles.include?(role_symbol)
45
+ false
46
+ else
47
+ engine.roles << role_symbol
48
+ true
49
+ end
50
+ when :add_privilege
51
+ privilege, context, role = change[1,3]
52
+ role = Role.for_sym(role.to_sym, engine)
53
+ privilege = Privilege.for_sym(privilege.to_sym, engine)
54
+ if ([privilege] + privilege.ancestors).any? {|ancestor_privilege| ([role] + role.ancestors).any? {|ancestor_role| !ancestor_role.rules_for_permission(ancestor_privilege, context).empty?}}
55
+ false
56
+ else
57
+ engine.auth_rules << AuthorizationRule.new(role.to_sym,
58
+ [privilege.to_sym], [context])
59
+ true
60
+ end
61
+ when :remove_privilege
62
+ privilege, context, role = change[1,3]
63
+ role = Role.for_sym(role.to_sym, engine)
64
+ privilege = Privilege.for_sym(privilege.to_sym, engine)
65
+ rules_with_priv = role.rules_for_permission(privilege, context)
66
+ if rules_with_priv.empty?
67
+ false
68
+ else
69
+ rules_with_priv.each do |rule|
70
+ rule.rule.privileges.delete(privilege.to_sym)
71
+ engine.auth_rules.delete(rule.rule) if rule.rule.privileges.empty?
72
+ end
73
+ true
74
+ end
75
+ end
76
+ end
77
+
78
+ class Role
79
+ @@role_objects = {}
80
+ attr_reader :role
81
+ def initialize (role, rules, engine)
82
+ @role = role
83
+ @rules = rules
84
+ @engine = engine
85
+ end
86
+
87
+ def source_line
88
+ @rules.empty? ? nil : @rules.first.source_line
89
+ end
90
+ def source_file
91
+ @rules.empty? ? nil : @rules.first.source_file
92
+ end
93
+
94
+ # ancestors' privileges are included in in the current role
95
+ def ancestors (role_symbol = nil)
96
+ role_symbol ||= @role
97
+ (@engine.role_hierarchy[role_symbol] || []).
98
+ collect {|lower_role| ancestors(lower_role) }.flatten +
99
+ (role_symbol == @role ? [] : [Role.for_sym(role_symbol, @engine)])
100
+ end
101
+ def descendants (role_symbol = nil)
102
+ role_symbol ||= @role
103
+ (@engine.rev_role_hierarchy[role_symbol] || []).
104
+ collect {|higher_role| descendants(higher_role) }.flatten +
105
+ (role_symbol == @role ? [] : [Role.for_sym(role_symbol, @engine)])
106
+ end
107
+
108
+ def rules
109
+ @rules ||= @engine.auth_rules.select {|rule| rule.role == @role}.
110
+ collect {|rule| Rule.new(rule, @engine)}
111
+ end
112
+ def rules_for_permission (privilege, context)
113
+ rules.select do |rule|
114
+ rule.matches?([@role], [privilege.to_sym], context)
115
+ end
116
+ end
117
+
118
+ def to_sym
119
+ @role
120
+ end
121
+ def self.for_sym (role_sym, engine)
122
+ @@role_objects[[role_sym, engine]] ||= new(role_sym, nil, engine)
123
+ end
124
+
125
+ def self.all (engine)
126
+ rules_by_role = engine.auth_rules.inject({}) do |memo, rule|
127
+ memo[rule.role] ||= []
128
+ memo[rule.role] << rule
129
+ memo
130
+ end
131
+ engine.roles.collect do |role|
132
+ new(role, (rules_by_role[role] || []).
133
+ collect {|rule| Rule.new(rule, engine)}, engine)
134
+ end
135
+ end
136
+ def self.all_for_privilege (privilege, context, engine)
137
+ privilege = privilege.is_a?(Symbol) ? Privilege.for_sym(privilege, engine) : privilege
138
+ privilege_symbols = ([privilege] + privilege.ancestors).map(&:to_sym)
139
+ all(engine).select {|role| role.rules.any? {|rule| rule.matches?([role.to_sym], privilege_symbols, context)}}.
140
+ collect {|role| [role] + role.descendants}.flatten.uniq
141
+ end
142
+ end
143
+
144
+ class Rule
145
+ @@rule_objects = {}
146
+ delegate :source_line, :source_file, :contexts, :matches?, :to => :@rule
147
+ attr_reader :rule
148
+ def initialize (rule, engine)
149
+ @rule = rule
150
+ @engine = engine
151
+ end
152
+ def privileges
153
+ PrivilegesSet.new(self, @engine, @rule.privileges.collect {|privilege| Privilege.for_sym(privilege, @engine) })
154
+ end
155
+ def self.for_rule (rule, engine)
156
+ @@rule_objects[[rule, engine]] ||= new(rule, engine)
157
+ end
158
+ end
159
+
160
+ class Privilege
161
+ @@privilege_objects = {}
162
+ def initialize (privilege, engine)
163
+ @privilege = privilege
164
+ @engine = engine
165
+ end
166
+
167
+ # Ancestor privileges are higher in the hierarchy.
168
+ # Doesn't take context into account.
169
+ def ancestors (priv_symbol = nil)
170
+ priv_symbol ||= @privilege
171
+ # context-specific?
172
+ (@engine.rev_priv_hierarchy[[priv_symbol, nil]] || []).
173
+ collect {|higher_priv| ancestors(higher_priv) }.flatten +
174
+ (priv_symbol == @privilege ? [] : [Privilege.for_sym(priv_symbol, @engine)])
175
+ end
176
+ def descendants (priv_symbol = nil)
177
+ priv_symbol ||= @privilege
178
+ # context-specific?
179
+ (@engine.privilege_hierarchy[priv_symbol] || []).
180
+ collect {|lower_priv, context| descendants(lower_priv) }.flatten +
181
+ (priv_symbol == @privilege ? [] : [Privilege.for_sym(priv_symbol, @engine)])
182
+ end
183
+
184
+ def rules
185
+ @rules ||= find_rules_for_privilege
186
+ end
187
+ def source_line
188
+ rules.empty? ? nil : rules.first.source_line
189
+ end
190
+ def source_file
191
+ rules.empty? ? nil : rules.first.source_file
192
+ end
193
+
194
+ def to_sym
195
+ @privilege
196
+ end
197
+ def self.for_sym (privilege_sym, engine)
198
+ @@privilege_objects[[privilege_sym, engine]] ||= new(privilege_sym, engine)
199
+ end
200
+
201
+ private
202
+ def find_rules_for_privilege
203
+ @engine.auth_rules.select {|rule| rule.privileges.include?(@privilege)}.
204
+ collect {|rule| Rule.for_rule(rule, @engine)}
205
+ end
206
+ end
207
+
208
+ class PrivilegesSet < Set
209
+ def initialize (*args)
210
+ if args.length > 2
211
+ @rule = args.shift
212
+ @engine = args.shift
213
+ end
214
+ super(*args)
215
+ end
216
+ def include? (privilege)
217
+ if privilege.is_a?(Symbol)
218
+ super(privilege_from_symbol(privilege))
219
+ else
220
+ super
221
+ end
222
+ end
223
+ def delete (privilege)
224
+ @rule.rule.privileges.delete(privilege.to_sym)
225
+ if privilege.is_a?(Symbol)
226
+ super(privilege_from_symbol(privilege))
227
+ else
228
+ super
229
+ end
230
+ end
231
+
232
+ def intersects? (privileges)
233
+ intersection(privileges).length > 0
234
+ end
235
+
236
+ private
237
+ def privilege_from_symbol (privilege_sym)
238
+ Privilege.for_sym(privilege_sym, @engine)
239
+ end
240
+ end
241
+ end
242
+ end
243
+ end
@@ -23,9 +23,13 @@ module Authorization
23
23
  # <% for user in @users %>
24
24
  # <%= link_to 'Edit', edit_user_path(user) if permitted_to? :update, user %>
25
25
  # <% end %>
26
+ #
27
+ # To pass in an object and override the context, you can use the optional
28
+ # options:
29
+ # permitted_to? :update, user, :context => :account
26
30
  #
27
- def permitted_to? (privilege, object_or_sym = nil, &block)
28
- controller.permitted_to?(privilege, object_or_sym, &block)
31
+ def permitted_to? (privilege, object_or_sym = nil, options = {}, &block)
32
+ controller.permitted_to?(privilege, object_or_sym, options, &block)
29
33
  end
30
34
 
31
35
  # While permitted_to? is used for authorization in views, in some cases
@@ -24,36 +24,38 @@ module Authorization
24
24
  #
25
25
  # See examples for Authorization::AuthorizationHelper #permitted_to?
26
26
  #
27
- def permitted_to? (privilege, object_or_sym = nil, &block)
28
- context = object = nil
29
- if object_or_sym.is_a?(Symbol)
30
- context = object_or_sym
31
- else
32
- object = object_or_sym
33
- end
34
- # TODO infer context also from self.class.name
35
- authorization_engine.permit?(privilege,
36
- {:user => current_user,
37
- :object => object,
38
- :context => context,
39
- :skip_attribute_test => object.nil?},
40
- &block)
27
+ # If no object or context is specified, the controller_name is used as
28
+ # context.
29
+ #
30
+ def permitted_to? (privilege, object_or_sym = nil, options = {}, &block)
31
+ permitted_to!(privilege, object_or_sym, options.merge(:non_bang => true), &block)
41
32
  end
42
33
 
43
- # Works similar to the permitted_to? method, but doesn't accept a block
44
- # and throws the authorization exceptions, just like Engine#permit!
45
- def permitted_to! (privilege, object_or_sym = nil)
34
+ # Works similar to the permitted_to? method, but
35
+ # throws the authorization exceptions, just like Engine#permit!
36
+ def permitted_to! (privilege, object_or_sym = nil, options = {}, &block)
46
37
  context = object = nil
47
- if object_or_sym.is_a?(Symbol)
38
+ if object_or_sym.nil?
39
+ context = self.class.controller_name.to_sym
40
+ elsif object_or_sym.is_a?(Symbol)
48
41
  context = object_or_sym
49
42
  else
50
43
  object = object_or_sym
51
44
  end
52
- authorization_engine.permit!(privilege,
53
- {:user => current_user,
54
- :object => object,
55
- :context => context,
56
- :skip_attribute_test => object.nil?})
45
+
46
+ non_bang = options.delete(:non_bang)
47
+ args = [
48
+ privilege,
49
+ {:user => current_user,
50
+ :object => object,
51
+ :context => context,
52
+ :skip_attribute_test => object.nil?}.merge(options)
53
+ ]
54
+ if non_bang
55
+ authorization_engine.permit?(*args, &block)
56
+ else
57
+ authorization_engine.permit!(*args, &block)
58
+ end
57
59
  end
58
60
 
59
61
  # While permitted_to? is used for authorization, in some cases
@@ -115,7 +117,36 @@ module Authorization
115
117
  end
116
118
  end
117
119
  end
118
-
120
+
121
+ def load_controller_object (context) # :nodoc:
122
+ instance_var = :"@#{context.to_s.singularize}"
123
+ model = context.to_s.classify.constantize
124
+ instance_variable_set(instance_var, model.find(params[:id]))
125
+ end
126
+
127
+ def load_parent_controller_object (parent_context) # :nodoc:
128
+ instance_var = :"@#{parent_context.to_s.singularize}"
129
+ model = parent_context.to_s.classify.constantize
130
+ instance_variable_set(instance_var, model.find(params[:"#{parent_context.to_s.singularize}_id"]))
131
+ end
132
+
133
+ def new_controller_object_from_params (context, parent_context) # :nodoc:
134
+ model_or_proxy = parent_context ?
135
+ instance_variable_get(:"@#{parent_context.to_s.singularize}").send(context.to_sym) :
136
+ context.to_s.classify.constantize
137
+ instance_var = :"@#{context.to_s.singularize}"
138
+ instance_variable_set(instance_var,
139
+ model_or_proxy.new(params[context.to_s.singularize]))
140
+ end
141
+
142
+ def new_controller_object_for_collection (context, parent_context) # :nodoc:
143
+ model_or_proxy = parent_context ?
144
+ instance_variable_get(:"@#{parent_context.to_s.singularize}").send(context.to_sym) :
145
+ context.to_s.classify.constantize
146
+ instance_var = :"@#{context.to_s.singularize}"
147
+ instance_variable_set(instance_var, model_or_proxy.new)
148
+ end
149
+
119
150
  module ClassMethods
120
151
  #
121
152
  # Defines a filter to be applied according to the authorization of the
@@ -258,6 +289,176 @@ module Authorization
258
289
  end
259
290
  end
260
291
  end
292
+
293
+ # To DRY up the filter_access_to statements in restful controllers,
294
+ # filter_resource_access combines typical filter_access_to and
295
+ # before_filter calls, which set up the instance variables.
296
+ #
297
+ # The simplest case are top-level resource controllers with only the
298
+ # seven CRUD methods, e.g.
299
+ # class CompanyController < ApplicationController
300
+ # filter_resource_access
301
+ #
302
+ # def index...
303
+ # end
304
+ # Here, all CRUD actions are protected through a filter_access_to :all
305
+ # statement. :+attribute_check+ is enabled for all actions except for
306
+ # the collection action :+index+. To have an object for attribute checks
307
+ # available, filter_resource_access will set the instance variable
308
+ # @+company+ in before filters. For the member actions (:+show+, :+edit+,
309
+ # :+update+, :+destroy+) @company is set to Company.find(params[:id]).
310
+ # For +new+ actions (:+new+, :+create+), filter_resource_access creates
311
+ # a new object from company parameters: Company.new(params[:company].
312
+ #
313
+ # For nested resources, the parent object may be loaded automatically.
314
+ # class BranchController < ApplicationController
315
+ # filter_resource_access :nested_in => :companies
316
+ # end
317
+ # Again, the CRUD actions are protected. Now, for all CRUD actions,
318
+ # the parent object @company is loaded from params[:company_id]. It is
319
+ # also used when creating @branch for +new+ actions. Here, attribute_check
320
+ # is enabled for the collection :+index+ as well, checking attributes on a
321
+ # @company.branches.new method.
322
+ #
323
+ # You can override the default object loading by implementing any of the
324
+ # following instance methods on the controller. Examples are given for the
325
+ # BranchController (with +nested_in+ set to :+companies+):
326
+ # [+new_branch_from_params+]
327
+ # Used for +new+ actions.
328
+ # [+new_branch_for_collection+]
329
+ # Used for +collection+ actions if the +nested_in+ option is set.
330
+ # [+load_branch+]
331
+ # Used for +member+ actions.
332
+ # [+load_company+]
333
+ # Used for all +new+, +member+, and +collection+ actions if the
334
+ # +nested_in+ option is set.
335
+ #
336
+ # All options:
337
+ # [:+member+]
338
+ # Member methods are actions like +show+, which have an params[:id] from
339
+ # which to load the controller object and assign it to @controller_name,
340
+ # e.g. @+branch+. By default, member actions are [:+show+, :+edit+, :+update+,
341
+ # :+destroy+].
342
+ # [:+additional_member+]
343
+ # Allows to add additional member actions to the default resource +member+
344
+ # actions.
345
+ # [:+collection+]
346
+ # Collection actions are like :+index+, actions without any controller object
347
+ # to check attributes of. If +nested_in+ is given, a new object is
348
+ # created from the parent object, e.g. @company.branches.new. Without
349
+ # +nested_in+, attribute check is deactivated for these actions. By
350
+ # default, collection is set to :+index+.
351
+ # [:+additional_collection+]
352
+ # Allows to add additional collaction actions to the default resource +collection+
353
+ # actions.
354
+ # [:+new+]
355
+ # +new+ methods are actions such as +new+ and +create+, which don't
356
+ # receive a params[:id] to load an object from, but
357
+ # a params[:controller_name_singular] hash with attributes for a new
358
+ # object. The attributes will be used here to create a new object and
359
+ # check the object against the authorization rules. The object is
360
+ # assigned to @controller_name_singular, e.g. @branch.
361
+ #
362
+ # If +nested_in+ is given, the new object
363
+ # is created from the parent_object.controller_name
364
+ # proxy, e.g. company.branches.new(params[:branch]). By default,
365
+ # +new+ is set to [:new, :create].
366
+ # [:+additional_new+]
367
+ # Allows to add additional new actions to the default resource +new+ actions.
368
+ # [:+context+]
369
+ # The context is used to determine the model to load objects from for the
370
+ # before_filters and the context of privileges to use in authorization
371
+ # checks.
372
+ # [:+nested_in+]
373
+ # Specifies the parent controller if the resource is nested in another
374
+ # one. This is used to automatically load the parent object, e.g.
375
+ # @+company+ from params[:company_id] for a BranchController nested in
376
+ # a CompanyController.
377
+ # [:+no_attribute_check+]
378
+ # Allows to set actions for which no attribute check should be perfomed.
379
+ # See filter_access_to on details. By default, with no +nested_in+,
380
+ # +no_attribute_check+ is set to all collections. If +nested_in+ is given
381
+ # +no_attribute_check+ is empty by default.
382
+ #
383
+ def filter_resource_access(options = {})
384
+ options = {
385
+ :new => [:new, :create],
386
+ :additional_new => nil,
387
+ :member => [:show, :edit, :update, :destroy],
388
+ :additional_member => nil,
389
+ :collection => [:index],
390
+ :additional_collection => nil,
391
+ #:new_method_for_collection => nil, # only symbol method name
392
+ #:new_method => nil, # only symbol method name
393
+ #:load_method => nil, # only symbol method name
394
+ :no_attribute_check => nil,
395
+ :context => nil,
396
+ :nested_in => nil,
397
+ }.merge(options)
398
+
399
+ new_actions = actions_from_option(options[:new]).merge(
400
+ actions_from_option(options[:additional_new]))
401
+ members = actions_from_option(options[:member]).merge(
402
+ actions_from_option(options[:additional_member]))
403
+ collections = actions_from_option(options[:collection]).merge(
404
+ actions_from_option(options[:additional_collection]))
405
+
406
+ options[:no_attribute_check] ||= collections.keys unless options[:nested_in]
407
+
408
+ unless options[:nested_in].blank?
409
+ load_method = :"load_#{options[:nested_in].to_s.singularize}"
410
+ before_filter do |controller|
411
+ if controller.respond_to?(load_method)
412
+ controller.send(load_method)
413
+ else
414
+ controller.send(:load_parent_controller_object, options[:nested_in])
415
+ end
416
+ end
417
+
418
+ new_for_collection_method = :"new_#{controller_name.singularize}_for_collection"
419
+ before_filter :only => collections.keys do |controller|
420
+ # new_for_collection
421
+ if controller.respond_to?(new_for_collection_method)
422
+ controller.send(new_for_collection_method)
423
+ else
424
+ controller.send(:new_controller_object_for_collection,
425
+ options[:context] || controller_name, options[:nested_in])
426
+ end
427
+ end
428
+ end
429
+
430
+ new_from_params_method = :"new_#{controller_name.singularize}_from_params"
431
+ before_filter :only => new_actions.keys do |controller|
432
+ # new_from_params
433
+ if controller.respond_to?(new_from_params_method)
434
+ controller.send(new_from_params_method)
435
+ else
436
+ controller.send(:new_controller_object_from_params,
437
+ options[:context] || controller_name, options[:nested_in])
438
+ end
439
+ end
440
+ load_method = :"load_#{controller_name.singularize}"
441
+ before_filter :only => members.keys do |controller|
442
+ # load controller object
443
+ if controller.respond_to?(load_method)
444
+ controller.send(load_method)
445
+ else
446
+ controller.send(:load_controller_object, options[:context] || controller_name)
447
+ end
448
+ end
449
+ filter_access_to :all, :attribute_check => true, :context => options[:context]
450
+
451
+ members.merge(new_actions).merge(collections).each do |action, privilege|
452
+ if action != privilege or (options[:no_attribute_check] and options[:no_attribute_check].include?(action))
453
+ filter_options = {
454
+ :context => options[:context],
455
+ :attribute_check => !options[:no_attribute_check] || !options[:no_attribute_check].include?(action)
456
+ }
457
+ filter_options[:require] = privilege if action != privilege
458
+ filter_access_to(action, filter_options)
459
+ end
460
+ end
461
+ end
261
462
 
262
463
  protected
263
464
  def filter_access_permissions # :nodoc:
@@ -273,6 +474,26 @@ module Authorization
273
474
  def filter_access_permissions? # :nodoc:
274
475
  class_variable_defined?(:@@declarative_authorization_permissions)
275
476
  end
477
+
478
+ def actions_from_option (option) # :nodoc:
479
+ case option
480
+ when nil
481
+ {}
482
+ when Symbol, String
483
+ {option.to_sym => option.to_sym}
484
+ when Hash
485
+ option
486
+ when Enumerable
487
+ option.each_with_object({}) do |action, hash|
488
+ if action.is_a?(Array)
489
+ raise "Unexpected option format: #{option.inspect}" if action.length != 2
490
+ hash[action.first] = action.last
491
+ else
492
+ hash[action.to_sym] = action.to_sym
493
+ end
494
+ end
495
+ end
496
+ end
276
497
  end
277
498
  end
278
499
 
@@ -332,8 +553,15 @@ module Authorization
332
553
  instance_var = :"@#{load_object_model.name.underscore}"
333
554
  object = contr.instance_variable_get(instance_var)
334
555
  unless object
335
- # catch ActiveRecord::RecordNotFound?
336
- object = load_object_model.find(contr.params[:id])
556
+ begin
557
+ object = load_object_model.find(contr.params[:id])
558
+ rescue ActiveRecord::RecordNotFound
559
+ logger.debug("filter_access_to tried to find " +
560
+ "#{load_object_model.inspect} from params[:id] " +
561
+ "(#{contr.params[:id].inspect}), because attribute_check is enabled " +
562
+ "and #{instance_var.to_s} isn't set.")
563
+ raise
564
+ end
337
565
  contr.instance_variable_set(instance_var, object)
338
566
  end
339
567
  object