cancancan 1.15.0 → 3.1.0

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 (74) hide show
  1. checksums.yaml +5 -5
  2. data/cancancan.gemspec +18 -18
  3. data/init.rb +2 -0
  4. data/lib/cancan.rb +9 -11
  5. data/lib/cancan/ability.rb +90 -203
  6. data/lib/cancan/ability/actions.rb +93 -0
  7. data/lib/cancan/ability/rules.rb +93 -0
  8. data/lib/cancan/ability/strong_parameter_support.rb +41 -0
  9. data/lib/cancan/conditions_matcher.rb +106 -0
  10. data/lib/cancan/controller_additions.rb +29 -36
  11. data/lib/cancan/controller_resource.rb +46 -211
  12. data/lib/cancan/controller_resource_builder.rb +26 -0
  13. data/lib/cancan/controller_resource_finder.rb +42 -0
  14. data/lib/cancan/controller_resource_loader.rb +120 -0
  15. data/lib/cancan/controller_resource_name_finder.rb +23 -0
  16. data/lib/cancan/controller_resource_sanitizer.rb +32 -0
  17. data/lib/cancan/exceptions.rb +17 -5
  18. data/lib/cancan/matchers.rb +12 -3
  19. data/lib/cancan/model_adapters/abstract_adapter.rb +10 -8
  20. data/lib/cancan/model_adapters/active_record_4_adapter.rb +39 -43
  21. data/lib/cancan/model_adapters/active_record_5_adapter.rb +68 -0
  22. data/lib/cancan/model_adapters/active_record_adapter.rb +77 -82
  23. data/lib/cancan/model_adapters/conditions_extractor.rb +75 -0
  24. data/lib/cancan/model_adapters/conditions_normalizer.rb +49 -0
  25. data/lib/cancan/model_adapters/default_adapter.rb +2 -0
  26. data/lib/cancan/model_additions.rb +2 -1
  27. data/lib/cancan/parameter_validators.rb +9 -0
  28. data/lib/cancan/relevant.rb +29 -0
  29. data/lib/cancan/rule.rb +76 -106
  30. data/lib/cancan/rules_compressor.rb +23 -0
  31. data/lib/cancan/unauthorized_message_resolver.rb +24 -0
  32. data/lib/cancan/version.rb +3 -1
  33. data/lib/cancancan.rb +2 -0
  34. data/lib/generators/cancan/ability/ability_generator.rb +4 -2
  35. data/lib/generators/cancan/ability/templates/ability.rb +2 -0
  36. metadata +66 -57
  37. data/.gitignore +0 -15
  38. data/.rspec +0 -1
  39. data/.travis.yml +0 -33
  40. data/Appraisals +0 -104
  41. data/CHANGELOG.rdoc +0 -527
  42. data/CONTRIBUTING.md +0 -23
  43. data/Gemfile +0 -3
  44. data/LICENSE +0 -22
  45. data/README.md +0 -217
  46. data/Rakefile +0 -9
  47. data/gemfiles/activerecord_3.2.gemfile +0 -17
  48. data/gemfiles/activerecord_4.0.gemfile +0 -18
  49. data/gemfiles/activerecord_4.1.gemfile +0 -18
  50. data/gemfiles/activerecord_4.2.gemfile +0 -19
  51. data/gemfiles/activerecord_5.0.gemfile +0 -19
  52. data/gemfiles/mongoid_2.x.gemfile +0 -17
  53. data/gemfiles/sequel_3.x.gemfile +0 -17
  54. data/lib/cancan/inherited_resource.rb +0 -20
  55. data/lib/cancan/model_adapters/active_record_3_adapter.rb +0 -16
  56. data/lib/cancan/model_adapters/mongoid_adapter.rb +0 -75
  57. data/lib/cancan/model_adapters/sequel_adapter.rb +0 -87
  58. data/spec/README.rdoc +0 -27
  59. data/spec/cancan/ability_spec.rb +0 -544
  60. data/spec/cancan/controller_additions_spec.rb +0 -151
  61. data/spec/cancan/controller_resource_spec.rb +0 -643
  62. data/spec/cancan/exceptions_spec.rb +0 -58
  63. data/spec/cancan/inherited_resource_spec.rb +0 -71
  64. data/spec/cancan/matchers_spec.rb +0 -29
  65. data/spec/cancan/model_adapters/active_record_4_adapter_spec.rb +0 -154
  66. data/spec/cancan/model_adapters/active_record_adapter_spec.rb +0 -405
  67. data/spec/cancan/model_adapters/default_adapter_spec.rb +0 -7
  68. data/spec/cancan/model_adapters/mongoid_adapter_spec.rb +0 -247
  69. data/spec/cancan/model_adapters/sequel_adapter_spec.rb +0 -132
  70. data/spec/cancan/rule_spec.rb +0 -52
  71. data/spec/matchers.rb +0 -13
  72. data/spec/spec.opts +0 -2
  73. data/spec/spec_helper.rb +0 -27
  74. data/spec/support/ability.rb +0 -7
