cancancan 1.17.0 → 3.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +5 -5
  2. data/cancancan.gemspec +10 -11
  3. data/init.rb +2 -0
  4. data/lib/cancan/ability/actions.rb +93 -0
  5. data/lib/cancan/ability/rules.rb +96 -0
  6. data/lib/cancan/ability/strong_parameter_support.rb +41 -0
  7. data/lib/cancan/ability.rb +87 -198
  8. data/lib/cancan/class_matcher.rb +30 -0
  9. data/lib/cancan/conditions_matcher.rb +147 -0
  10. data/lib/cancan/config.rb +101 -0
  11. data/lib/cancan/controller_additions.rb +13 -30
  12. data/lib/cancan/controller_resource.rb +33 -225
  13. data/lib/cancan/controller_resource_builder.rb +26 -0
  14. data/lib/cancan/controller_resource_finder.rb +42 -0
  15. data/lib/cancan/controller_resource_loader.rb +120 -0
  16. data/lib/cancan/controller_resource_name_finder.rb +23 -0
  17. data/lib/cancan/controller_resource_sanitizer.rb +32 -0
  18. data/lib/cancan/exceptions.rb +24 -4
  19. data/lib/cancan/matchers.rb +12 -1
  20. data/lib/cancan/model_adapters/abstract_adapter.rb +22 -1
  21. data/lib/cancan/model_adapters/active_record_4_adapter.rb +25 -44
  22. data/lib/cancan/model_adapters/active_record_5_adapter.rb +61 -0
  23. data/lib/cancan/model_adapters/active_record_adapter.rb +157 -83
  24. data/lib/cancan/model_adapters/conditions_extractor.rb +75 -0
  25. data/lib/cancan/model_adapters/conditions_normalizer.rb +49 -0
  26. data/lib/cancan/model_adapters/default_adapter.rb +2 -0
  27. data/lib/cancan/model_adapters/sti_normalizer.rb +47 -0
  28. data/lib/cancan/model_adapters/strategies/base.rb +40 -0
  29. data/lib/cancan/model_adapters/strategies/joined_alias_each_rule_as_exists_subquery.rb +93 -0
  30. data/lib/cancan/model_adapters/strategies/joined_alias_exists_subquery.rb +31 -0
  31. data/lib/cancan/model_adapters/strategies/left_join.rb +11 -0
  32. data/lib/cancan/model_adapters/strategies/subquery.rb +18 -0
  33. data/lib/cancan/model_additions.rb +6 -2
  34. data/lib/cancan/parameter_validators.rb +9 -0
  35. data/lib/cancan/relevant.rb +29 -0
  36. data/lib/cancan/rule.rb +67 -90
  37. data/lib/cancan/rules_compressor.rb +23 -0
  38. data/lib/cancan/sti_detector.rb +12 -0
  39. data/lib/cancan/unauthorized_message_resolver.rb +24 -0
  40. data/lib/cancan/version.rb +3 -1
  41. data/lib/cancan.rb +15 -10
  42. data/lib/cancancan.rb +2 -0
  43. data/lib/generators/cancan/ability/ability_generator.rb +3 -1
  44. data/lib/generators/cancan/ability/templates/ability.rb +9 -9
  45. metadata +64 -86
  46. data/.gitignore +0 -15
  47. data/.rspec +0 -1
  48. data/.rubocop.yml +0 -39
  49. data/.rubocop_todo.yml +0 -54
  50. data/.travis.yml +0 -39
  51. data/Appraisals +0 -105
  52. data/CHANGELOG.rdoc +0 -536
  53. data/CONTRIBUTING.md +0 -23
  54. data/Gemfile +0 -3
  55. data/LICENSE +0 -22
  56. data/README.md +0 -234
  57. data/Rakefile +0 -13
  58. data/gemfiles/activerecord_3.2.gemfile +0 -18
  59. data/gemfiles/activerecord_4.0.gemfile +0 -19
  60. data/gemfiles/activerecord_4.1.gemfile +0 -19
  61. data/gemfiles/activerecord_4.2.gemfile +0 -21
  62. data/gemfiles/activerecord_5.0.gemfile +0 -20
  63. data/gemfiles/mongoid_2.x.gemfile +0 -18
  64. data/gemfiles/sequel_3.x.gemfile +0 -18
  65. data/lib/cancan/inherited_resource.rb +0 -20
  66. data/lib/cancan/model_adapters/active_record_3_adapter.rb +0 -16
  67. data/lib/cancan/model_adapters/mongoid_adapter.rb +0 -80
  68. data/lib/cancan/model_adapters/sequel_adapter.rb +0 -87
  69. data/spec/README.rdoc +0 -27
  70. data/spec/cancan/ability_spec.rb +0 -553
  71. data/spec/cancan/controller_additions_spec.rb +0 -164
  72. data/spec/cancan/controller_resource_spec.rb +0 -645
  73. data/spec/cancan/exceptions_spec.rb +0 -58
  74. data/spec/cancan/inherited_resource_spec.rb +0 -71
  75. data/spec/cancan/matchers_spec.rb +0 -29
  76. data/spec/cancan/model_adapters/active_record_4_adapter_spec.rb +0 -160
  77. data/spec/cancan/model_adapters/active_record_adapter_spec.rb +0 -415
  78. data/spec/cancan/model_adapters/default_adapter_spec.rb +0 -7
  79. data/spec/cancan/model_adapters/mongoid_adapter_spec.rb +0 -246
  80. data/spec/cancan/model_adapters/sequel_adapter_spec.rb +0 -129
  81. data/spec/cancan/rule_spec.rb +0 -52
  82. data/spec/matchers.rb +0 -13
  83. data/spec/spec.opts +0 -2
  84. data/spec/spec_helper.rb +0 -27
  85. data/spec/support/ability.rb +0 -6
