cancan 1.6.0 → 1.6.10
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.
- data/CHANGELOG.rdoc +157 -0
- data/CONTRIBUTING.md +11 -0
- data/Gemfile +2 -2
- data/README.rdoc +10 -1
- data/lib/cancan/ability.rb +14 -0
- data/lib/cancan/controller_additions.rb +16 -8
- data/lib/cancan/controller_resource.rb +73 -18
- data/lib/cancan/exceptions.rb +1 -1
- data/lib/cancan/inherited_resource.rb +3 -2
- data/lib/cancan/matchers.rb +2 -2
- data/lib/cancan/model_adapters/abstract_adapter.rb +5 -0
- data/lib/cancan/model_adapters/active_record_adapter.rb +24 -8
- data/lib/cancan/model_adapters/data_mapper_adapter.rb +16 -15
- data/lib/cancan/model_adapters/mongoid_adapter.rb +23 -7
- data/lib/cancan/model_additions.rb +2 -2
- data/lib/cancan/rule.rb +9 -4
- data/lib/cancan.rb +1 -1
- data/lib/generators/cancan/ability/templates/ability.rb +11 -7
- data/spec/cancan/ability_spec.rb +51 -3
- data/spec/cancan/controller_additions_spec.rb +5 -5
- data/spec/cancan/controller_resource_spec.rb +116 -26
- data/spec/cancan/exceptions_spec.rb +23 -0
- data/spec/cancan/inherited_resource_spec.rb +20 -2
- data/spec/cancan/model_adapters/active_record_adapter_spec.rb +63 -6
- data/spec/cancan/model_adapters/data_mapper_adapter_spec.rb +5 -1
- data/spec/cancan/model_adapters/mongoid_adapter_spec.rb +44 -2
- data/spec/cancan/rule_spec.rb +13 -0
- data/spec/spec_helper.rb +40 -0
- metadata +12 -11
|
@@ -20,7 +20,7 @@ describe CanCan::ControllerResource do
|
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
it "should not load resource into an instance variable if already set" do
|
|
23
|
-
@params.merge!(:action => "show", :id => 123)
|
|
23
|
+
@params.merge!(:action => "show", :id => "123")
|
|
24
24
|
@controller.instance_variable_set(:@project, :some_project)
|
|
25
25
|
resource = CanCan::ControllerResource.new(@controller)
|
|
26
26
|
resource.load_resource
|
|
@@ -35,6 +35,30 @@ describe CanCan::ControllerResource do
|
|
|
35
35
|
@controller.instance_variable_get(:@project).should == project
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
+
it "should attempt to load a resource with the same namespace as the controller when using :: for namespace" do
|
|
39
|
+
module MyEngine
|
|
40
|
+
class Project < ::Project; end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
project = MyEngine::Project.create!
|
|
44
|
+
@params.merge!(:controller => "MyEngine::ProjectsController", :action => "show", :id => project.id)
|
|
45
|
+
resource = CanCan::ControllerResource.new(@controller)
|
|
46
|
+
resource.load_resource
|
|
47
|
+
@controller.instance_variable_get(:@project).should == project
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Rails includes namespace in params, see issue #349
|
|
51
|
+
it "should create through the namespaced params" do
|
|
52
|
+
module MyEngine
|
|
53
|
+
class Project < ::Project; end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
@params.merge!(:controller => "MyEngine::ProjectsController", :action => "create", :my_engine_project => {:name => "foobar"})
|
|
57
|
+
resource = CanCan::ControllerResource.new(@controller)
|
|
58
|
+
resource.load_resource
|
|
59
|
+
@controller.instance_variable_get(:@project).name.should == "foobar"
|
|
60
|
+
end
|
|
61
|
+
|
|
38
62
|
it "should properly load resource for namespaced controller when using '::' for namespace" do
|
|
39
63
|
project = Project.create!
|
|
40
64
|
@params.merge!(:controller => "Admin::ProjectsController", :action => "show", :id => project.id)
|
|
@@ -43,6 +67,16 @@ describe CanCan::ControllerResource do
|
|
|
43
67
|
@controller.instance_variable_get(:@project).should == project
|
|
44
68
|
end
|
|
45
69
|
|
|
70
|
+
it "has the specified nested resource_class when using / for namespace" do
|
|
71
|
+
module Admin
|
|
72
|
+
class Dashboard; end
|
|
73
|
+
end
|
|
74
|
+
@ability.can(:index, "admin/dashboard")
|
|
75
|
+
@params.merge!(:controller => "admin/dashboard", :action => "index")
|
|
76
|
+
resource = CanCan::ControllerResource.new(@controller, :authorize => true)
|
|
77
|
+
resource.send(:resource_class).should == Admin::Dashboard
|
|
78
|
+
end
|
|
79
|
+
|
|
46
80
|
it "should build a new resource with hash if params[:id] is not specified" do
|
|
47
81
|
@params.merge!(:action => "create", :project => {:name => "foobar"})
|
|
48
82
|
resource = CanCan::ControllerResource.new(@controller)
|
|
@@ -50,6 +84,20 @@ describe CanCan::ControllerResource do
|
|
|
50
84
|
@controller.instance_variable_get(:@project).name.should == "foobar"
|
|
51
85
|
end
|
|
52
86
|
|
|
87
|
+
it "should build a new resource for namespaced model with hash if params[:id] is not specified" do
|
|
88
|
+
@params.merge!(:action => "create", 'sub_project' => {:name => "foobar"})
|
|
89
|
+
resource = CanCan::ControllerResource.new(@controller, :class => ::Sub::Project)
|
|
90
|
+
resource.load_resource
|
|
91
|
+
@controller.instance_variable_get(:@project).name.should == "foobar"
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
it "should build a new resource for namespaced controller and namespaced model with hash if params[:id] is not specified" do
|
|
95
|
+
@params.merge!(:controller => "Admin::SubProjectsController", :action => "create", 'sub_project' => {:name => "foobar"})
|
|
96
|
+
resource = CanCan::ControllerResource.new(@controller, :class => Project)
|
|
97
|
+
resource.load_resource
|
|
98
|
+
@controller.instance_variable_get(:@sub_project).name.should == "foobar"
|
|
99
|
+
end
|
|
100
|
+
|
|
53
101
|
it "should build a new resource with attributes from current ability" do
|
|
54
102
|
@params.merge!(:action => "new")
|
|
55
103
|
@ability.can(:create, Project, :name => "from conditions")
|
|
@@ -104,13 +152,13 @@ describe CanCan::ControllerResource do
|
|
|
104
152
|
it "should authorize parent resource in collection action" do
|
|
105
153
|
@params[:action] = "index"
|
|
106
154
|
@controller.instance_variable_set(:@category, :some_category)
|
|
107
|
-
stub(@controller).authorize!(:
|
|
155
|
+
stub(@controller).authorize!(:show, :some_category) { raise CanCan::AccessDenied }
|
|
108
156
|
resource = CanCan::ControllerResource.new(@controller, :category, :parent => true)
|
|
109
157
|
lambda { resource.authorize_resource }.should raise_error(CanCan::AccessDenied)
|
|
110
158
|
end
|
|
111
159
|
|
|
112
160
|
it "should perform authorization using controller action and loaded model" do
|
|
113
|
-
@params.merge!(:action => "show", :id => 123)
|
|
161
|
+
@params.merge!(:action => "show", :id => "123")
|
|
114
162
|
@controller.instance_variable_set(:@project, :some_project)
|
|
115
163
|
stub(@controller).authorize!(:show, :some_project) { raise CanCan::AccessDenied }
|
|
116
164
|
resource = CanCan::ControllerResource.new(@controller)
|
|
@@ -118,14 +166,14 @@ describe CanCan::ControllerResource do
|
|
|
118
166
|
end
|
|
119
167
|
|
|
120
168
|
it "should perform authorization using controller action and non loaded model" do
|
|
121
|
-
@params.merge!(:action => "show", :id => 123)
|
|
169
|
+
@params.merge!(:action => "show", :id => "123")
|
|
122
170
|
stub(@controller).authorize!(:show, Project) { raise CanCan::AccessDenied }
|
|
123
171
|
resource = CanCan::ControllerResource.new(@controller)
|
|
124
172
|
lambda { resource.authorize_resource }.should raise_error(CanCan::AccessDenied)
|
|
125
173
|
end
|
|
126
174
|
|
|
127
175
|
it "should call load_resource and authorize_resource for load_and_authorize_resource" do
|
|
128
|
-
@params.merge!(:action => "show", :id => 123)
|
|
176
|
+
@params.merge!(:action => "show", :id => "123")
|
|
129
177
|
resource = CanCan::ControllerResource.new(@controller)
|
|
130
178
|
mock(resource).load_resource
|
|
131
179
|
mock(resource).authorize_resource
|
|
@@ -133,7 +181,7 @@ describe CanCan::ControllerResource do
|
|
|
133
181
|
end
|
|
134
182
|
|
|
135
183
|
it "should not build a single resource when on custom collection action even with id" do
|
|
136
|
-
@params.merge!(:action => "sort", :id => 123)
|
|
184
|
+
@params.merge!(:action => "sort", :id => "123")
|
|
137
185
|
resource = CanCan::ControllerResource.new(@controller, :collection => [:sort, :list])
|
|
138
186
|
resource.load_resource
|
|
139
187
|
@controller.instance_variable_get(:@project).should be_nil
|
|
@@ -149,7 +197,7 @@ describe CanCan::ControllerResource do
|
|
|
149
197
|
end
|
|
150
198
|
|
|
151
199
|
it "should build a resource when on custom new action even when params[:id] exists" do
|
|
152
|
-
@params.merge!(:action => "build", :id => 123)
|
|
200
|
+
@params.merge!(:action => "build", :id => "123")
|
|
153
201
|
stub(Project).new { :some_project }
|
|
154
202
|
resource = CanCan::ControllerResource.new(@controller, :new => :build)
|
|
155
203
|
resource.load_resource
|
|
@@ -183,39 +231,47 @@ describe CanCan::ControllerResource do
|
|
|
183
231
|
resource.should_not be_parent
|
|
184
232
|
end
|
|
185
233
|
|
|
234
|
+
it "should have the specified resource_class if 'name' is passed to load_resource" do
|
|
235
|
+
class Section
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
resource = CanCan::ControllerResource.new(@controller, :section)
|
|
239
|
+
resource.send(:resource_class).should == Section
|
|
240
|
+
end
|
|
241
|
+
|
|
186
242
|
it "should load parent resource through proper id parameter" do
|
|
187
243
|
project = Project.create!
|
|
188
|
-
@params.merge!(:action => "index", :project_id => project.id)
|
|
189
|
-
resource = CanCan::ControllerResource.new(@controller, :project
|
|
244
|
+
@params.merge!(:controller => "categories", :action => "index", :project_id => project.id)
|
|
245
|
+
resource = CanCan::ControllerResource.new(@controller, :project)
|
|
190
246
|
resource.load_resource
|
|
191
247
|
@controller.instance_variable_get(:@project).should == project
|
|
192
248
|
end
|
|
193
249
|
|
|
194
250
|
it "should load resource through the association of another parent resource using instance variable" do
|
|
195
|
-
@params.merge!(:action => "show", :id => 123)
|
|
251
|
+
@params.merge!(:action => "show", :id => "123")
|
|
196
252
|
category = Object.new
|
|
197
253
|
@controller.instance_variable_set(:@category, category)
|
|
198
|
-
stub(category).projects.stub!.find(123) { :some_project }
|
|
254
|
+
stub(category).projects.stub!.find("123") { :some_project }
|
|
199
255
|
resource = CanCan::ControllerResource.new(@controller, :through => :category)
|
|
200
256
|
resource.load_resource
|
|
201
257
|
@controller.instance_variable_get(:@project).should == :some_project
|
|
202
258
|
end
|
|
203
259
|
|
|
204
260
|
it "should load resource through the custom association name" do
|
|
205
|
-
@params.merge!(:action => "show", :id => 123)
|
|
261
|
+
@params.merge!(:action => "show", :id => "123")
|
|
206
262
|
category = Object.new
|
|
207
263
|
@controller.instance_variable_set(:@category, category)
|
|
208
|
-
stub(category).custom_projects.stub!.find(123) { :some_project }
|
|
264
|
+
stub(category).custom_projects.stub!.find("123") { :some_project }
|
|
209
265
|
resource = CanCan::ControllerResource.new(@controller, :through => :category, :through_association => :custom_projects)
|
|
210
266
|
resource.load_resource
|
|
211
267
|
@controller.instance_variable_get(:@project).should == :some_project
|
|
212
268
|
end
|
|
213
269
|
|
|
214
270
|
it "should load resource through the association of another parent resource using method" do
|
|
215
|
-
@params.merge!(:action => "show", :id => 123)
|
|
271
|
+
@params.merge!(:action => "show", :id => "123")
|
|
216
272
|
category = Object.new
|
|
217
273
|
stub(@controller).category { category }
|
|
218
|
-
stub(category).projects.stub!.find(123) { :some_project }
|
|
274
|
+
stub(category).projects.stub!.find("123") { :some_project }
|
|
219
275
|
resource = CanCan::ControllerResource.new(@controller, :through => :category)
|
|
220
276
|
resource.load_resource
|
|
221
277
|
@controller.instance_variable_get(:@project).should == :some_project
|
|
@@ -235,7 +291,10 @@ describe CanCan::ControllerResource do
|
|
|
235
291
|
resource = CanCan::ControllerResource.new(@controller, :through => :category)
|
|
236
292
|
lambda {
|
|
237
293
|
resource.load_resource
|
|
238
|
-
}.should raise_error(CanCan::AccessDenied)
|
|
294
|
+
}.should raise_error(CanCan::AccessDenied) { |exception|
|
|
295
|
+
exception.action.should == :show
|
|
296
|
+
exception.subject.should == Project
|
|
297
|
+
}
|
|
239
298
|
@controller.instance_variable_get(:@project).should be_nil
|
|
240
299
|
end
|
|
241
300
|
|
|
@@ -249,17 +308,17 @@ describe CanCan::ControllerResource do
|
|
|
249
308
|
end
|
|
250
309
|
|
|
251
310
|
it "should load through first matching if multiple are given" do
|
|
252
|
-
@params.merge!(:action => "show", :id => 123)
|
|
311
|
+
@params.merge!(:action => "show", :id => "123")
|
|
253
312
|
category = Object.new
|
|
254
313
|
@controller.instance_variable_set(:@category, category)
|
|
255
|
-
stub(category).projects.stub!.find(123) { :some_project }
|
|
314
|
+
stub(category).projects.stub!.find("123") { :some_project }
|
|
256
315
|
resource = CanCan::ControllerResource.new(@controller, :through => [:category, :user])
|
|
257
316
|
resource.load_resource
|
|
258
317
|
@controller.instance_variable_get(:@project).should == :some_project
|
|
259
318
|
end
|
|
260
319
|
|
|
261
|
-
it "should find record through has_one association with :singleton option" do
|
|
262
|
-
@params.merge!(:action => "show", :id =>
|
|
320
|
+
it "should find record through has_one association with :singleton option without id param" do
|
|
321
|
+
@params.merge!(:action => "show", :id => nil)
|
|
263
322
|
category = Object.new
|
|
264
323
|
@controller.instance_variable_set(:@category, category)
|
|
265
324
|
stub(category).project { :some_project }
|
|
@@ -268,14 +327,14 @@ describe CanCan::ControllerResource do
|
|
|
268
327
|
@controller.instance_variable_get(:@project).should == :some_project
|
|
269
328
|
end
|
|
270
329
|
|
|
271
|
-
it "should build record through has_one association with :singleton option" do
|
|
330
|
+
it "should not build record through has_one association with :singleton option because it can cause it to delete it in the database" do
|
|
272
331
|
@params.merge!(:action => "create", :project => {:name => "foobar"})
|
|
273
|
-
category =
|
|
332
|
+
category = Category.new
|
|
274
333
|
@controller.instance_variable_set(:@category, category)
|
|
275
|
-
stub(category).build_project { |attributes| Project.new(attributes) }
|
|
276
334
|
resource = CanCan::ControllerResource.new(@controller, :through => :category, :singleton => true)
|
|
277
335
|
resource.load_resource
|
|
278
336
|
@controller.instance_variable_get(:@project).name.should == "foobar"
|
|
337
|
+
@controller.instance_variable_get(:@project).category.should == category
|
|
279
338
|
end
|
|
280
339
|
|
|
281
340
|
it "should find record through has_one association with :singleton and :shallow options" do
|
|
@@ -293,10 +352,10 @@ describe CanCan::ControllerResource do
|
|
|
293
352
|
@controller.instance_variable_get(:@project).name.should == "foobar"
|
|
294
353
|
end
|
|
295
354
|
|
|
296
|
-
it "should only authorize :
|
|
355
|
+
it "should only authorize :show action on parent resource" do
|
|
297
356
|
project = Project.create!
|
|
298
357
|
@params.merge!(:action => "new", :project_id => project.id)
|
|
299
|
-
stub(@controller).authorize!(:
|
|
358
|
+
stub(@controller).authorize!(:show, project) { raise CanCan::AccessDenied }
|
|
300
359
|
resource = CanCan::ControllerResource.new(@controller, :project, :parent => true)
|
|
301
360
|
lambda { resource.load_and_authorize_resource }.should raise_error(CanCan::AccessDenied)
|
|
302
361
|
end
|
|
@@ -309,8 +368,16 @@ describe CanCan::ControllerResource do
|
|
|
309
368
|
@controller.instance_variable_get(:@project).should == project
|
|
310
369
|
end
|
|
311
370
|
|
|
371
|
+
it "should load the model using a custom namespaced class" do
|
|
372
|
+
project = Sub::Project.create!
|
|
373
|
+
@params.merge!(:action => "show", :id => project.id)
|
|
374
|
+
resource = CanCan::ControllerResource.new(@controller, :class => ::Sub::Project)
|
|
375
|
+
resource.load_resource
|
|
376
|
+
@controller.instance_variable_get(:@project).should == project
|
|
377
|
+
end
|
|
378
|
+
|
|
312
379
|
it "should authorize based on resource name if class is false" do
|
|
313
|
-
@params.merge!(:action => "show", :id => 123)
|
|
380
|
+
@params.merge!(:action => "show", :id => "123")
|
|
314
381
|
stub(@controller).authorize!(:show, :project) { raise CanCan::AccessDenied }
|
|
315
382
|
resource = CanCan::ControllerResource.new(@controller, :class => false)
|
|
316
383
|
lambda { resource.authorize_resource }.should raise_error(CanCan::AccessDenied)
|
|
@@ -325,6 +392,21 @@ describe CanCan::ControllerResource do
|
|
|
325
392
|
@controller.instance_variable_get(:@custom_project).should == project
|
|
326
393
|
end
|
|
327
394
|
|
|
395
|
+
it "should load resource using custom ID param" do
|
|
396
|
+
project = Project.create!
|
|
397
|
+
@params.merge!(:action => "show", :the_project => project.id)
|
|
398
|
+
resource = CanCan::ControllerResource.new(@controller, :id_param => :the_project)
|
|
399
|
+
resource.load_resource
|
|
400
|
+
@controller.instance_variable_get(:@project).should == project
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
# CVE-2012-5664
|
|
404
|
+
it "should always convert id param to string" do
|
|
405
|
+
@params.merge!(:action => "show", :the_project => { :malicious => "I am" })
|
|
406
|
+
resource = CanCan::ControllerResource.new(@controller, :id_param => :the_project)
|
|
407
|
+
resource.send(:id_param).class.should == String
|
|
408
|
+
end
|
|
409
|
+
|
|
328
410
|
it "should load resource using custom find_by attribute" do
|
|
329
411
|
project = Project.create!(:name => "foo")
|
|
330
412
|
@params.merge!(:action => "show", :id => "foo")
|
|
@@ -333,6 +415,14 @@ describe CanCan::ControllerResource do
|
|
|
333
415
|
@controller.instance_variable_get(:@project).should == project
|
|
334
416
|
end
|
|
335
417
|
|
|
418
|
+
it "should allow full find method to be passed into find_by option" do
|
|
419
|
+
project = Project.create!(:name => "foo")
|
|
420
|
+
@params.merge!(:action => "show", :id => "foo")
|
|
421
|
+
resource = CanCan::ControllerResource.new(@controller, :find_by => :find_by_name)
|
|
422
|
+
resource.load_resource
|
|
423
|
+
@controller.instance_variable_get(:@project).should == project
|
|
424
|
+
end
|
|
425
|
+
|
|
336
426
|
it "should raise ImplementationRemoved when adding :name option" do
|
|
337
427
|
lambda {
|
|
338
428
|
CanCan::ControllerResource.new(@controller, :name => :foo)
|
|
@@ -32,4 +32,27 @@ describe CanCan::AccessDenied do
|
|
|
32
32
|
@exception.message.should == "Access denied!"
|
|
33
33
|
end
|
|
34
34
|
end
|
|
35
|
+
|
|
36
|
+
describe "i18n in the default message" do
|
|
37
|
+
after(:each) do
|
|
38
|
+
I18n.backend = nil
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it "uses i18n for the default message" do
|
|
42
|
+
I18n.backend.store_translations :en, :unauthorized => {:default => "This is a different message"}
|
|
43
|
+
@exception = CanCan::AccessDenied.new
|
|
44
|
+
@exception.message.should == "This is a different message"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "defaults to a nice message" do
|
|
48
|
+
@exception = CanCan::AccessDenied.new
|
|
49
|
+
@exception.message.should == "You are not authorized to access this page."
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it "does not use translation if a message is given" do
|
|
53
|
+
@exception = CanCan::AccessDenied.new("Hey! You're not welcome here")
|
|
54
|
+
@exception.message.should == "Hey! You're not welcome here"
|
|
55
|
+
@exception.message.should_not == "You are not authorized to access this page."
|
|
56
|
+
end
|
|
57
|
+
end
|
|
35
58
|
end
|
|
@@ -32,11 +32,29 @@ describe CanCan::InheritedResource do
|
|
|
32
32
|
@controller.instance_variable_get(:@project).should == :project_resource
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
-
it "index should load through @controller.
|
|
35
|
+
it "index should load through @controller.end_of_association_chain" do
|
|
36
36
|
@params[:action] = "index"
|
|
37
37
|
stub(Project).accessible_by(@ability, :index) { :projects }
|
|
38
|
-
stub(@controller).
|
|
38
|
+
stub(@controller).end_of_association_chain { Project }
|
|
39
39
|
CanCan::InheritedResource.new(@controller).load_resource
|
|
40
40
|
@controller.instance_variable_get(:@projects).should == :projects
|
|
41
41
|
end
|
|
42
|
+
|
|
43
|
+
it "should build a new resource with attributes from current ability" do
|
|
44
|
+
@params[:action] = "new"
|
|
45
|
+
@ability.can(:create, Project, :name => "from conditions")
|
|
46
|
+
stub(@controller).build_resource { Struct.new(:name).new }
|
|
47
|
+
resource = CanCan::InheritedResource.new(@controller)
|
|
48
|
+
resource.load_resource
|
|
49
|
+
@controller.instance_variable_get(:@project).name.should == "from conditions"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it "should override initial attributes with params" do
|
|
53
|
+
@params.merge!(:action => "new", :project => {:name => "from params"})
|
|
54
|
+
@ability.can(:create, Project, :name => "from conditions")
|
|
55
|
+
stub(@controller).build_resource { Struct.new(:name).new }
|
|
56
|
+
resource = CanCan::ControllerResource.new(@controller)
|
|
57
|
+
resource.load_resource
|
|
58
|
+
@controller.instance_variable_get(:@project).name.should == "from params"
|
|
59
|
+
end
|
|
42
60
|
end
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
if ENV["MODEL_ADAPTER"].nil? || ENV["MODEL_ADAPTER"] == "active_record"
|
|
2
2
|
require "spec_helper"
|
|
3
3
|
|
|
4
|
-
RSpec.configure do |config|
|
|
5
|
-
config.extend WithModel
|
|
6
|
-
end
|
|
7
|
-
|
|
8
4
|
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
|
|
9
5
|
|
|
10
6
|
describe CanCan::ModelAdapters::ActiveRecordAdapter do
|
|
@@ -24,10 +20,12 @@ if ENV["MODEL_ADAPTER"].nil? || ENV["MODEL_ADAPTER"] == "active_record"
|
|
|
24
20
|
t.boolean "secret"
|
|
25
21
|
t.integer "priority"
|
|
26
22
|
t.integer "category_id"
|
|
23
|
+
t.integer "user_id"
|
|
27
24
|
end
|
|
28
25
|
model do
|
|
29
26
|
belongs_to :category
|
|
30
27
|
has_many :comments
|
|
28
|
+
belongs_to :user
|
|
31
29
|
end
|
|
32
30
|
end
|
|
33
31
|
|
|
@@ -41,6 +39,15 @@ if ENV["MODEL_ADAPTER"].nil? || ENV["MODEL_ADAPTER"] == "active_record"
|
|
|
41
39
|
end
|
|
42
40
|
end
|
|
43
41
|
|
|
42
|
+
with_model :user do
|
|
43
|
+
table do |t|
|
|
44
|
+
|
|
45
|
+
end
|
|
46
|
+
model do
|
|
47
|
+
has_many :articles
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
44
51
|
before(:each) do
|
|
45
52
|
Article.delete_all
|
|
46
53
|
Comment.delete_all
|
|
@@ -56,6 +63,11 @@ if ENV["MODEL_ADAPTER"].nil? || ENV["MODEL_ADAPTER"] == "active_record"
|
|
|
56
63
|
CanCan::ModelAdapters::AbstractAdapter.adapter_class(Article).should == CanCan::ModelAdapters::ActiveRecordAdapter
|
|
57
64
|
end
|
|
58
65
|
|
|
66
|
+
it "should find record" do
|
|
67
|
+
article = Article.create!
|
|
68
|
+
CanCan::ModelAdapters::ActiveRecordAdapter.find(Article, article.id).should == article
|
|
69
|
+
end
|
|
70
|
+
|
|
59
71
|
it "should not fetch any records when no abilities are defined" do
|
|
60
72
|
Article.create!
|
|
61
73
|
Article.accessible_by(@ability).should be_empty
|
|
@@ -125,6 +137,15 @@ if ENV["MODEL_ADAPTER"].nil? || ENV["MODEL_ADAPTER"] == "active_record"
|
|
|
125
137
|
Article.accessible_by(@ability).should == [article1]
|
|
126
138
|
end
|
|
127
139
|
|
|
140
|
+
it "should fetch only associated records when using with a scope for conditions" do
|
|
141
|
+
@ability.can :read, Article, Article.where(:secret => true)
|
|
142
|
+
category1 = Category.create!(:visible => false)
|
|
143
|
+
category2 = Category.create!(:visible => true)
|
|
144
|
+
article1 = Article.create!(:secret => true, :category => category1)
|
|
145
|
+
article2 = Article.create!(:secret => true, :category => category2)
|
|
146
|
+
category1.articles.accessible_by(@ability).should == [article1]
|
|
147
|
+
end
|
|
148
|
+
|
|
128
149
|
it "should raise an exception when trying to merge scope with other conditions" do
|
|
129
150
|
@ability.can :read, Article, :published => true
|
|
130
151
|
@ability.can :read, Article, Article.where(:secret => true)
|
|
@@ -186,6 +207,16 @@ if ENV["MODEL_ADAPTER"].nil? || ENV["MODEL_ADAPTER"] == "active_record"
|
|
|
186
207
|
@ability.model_adapter(Article, :read).conditions.should == "'t'='t'"
|
|
187
208
|
end
|
|
188
209
|
|
|
210
|
+
it "should return appropriate sql conditions in complex case with nested joins" do
|
|
211
|
+
@ability.can :read, Comment, :article => { :category => { :visible => true } }
|
|
212
|
+
@ability.model_adapter(Comment, :read).conditions.should == { Category.table_name.to_sym => { :visible => true } }
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
it "should return appropriate sql conditions in complex case with nested joins of different depth" do
|
|
216
|
+
@ability.can :read, Comment, :article => { :published => true, :category => { :visible => true } }
|
|
217
|
+
@ability.model_adapter(Comment, :read).conditions.should == { Article.table_name.to_sym => { :published => true }, Category.table_name.to_sym => { :visible => true } }
|
|
218
|
+
end
|
|
219
|
+
|
|
189
220
|
it "should not forget conditions when calling with SQL string" do
|
|
190
221
|
@ability.can :read, Article, :published => true
|
|
191
222
|
@ability.can :read, Article, ['secret=?', false]
|
|
@@ -217,6 +248,21 @@ if ENV["MODEL_ADAPTER"].nil? || ENV["MODEL_ADAPTER"] == "active_record"
|
|
|
217
248
|
@ability.model_adapter(Article, :read).joins.should == [:project]
|
|
218
249
|
end
|
|
219
250
|
|
|
251
|
+
it "should merge nested and non-nested joins" do
|
|
252
|
+
@ability.can :read, Article, :project => { :blocked => false }
|
|
253
|
+
@ability.can :read, Article, :project => { :comments => { :spam => true } }
|
|
254
|
+
@ability.model_adapter(Article, :read).joins.should == [{:project=>[:comments]}]
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
it "should merge :all conditions with other conditions" do
|
|
258
|
+
user = User.create!
|
|
259
|
+
article = Article.create!(:user => user)
|
|
260
|
+
ability = Ability.new(user)
|
|
261
|
+
ability.can :manage, :all
|
|
262
|
+
ability.can :manage, Article, :user_id => user.id
|
|
263
|
+
Article.accessible_by(ability).should == [article]
|
|
264
|
+
end
|
|
265
|
+
|
|
220
266
|
it "should restrict articles given a MetaWhere condition" do
|
|
221
267
|
@ability.can :read, Article, :priority.lt => 2
|
|
222
268
|
article1 = Article.create!(:priority => 1)
|
|
@@ -226,6 +272,16 @@ if ENV["MODEL_ADAPTER"].nil? || ENV["MODEL_ADAPTER"] == "active_record"
|
|
|
226
272
|
@ability.should_not be_able_to(:read, article2)
|
|
227
273
|
end
|
|
228
274
|
|
|
275
|
+
it "should merge MetaWhere and non-MetaWhere conditions" do
|
|
276
|
+
@ability.can :read, Article, :priority.lt => 2
|
|
277
|
+
@ability.can :read, Article, :priority => 1
|
|
278
|
+
article1 = Article.create!(:priority => 1)
|
|
279
|
+
article2 = Article.create!(:priority => 3)
|
|
280
|
+
Article.accessible_by(@ability).should == [article1]
|
|
281
|
+
@ability.should be_able_to(:read, article1)
|
|
282
|
+
@ability.should_not be_able_to(:read, article2)
|
|
283
|
+
end
|
|
284
|
+
|
|
229
285
|
it "should match any MetaWhere condition" do
|
|
230
286
|
adapter = CanCan::ModelAdapters::ActiveRecordAdapter
|
|
231
287
|
article1 = Article.new(:priority => 1, :name => "Hello World")
|
|
@@ -256,8 +312,9 @@ if ENV["MODEL_ADAPTER"].nil? || ENV["MODEL_ADAPTER"] == "active_record"
|
|
|
256
312
|
adapter.matches_condition?(article1, :name.like, "%helo%").should be_false
|
|
257
313
|
adapter.matches_condition?(article1, :name.like, "hello").should be_false
|
|
258
314
|
adapter.matches_condition?(article1, :name.like, "hello.world").should be_false
|
|
259
|
-
|
|
260
|
-
adapter.matches_condition?(article1, :name.nlike, "%
|
|
315
|
+
# For some reason this is reporting "The not_matches MetaWhere condition is not supported."
|
|
316
|
+
# adapter.matches_condition?(article1, :name.nlike, "%helo%").should be_true
|
|
317
|
+
# adapter.matches_condition?(article1, :name.nlike, "%ello worl%").should be_false
|
|
261
318
|
end
|
|
262
319
|
end
|
|
263
320
|
end
|
|
@@ -36,6 +36,11 @@ if ENV["MODEL_ADAPTER"] == "data_mapper"
|
|
|
36
36
|
CanCan::ModelAdapters::AbstractAdapter.adapter_class(Article).should == CanCan::ModelAdapters::DataMapperAdapter
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
+
it "should find record" do
|
|
40
|
+
article = Article.create
|
|
41
|
+
CanCan::ModelAdapters::DataMapperAdapter.find(Article, article.id).should == article
|
|
42
|
+
end
|
|
43
|
+
|
|
39
44
|
it "should not fetch any records when no abilities are defined" do
|
|
40
45
|
Article.create
|
|
41
46
|
Article.accessible_by(@ability).should be_empty
|
|
@@ -65,7 +70,6 @@ if ENV["MODEL_ADAPTER"] == "data_mapper"
|
|
|
65
70
|
end
|
|
66
71
|
|
|
67
72
|
it "should fetch only the articles that are published and not secret" do
|
|
68
|
-
pending "the `cannot` may require some custom SQL, maybe abstract out from Active Record adapter"
|
|
69
73
|
@ability.can :read, Article, :published => true
|
|
70
74
|
@ability.cannot :read, Article, :secret => true
|
|
71
75
|
article1 = Article.create(:published => true, :secret => false)
|
|
@@ -36,12 +36,26 @@ if ENV["MODEL_ADAPTER"] == "mongoid"
|
|
|
36
36
|
CanCan::ModelAdapters::AbstractAdapter.adapter_class(MongoidProject).should == CanCan::ModelAdapters::MongoidAdapter
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
+
it "should find record" do
|
|
40
|
+
project = MongoidProject.create
|
|
41
|
+
CanCan::ModelAdapters::MongoidAdapter.find(MongoidProject, project.id).should == project
|
|
42
|
+
end
|
|
43
|
+
|
|
39
44
|
it "should compare properties on mongoid documents with the conditions hash" do
|
|
40
45
|
model = MongoidProject.new
|
|
41
46
|
@ability.can :read, MongoidProject, :id => model.id
|
|
42
47
|
@ability.should be_able_to(:read, model)
|
|
43
48
|
end
|
|
44
49
|
|
|
50
|
+
it "should be able to read hashes when field is array" do
|
|
51
|
+
one_to_three = MongoidProject.create(:numbers => ['one', 'two', 'three'])
|
|
52
|
+
two_to_five = MongoidProject.create(:numbers => ['two', 'three', 'four', 'five'])
|
|
53
|
+
|
|
54
|
+
@ability.can :foo, MongoidProject, :numbers => 'one'
|
|
55
|
+
@ability.should be_able_to(:foo, one_to_three)
|
|
56
|
+
@ability.should_not be_able_to(:foo, two_to_five)
|
|
57
|
+
end
|
|
58
|
+
|
|
45
59
|
it "should return [] when no ability is defined so no records are found" do
|
|
46
60
|
MongoidProject.create(:title => 'Sir')
|
|
47
61
|
MongoidProject.create(:title => 'Lord')
|
|
@@ -59,6 +73,26 @@ if ENV["MODEL_ADAPTER"] == "mongoid"
|
|
|
59
73
|
MongoidProject.accessible_by(@ability, :read).entries.should == [sir]
|
|
60
74
|
end
|
|
61
75
|
|
|
76
|
+
it "should return the correct records when a mix of can and cannot rules in defined ability" do
|
|
77
|
+
@ability.can :manage, MongoidProject, :title => 'Sir'
|
|
78
|
+
@ability.cannot :destroy, MongoidProject
|
|
79
|
+
|
|
80
|
+
sir = MongoidProject.create(:title => 'Sir')
|
|
81
|
+
lord = MongoidProject.create(:title => 'Lord')
|
|
82
|
+
dude = MongoidProject.create(:title => 'Dude')
|
|
83
|
+
|
|
84
|
+
MongoidProject.accessible_by(@ability, :destroy).entries.should == [sir]
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it "should be able to mix empty conditions and hashes" do
|
|
88
|
+
@ability.can :read, MongoidProject
|
|
89
|
+
@ability.can :read, MongoidProject, :title => 'Sir'
|
|
90
|
+
sir = MongoidProject.create(:title => 'Sir')
|
|
91
|
+
lord = MongoidProject.create(:title => 'Lord')
|
|
92
|
+
|
|
93
|
+
MongoidProject.accessible_by(@ability, :read).count.should == 2
|
|
94
|
+
end
|
|
95
|
+
|
|
62
96
|
it "should return everything when the defined ability is manage all" do
|
|
63
97
|
@ability.can :manage, :all
|
|
64
98
|
sir = MongoidProject.create(:title => 'Sir')
|
|
@@ -68,6 +102,14 @@ if ENV["MODEL_ADAPTER"] == "mongoid"
|
|
|
68
102
|
MongoidProject.accessible_by(@ability, :read).entries.should == [sir, lord, dude]
|
|
69
103
|
end
|
|
70
104
|
|
|
105
|
+
it "should allow a scope for conditions" do
|
|
106
|
+
@ability.can :read, MongoidProject, MongoidProject.where(:title => 'Sir')
|
|
107
|
+
sir = MongoidProject.create(:title => 'Sir')
|
|
108
|
+
lord = MongoidProject.create(:title => 'Lord')
|
|
109
|
+
dude = MongoidProject.create(:title => 'Dude')
|
|
110
|
+
|
|
111
|
+
MongoidProject.accessible_by(@ability, :read).entries.should == [sir]
|
|
112
|
+
end
|
|
71
113
|
|
|
72
114
|
describe "Mongoid::Criteria where clause Symbol extensions using MongoDB expressions" do
|
|
73
115
|
it "should handle :field.in" do
|
|
@@ -154,7 +196,7 @@ if ENV["MODEL_ADAPTER"] == "mongoid"
|
|
|
154
196
|
@ability.can :read, MongoidProject, :foo => {:bar => 1}
|
|
155
197
|
MongoidProject.accessible_by(@ability, :read).entries.first.should == obj
|
|
156
198
|
end
|
|
157
|
-
|
|
199
|
+
|
|
158
200
|
it "should exclude from the result if set to cannot" do
|
|
159
201
|
obj = MongoidProject.create(:bar => 1)
|
|
160
202
|
obj2 = MongoidProject.create(:bar => 2)
|
|
@@ -171,7 +213,7 @@ if ENV["MODEL_ADAPTER"] == "mongoid"
|
|
|
171
213
|
@ability.can :read, MongoidProject, :bar => 2
|
|
172
214
|
MongoidProject.accessible_by(@ability, :read).entries.should =~ [obj, obj2]
|
|
173
215
|
end
|
|
174
|
-
|
|
216
|
+
|
|
175
217
|
it "should not allow to fetch records when ability with just block present" do
|
|
176
218
|
@ability.can :read, MongoidProject do
|
|
177
219
|
false
|
data/spec/cancan/rule_spec.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require "spec_helper"
|
|
2
|
+
require "ostruct" # for OpenStruct below
|
|
2
3
|
|
|
3
4
|
# Most of Rule functionality is tested in Ability specs
|
|
4
5
|
describe CanCan::Rule do
|
|
@@ -36,4 +37,16 @@ describe CanCan::Rule do
|
|
|
36
37
|
rule = CanCan::Rule.new(true, :read, Integer, nil, nil)
|
|
37
38
|
rule.associations_hash.should == {}
|
|
38
39
|
end
|
|
40
|
+
|
|
41
|
+
it "should not be mergeable if conditions are not simple hashes" do
|
|
42
|
+
meta_where = OpenStruct.new(:name => 'metawhere', :column => 'test')
|
|
43
|
+
@conditions[meta_where] = :bar
|
|
44
|
+
|
|
45
|
+
@rule.should be_unmergeable
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "should be mergeable if conditions is an empty hash" do
|
|
49
|
+
@conditions = {}
|
|
50
|
+
@rule.should_not be_unmergeable
|
|
51
|
+
end
|
|
39
52
|
end
|