@@ -1,75 +0,0 @@
1
- module CanCan
2
- module ModelAdapters
3
- class MongoidAdapter < AbstractAdapter
4
- def self.for_class?(model_class)
5
- model_class <= Mongoid::Document
6
- end
7
-
8
- def self.override_conditions_hash_matching?(subject, conditions)
9
- conditions.any? do |k,v|
10
- key_is_not_symbol = lambda { !k.kind_of?(Symbol) }
11
- subject_value_is_array = lambda do
12
- subject.respond_to?(k) && subject.send(k).is_a?(Array)
13
- end
14
-
15
- key_is_not_symbol.call || subject_value_is_array.call
16
- end
17
- end
18
-
19
- def self.matches_conditions_hash?(subject, conditions)
20
- # To avoid hitting the db, retrieve the raw Mongo selector from
21
- # the Mongoid Criteria and use Mongoid::Matchers#matches?
22
- subject.matches?( subject.class.where(conditions).selector )
23
- end
24
-
25
- def database_records
26
- if @rules.size == 0
27
- @model_class.where(:_id => {'$exists' => false, '$type' => 7}) # return no records in Mongoid
28
- elsif @rules.size == 1 && @rules[0].conditions.is_a?(Mongoid::Criteria)
29
- @rules[0].conditions
30
- else
31
- # we only need to process can rules if
32
- # there are no rules with empty conditions
33
- rules = @rules.reject { |rule| rule.conditions.empty? && rule.base_behavior }
34
- process_can_rules = @rules.count == rules.count
35
-
36
- rules.inject(@model_class.all) do |records, rule|
37
- if process_can_rules && rule.base_behavior
38
- records.or simplify_relations(@model_class, rule.conditions)
39
- elsif !rule.base_behavior
40
- records.excludes simplify_relations(@model_class, rule.conditions)
41
- else
42
- records
43
- end
44
- end
45
- end
46
- end
47
-
48
- private
49
- # Look for criteria on relations and replace with simple id queries
50
- # eg.
51
- # {user: {:tags.all => []}} becomes {"user_id" => {"$in" => [__, ..]}}
52
- # {user: {:session => {:tags.all => []}}} becomes {"user_id" => {"session_id" => {"$in" => [__, ..]} }}
53
- def simplify_relations model_class, conditions
54
- model_relations = model_class.relations.with_indifferent_access
55
- Hash[
56
- conditions.map {|k,v|
57
- if relation = model_relations[k]
58
- relation_class_name = relation[:class_name].blank? ? k.to_s.classify : relation[:class_name]
59
- v = simplify_relations(relation_class_name.constantize, v)
60
- relation_ids = relation_class_name.constantize.where(v).only(:id).map(&:id)
61
- k = "#{k}_id"
62
- v = { "$in" => relation_ids }
63
- end
64
- [k,v]
65
- }
66
- ]
67
- end
68
- end
69
- end
70
- end
71
-
72
- # simplest way to add `accessible_by` to all Mongoid Documents
73
- module Mongoid::Document::ClassMethods
74
- include CanCan::ModelAdditions::ClassMethods
75
- end
@@ -1,87 +0,0 @@
1
- module CanCan
2
- module ModelAdapters
3
- class SequelAdapter < AbstractAdapter
4
- def self.for_class?(model_class)
5
- model_class <= Sequel::Model
6
- end
7
-
8
- def self.find(model_class, id)
9
- model_class[id]
10
- end
11
-
12
- def self.override_condition_matching?(subject, name, value)
13
- value.kind_of?(Hash)
14
- end
15
-
16
- def self.matches_condition?(subject, name, value)
17
- obj = subject.send(name)
18
- if obj.nil?
19
- false
20
- else
21
- value.each do |k, v|
22
- if v.kind_of?(Hash)
23
- return false unless self.matches_condition?(obj, k, v)
24
- elsif obj.send(k) != v
25
- return false
26
- end
27
- end
28
- end
29
- end
30
-
31
- def database_records
32
- if @rules.size == 0
33
- @model_class.where('1=0')
34
- else
35
- # only need to process can rules if there are no can rule with empty conditions
36
- rules = @rules.reject { |rule| rule.base_behavior && rule.conditions.empty? }
37
- rules.reject! { |rule| rule.base_behavior } if rules.count < @rules.count
38
-
39
- can_condition_added = false
40
- rules.reverse.inject(@model_class.dataset) do |records, rule|
41
- normalized_conditions = normalize_conditions(rule.conditions)
42
- if rule.base_behavior
43
- if can_condition_added
44
- records.or normalized_conditions
45
- else
46
- can_condition_added = true
47
- records.where normalized_conditions
48
- end
49
- else
50
- records.exclude normalized_conditions
51
- end
52
- end
53
- end
54
- end
55
-
56
- private
57
-
58
- def normalize_conditions(conditions, model_class = @model_class)
59
- return conditions unless conditions.kind_of? Hash
60
- conditions.inject({}) do |result_hash, (name, value)|
61
- if value.kind_of? Hash
62
- value = value.dup
63
- association_class = model_class.association_reflection(name).associated_class
64
- nested = value.inject({}) do |nested, (k, v)|
65
- if v.kind_of?(Hash)
66
- value.delete(k)
67
- nested_class = association_class.association_reflection(k).associated_class
68
- nested[k] = nested_class.where(normalize_conditions(v, association_class))
69
- else
70
- nested[k] = v
71
- end
72
- nested
73
- end
74
- result_hash[name] = association_class.where(nested)
75
- else
76
- result_hash[name] = value
77
- end
78
- result_hash
79
- end
80
- end
81
- end
82
- end
83
- end
84
-
85
- Sequel::Model.class_eval do
86
- include CanCan::ModelAdditions
87
- end
@@ -1,27 +0,0 @@
1
- = CanCan Specs
2
-
3
- == Running the specs
4
-
5
- To run the specs first run the +bundle+ command to install the necessary gems and the +rake+ command to run the specs.
6
-
7
- bundle
8
-
9
- Then run the appraisal command to install all the necessary test sets:
10
-
11
- appraisal install
12
-
13
- You can then run all test sets:
14
-
15
- appraisal rake
16
-
17
- Or individual ones:
18
-
19
- appraisal activerecord_3.2 rake
20
-
21
- A list of the tests is in the +Appraisal+ file.
22
-
23
- The specs support Ruby 1.8.7+
24
-
25
- == Model Adapters
26
-
27
- The model adapter ENV setting has been removed and replaced with the +Appraisal+ file.
@@ -1,544 +0,0 @@
1
- require "spec_helper"
2
-
3
- describe CanCan::Ability do
4
- before(:each) do
5
- (@ability = double).extend(CanCan::Ability)
6
- end
7
-
8
- it "is able to :read anything" do
9
- @ability.can :read, :all
10
- expect(@ability.can?(:read, String)).to be(true)
11
- expect(@ability.can?(:read, 123)).to be(true)
12
- end
13
-
14
- it "does not have permission to do something it doesn't know about" do
15
- expect(@ability.can?(:foodfight, String)).to be(false)
16
- end
17
-
18
- it "passes true to `can?` when non false/nil is returned in block" do
19
- @ability.can :read, :all
20
- @ability.can :read, Symbol do |sym|
21
- "foo" # TODO test that sym is nil when no instance is passed
22
- end
23
- expect(@ability.can?(:read, :some_symbol)).to be(true)
24
- end
25
-
26
- it "passes nil to a block when no instance is passed" do
27
- @ability.can :read, Symbol do |sym|
28
- expect(sym).to be_nil
29
- true
30
- end
31
- expect(@ability.can?(:read, Symbol)).to be(true)
32
- end
33
-
34
- it "passes to previous rule, if block returns false or nil" do
35
- @ability.can :read, Symbol
36
- @ability.can :read, Integer do |i|
37
- i < 5
38
- end
39
- @ability.can :read, Integer do |i|
40
- i > 10
41
- end
42
- expect(@ability.can?(:read, Symbol)).to be(true)
43
- expect(@ability.can?(:read, 11)).to be(true)
44
- expect(@ability.can?(:read, 1)).to be(true)
45
- expect(@ability.can?(:read, 6)).to be(false)
46
- end
47
-
48
- it "overrides earlier rules with later ones (even if a different exact subject)" do
49
- @ability.cannot :read, Numeric
50
- @ability.can :read, Integer
51
-
52
- expect(@ability.can?(:read, 6)).to be(true)
53
- end
54
-
55
- it "performs can(_, :all) before other checks when can(_, :all) is defined before" do
56
- @ability.can :manage, :all
57
- @ability.can :edit, String do |_string|
58
- fail 'Performed a check for :edit before the check for :all'
59
- end
60
- expect { @ability.can? :edit, 'a' }.to_not raise_exception
61
- end
62
-
63
- it "performs can(_, :all) before other checks when can(_, :all) is defined after" do
64
- @ability.can :edit, String do |_string|
65
- fail 'Performed a check for :edit before the check for :all'
66
- end
67
- @ability.can :manage, :all
68
- expect { @ability.can? :edit, 'a' }.to_not raise_exception
69
- end
70
-
71
- it "does not pass class with object if :all objects are accepted" do
72
- @ability.can :preview, :all do |object|
73
- expect(object).to eq(123)
74
- @block_called = true
75
- end
76
- @ability.can?(:preview, 123)
77
- expect(@block_called).to be(true)
78
- end
79
-
80
- it "does not call block when only class is passed, only return true" do
81
- @block_called = false
82
- @ability.can :preview, :all do |object|
83
- @block_called = true
84
- end
85
- expect(@ability.can?(:preview, Hash)).to be(true)
86
- expect(@block_called).to be(false)
87
- end
88
-
89
- it "passes only object for global manage actions" do
90
- @ability.can :manage, String do |object|
91
- expect(object).to eq("foo")
92
- @block_called = true
93
- end
94
- expect(@ability.can?(:stuff, "foo")).to be(true)
95
- expect(@block_called).to be(true)
96
- end
97
-
98
- it "makes alias for update or destroy actions to modify action" do
99
- @ability.alias_action :update, :destroy, :to => :modify
100
- @ability.can :modify, :all
101
- expect(@ability.can?(:update, 123)).to be(true)
102
- expect(@ability.can?(:destroy, 123)).to be(true)
103
- end
104
-
105
- it "allows deeply nested aliased actions" do
106
- @ability.alias_action :increment, :to => :sort
107
- @ability.alias_action :sort, :to => :modify
108
- @ability.can :modify, :all
109
- expect(@ability.can?(:increment, 123)).to be(true)
110
- end
111
-
112
- it "raises an Error if alias target is an exist action" do
113
- expect { @ability.alias_action :show, :to => :show }.to raise_error(CanCan::Error, "You can't specify target (show) as alias because it is real action name")
114
- end
115
-
116
- it "always calls block with arguments when passing no arguments to can" do
117
- @ability.can do |action, object_class, object|
118
- expect(action).to eq(:foo)
119
- expect(object_class).to eq(123.class)
120
- expect(object).to eq(123)
121
- @block_called = true
122
- end
123
- @ability.can?(:foo, 123)
124
- expect(@block_called).to be(true)
125
- end
126
-
127
- it "passes nil to object when comparing class with can check" do
128
- @ability.can do |action, object_class, object|
129
- expect(action).to eq(:foo)
130
- expect(object_class).to eq(Hash)
131
- expect(object).to be_nil
132
- @block_called = true
133
- end
134
- @ability.can?(:foo, Hash)
135
- expect(@block_called).to be(true)
136
- end
137
-
138
- it "automatically makes alias for index and show into read calls" do
139
- @ability.can :read, :all
140
- expect(@ability.can?(:index, 123)).to be(true)
141
- expect(@ability.can?(:show, 123)).to be(true)
142
- end
143
-
144
- it "automatically makes alias for new and edit into create and update respectively" do
145
- @ability.can :create, :all
146
- @ability.can :update, :all
147
- expect(@ability.can?(:new, 123)).to be(true)
148
- expect(@ability.can?(:edit, 123)).to be(true)
149
- end
150
-
151
- it "does not respond to prepare (now using initialize)" do
152
- expect(@ability).to_not respond_to(:prepare)
153
- end
154
-
155
- it "offers cannot? method which is simply invert of can?" do
156
- expect(@ability.cannot?(:tie, String)).to be(true)
157
- end
158
-
159
- it "is able to specify multiple actions and match any" do
160
- @ability.can [:read, :update], :all
161
- expect(@ability.can?(:read, 123)).to be(true)
162
- expect(@ability.can?(:update, 123)).to be(true)
163
- expect(@ability.can?(:count, 123)).to be(false)
164
- end
165
-
166
- it "is able to specify multiple classes and match any" do
167
- @ability.can :update, [String, Range]
168
- expect(@ability.can?(:update, "foo")).to be(true)
169
- expect(@ability.can?(:update, 1..3)).to be(true)
170
- expect(@ability.can?(:update, 123)).to be(false)
171
- end
172
-
173
- it "checks if there is a permission for any of given subjects" do
174
- @ability.can :update, [String, Range]
175
- expect(@ability.can?(:update, {:any => ["foo", 1..3]})).to be(true)
176
- expect(@ability.can?(:update, {:any => [1..3, "foo"]})).to be(true)
177
- expect(@ability.can?(:update, {:any => [123, "foo"]})).to be(true)
178
- expect(@ability.can?(:update, {:any => [123, 1.0]})).to be(false)
179
- end
180
-
181
- it "lists all permissions" do
182
- @ability.can :manage, :all
183
- @ability.can :learn, Range
184
- @ability.cannot :read, String
185
- @ability.cannot :read, Hash
186
- @ability.cannot :preview, Array
187
-
188
- expected_list = {:can => {:manage => ["all"],
189
- :learn => ["Range"]
190
- },
191
- :cannot => {:read => ["String", "Hash"],
192
- :index => ["String", "Hash"],
193
- :show => ["String", "Hash"],
194
- :preview => ["Array"]
195
- }
196
- }
197
-
198
- expect(@ability.permissions).to eq(expected_list)
199
- end
200
-
201
- it "supports custom objects in the rule" do
202
- @ability.can :read, :stats
203
- expect(@ability.can?(:read, :stats)).to be(true)
204
- expect(@ability.can?(:update, :stats)).to be(false)
205
- expect(@ability.can?(:read, :nonstats)).to be(false)
206
- expect(@ability.can?(:read, {:any => [:stats, :nonstats]})).to be(true)
207
- expect(@ability.can?(:read, {:any => [:nonstats, :neitherstats]})).to be(false)
208
- end
209
-
210
- it "checks ancestors of class" do
211
- @ability.can :read, Numeric
212
- expect(@ability.can?(:read, Integer)).to be(true)
213
- expect(@ability.can?(:read, 1.23)).to be(true)
214
- expect(@ability.can?(:read, "foo")).to be(false)
215
- expect(@ability.can?(:read, {:any => [Integer, String]})).to be(true)
216
- end
217
-
218
- it "supports 'cannot' method to define what user cannot do" do
219
- @ability.can :read, :all
220
- @ability.cannot :read, Integer
221
- expect(@ability.can?(:read, "foo")).to be(true)
222
- expect(@ability.can?(:read, 123)).to be(false)
223
- expect(@ability.can?(:read, {:any => ["foo", "bar"]})).to be(true)
224
- expect(@ability.can?(:read, {:any => [123, "foo"]})).to be(false)
225
- expect(@ability.can?(:read, {:any => [123, 456]})).to be(false)
226
- end
227
-
228
- it "passes to previous rule, if block returns false or nil" do
229
- @ability.can :read, :all
230
- @ability.cannot :read, Integer do |int|
231
- int > 10 ? nil : ( int > 5 )
232
- end
233
-
234
- expect(@ability.can?(:read, "foo")).to be(true)
235
- expect(@ability.can?(:read, 3)).to be(true)
236
- expect(@ability.can?(:read, 8)).to be(false)
237
- expect(@ability.can?(:read, 123)).to be(true)
238
- expect(@ability.can?(:read, {:any => [123, 8]})).to be(true)
239
- expect(@ability.can?(:read, {:any => [8, 9]})).to be(false)
240
- end
241
-
242
- it "always returns `false` for single cannot definition" do
243
- @ability.cannot :read, Integer do |int|
244
- int > 10 ? nil : ( int > 5 )
245
- end
246
- expect(@ability.can?(:read, "foo")).to be(false)
247
- expect(@ability.can?(:read, 3)).to be(false)
248
- expect(@ability.can?(:read, 8)).to be(false)
249
- expect(@ability.can?(:read, 123)).to be(false)
250
- end
251
-
252
- it "passes to previous cannot definition, if block returns false or nil" do
253
- @ability.cannot :read, :all
254
- @ability.can :read, Integer do |int|
255
- int > 10 ? nil : ( int > 5 )
256
- end
257
- expect(@ability.can?(:read, "foo")).to be(false)
258
- expect(@ability.can?(:read, 3)).to be(false)
259
- expect(@ability.can?(:read, 10)).to be(true)
260
- expect(@ability.can?(:read, 123)).to be(false)
261
- end
262
-
263
- it "appends aliased actions" do
264
- @ability.alias_action :update, :to => :modify
265
- @ability.alias_action :destroy, :to => :modify
266
- expect(@ability.aliased_actions[:modify]).to eq([:update, :destroy])
267
- end
268
-
269
- it "clears aliased actions" do
270
- @ability.alias_action :update, :to => :modify
271
- @ability.clear_aliased_actions
272
- expect(@ability.aliased_actions[:modify]).to be_nil
273
- end
274
-
275
- it "passes additional arguments to block from can?" do
276
- @ability.can :read, Integer do |int, x|
277
- int > x
278
- end
279
-
280
- expect(@ability.can?(:read, 2, 1)).to be(true)
281
- expect(@ability.can?(:read, 2, 3)).to be(false)
282
- expect(@ability.can?(:read, {:any => [4, 5]}, 3)).to be(true)
283
- expect(@ability.can?(:read, {:any => [2, 3]}, 3)).to be(false)
284
- end
285
-
286
- it "uses conditions as third parameter and determine abilities from it" do
287
- @ability.can :read, Range, :begin => 1, :end => 3
288
-
289
- expect(@ability.can?(:read, 1..3)).to be(true)
290
- expect(@ability.can?(:read, 1..4)).to be(false)
291
- expect(@ability.can?(:read, Range)).to be(true)
292
- expect(@ability.can?(:read, {:any => [1..3, 1..4]})).to be(true)
293
- expect(@ability.can?(:read, {:any => [1..4, 2..4]})).to be(false)
294
- end
295
-
296
- it "allows an array of options in conditions hash" do
297
- @ability.can :read, Range, :begin => [1, 3, 5]
298
-
299
- expect(@ability.can?(:read, 1..3)).to be(true)
300
- expect(@ability.can?(:read, 2..4)).to be(false)
301
- expect(@ability.can?(:read, 3..5)).to be(true)
302
- expect(@ability.can?(:read, {:any => [2..4, 3..5]})).to be(true)
303
- expect(@ability.can?(:read, {:any => [2..4, 2..5]})).to be(false)
304
- end
305
-
306
- it "allows a range of options in conditions hash" do
307
- @ability.can :read, Range, :begin => 1..3
308
- expect(@ability.can?(:read, 1..10)).to be(true)
309
- expect(@ability.can?(:read, 3..30)).to be(true)
310
- expect(@ability.can?(:read, 4..40)).to be(false)
311
- end
312
-
313
- it "allows a range of time in conditions hash" do
314
- @ability.can :read, Range, :begin => 1.day.from_now..3.days.from_now
315
- expect(@ability.can?(:read, 1.day.from_now..10.days.from_now)).to be(true)
316
- expect(@ability.can?(:read, 2.days.from_now..20.days.from_now)).to be(true)
317
- expect(@ability.can?(:read, 4.days.from_now..40.days.from_now)).to be(false)
318
- end
319
-
320
- it "allows nested hashes in conditions hash" do
321
- @ability.can :read, Range, :begin => { :to_i => 5 }
322
- expect(@ability.can?(:read, 5..7)).to be(true)
323
- expect(@ability.can?(:read, 6..8)).to be(false)
324
- end
325
-
326
- it "matches any element passed in to nesting if it's an array (for has_many associations)" do
327
- @ability.can :read, Range, :to_a => { :to_i => 3 }
328
- expect(@ability.can?(:read, 1..5)).to be(true)
329
- expect(@ability.can?(:read, 4..6)).to be(false)
330
- end
331
-
332
- it "accepts a set as a condition value" do
333
- expect(object_with_foo_2 = double(:foo => 2)).to receive(:foo)
334
- expect(object_with_foo_3 = double(:foo => 3)).to receive(:foo)
335
- @ability.can :read, Object, :foo => [1, 2, 5].to_set
336
- expect(@ability.can?(:read, object_with_foo_2)).to be(true)
337
- expect(@ability.can?(:read, object_with_foo_3)).to be(false)
338
- end
339
-
340
- it "does not match subjects return nil for methods that must match nested a nested conditions hash" do
341
- expect(object_with_foo = double(:foo => :bar)).to receive(:foo)
342
- @ability.can :read, Array, :first => { :foo => :bar }
343
- expect(@ability.can?(:read, [object_with_foo])).to be(true)
344
- expect(@ability.can?(:read, [])).to be(false)
345
- end
346
-
347
- it "matches strings but not substrings specified in a conditions hash" do
348
- @ability.can :read, String, :presence => "declassified"
349
- expect(@ability.can?(:read, "declassified")).to be(true)
350
- expect(@ability.can?(:read, "classified")).to be(false)
351
- end
352
-
353
- it "does not stop at cannot definition when comparing class" do
354
- @ability.can :read, Range
355
- @ability.cannot :read, Range, :begin => 1
356
- expect(@ability.can?(:read, 2..5)).to be(true)
357
- expect(@ability.can?(:read, 1..5)).to be(false)
358
- expect(@ability.can?(:read, Range)).to be(true)
359
- end
360
-
361
- it "stops at cannot definition when no hash is present" do
362
- @ability.can :read, :all
363
- @ability.cannot :read, Range
364
- expect(@ability.can?(:read, 1..5)).to be(false)
365
- expect(@ability.can?(:read, Range)).to be(false)
366
- end
367
-
368
- it "allows to check ability for Module" do
369
- module B; end
370
- class A; include B; end
371
- @ability.can :read, B
372
- expect(@ability.can?(:read, A)).to be(true)
373
- expect(@ability.can?(:read, A.new)).to be(true)
374
- end
375
-
376
- it "passes nil to a block for ability on Module when no instance is passed" do
377
- module B; end
378
- class A; include B; end
379
- @ability.can :read, B do |sym|
380
- expect(sym).to be_nil
381
- true
382
- end
383
- expect(@ability.can?(:read, B)).to be(true)
384
- expect(@ability.can?(:read, A)).to be(true)
385
- end
386
-
387
- it "checks permissions through association when passing a hash of subjects" do
388
- @ability.can :read, Range, :string => {:length => 3}
389
-
390
- expect(@ability.can?(:read, "foo" => Range)).to be(true)
391
- expect(@ability.can?(:read, "foobar" => Range)).to be(false)
392
- expect(@ability.can?(:read, 123 => Range)).to be(true)
393
- expect(@ability.can?(:read, {:any => [{"foo" => Range}, {"foobar" => Range}]})).to be(true)
394
- expect(@ability.can?(:read, {:any => [{"food" => Range}, {"foobar" => Range}]})).to be(false)
395
- end
396
-
397
- it "checks permissions correctly when passing a hash of subjects with multiple definitions" do
398
- @ability.can :read, Range, :string => {:length => 4}
399
- @ability.can [:create, :read], Range, :string => {:upcase => 'FOO'}
400
-
401
- expect(@ability.can?(:read, "foo" => Range)).to be(true)
402
- expect(@ability.can?(:read, "foobar" => Range)).to be(false)
403
- expect(@ability.can?(:read, 1234 => Range)).to be(true)
404
- expect(@ability.can?(:read, {:any => [{"foo" => Range}, {"foobar" => Range}]})).to be(true)
405
- expect(@ability.can?(:read, {:any => [{"foo.bar" => Range}, {"foobar" => Range}]})).to be(false)
406
- end
407
-
408
- it "allows to check ability on Hash-like object" do
409
- class Container < Hash; end
410
- @ability.can :read, Container
411
- expect(@ability.can?(:read, Container.new)).to be(true)
412
- end
413
-
414
- it "has initial attributes based on hash conditions of 'new' action" do
415
- @ability.can :manage, Range, :foo => "foo", :hash => {:skip => "hashes"}
416
- @ability.can :create, Range, :bar => 123, :array => %w[skip arrays]
417
- @ability.can :new, Range, :baz => "baz", :range => 1..3
418
- @ability.cannot :new, Range, :ignore => "me"
419
- expect(@ability.attributes_for(:new, Range)).to eq({:foo => "foo", :bar => 123, :baz => "baz"})
420
- end
421
-
422
- it "raises access denied exception if ability us unauthorized to perform a certain action" do
423
- begin
424
- @ability.authorize! :read, :foo, 1, 2, 3, :message => "Access denied!"
425
- rescue CanCan::AccessDenied => e
426
- expect(e.message).to eq("Access denied!")
427
- expect(e.action).to eq(:read)
428
- expect(e.subject).to eq(:foo)
429
- else
430
- fail "Expected CanCan::AccessDenied exception to be raised"
431
- end
432
- end
433
-
434
- it "does not raise access denied exception if ability is authorized to perform an action and return subject" do
435
- @ability.can :read, :foo
436
- expect {
437
- expect(@ability.authorize!(:read, :foo)).to eq(:foo)
438
- }.to_not raise_error
439
- end
440
-
441
- it "knows when block is used in conditions" do
442
- @ability.can :read, :foo
443
- expect(@ability).to_not have_block(:read, :foo)
444
- @ability.can :read, :foo do |foo|
445
- false
446
- end
447
- expect(@ability).to have_block(:read, :foo)
448
- end
449
-
450
- it "knows when raw sql is used in conditions" do
451
- @ability.can :read, :foo
452
- expect(@ability).to_not have_raw_sql(:read, :foo)
453
- @ability.can :read, :foo, 'false'
454
- expect(@ability).to have_raw_sql(:read, :foo)
455
- end
456
-
457
- it "raises access denied exception with default message if not specified" do
458
- begin
459
- @ability.authorize! :read, :foo
460
- rescue CanCan::AccessDenied => e
461
- e.default_message = "Access denied!"
462
- expect(e.message).to eq("Access denied!")
463
- else
464
- fail "Expected CanCan::AccessDenied exception to be raised"
465
- end
466
- end
467
-
468
- it "determines model adapterO class by asking AbstractAdapter" do
469
- adapter_class, model_class = double, double
470
- allow(CanCan::ModelAdapters::AbstractAdapter).to receive(:adapter_class).with(model_class) { adapter_class }
471
- allow(adapter_class).to receive(:new).with(model_class, []) { :adapter_instance }
472
- expect(@ability.model_adapter(model_class, :read)).to eq(:adapter_instance)
473
- end
474
-
475
- it "raises an error when attempting to use a block with a hash condition since it's not likely what they want" do
476
- expect {
477
- @ability.can :read, Array, :published => true do
478
- false
479
- end
480
- }.to raise_error(CanCan::Error, "You are not able to supply a block with a hash of conditions in read Array ability. Use either one.")
481
- end
482
-
483
- describe "unauthorized message" do
484
- after(:each) do
485
- I18n.backend = nil
486
- end
487
-
488
- it "uses action/subject in i18n" do
489
- I18n.backend.store_translations :en, :unauthorized => {:update => {:array => "foo"}}
490
- expect(@ability.unauthorized_message(:update, Array)).to eq("foo")
491
- expect(@ability.unauthorized_message(:update, [1, 2, 3])).to eq("foo")
492
- expect(@ability.unauthorized_message(:update, :missing)).to be_nil
493
- end
494
-
495
- it "uses symbol as subject directly" do
496
- I18n.backend.store_translations :en, :unauthorized => {:has => {:cheezburger => "Nom nom nom. I eated it."}}
497
- expect(@ability.unauthorized_message(:has, :cheezburger)).to eq("Nom nom nom. I eated it.")
498
- end
499
-
500
- it "falls back to 'manage' and 'all'" do
501
- I18n.backend.store_translations :en, :unauthorized => {
502
- :manage => {:all => "manage all", :array => "manage array"},
503
- :update => {:all => "update all", :array => "update array"}
504
- }
505
- expect(@ability.unauthorized_message(:update, Array)).to eq("update array")
506
- expect(@ability.unauthorized_message(:update, Hash)).to eq("update all")
507
- expect(@ability.unauthorized_message(:foo, Array)).to eq("manage array")
508
- expect(@ability.unauthorized_message(:foo, Hash)).to eq("manage all")
509
- end
510
-
511
- it "follows aliased actions" do
512
- I18n.backend.store_translations :en, :unauthorized => {:modify => {:array => "modify array"}}
513
- @ability.alias_action :update, :to => :modify
514
- expect(@ability.unauthorized_message(:update, Array)).to eq("modify array")
515
- expect(@ability.unauthorized_message(:edit, Array)).to eq("modify array")
516
- end
517
-
518
- it "has variables for action and subject" do
519
- I18n.backend.store_translations :en, :unauthorized => {:manage => {:all => "%{action} %{subject}"}} # old syntax for now in case testing with old I18n
520
- expect(@ability.unauthorized_message(:update, Array)).to eq("update array")
521
- expect(@ability.unauthorized_message(:update, ArgumentError)).to eq("update argument error")
522
- expect(@ability.unauthorized_message(:edit, 1..3)).to eq("edit range")
523
- end
524
- end
525
-
526
- describe "#merge" do
527
- it "adds the rules from the given ability" do
528
- @ability.can :use, :tools
529
- (another_ability = double).extend(CanCan::Ability)
530
- another_ability.can :use, :search
531
-
532
- @ability.merge(another_ability)
533
- expect(@ability.can?(:use, :search)).to be(true)
534
- expect(@ability.send(:rules).size).to eq(2)
535
- end
536
-
537
- it "can add an empty ability" do
538
- (another_ability = double).extend(CanCan::Ability)
539
-
540
- @ability.merge(another_ability)
541
- expect(@ability.send(:rules).size).to eq(0)
542
- end
543
- end
544
- end