@@ -1,8 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'controller_resource_loader.rb'
1
4
  module CanCan
2
5
  # Handle the load and authorization controller logic
3
6
  # so we don't clutter up all controllers with non-interface methods.
4
7
  # This class is used internally, so you do not need to call methods directly on it.
5
8
  class ControllerResource # :nodoc:
9
+ include ControllerResourceLoader
10
+
6
11
  def self.add_before_action(controller_class, method, *args)
7
12
  options = args.extract_options!
8
13
  resource_name = args.first
@@ -14,11 +19,7 @@ module CanCan
14
19
  end
15
20
 
16
21
  def self.before_callback_name(options)
17
- if ActiveSupport.respond_to?(:version) && ActiveSupport.version >= Gem::Version.new('4')
18
- options.delete(:prepend) ? :prepend_before_action : :before_action
19
- else
20
- options.delete(:prepend) ? :prepend_before_filter : :before_filter
21
- end
22
+ options.delete(:prepend) ? :prepend_before_action : :before_action
22
23
  end
23
24
 
24
25
  def initialize(controller, *args)
@@ -26,12 +27,6 @@ module CanCan
26
27
  @params = controller.params
27
28
  @options = args.extract_options!
28
29
  @name = args.first
29
- nested_err = 'The :nested option is no longer supported, instead use :through with separate load/authorize call.'
30
- raise CanCan::ImplementationRemoved, nested_err if @options[:nested]
31
- name_err = 'The :name option is no longer supported, instead pass the name as the first argument.'
32
- raise CanCan::ImplementationRemoved, name_err if @options[:name]
33
- resource_err = 'The :resource option has been renamed back to :class, use false if no class.'
34
- raise CanCan::ImplementationRemoved, resource_err if @options[:resource]
35
30
  end
36
31
 
37
32
  def load_and_authorize_resource
@@ -39,17 +34,9 @@ module CanCan
39
34
  authorize_resource
40
35
  end
41
36
 
42
- def load_resource
43
- return if skip?(:load)
44
- if load_instance?
45
- self.resource_instance ||= load_resource_instance
46
- elsif load_collection?
47
- self.collection_instance ||= load_collection
48
- end
49
- end
50
-
51
37
  def authorize_resource
52
38
  return if skip?(:authorize)
39
+
53
40
  @controller.authorize!(authorization_action, resource_instance || resource_class_with_parent)
54
41
  end
55
42
 
@@ -59,6 +46,7 @@ module CanCan
59
46
 
60
47
  def skip?(behavior)
61
48
  return false unless (options = @controller.class.cancan_skipper[behavior][@name])
49
+
62
50
  options == {} ||
63
51
  options[:except] && !action_exists_in?(options[:except]) ||
64
52
  action_exists_in?(options[:only])
@@ -66,11 +54,19 @@ module CanCan
66
54
 
67
55
  protected
68
56
 
69
- def load_resource_instance
70
- if !parent? && new_actions.include?(@params[:action].to_sym)
71
- build_resource
72
- elsif id_param || @options[:singleton]
73
- find_resource
57
+ # Returns the class used for this resource. This can be overridden by the :class option.
58
+ # If +false+ is passed in it will use the resource name as a symbol in which case it should
59
+ # only be used for authorization, not loading since there's no class to load through.
60
+ def resource_class
61
+ case @options[:class]
62
+ when false
63
+ name.to_sym
64
+ when nil
65
+ namespaced_name.to_s.camelize.constantize
66
+ when String
67
+ @options[:class].constantize
68
+ else
69
+ @options[:class]
74
70
  end
75
71
  end
76
72
 
@@ -82,95 +78,12 @@ module CanCan
82
78
  resource_base.respond_to?(:accessible_by) && !current_ability.has_block?(authorization_action, resource_class)
83
79
  end
84
80
 
85
- def load_collection
86
- resource_base.accessible_by(current_ability, authorization_action)
87
- end
88
-
89
- def build_resource
90
- resource = resource_base.new(resource_params || {})
91
- assign_attributes(resource)
92
- end
93
-
94
- def assign_attributes(resource)
95
- resource.send("#{parent_name}=", parent_resource) if @options[:singleton] && parent_resource
96
- initial_attributes.each do |attr_name, value|
97
- resource.send("#{attr_name}=", value)
98
- end
99
- resource
100
- end
101
-
102
- def initial_attributes
103
- current_ability.attributes_for(@params[:action].to_sym, resource_class).delete_if do |key, _value|
104
- resource_params && resource_params.include?(key)
105
- end
106
- end
107
-
108
- def find_resource
109
- if @options[:singleton] && parent_resource.respond_to?(name)
110
- parent_resource.send(name)
111
- elsif @options[:find_by]
112
- find_resource_using_find_by
113
- else
114
- adapter.find(resource_base, id_param)
115
- end
116
- end
117
-
118
- def find_resource_using_find_by
119
- if resource_base.respond_to? "find_by_#{@options[:find_by]}!"
120
- resource_base.send("find_by_#{@options[:find_by]}!", id_param)
121
- elsif resource_base.respond_to? 'find_by'
122
- resource_base.send('find_by', @options[:find_by].to_sym => id_param)
123
- else
124
- resource_base.send(@options[:find_by], id_param)
125
- end
126
- end
127
-
128
- def adapter
129
- ModelAdapters::AbstractAdapter.adapter_class(resource_class)
130
- end
131
-
132
- def authorization_action
133
- parent? ? parent_authorization_action : @params[:action].to_sym
134
- end
135
-
136
- def parent_authorization_action
137
- @options[:parent_action] || :show
138
- end
139
-
140
- def id_param
141
- @params[id_param_key].to_s if @params[id_param_key]
142
- end
143
-
144
- def id_param_key
145
- if @options[:id_param]
146
- @options[:id_param]
147
- else
148
- parent? ? :"#{name}_id" : :id
149
- end
150
- end
151
-
152
81
  def member_action?
153
82
  new_actions.include?(@params[:action].to_sym) || @options[:singleton] ||
154
83
  ((@params[:id] || @params[@options[:id_param]]) &&
155
84
  !collection_actions.include?(@params[:action].to_sym))
156
85
  end
157
86
 
158
- # Returns the class used for this resource. This can be overriden by the :class option.
159
- # If +false+ is passed in it will use the resource name as a symbol in which case it should
160
- # only be used for authorization, not loading since there's no class to load through.
161
- def resource_class
162
- case @options[:class]
163
- when false then
164
- name.to_sym
165
- when nil then
166
- namespaced_name.to_s.camelize.constantize
167
- when String then
168
- @options[:class].constantize
169
- else
170
- @options[:class]
171
- end
172
- end
173
-
174
87
  def resource_class_with_parent
175
88
  parent_resource ? { parent_resource => resource_class } : resource_class
176
89
  end
@@ -180,7 +93,9 @@ module CanCan
180
93
  end
181
94
 
182
95
  def resource_instance
183
- @controller.instance_variable_get("@#{instance_name}") if load_instance?
96
+ return unless load_instance? && @controller.instance_variable_defined?("@#{instance_name}")
97
+
98
+ @controller.instance_variable_get("@#{instance_name}")
184
99
  end
185
100
 
186
101
  def collection_instance=(instance)
@@ -188,122 +103,15 @@ module CanCan
188
103
  end
189
104
 
190
105
  def collection_instance
191
- @controller.instance_variable_get("@#{instance_name.to_s.pluralize}")
192
- end
193
-
194
- # The object that methods (such as "find", "new" or "build") are called on.
195
- # If the :through option is passed it will go through an association on that instance.
196
- # If the :shallow option is passed it will use the resource_class if there's no parent
197
- # If the :singleton option is passed it won't use the association because it needs to be handled later.
198
- def resource_base
199
- if @options[:through]
200
- resource_base_through
201
- else
202
- resource_class
203
- end
204
- end
205
-
206
- def resource_base_through
207
- if parent_resource
208
- base = if @options[:singleton]
209
- resource_class
210
- else
211
- parent_resource.send(@options[:through_association] || name.to_s.pluralize)
212
- end
213
- base = base.scoped if base.respond_to?(:scoped) && active_record_3?
214
- base
215
- elsif @options[:shallow]
216
- resource_class
217
- else
218
- # maybe this should be a record not found error instead?
219
- raise AccessDenied.new(nil, authorization_action, resource_class)
220
- end
221
- end
222
-
223
- def active_record_3?
224
- defined?(ActiveRecord) && ActiveRecord::VERSION::MAJOR == 3
225
- end
226
-
227
- def parent_name
228
- @options[:through] && [@options[:through]].flatten.detect { |i| fetch_parent(i) }
229
- end
230
-
231
- # The object to load this resource through.
232
- def parent_resource
233
- parent_name && fetch_parent(parent_name)
234
- end
235
-
236
- def fetch_parent(name)
237
- if @controller.instance_variable_defined? "@#{name}"
238
- @controller.instance_variable_get("@#{name}")
239
- elsif @controller.respond_to?(name, true)
240
- @controller.send(name)
241
- end
242
- end
243
-
244
- def current_ability
245
- @controller.send(:current_ability)
246
- end
106
+ return unless @controller.instance_variable_defined?("@#{instance_name.to_s.pluralize}")
247
107
 
248
- def name
249
- @name || name_from_controller
250
- end
251
-
252
- def resource_params
253
- if parameters_require_sanitizing? && params_method.present?
254
- case params_method
255
- when Symbol then
256
- @controller.send(params_method)
257
- when String then
258
- @controller.instance_eval(params_method)
259
- when Proc then
260
- params_method.call(@controller)
261
- end
262
- else
263
- resource_params_by_namespaced_name
264
- end
108
+ @controller.instance_variable_get("@#{instance_name.to_s.pluralize}")
265
109
  end
266
110
 
267
111
  def parameters_require_sanitizing?
268
112
  save_actions.include?(@params[:action].to_sym) || resource_params_by_namespaced_name.present?
269
113
  end
270
114
 
271
- def resource_params_by_namespaced_name
272
- if @options[:instance_name] && @params.key?(extract_key(@options[:instance_name]))
273
- @params[extract_key(@options[:instance_name])]
274
- elsif @options[:class] && @params.key?(extract_key(@options[:class]))
275
- @params[extract_key(@options[:class])]
276
- else
277
- @params[extract_key(namespaced_name)]
278
- end
279
- end
280
-
281
- def params_method
282
- params_methods.each do |method|
283
- return method if (method.is_a?(Symbol) && @controller.respond_to?(method, true)) ||
284
- method.is_a?(String) || method.is_a?(Proc)
285
- end
286
- nil
287
- end
288
-
289
- def params_methods
290
- methods = ["#{@params[:action]}_params".to_sym, "#{name}_params".to_sym, :resource_params]
291
- methods.unshift(@options[:param_method]) if @options[:param_method].present?
292
- methods
293
- end
294
-
295
- def namespace
296
- @params[:controller].split('/')[0..-2]
297
- end
298
-
299
- def namespaced_name
300
- [namespace, name].join('/').singularize.camelize.safe_constantize || name
301
- end
302
-
303
- def name_from_controller
304
- @params[:controller].split('/').last.singularize
305
- end
306
-
307
115
  def instance_name
308
116
  @options[:instance_name] || name
309
117
  end
@@ -312,12 +120,8 @@ module CanCan
312
120
  [:index] + Array(@options[:collection])
313
121
  end
314
122
 
315
- def new_actions
316
- [:new, :create] + Array(@options[:new])
317
- end
318
-
319
123
  def save_actions
320
- [:create, :update]
124
+ %i[create update]
321
125
  end
322
126
 
323
127
  private
@@ -326,8 +130,12 @@ module CanCan
326
130
  Array(options).include?(@params[:action].to_sym)
327
131
  end
328
132
 
329
- def extract_key(value)
330
- value.to_s.underscore.tr('/', '_')
133
+ def adapter
134
+ ModelAdapters::AbstractAdapter.adapter_class(resource_class)
135
+ end
136
+
137
+ def current_ability
138
+ @controller.send(:current_ability)
331
139
  end
332
140
  end
333
141
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CanCan
4
+ module ControllerResourceBuilder
5
+ protected
6
+
7
+ def build_resource
8
+ resource = resource_base.new(resource_params || {})
9
+ assign_attributes(resource)
10
+ end
11
+
12
+ def assign_attributes(resource)
13
+ resource.send("#{parent_name}=", parent_resource) if @options[:singleton] && parent_resource
14
+ initial_attributes.each do |attr_name, value|
15
+ resource.send("#{attr_name}=", value)
16
+ end
17
+ resource
18
+ end
19
+
20
+ def initial_attributes
21
+ current_ability.attributes_for(@params[:action].to_sym, resource_class).delete_if do |key, _value|
22
+ resource_params && resource_params.include?(key)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CanCan
4
+ module ControllerResourceFinder
5
+ protected
6
+
7
+ def find_resource
8
+ if @options[:singleton] && parent_resource.respond_to?(name)
9
+ parent_resource.send(name)
10
+ elsif @options[:find_by]
11
+ find_resource_using_find_by
12
+ else
13
+ adapter.find(resource_base, id_param)
14
+ end
15
+ end
16
+
17
+ def find_resource_using_find_by
18
+ find_by_dynamic_finder || find_by_find_by_finder || resource_base.send(@options[:find_by], id_param)
19
+ end
20
+
21
+ def find_by_dynamic_finder
22
+ method_name = "find_by_#{@options[:find_by]}!"
23
+ resource_base.send(method_name, id_param) if resource_base.respond_to? method_name
24
+ end
25
+
26
+ def find_by_find_by_finder
27
+ resource_base.find_by(@options[:find_by].to_sym => id_param) if resource_base.respond_to? :find_by
28
+ end
29
+
30
+ def id_param
31
+ @params[id_param_key].to_s if @params[id_param_key].present?
32
+ end
33
+
34
+ def id_param_key
35
+ if @options[:id_param]
36
+ @options[:id_param]
37
+ else
38
+ parent? ? :"#{name}_id" : :id
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'controller_resource_finder.rb'
4
+ require_relative 'controller_resource_name_finder.rb'
5
+ require_relative 'controller_resource_builder.rb'
6
+ require_relative 'controller_resource_sanitizer.rb'
7
+ module CanCan
8
+ module ControllerResourceLoader
9
+ include CanCan::ControllerResourceNameFinder
10
+ include CanCan::ControllerResourceFinder
11
+ include CanCan::ControllerResourceBuilder
12
+ include CanCan::ControllerResourceSanitizer
13
+
14
+ def load_resource
15
+ return if skip?(:load)
16
+
17
+ if load_instance?
18
+ self.resource_instance ||= load_resource_instance
19
+ elsif load_collection?
20
+ self.collection_instance ||= load_collection
21
+ end
22
+ end
23
+
24
+ protected
25
+
26
+ def new_actions
27
+ %i[new create] + Array(@options[:new])
28
+ end
29
+
30
+ def resource_params_by_key(key)
31
+ return unless @options[key] && @params.key?(extract_key(@options[key]))
32
+
33
+ @params[extract_key(@options[key])]
34
+ end
35
+
36
+ def resource_params_by_namespaced_name
37
+ resource_params_by_key(:instance_name) || resource_params_by_key(:class) || (
38
+ params = @params[extract_key(namespaced_name)]
39
+ params.respond_to?(:to_h) ? params : nil)
40
+ end
41
+
42
+ def resource_params
43
+ if parameters_require_sanitizing? && params_method.present?
44
+ sanitize_parameters
45
+ else
46
+ resource_params_by_namespaced_name
47
+ end
48
+ end
49
+
50
+ def fetch_parent(name)
51
+ if @controller.instance_variable_defined? "@#{name}"
52
+ @controller.instance_variable_get("@#{name}")
53
+ elsif @controller.respond_to?(name, true)
54
+ @controller.send(name)
55
+ end
56
+ end
57
+
58
+ # The object to load this resource through.
59
+ def parent_resource
60
+ parent_name && fetch_parent(parent_name)
61
+ end
62
+
63
+ def parent_name
64
+ @options[:through] && [@options[:through]].flatten.detect { |i| fetch_parent(i) }
65
+ end
66
+
67
+ def resource_base_through_parent_resource
68
+ if @options[:singleton]
69
+ resource_class
70
+ else
71
+ parent_resource.send(@options[:through_association] || name.to_s.pluralize)
72
+ end
73
+ end
74
+
75
+ def resource_base_through
76
+ if parent_resource
77
+ resource_base_through_parent_resource
78
+ elsif @options[:shallow]
79
+ resource_class
80
+ else
81
+ # maybe this should be a record not found error instead?
82
+ raise AccessDenied.new(nil, authorization_action, resource_class)
83
+ end
84
+ end
85
+
86
+ # The object that methods (such as "find", "new" or "build") are called on.
87
+ # If the :through option is passed it will go through an association on that instance.
88
+ # If the :shallow option is passed it will use the resource_class if there's no parent
89
+ # If the :singleton option is passed it won't use the association because it needs to be handled later.
90
+ def resource_base
91
+ @options[:through] ? resource_base_through : resource_class
92
+ end
93
+
94
+ def parent_authorization_action
95
+ @options[:parent_action] || :show
96
+ end
97
+
98
+ def authorization_action
99
+ parent? ? parent_authorization_action : @params[:action].to_sym
100
+ end
101
+
102
+ def load_collection
103
+ resource_base.accessible_by(current_ability, authorization_action)
104
+ end
105
+
106
+ def load_resource_instance
107
+ if !parent? && new_actions.include?(@params[:action].to_sym)
108
+ build_resource
109
+ elsif id_param || @options[:singleton]
110
+ find_resource
111
+ end
112
+ end
113
+
114
+ private
115
+
116
+ def extract_key(value)
117
+ value.to_s.underscore.tr('/', '_')
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CanCan
4
+ module ControllerResourceNameFinder
5
+ protected
6
+
7
+ def name_from_controller
8
+ @params[:controller].split('/').last.singularize
9
+ end
10
+
11
+ def namespaced_name
12
+ [namespace, name].join('/').singularize.camelize.safe_constantize || name
13
+ end
14
+
15
+ def name
16
+ @name || name_from_controller
17
+ end
18
+
19
+ def namespace
20
+ @params[:controller].split('/')[0..-2]
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CanCan
4
+ module ControllerResourceSanitizer
5
+ protected
6
+
7
+ def sanitize_parameters
8
+ case params_method
9
+ when Symbol
10
+ @controller.send(params_method)
11
+ when String
12
+ @controller.instance_eval(params_method)
13
+ when Proc
14
+ params_method.call(@controller)
15
+ end
16
+ end
17
+
18
+ def params_methods
19
+ methods = ["#{@params[:action]}_params".to_sym, "#{name}_params".to_sym, :resource_params]
20
+ methods.unshift(@options[:param_method]) if @options[:param_method].present?
21
+ methods
22
+ end
23
+
24
+ def params_method
25
+ params_methods.each do |method|
26
+ return method if (method.is_a?(Symbol) && @controller.respond_to?(method, true)) ||
27
+ method.is_a?(String) || method.is_a?(Proc)
28
+ end
29
+ nil
30
+ end
31
+ end
32
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module CanCan
2
4
  # A general CanCan exception
3
5
  class Error < StandardError; end
@@ -8,9 +10,18 @@ module CanCan
8
10
  # Raised when removed code is called, an alternative solution is provided in message.
9
11
  class ImplementationRemoved < Error; end
10
12
 
11
- # Raised when using check_authorization without calling authorized!
13
+ # Raised when using check_authorization without calling authorize!
12
14
  class AuthorizationNotPerformed < Error; end
13
15
 
16
+ # Raised when a rule is created with both a block and a hash of conditions
17
+ class BlockAndConditionsError < Error; end
18
+
19
+ # Raised when an unexpected argument is passed as an attribute
20
+ class AttributeArgumentError < Error; end
21
+
22
+ # Raised when using a wrong association name
23
+ class WrongAssociationName < Error; end
24
+
14
25
  # This error is raised when a user isn't allowed to access a given controller action.
15
26
  # This usually happens within a call to ControllerAdditions#authorize! but can be
16
27
  # raised manually.
@@ -30,21 +41,30 @@ module CanCan
30
41
  # exception.default_message = "Default error message"
31
42
  # exception.message # => "Default error message"
32
43
  #
33
- # See ControllerAdditions#authorized! for more information on rescuing from this exception
44
+ # See ControllerAdditions#authorize! for more information on rescuing from this exception
34
45
  # and customizing the message using I18n.
35
46
  class AccessDenied < Error
36
- attr_reader :action, :subject
47
+ attr_reader :action, :subject, :conditions
37
48
  attr_writer :default_message
38
49
 
39
- def initialize(message = nil, action = nil, subject = nil)
50
+ def initialize(message = nil, action = nil, subject = nil, conditions = nil)
40
51
  @message = message
41
52
  @action = action
42
53
  @subject = subject
54
+ @conditions = conditions
43
55
  @default_message = I18n.t(:"unauthorized.default", default: 'You are not authorized to access this page.')
44
56
  end
45
57
 
46
58
  def to_s
47
59
  @message || @default_message
48
60
  end
61
+
62
+ def inspect
63
+ details = %i[action subject conditions message].map do |attribute|
64
+ value = instance_variable_get "@#{attribute}"
65
+ "#{attribute}: #{value.inspect}" if value.present?
66
+ end.compact.join(', ')
67
+ "#<#{self.class.name} #{details}>"
68
+ end
49
69
  end
50
70
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  rspec_module = defined?(RSpec::Core) ? 'RSpec' : 'Spec' # RSpec 1 compatability
2
4
 
3
5
  if rspec_module == 'RSpec'
@@ -9,7 +11,16 @@ end
9
11
 
10
12
  Kernel.const_get(rspec_module)::Matchers.define :be_able_to do |*args|
11
13
  match do |ability|
12
- ability.can?(*args)
14
+ actions = args.first
15
+ if actions.is_a? Array
16
+ if actions.empty?
17
+ false
18
+ else
19
+ actions.all? { |action| ability.can?(action, *args[1..-1]) }
20
+ end
21
+ else
22
+ ability.can?(*args)
23
+ end
13
24
  end
14
25
 
15
26
  # Check that RSpec is < 2.99