cancancan 1.7.1 → 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -99,31 +99,19 @@ module CanCan
99
99
  # override_matching_for_conditions?(subject, conditions) and
100
100
  # matches_conditions_hash?(subject, conditions)
101
101
  def matches_conditions_hash?(subject, conditions = @conditions)
102
- if conditions.empty?
103
- true
104
- else
105
- if model_adapter(subject).override_conditions_hash_matching? subject, conditions
106
- model_adapter(subject).matches_conditions_hash? subject, conditions
107
- else
108
- conditions.all? do |name, value|
109
- if model_adapter(subject).override_condition_matching? subject, name, value
110
- model_adapter(subject).matches_condition? subject, name, value
111
- else
112
- attribute = subject.send(name)
113
- if value.kind_of?(Hash)
114
- if attribute.kind_of?(Array) || (defined?(ActiveRecord) && attribute.kind_of?(ActiveRecord::Relation))
115
- attribute.any? { |element| matches_conditions_hash? element, value }
116
- else
117
- !attribute.nil? && matches_conditions_hash?(attribute, value)
118
- end
119
- elsif !value.is_a?(String) && value.kind_of?(Enumerable)
120
- value.include? attribute
121
- else
122
- attribute == value
123
- end
124
- end
125
- end
102
+ return true if conditions.empty?
103
+ adapter = model_adapter(subject)
104
+
105
+ if adapter.override_conditions_hash_matching?(subject, conditions)
106
+ return adapter.matches_conditions_hash?(subject, conditions)
107
+ end
108
+
109
+ conditions.all? do |name, value|
110
+ if adapter.override_condition_matching?(subject, name, value)
111
+ return adapter.matches_condition?(subject, name, value)
126
112
  end
113
+
114
+ condition_match?(subject.send(name), value)
127
115
  end
128
116
  end
129
117
 
@@ -143,5 +131,22 @@ module CanCan
143
131
  def model_adapter(subject)
144
132
  CanCan::ModelAdapters::AbstractAdapter.adapter_class(subject_class?(subject) ? subject : subject.class)
145
133
  end
134
+
135
+ def condition_match?(attribute, value)
136
+ case value
137
+ when Hash then hash_condition_match?(attribute, value)
138
+ when String then attribute == value
139
+ when Enumerable then value.include?(attribute)
140
+ else attribute == value
141
+ end
142
+ end
143
+
144
+ def hash_condition_match?(attribute, value)
145
+ if attribute.kind_of?(Array) || (defined?(ActiveRecord) && attribute.kind_of?(ActiveRecord::Relation))
146
+ attribute.any? { |element| matches_conditions_hash?(element, value) }
147
+ else
148
+ !attribute.nil? && matches_conditions_hash?(attribute, value)
149
+ end
150
+ end
146
151
  end
147
152
  end
@@ -1,3 +1,3 @@
1
1
  module CanCan
2
- VERSION = "1.7.1"
2
+ VERSION = "1.8.0"
3
3
  end
@@ -1 +1,4 @@
1
1
  require 'cancan'
2
+
3
+ module CanCanCan
4
+ end
@@ -147,11 +147,21 @@ describe CanCan::Ability do
147
147
  expect(@ability.can?(:update, 123)).to be_false
148
148
  end
149
149
 
150
+ it "checks if there is a permission for any of given subjects" do
151
+ @ability.can :update, [String, Range]
152
+ expect(@ability.can?(:update, {:any => ["foo", 1..3]})).to be_true
153
+ expect(@ability.can?(:update, {:any => [1..3, "foo"]})).to be_true
154
+ expect(@ability.can?(:update, {:any => [123, "foo"]})).to be_true
155
+ expect(@ability.can?(:update, {:any => [123, 1.0]})).to be_false
156
+ end
157
+
150
158
  it "supports custom objects in the rule" do
151
159
  @ability.can :read, :stats
152
160
  expect(@ability.can?(:read, :stats)).to be_true
153
161
  expect(@ability.can?(:update, :stats)).to be_false
154
162
  expect(@ability.can?(:read, :nonstats)).to be_false
163
+ expect(@ability.can?(:read, {:any => [:stats, :nonstats]})).to be_true
164
+ expect(@ability.can?(:read, {:any => [:nonstats, :neitherstats]})).to be_false
155
165
  end
156
166
 
157
167
  it "checks ancestors of class" do
@@ -159,6 +169,7 @@ describe CanCan::Ability do
159
169
  expect(@ability.can?(:read, Integer)).to be_true
160
170
  expect(@ability.can?(:read, 1.23)).to be_true
161
171
  expect(@ability.can?(:read, "foo")).to be_false
172
+ expect(@ability.can?(:read, {:any => [Integer, String]})).to be_true
162
173
  end
163
174
 
164
175
  it "supports 'cannot' method to define what user cannot do" do
@@ -166,6 +177,9 @@ describe CanCan::Ability do
166
177
  @ability.cannot :read, Integer
167
178
  expect(@ability.can?(:read, "foo")).to be_true
168
179
  expect(@ability.can?(:read, 123)).to be_false
180
+ expect(@ability.can?(:read, {:any => ["foo", "bar"]})).to be_true
181
+ expect(@ability.can?(:read, {:any => [123, "foo"]})).to be_false
182
+ expect(@ability.can?(:read, {:any => [123, 456]})).to be_false
169
183
  end
170
184
 
171
185
  it "passes to previous rule, if block returns false or nil" do
@@ -177,6 +191,8 @@ describe CanCan::Ability do
177
191
  expect(@ability.can?(:read, 3)).to be_true
178
192
  expect(@ability.can?(:read, 8)).to be_false
179
193
  expect(@ability.can?(:read, 123)).to be_true
194
+ expect(@ability.can?(:read, {:any => [123, 8]})).to be_true
195
+ expect(@ability.can?(:read, {:any => [8, 9]})).to be_false
180
196
  end
181
197
 
182
198
  it "always returns `false` for single cannot definition" do
@@ -218,6 +234,8 @@ describe CanCan::Ability do
218
234
  end
219
235
  expect(@ability.can?(:read, 2, 1)).to be_true
220
236
  expect(@ability.can?(:read, 2, 3)).to be_false
237
+ expect(@ability.can?(:read, {:any => [4, 5]}, 3)).to be_true
238
+ expect(@ability.can?(:read, {:any => [2, 3]}, 3)).to be_false
221
239
  end
222
240
 
223
241
  it "uses conditions as third parameter and determine abilities from it" do
@@ -225,6 +243,8 @@ describe CanCan::Ability do
225
243
  expect(@ability.can?(:read, 1..3)).to be_true
226
244
  expect(@ability.can?(:read, 1..4)).to be_false
227
245
  expect(@ability.can?(:read, Range)).to be_true
246
+ expect(@ability.can?(:read, {:any => [1..3, 1..4]})).to be_true
247
+ expect(@ability.can?(:read, {:any => [1..4, 2..4]})).to be_false
228
248
  end
229
249
 
230
250
  it "allows an array of options in conditions hash" do
@@ -232,6 +252,8 @@ describe CanCan::Ability do
232
252
  expect(@ability.can?(:read, 1..3)).to be_true
233
253
  expect(@ability.can?(:read, 2..4)).to be_false
234
254
  expect(@ability.can?(:read, 3..5)).to be_true
255
+ expect(@ability.can?(:read, {:any => [2..4, 3..5]})).to be_true
256
+ expect(@ability.can?(:read, {:any => [2..4, 2..5]})).to be_false
235
257
  end
236
258
 
237
259
  it "allows a range of options in conditions hash" do
@@ -313,6 +335,8 @@ describe CanCan::Ability do
313
335
  expect(@ability.can?(:read, "foo" => Range)).to be_true
314
336
  expect(@ability.can?(:read, "foobar" => Range)).to be_false
315
337
  expect(@ability.can?(:read, 123 => Range)).to be_true
338
+ expect(@ability.can?(:read, {:any => [{"foo" => Range}, {"foobar" => Range}]})).to be_true
339
+ expect(@ability.can?(:read, {:any => [{"food" => Range}, {"foobar" => Range}]})).to be_false
316
340
  end
317
341
 
318
342
  it "checks permissions correctly when passing a hash of subjects with multiple definitions" do
@@ -321,6 +345,8 @@ describe CanCan::Ability do
321
345
  expect(@ability.can?(:read, "foo" => Range)).to be_true
322
346
  expect(@ability.can?(:read, "foobar" => Range)).to be_false
323
347
  expect(@ability.can?(:read, 1234 => Range)).to be_true
348
+ expect(@ability.can?(:read, {:any => [{"foo" => Range}, {"foobar" => Range}]})).to be_true
349
+ expect(@ability.can?(:read, {:any => [{"foo.bar" => Range}, {"foobar" => Range}]})).to be_false
324
350
  end
325
351
 
326
352
  it "allows to check ability on Hash-like object" do
@@ -1,553 +1,626 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe CanCan::ControllerResource do
4
+ let(:ability) { Ability.new(nil) }
5
+ let(:params) { HashWithIndifferentAccess.new(:controller => "models") }
6
+ let(:controller_class) { Class.new }
7
+ let(:controller) { controller_class.new }
8
+
4
9
  before(:each) do
5
- @params = HashWithIndifferentAccess.new(:controller => "projects")
6
- @controller_class = Class.new
7
- @controller = @controller_class.new
8
- @ability = Ability.new(nil)
9
- allow(@controller).to receive(:params) { @params }
10
- allow(@controller).to receive(:current_ability) { @ability }
11
- allow(@controller_class).to receive(:cancan_skipper) { {:authorize => {}, :load => {}} }
12
- end
10
+ class Model
11
+ attr_accessor :name
12
+
13
+ def initialize(attributes={})
14
+ attributes.each do |attribute, value|
15
+ send("#{attribute}=", value)
16
+ end
17
+ end
18
+ end
13
19
 
14
- it "loads the resource into an instance variable if params[:id] is specified" do
15
- project = Project.create!
16
- @params.merge!(:action => "show", :id => project.id)
17
- resource = CanCan::ControllerResource.new(@controller)
18
- resource.load_resource
19
- expect(@controller.instance_variable_get(:@project)).to eq(project)
20
+ allow(controller).to receive(:params) { params }
21
+ allow(controller).to receive(:current_ability) { ability }
22
+ allow(controller_class).to receive(:cancan_skipper) { {:authorize => {}, :load => {}} }
20
23
  end
21
24
 
22
- it "does not load resource into an instance variable if already set" do
23
- @params.merge!(:action => "show", :id => "123")
24
- @controller.instance_variable_set(:@project, :some_project)
25
- resource = CanCan::ControllerResource.new(@controller)
26
- resource.load_resource
27
- expect(@controller.instance_variable_get(:@project)).to eq(:some_project)
28
- end
25
+ context "on build actions" do
26
+ before :each do
27
+ params.merge!(:action => "new")
28
+ end
29
29
 
30
- it "loads resource for namespaced controller" do
31
- project = Project.create!
32
- @params.merge!(:controller => "admin/projects", :action => "show", :id => project.id)
33
- resource = CanCan::ControllerResource.new(@controller)
34
- resource.load_resource
35
- expect(@controller.instance_variable_get(:@project)).to eq(project)
36
- end
30
+ it "builds a new resource with attributes from current ability" do
31
+ ability.can(:create, Model, :name => "from conditions")
32
+ resource = CanCan::ControllerResource.new(controller)
33
+ resource.load_resource
34
+ expect(controller.instance_variable_get(:@model).name).to eq("from conditions")
35
+ end
36
+
37
+ it "overrides initial attributes with params" do
38
+ params.merge!(:model => {:name => "from params"})
39
+ ability.can(:create, Model, :name => "from conditions")
40
+ resource = CanCan::ControllerResource.new(controller)
41
+ resource.load_resource
42
+ expect(controller.instance_variable_get(:@model).name).to eq("from params")
43
+ end
37
44
 
38
- it "attempts to load a resource with the same namespace as the controller when using :: for namespace" do
39
- module MyEngine
40
- class Project < ::Project; end
45
+ it "builds a resource when on custom new action even when params[:id] exists" do
46
+ params.merge!(:action => "build", :id => "123")
47
+ allow(Model).to receive(:new) { :some_model }
48
+ resource = CanCan::ControllerResource.new(controller, :new => :build)
49
+ resource.load_resource
50
+ expect(controller.instance_variable_get(:@model)).to eq(:some_model)
41
51
  end
42
52
 
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
- expect(@controller.instance_variable_get(:@project)).to eq(project)
53
+ it "only authorizes :show action on parent resource" do
54
+ model = Model.new
55
+ allow(Model).to receive(:find).with("123") { model }
56
+
57
+ params.merge!(:model_id => 123)
58
+ allow(controller).to receive(:authorize!).with(:show, model) { raise CanCan::AccessDenied }
59
+ resource = CanCan::ControllerResource.new(controller, :model, :parent => true)
60
+ expect { resource.load_and_authorize_resource }.to raise_error(CanCan::AccessDenied)
61
+ end
48
62
  end
49
63
 
50
- # Rails includes namespace in params, see issue #349
51
- it "creates through the namespaced params" do
52
- module MyEngine
53
- class Project < ::Project; end
64
+ context "on create actions" do
65
+ before :each do
66
+ params.merge!(:action => 'create')
54
67
  end
55
68
 
56
- @params.merge!(:controller => "MyEngine::ProjectsController", :action => "create", :my_engine_project => {:name => "foobar"})
57
- resource = CanCan::ControllerResource.new(@controller)
58
- resource.load_resource
59
- expect(@controller.instance_variable_get(:@project).name).to eq("foobar")
60
- end
69
+ # Rails includes namespace in params, see issue #349
70
+ it "creates through the namespaced params" do
71
+ module MyEngine
72
+ class Model < ::Model; end
73
+ end
61
74
 
62
- it "loads resource for namespaced controller when using '::' for namespace" do
63
- project = Project.create!
64
- @params.merge!(:controller => "Admin::ProjectsController", :action => "show", :id => project.id)
65
- resource = CanCan::ControllerResource.new(@controller)
66
- resource.load_resource
67
- expect(@controller.instance_variable_get(:@project)).to eq(project)
68
- end
75
+ params.merge!(:controller => "MyEngine::ModelsController", :my_engine_model => {:name => "foobar"})
76
+ resource = CanCan::ControllerResource.new(controller)
77
+ resource.load_resource
78
+ expect(controller.instance_variable_get(:@model).name).to eq("foobar")
79
+ end
69
80
 
70
- it "has the specified nested resource_class when using / for namespace" do
71
- module Admin
72
- class Dashboard; end
81
+ it "builds a new resource with hash if params[:id] is not specified" do
82
+ params.merge!(:model => {:name => "foobar"})
83
+ resource = CanCan::ControllerResource.new(controller)
84
+ resource.load_resource
85
+ expect(controller.instance_variable_get(:@model).name).to eq("foobar")
73
86
  end
74
- @ability.can(:index, "admin/dashboard")
75
- @params.merge!(:controller => "admin/dashboard", :action => "index")
76
- resource = CanCan::ControllerResource.new(@controller, :authorize => true)
77
- expect(resource.send(:resource_class)).to eq(Admin::Dashboard)
78
- end
79
87
 
80
- it "builds a new resource with hash if params[:id] is not specified" do
81
- @params.merge!(:action => "create", :project => {:name => "foobar"})
82
- resource = CanCan::ControllerResource.new(@controller)
83
- resource.load_resource
84
- expect(@controller.instance_variable_get(:@project).name).to eq("foobar")
85
- end
88
+ it "builds a new resource for namespaced model with hash if params[:id] is not specified" do
89
+ module Sub
90
+ class Model < ::Model; end
91
+ end
86
92
 
87
- it "builds 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
- expect(@controller.instance_variable_get(:@project).name).to eq("foobar")
92
- end
93
+ params.merge!('sub_model' => {:name => "foobar"})
94
+ resource = CanCan::ControllerResource.new(controller, :class => ::Sub::Model)
95
+ resource.load_resource
96
+ expect(controller.instance_variable_get(:@model).name).to eq("foobar")
97
+ end
93
98
 
94
- it "builds 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
- expect(@controller.instance_variable_get(:@sub_project).name).to eq("foobar")
99
- end
99
+ it "builds a new resource for namespaced controller and namespaced model with hash if params[:id] is not specified" do
100
+ params.merge!(:controller => "Admin::SubModelsController", 'sub_model' => {:name => "foobar"})
101
+ resource = CanCan::ControllerResource.new(controller, :class => Model)
102
+ resource.load_resource
103
+ expect(controller.instance_variable_get(:@sub_model).name).to eq("foobar")
104
+ end
100
105
 
101
- it "builds a new resource with attributes from current ability" do
102
- @params.merge!(:action => "new")
103
- @ability.can(:create, Project, :name => "from conditions")
104
- resource = CanCan::ControllerResource.new(@controller)
105
- resource.load_resource
106
- expect(@controller.instance_variable_get(:@project).name).to eq("from conditions")
107
- end
106
+ it "does not build record through has_one association with :singleton option because it can cause it to delete it in the database" do
107
+ category = Class.new
108
+ allow_any_instance_of(Model).to receive('category=').with(category)
109
+ allow_any_instance_of(Model).to receive('category') { category }
108
110
 
109
- it "overrides initial attributes with params" do
110
- @params.merge!(:action => "new", :project => {:name => "from params"})
111
- @ability.can(:create, Project, :name => "from conditions")
112
- resource = CanCan::ControllerResource.new(@controller)
113
- resource.load_resource
114
- expect(@controller.instance_variable_get(:@project).name).to eq("from params")
115
- end
111
+ params.merge!(:model => {:name => "foobar"})
116
112
 
117
- it "builds a collection when on index action when class responds to accessible_by" do
118
- allow(Project).to receive(:accessible_by).with(@ability, :index) { :found_projects }
119
- @params[:action] = "index"
120
- resource = CanCan::ControllerResource.new(@controller, :project)
121
- resource.load_resource
122
- expect(@controller.instance_variable_get(:@project)).to be_nil
123
- expect(@controller.instance_variable_get(:@projects)).to eq(:found_projects)
124
- end
113
+ controller.instance_variable_set(:@category, category)
114
+ resource = CanCan::ControllerResource.new(controller, :through => :category, :singleton => true)
115
+ resource.load_resource
116
+ expect(controller.instance_variable_get(:@model).name).to eq("foobar")
117
+ expect(controller.instance_variable_get(:@model).category).to eq(category)
118
+ end
125
119
 
126
- it "does not build a collection when on index action when class does not respond to accessible_by" do
127
- @params[:action] = "index"
128
- resource = CanCan::ControllerResource.new(@controller)
129
- resource.load_resource
130
- expect(@controller.instance_variable_get(:@project)).to be_nil
131
- expect(@controller.instance_variable_defined?(:@projects)).to be_false
132
- end
120
+ it "builds record through has_one association with :singleton and :shallow options" do
121
+ params.merge!(:model => {:name => "foobar"})
122
+ resource = CanCan::ControllerResource.new(controller, :through => :category, :singleton => true, :shallow => true)
123
+ resource.load_resource
124
+ expect(controller.instance_variable_get(:@model).name).to eq("foobar")
125
+ end
133
126
 
134
- it "does not use accessible_by when defining abilities through a block" do
135
- allow(Project).to receive(:accessible_by).with(@ability) { :found_projects }
136
- @params[:action] = "index"
137
- @ability.can(:read, Project) { |p| false }
138
- resource = CanCan::ControllerResource.new(@controller)
139
- resource.load_resource
140
- expect(@controller.instance_variable_get(:@project)).to be_nil
141
- expect(@controller.instance_variable_defined?(:@projects)).to be_false
127
+ context "with a strong parameters method" do
128
+ it "accepts and uses the specified symbol for santitizing input" do
129
+ params.merge!(:controller => "model")
130
+ controller.stub(:resource_params).and_return(:resource => 'params')
131
+ controller.stub(:model_params).and_return(:model => 'params')
132
+ controller.stub(:create_params).and_return(:create => 'params')
133
+ controller.stub(:custom_params).and_return(:custom => 'params')
134
+ resource = CanCan::ControllerResource.new(controller, {:param_method => :custom_params})
135
+ expect(resource.send("resource_params")).to eq(:custom => 'params')
136
+ end
137
+
138
+ it "accepts the specified string for sanitizing input" do
139
+ params.merge!(:controller => "model")
140
+ resource = CanCan::ControllerResource.new(controller, {:param_method => "{:custom => 'params'}"})
141
+ expect(resource.send("resource_params")).to eq(:custom => 'params')
142
+ end
143
+
144
+ it "accepts the specified proc for sanitizing input" do
145
+ params.merge!(:controller => "model")
146
+ resource = CanCan::ControllerResource.new(controller, {:param_method => Proc.new { |c| {:custom => 'params'}}})
147
+ expect(resource.send("resource_params")).to eq(:custom => 'params')
148
+ end
149
+
150
+ it "prefers to use the create_params method for santitizing input" do
151
+ params.merge!(:controller => "model")
152
+ controller.stub(:resource_params).and_return(:resource => 'params')
153
+ controller.stub(:model_params).and_return(:model => 'params')
154
+ controller.stub(:create_params).and_return(:create => 'params')
155
+ controller.stub(:custom_params).and_return(:custom => 'params')
156
+ resource = CanCan::ControllerResource.new(controller)
157
+ expect(resource.send("resource_params")).to eq(:create => 'params')
158
+ end
159
+
160
+ it "prefers to use the <model_name>_params method for santitizing input if create is not found" do
161
+ params.merge!(:controller => "model")
162
+ controller.stub(:resource_params).and_return(:resource => 'params')
163
+ controller.stub(:model_params).and_return(:model => 'params')
164
+ controller.stub(:custom_params).and_return(:custom => 'params')
165
+ resource = CanCan::ControllerResource.new(controller)
166
+ expect(resource.send("resource_params")).to eq(:model => 'params')
167
+ end
168
+
169
+ it "prefers to use the resource_params method for santitizing input if create or model is not found" do
170
+ params.merge!(:controller => "model")
171
+ controller.stub(:resource_params).and_return(:resource => 'params')
172
+ controller.stub(:custom_params).and_return(:custom => 'params')
173
+ resource = CanCan::ControllerResource.new(controller)
174
+ expect(resource.send("resource_params")).to eq(:resource => 'params')
175
+ end
176
+ end
142
177
  end
143
178
 
144
- it "does not authorize single resource in collection action" do
145
- @params[:action] = "index"
146
- @controller.instance_variable_set(:@project, :some_project)
147
- allow(@controller).to receive(:authorize!).with(:index, Project) { raise CanCan::AccessDenied }
148
- resource = CanCan::ControllerResource.new(@controller)
149
- expect { resource.authorize_resource }.to raise_error(CanCan::AccessDenied)
150
- end
179
+ context "on collection actions" do
180
+ before :each do
181
+ params[:action] = 'index'
182
+ end
151
183
 
152
- it "authorizes parent resource in collection action" do
153
- @params[:action] = "index"
154
- @controller.instance_variable_set(:@category, :some_category)
155
- allow(@controller).to receive(:authorize!).with(:show, :some_category) { raise CanCan::AccessDenied }
156
- resource = CanCan::ControllerResource.new(@controller, :category, :parent => true)
157
- expect { resource.authorize_resource }.to raise_error(CanCan::AccessDenied)
158
- end
184
+ it "builds a collection when on index action when class responds to accessible_by" do
185
+ allow(Model).to receive(:accessible_by).with(ability, :index) { :found_models }
159
186
 
160
- it "performs authorization using controller action and loaded model" do
161
- @params.merge!(:action => "show", :id => "123")
162
- @controller.instance_variable_set(:@project, :some_project)
163
- allow(@controller).to receive(:authorize!).with(:show, :some_project) { raise CanCan::AccessDenied }
164
- resource = CanCan::ControllerResource.new(@controller)
165
- expect { resource.authorize_resource }.to raise_error(CanCan::AccessDenied)
166
- end
187
+ resource = CanCan::ControllerResource.new(controller, :model)
188
+ resource.load_resource
189
+ expect(controller.instance_variable_get(:@model)).to be_nil
190
+ expect(controller.instance_variable_get(:@models)).to eq(:found_models)
191
+ end
167
192
 
168
- it "performs authorization using controller action and non loaded model" do
169
- @params.merge!(:action => "show", :id => "123")
170
- allow(@controller).to receive(:authorize!).with(:show, Project) { raise CanCan::AccessDenied }
171
- resource = CanCan::ControllerResource.new(@controller)
172
- expect { resource.authorize_resource }.to raise_error(CanCan::AccessDenied)
173
- end
193
+ it "does not build a collection when on index action when class does not respond to accessible_by" do
194
+ resource = CanCan::ControllerResource.new(controller)
195
+ resource.load_resource
196
+ expect(controller.instance_variable_get(:@model)).to be_nil
197
+ expect(controller.instance_variable_defined?(:@models)).to be_false
198
+ end
174
199
 
175
- it "calls load_resource and authorize_resource for load_and_authorize_resource" do
176
- @params.merge!(:action => "show", :id => "123")
177
- resource = CanCan::ControllerResource.new(@controller)
178
- expect(resource).to receive(:load_resource)
179
- expect(resource).to receive(:authorize_resource)
180
- resource.load_and_authorize_resource
181
- end
200
+ it "does not use accessible_by when defining abilities through a block" do
201
+ allow(Model).to receive(:accessible_by).with(ability) { :found_models }
182
202
 
183
- it "does not build a single resource when on custom collection action even with id" do
184
- @params.merge!(:action => "sort", :id => "123")
185
- resource = CanCan::ControllerResource.new(@controller, :collection => [:sort, :list])
186
- resource.load_resource
187
- expect(@controller.instance_variable_get(:@project)).to be_nil
188
- end
203
+ ability.can(:read, Model) { |p| false }
204
+ resource = CanCan::ControllerResource.new(controller)
205
+ resource.load_resource
206
+ expect(controller.instance_variable_get(:@model)).to be_nil
207
+ expect(controller.instance_variable_defined?(:@models)).to be_false
208
+ end
189
209
 
190
- it "loads a collection resource when on custom action with no id param" do
191
- allow(Project).to receive(:accessible_by).with(@ability, :sort) { :found_projects }
192
- @params[:action] = "sort"
193
- resource = CanCan::ControllerResource.new(@controller)
194
- resource.load_resource
195
- expect(@controller.instance_variable_get(:@project)).to be_nil
196
- expect(@controller.instance_variable_get(:@projects)).to eq(:found_projects)
197
- end
210
+ it "does not authorize single resource in collection action" do
211
+ allow(controller).to receive(:authorize!).with(:index, Model) { raise CanCan::AccessDenied }
212
+ resource = CanCan::ControllerResource.new(controller)
198
213
 
199
- it "builds a resource when on custom new action even when params[:id] exists" do
200
- @params.merge!(:action => "build", :id => "123")
201
- allow(Project).to receive(:new) { :some_project }
202
- resource = CanCan::ControllerResource.new(@controller, :new => :build)
203
- resource.load_resource
204
- expect(@controller.instance_variable_get(:@project)).to eq(:some_project)
205
- end
214
+ expect { resource.authorize_resource }.to raise_error(CanCan::AccessDenied)
215
+ end
206
216
 
207
- it "does not try to load resource for other action if params[:id] is undefined" do
208
- @params[:action] = "list"
209
- resource = CanCan::ControllerResource.new(@controller)
210
- resource.load_resource
211
- expect(@controller.instance_variable_get(:@project)).to be_nil
212
- end
217
+ it "authorizes parent resource in collection action" do
218
+ controller.instance_variable_set(:@category, :some_category)
219
+ allow(controller).to receive(:authorize!).with(:show, :some_category) { raise CanCan::AccessDenied }
213
220
 
214
- it "is a parent resource when name is provided which doesn't match controller" do
215
- resource = CanCan::ControllerResource.new(@controller, :category)
216
- expect(resource).to be_parent
217
- end
221
+ resource = CanCan::ControllerResource.new(controller, :category, :parent => true)
222
+ expect { resource.authorize_resource }.to raise_error(CanCan::AccessDenied)
223
+ end
218
224
 
219
- it "does not be a parent resource when name is provided which matches controller" do
220
- resource = CanCan::ControllerResource.new(@controller, :project)
221
- expect(resource).to_not be_parent
222
- end
225
+ it "has the specified nested resource_class when using / for namespace" do
226
+ module Admin
227
+ class Dashboard; end
228
+ end
229
+ ability.can(:index, "admin/dashboard")
230
+ params.merge!(:controller => "admin/dashboard")
231
+ resource = CanCan::ControllerResource.new(controller, :authorize => true)
232
+ expect(resource.send(:resource_class)).to eq(Admin::Dashboard)
233
+ end
223
234
 
224
- it "is parent if specified in options" do
225
- resource = CanCan::ControllerResource.new(@controller, :project, {:parent => true})
226
- expect(resource).to be_parent
227
- end
235
+ it "does not build a single resource when on custom collection action even with id" do
236
+ params.merge!(:action => "sort", :id => "123")
228
237
 
229
- it "does not be parent if specified in options" do
230
- resource = CanCan::ControllerResource.new(@controller, :category, {:parent => false})
231
- expect(resource).to_not be_parent
232
- end
238
+ resource = CanCan::ControllerResource.new(controller, :collection => [:sort, :list])
239
+ resource.load_resource
240
+ expect(controller.instance_variable_get(:@model)).to be_nil
241
+ end
233
242
 
234
- it "has the specified resource_class if 'name' is passed to load_resource" do
235
- class Section
243
+ it "loads a collection resource when on custom action with no id param" do
244
+ allow(Model).to receive(:accessible_by).with(ability, :sort) { :found_models }
245
+ params[:action] = "sort"
246
+ resource = CanCan::ControllerResource.new(controller)
247
+ resource.load_resource
248
+ expect(controller.instance_variable_get(:@model)).to be_nil
249
+ expect(controller.instance_variable_get(:@models)).to eq(:found_models)
236
250
  end
237
251
 
238
- resource = CanCan::ControllerResource.new(@controller, :section)
239
- expect(resource.send(:resource_class)).to eq(Section)
240
- end
252
+ it "loads parent resource through proper id parameter" do
253
+ model = Model.new
254
+ allow(Model).to receive(:find).with("1") { model }
241
255
 
242
- it "loads parent resource through proper id parameter" do
243
- project = Project.create!
244
- @params.merge!(:controller => "categories", :action => "index", :project_id => project.id)
245
- resource = CanCan::ControllerResource.new(@controller, :project)
246
- resource.load_resource
247
- expect(@controller.instance_variable_get(:@project)).to eq(project)
248
- end
256
+ params.merge!(:controller => "categories", :model_id => 1)
257
+ resource = CanCan::ControllerResource.new(controller, :model)
258
+ resource.load_resource
259
+ expect(controller.instance_variable_get(:@model)).to eq(model)
260
+ end
249
261
 
250
- it "loads resource through the association of another parent resource using instance variable" do
251
- @params.merge!(:action => "show", :id => "123")
252
- category = double(:projects => {})
253
- @controller.instance_variable_set(:@category, category)
254
- allow(category.projects).to receive(:find).with("123") { :some_project }
255
- resource = CanCan::ControllerResource.new(@controller, :through => :category)
256
- resource.load_resource
257
- expect(@controller.instance_variable_get(:@project)).to eq(:some_project)
262
+ it "authorizes nested resource through parent association on index action" do
263
+ controller.instance_variable_set(:@category, category = double)
264
+ allow(controller).to receive(:authorize!).with(:index, category => Model) { raise CanCan::AccessDenied }
265
+ resource = CanCan::ControllerResource.new(controller, :through => :category)
266
+ expect { resource.authorize_resource }.to raise_error(CanCan::AccessDenied)
267
+ end
258
268
  end
259
269
 
260
- it "loads resource through the custom association name" do
261
- @params.merge!(:action => "show", :id => "123")
262
- category = double(:custom_projects => {})
263
- @controller.instance_variable_set(:@category, category)
264
- allow(category.custom_projects).to receive(:find).with("123") { :some_project }
265
- resource = CanCan::ControllerResource.new(@controller, :through => :category, :through_association => :custom_projects)
266
- resource.load_resource
267
- expect(@controller.instance_variable_get(:@project)).to eq(:some_project)
268
- end
270
+ context "on instance read actions" do
271
+ before :each do
272
+ params.merge!(:action => "show", :id => "123")
273
+ end
269
274
 
270
- it "loads resource through the association of another parent resource using method" do
271
- @params.merge!(:action => "show", :id => "123")
272
- category = double(:projects => {})
273
- allow(@controller).to receive(:category) { category }
274
- allow(category.projects).to receive(:find).with("123") { :some_project }
275
- resource = CanCan::ControllerResource.new(@controller, :through => :category)
276
- resource.load_resource
277
- expect(@controller.instance_variable_get(:@project)).to eq(:some_project)
278
- end
275
+ it "loads the resource into an instance variable if params[:id] is specified" do
276
+ model = Model.new
277
+ allow(Model).to receive(:find).with("123") { model }
279
278
 
280
- it "does not load through parent resource if instance isn't loaded when shallow" do
281
- project = Project.create!
282
- @params.merge!(:action => "show", :id => project.id)
283
- resource = CanCan::ControllerResource.new(@controller, :through => :category, :shallow => true)
284
- resource.load_resource
285
- expect(@controller.instance_variable_get(:@project)).to eq(project)
286
- end
279
+ resource = CanCan::ControllerResource.new(controller)
280
+ resource.load_resource
281
+ expect(controller.instance_variable_get(:@model)).to eq(model)
282
+ end
287
283
 
288
- it "raises AccessDenied when attempting to load resource through nil" do
289
- project = Project.create!
290
- @params.merge!(:action => "show", :id => project.id)
291
- resource = CanCan::ControllerResource.new(@controller, :through => :category)
292
- expect {
284
+ it "does not load resource into an instance variable if already set" do
285
+ controller.instance_variable_set(:@model, :some_model)
286
+ resource = CanCan::ControllerResource.new(controller)
293
287
  resource.load_resource
294
- }.to raise_error(CanCan::AccessDenied) { |exception|
295
- expect(exception.action).to eq(:show)
296
- expect(exception.subject).to eq(Project)
297
- }
298
- expect(@controller.instance_variable_get(:@project)).to be_nil
299
- end
288
+ expect(controller.instance_variable_get(:@model)).to eq(:some_model)
289
+ end
300
290
 
301
- it "authorizes nested resource through parent association on index action" do
302
- @params.merge!(:action => "index")
303
- @controller.instance_variable_set(:@category, category = double)
304
- allow(@controller).to receive(:authorize!).with(:index, category => Project) { raise CanCan::AccessDenied }
305
- resource = CanCan::ControllerResource.new(@controller, :through => :category)
306
- expect { resource.authorize_resource }.to raise_error(CanCan::AccessDenied)
307
- end
291
+ it "loads resource for namespaced controller" do
292
+ model = Model.new
293
+ allow(Model).to receive(:find).with("123") { model }
294
+ params.merge!(:controller => "admin/models")
308
295
 
309
- it "loads through first matching if multiple are given" do
310
- @params.merge!(:action => "show", :id => "123")
311
- category = double(:projects => {})
312
- @controller.instance_variable_set(:@category, category)
313
- allow(category.projects).to receive(:find).with("123") { :some_project }
314
- resource = CanCan::ControllerResource.new(@controller, :through => [:category, :user])
315
- resource.load_resource
316
- expect(@controller.instance_variable_get(:@project)).to eq(:some_project)
317
- end
296
+ resource = CanCan::ControllerResource.new(controller)
297
+ resource.load_resource
298
+ expect(controller.instance_variable_get(:@model)).to eq(model)
299
+ end
318
300
 
319
- it "finds record through has_one association with :singleton option without id param" do
320
- @params.merge!(:action => "show", :id => nil)
321
- category = double(:project => :some_project)
322
- @controller.instance_variable_set(:@category, category)
323
- resource = CanCan::ControllerResource.new(@controller, :through => :category, :singleton => true)
324
- resource.load_resource
325
- expect(@controller.instance_variable_get(:@project)).to eq(:some_project)
326
- end
301
+ it "attempts to load a resource with the same namespace as the controller when using :: for namespace" do
302
+ module MyEngine
303
+ class Model < ::Model; end
304
+ end
327
305
 
328
- it "does not build record through has_one association with :singleton option because it can cause it to delete it in the database" do
329
- @params.merge!(:action => "create", :project => {:name => "foobar"})
330
- category = Category.new
331
- @controller.instance_variable_set(:@category, category)
332
- resource = CanCan::ControllerResource.new(@controller, :through => :category, :singleton => true)
333
- resource.load_resource
334
- expect(@controller.instance_variable_get(:@project).name).to eq("foobar")
335
- expect(@controller.instance_variable_get(:@project).category).to eq(category)
336
- end
306
+ model = MyEngine::Model.new
307
+ allow(MyEngine::Model).to receive(:find).with("123") { model }
337
308
 
338
- it "finds record through has_one association with :singleton and :shallow options" do
339
- project = Project.create!
340
- @params.merge!(:action => "show", :id => project.id)
341
- resource = CanCan::ControllerResource.new(@controller, :through => :category, :singleton => true, :shallow => true)
342
- resource.load_resource
343
- expect(@controller.instance_variable_get(:@project)).to eq(project)
344
- end
309
+ params.merge!(:controller => "MyEngine::ModelsController")
310
+ resource = CanCan::ControllerResource.new(controller)
311
+ resource.load_resource
312
+ expect(controller.instance_variable_get(:@model)).to eq(model)
313
+ end
345
314
 
346
- it "builds record through has_one association with :singleton and :shallow options" do
347
- @params.merge!(:action => "create", :project => {:name => "foobar"})
348
- resource = CanCan::ControllerResource.new(@controller, :through => :category, :singleton => true, :shallow => true)
349
- resource.load_resource
350
- expect(@controller.instance_variable_get(:@project).name).to eq("foobar")
351
- end
315
+ it "loads resource for namespaced controller when using '::' for namespace" do
316
+ model = Model.new
317
+ allow(Model).to receive(:find).with("123") { model }
352
318
 
353
- it "only authorizes :show action on parent resource" do
354
- project = Project.create!
355
- @params.merge!(:action => "new", :project_id => project.id)
356
- allow(@controller).to receive(:authorize!).with(:show, project) { raise CanCan::AccessDenied }
357
- resource = CanCan::ControllerResource.new(@controller, :project, :parent => true)
358
- expect { resource.load_and_authorize_resource }.to raise_error(CanCan::AccessDenied)
359
- end
319
+ params.merge!(:controller => "Admin::ModelsController")
320
+ resource = CanCan::ControllerResource.new(controller)
321
+ resource.load_resource
322
+ expect(controller.instance_variable_get(:@model)).to eq(model)
323
+ end
360
324
 
361
- it "loads the model using a custom class" do
362
- project = Project.create!
363
- @params.merge!(:action => "show", :id => project.id)
364
- resource = CanCan::ControllerResource.new(@controller, :class => Project)
365
- resource.load_resource
366
- expect(@controller.instance_variable_get(:@project)).to eq(project)
367
- end
325
+ it "performs authorization using controller action and loaded model" do
326
+ controller.instance_variable_set(:@model, :some_model)
327
+ allow(controller).to receive(:authorize!).with(:show, :some_model) { raise CanCan::AccessDenied }
368
328
 
369
- it "loads the model using a custom namespaced class" do
370
- project = Sub::Project.create!
371
- @params.merge!(:action => "show", :id => project.id)
372
- resource = CanCan::ControllerResource.new(@controller, :class => ::Sub::Project)
373
- resource.load_resource
374
- expect(@controller.instance_variable_get(:@project)).to eq(project)
375
- end
329
+ resource = CanCan::ControllerResource.new(controller)
330
+ expect { resource.authorize_resource }.to raise_error(CanCan::AccessDenied)
331
+ end
332
+
333
+ it "performs authorization using controller action and non loaded model" do
334
+ allow(controller).to receive(:authorize!).with(:show, Model) { raise CanCan::AccessDenied }
335
+ resource = CanCan::ControllerResource.new(controller)
336
+ expect { resource.authorize_resource }.to raise_error(CanCan::AccessDenied)
337
+ end
338
+
339
+ it "calls load_resource and authorize_resource for load_and_authorize_resource" do
340
+ resource = CanCan::ControllerResource.new(controller)
341
+ expect(resource).to receive(:load_resource)
342
+ expect(resource).to receive(:authorize_resource)
343
+ resource.load_and_authorize_resource
344
+ end
345
+
346
+ it "loads resource through the association of another parent resource using instance variable" do
347
+ category = double(:models => {})
348
+ controller.instance_variable_set(:@category, category)
349
+ allow(category.models).to receive(:find).with("123") { :some_model }
350
+ resource = CanCan::ControllerResource.new(controller, :through => :category)
351
+ resource.load_resource
352
+ expect(controller.instance_variable_get(:@model)).to eq(:some_model)
353
+ end
354
+
355
+ it "loads resource through the custom association name" do
356
+ category = double(:custom_models => {})
357
+ controller.instance_variable_set(:@category, category)
358
+ allow(category.custom_models).to receive(:find).with("123") { :some_model }
359
+ resource = CanCan::ControllerResource.new(controller, :through => :category, :through_association => :custom_models)
360
+ resource.load_resource
361
+ expect(controller.instance_variable_get(:@model)).to eq(:some_model)
362
+ end
363
+
364
+ it "loads resource through the association of another parent resource using method" do
365
+ category = double(:models => {})
366
+ allow(controller).to receive(:category) { category }
367
+ allow(category.models).to receive(:find).with("123") { :some_model }
368
+ resource = CanCan::ControllerResource.new(controller, :through => :category)
369
+ resource.load_resource
370
+ expect(controller.instance_variable_get(:@model)).to eq(:some_model)
371
+ end
372
+
373
+ it "does not load through parent resource if instance isn't loaded when shallow" do
374
+ model = Model.new
375
+ allow(Model).to receive(:find).with("123") { model }
376
+
377
+ resource = CanCan::ControllerResource.new(controller, :through => :category, :shallow => true)
378
+ resource.load_resource
379
+ expect(controller.instance_variable_get(:@model)).to eq(model)
380
+ end
381
+
382
+ it "raises AccessDenied when attempting to load resource through nil" do
383
+ resource = CanCan::ControllerResource.new(controller, :through => :category)
384
+ expect {
385
+ resource.load_resource
386
+ }.to raise_error(CanCan::AccessDenied) { |exception|
387
+ expect(exception.action).to eq(:show)
388
+ expect(exception.subject).to eq(Model)
389
+ }
390
+ expect(controller.instance_variable_get(:@model)).to be_nil
391
+ end
392
+
393
+ it "loads through first matching if multiple are given" do
394
+ category = double(:models => {})
395
+ controller.instance_variable_set(:@category, category)
396
+ allow(category.models).to receive(:find).with("123") { :some_model }
397
+
398
+ resource = CanCan::ControllerResource.new(controller, :through => [:category, :user])
399
+ resource.load_resource
400
+ expect(controller.instance_variable_get(:@model)).to eq(:some_model)
401
+ end
402
+
403
+ it "finds record through has_one association with :singleton option without id param" do
404
+ params.merge!(:id => nil)
405
+
406
+ category = double(:model => :some_model)
407
+ controller.instance_variable_set(:@category, category)
408
+ resource = CanCan::ControllerResource.new(controller, :through => :category, :singleton => true)
409
+ resource.load_resource
410
+ expect(controller.instance_variable_get(:@model)).to eq(:some_model)
411
+ end
412
+
413
+ it "does not try to load resource for other action if params[:id] is undefined" do
414
+ params.merge!(:action => 'list', :id => nil)
415
+ resource = CanCan::ControllerResource.new(controller)
416
+ resource.load_resource
417
+ expect(controller.instance_variable_get(:@model)).to be_nil
418
+ end
419
+
420
+ it "finds record through has_one association with :singleton and :shallow options" do
421
+ model = Model.new
422
+ allow(Model).to receive(:find).with("123") { model }
423
+
424
+ resource = CanCan::ControllerResource.new(controller, :through => :category, :singleton => true, :shallow => true)
425
+ resource.load_resource
426
+ expect(controller.instance_variable_get(:@model)).to eq(model)
427
+ end
428
+
429
+ it "loads the model using a custom class" do
430
+ model = Model.new
431
+ allow(Model).to receive(:find).with("123") { model }
432
+
433
+ resource = CanCan::ControllerResource.new(controller, :class => Model)
434
+ resource.load_resource
435
+ expect(controller.instance_variable_get(:@model)).to eq(model)
436
+ end
437
+
438
+ it "loads the model using a custom namespaced class" do
439
+ module Sub
440
+ class Model < ::Model; end
441
+ end
442
+
443
+ model = Sub::Model.new
444
+ allow(Sub::Model).to receive(:find).with("123") { model }
445
+
446
+ resource = CanCan::ControllerResource.new(controller, :class => ::Sub::Model)
447
+ resource.load_resource
448
+ expect(controller.instance_variable_get(:@model)).to eq(model)
449
+ end
376
450
 
377
- it "authorizes based on resource name if class is false" do
378
- @params.merge!(:action => "show", :id => "123")
379
- allow(@controller).to receive(:authorize!).with(:show, :project) { raise CanCan::AccessDenied }
380
- resource = CanCan::ControllerResource.new(@controller, :class => false)
381
- expect { resource.authorize_resource }.to raise_error(CanCan::AccessDenied)
451
+ it "authorizes based on resource name if class is false" do
452
+ allow(controller).to receive(:authorize!).with(:show, :model) { raise CanCan::AccessDenied }
453
+ resource = CanCan::ControllerResource.new(controller, :class => false)
454
+ expect { resource.authorize_resource }.to raise_error(CanCan::AccessDenied)
455
+ end
456
+
457
+ it "loads and authorize using custom instance name" do
458
+ model = Model.new
459
+ allow(Model).to receive(:find).with("123") { model }
460
+
461
+ allow(controller).to receive(:authorize!).with(:show, model) { raise CanCan::AccessDenied }
462
+ resource = CanCan::ControllerResource.new(controller, :instance_name => :custom_model)
463
+ expect { resource.load_and_authorize_resource }.to raise_error(CanCan::AccessDenied)
464
+ expect(controller.instance_variable_get(:@custom_model)).to eq(model)
465
+ end
466
+
467
+ it "loads resource using custom ID param" do
468
+ model = Model.new
469
+ allow(Model).to receive(:find).with("123") { model }
470
+
471
+ params.merge!(:the_model => 123)
472
+ resource = CanCan::ControllerResource.new(controller, :id_param => :the_model)
473
+ resource.load_resource
474
+ expect(controller.instance_variable_get(:@model)).to eq(model)
475
+ end
476
+
477
+ # CVE-2012-5664
478
+ it "always converts id param to string" do
479
+ params.merge!(:the_model => { :malicious => "I am" })
480
+ resource = CanCan::ControllerResource.new(controller, :id_param => :the_model)
481
+ expect(resource.send(:id_param).class).to eq(String)
482
+ end
483
+
484
+ it "should id param return nil if param is nil" do
485
+ params.merge!(:the_model => nil)
486
+ resource = CanCan::ControllerResource.new(controller, :id_param => :the_model)
487
+ expect(resource.send(:id_param)).to be_nil
488
+ end
489
+
490
+ it "loads resource using custom find_by attribute" do
491
+ model = Model.new
492
+ allow(Model).to receive(:name).with('foo') { model }
493
+
494
+ params.merge!(:action => "show", :id => "foo")
495
+ resource = CanCan::ControllerResource.new(controller, :find_by => :name)
496
+ resource.load_resource
497
+ expect(controller.instance_variable_get(:@model)).to eq(model)
498
+ end
499
+
500
+ it "allows full find method to be passed into find_by option" do
501
+ model = Model.new
502
+ allow(Model).to receive(:find_by_name).with('foo') { model }
503
+
504
+ params.merge!(:action => "show", :id => "foo")
505
+ resource = CanCan::ControllerResource.new(controller, :find_by => :find_by_name)
506
+ resource.load_resource
507
+ expect(controller.instance_variable_get(:@model)).to eq(model)
508
+ end
382
509
  end
383
510
 
384
- it "loads and authorize using custom instance name" do
385
- project = Project.create!
386
- @params.merge!(:action => "show", :id => project.id)
387
- allow(@controller).to receive(:authorize!).with(:show, project) { raise CanCan::AccessDenied }
388
- resource = CanCan::ControllerResource.new(@controller, :instance_name => :custom_project)
389
- expect { resource.load_and_authorize_resource }.to raise_error(CanCan::AccessDenied)
390
- expect(@controller.instance_variable_get(:@custom_project)).to eq(project)
511
+ context "on update actions" do
512
+ before :each do
513
+ params.merge!(:action => 'update')
514
+ end
515
+
516
+ context "with a strong parameters method" do
517
+ it "only calls the santitize method with actions matching param_actions" do
518
+ controller.stub(:resource_params).and_return(:resource => 'params')
519
+ resource = CanCan::ControllerResource.new(controller)
520
+ resource.stub(:param_actions => [:create])
521
+
522
+ controller.should_not_receive(:send).with(:resource_params)
523
+ resource.send("resource_params")
524
+ end
525
+
526
+ it "uses the proper action param based on the action" do
527
+ controller.stub(:create_params).and_return(:create => 'params')
528
+ controller.stub(:update_params).and_return(:update => 'params')
529
+ resource = CanCan::ControllerResource.new(controller)
530
+ expect(resource.send("resource_params")).to eq(:update => 'params')
531
+ end
532
+ end
391
533
  end
392
534
 
393
- it "loads resource using custom ID param" do
394
- project = Project.create!
395
- @params.merge!(:action => "show", :the_project => project.id)
396
- resource = CanCan::ControllerResource.new(@controller, :id_param => :the_project)
397
- resource.load_resource
398
- expect(@controller.instance_variable_get(:@project)).to eq(project)
535
+ it "is a parent resource when name is provided which doesn't match controller" do
536
+ resource = CanCan::ControllerResource.new(controller, :category)
537
+ expect(resource).to be_parent
399
538
  end
400
539
 
401
- # CVE-2012-5664
402
- it "always converts id param to string" do
403
- @params.merge!(:action => "show", :the_project => { :malicious => "I am" })
404
- resource = CanCan::ControllerResource.new(@controller, :id_param => :the_project)
405
- expect(resource.send(:id_param).class).to eq(String)
540
+ it "does not be a parent resource when name is provided which matches controller" do
541
+ resource = CanCan::ControllerResource.new(controller, :model)
542
+ expect(resource).to_not be_parent
406
543
  end
407
544
 
408
- it "should id param return nil if param is nil" do
409
- @params.merge!(:action => "show", :the_project => nil)
410
- resource = CanCan::ControllerResource.new(@controller, :id_param => :the_project)
411
- expect(resource.send(:id_param)).to be_nil
545
+ it "is parent if specified in options" do
546
+ resource = CanCan::ControllerResource.new(controller, :model, {:parent => true})
547
+ expect(resource).to be_parent
412
548
  end
413
549
 
414
- it "loads resource using custom find_by attribute" do
415
- project = Project.create!(:name => "foo")
416
- @params.merge!(:action => "show", :id => "foo")
417
- resource = CanCan::ControllerResource.new(@controller, :find_by => :name)
418
- resource.load_resource
419
- expect(@controller.instance_variable_get(:@project)).to eq(project)
550
+ it "does not be parent if specified in options" do
551
+ resource = CanCan::ControllerResource.new(controller, :category, {:parent => false})
552
+ expect(resource).to_not be_parent
420
553
  end
421
554
 
422
- it "allows full find method to be passed into find_by option" do
423
- project = Project.create!(:name => "foo")
424
- @params.merge!(:action => "show", :id => "foo")
425
- resource = CanCan::ControllerResource.new(@controller, :find_by => :find_by_name)
426
- resource.load_resource
427
- expect(@controller.instance_variable_get(:@project)).to eq(project)
555
+ it "has the specified resource_class if 'name' is passed to load_resource" do
556
+ class Section; end
557
+ resource = CanCan::ControllerResource.new(controller, :section)
558
+ expect(resource.send(:resource_class)).to eq(Section)
428
559
  end
429
560
 
430
561
  it "raises ImplementationRemoved when adding :name option" do
431
562
  expect {
432
- CanCan::ControllerResource.new(@controller, :name => :foo)
563
+ CanCan::ControllerResource.new(controller, :name => :foo)
433
564
  }.to raise_error(CanCan::ImplementationRemoved)
434
565
  end
435
566
 
436
567
  it "raises ImplementationRemoved exception when specifying :resource option since it is no longer used" do
437
568
  expect {
438
- CanCan::ControllerResource.new(@controller, :resource => Project)
569
+ CanCan::ControllerResource.new(controller, :resource => Model)
439
570
  }.to raise_error(CanCan::ImplementationRemoved)
440
571
  end
441
572
 
442
573
  it "raises ImplementationRemoved exception when passing :nested option" do
443
574
  expect {
444
- CanCan::ControllerResource.new(@controller, :nested => :project)
575
+ CanCan::ControllerResource.new(controller, :nested => :model)
445
576
  }.to raise_error(CanCan::ImplementationRemoved)
446
577
  end
447
578
 
448
579
  it "skips resource behavior for :only actions in array" do
449
- allow(@controller_class).to receive(:cancan_skipper) { {:load => {nil => {:only => [:index, :show]}}} }
450
- @params.merge!(:action => "index")
451
- expect(CanCan::ControllerResource.new(@controller).skip?(:load)).to be_true
452
- expect(CanCan::ControllerResource.new(@controller, :some_resource).skip?(:load)).to be_false
453
- @params.merge!(:action => "show")
454
- expect(CanCan::ControllerResource.new(@controller).skip?(:load)).to be_true
455
- @params.merge!(:action => "other_action")
456
- expect(CanCan::ControllerResource.new(@controller).skip?(:load)).to be_false
580
+ allow(controller_class).to receive(:cancan_skipper) { {:load => {nil => {:only => [:index, :show]}}} }
581
+ params.merge!(:action => "index")
582
+ expect(CanCan::ControllerResource.new(controller).skip?(:load)).to be_true
583
+ expect(CanCan::ControllerResource.new(controller, :some_resource).skip?(:load)).to be_false
584
+ params.merge!(:action => "show")
585
+ expect(CanCan::ControllerResource.new(controller).skip?(:load)).to be_true
586
+ params.merge!(:action => "other_action")
587
+ expect(CanCan::ControllerResource.new(controller).skip?(:load)).to be_false
457
588
  end
458
589
 
459
590
  it "skips resource behavior for :only one action on resource" do
460
- allow(@controller_class).to receive(:cancan_skipper) { {:authorize => {:project => {:only => :index}}} }
461
- @params.merge!(:action => "index")
462
- expect(CanCan::ControllerResource.new(@controller).skip?(:authorize)).to be_false
463
- expect(CanCan::ControllerResource.new(@controller, :project).skip?(:authorize)).to be_true
464
- @params.merge!(:action => "other_action")
465
- expect(CanCan::ControllerResource.new(@controller, :project).skip?(:authorize)).to be_false
591
+ allow(controller_class).to receive(:cancan_skipper) { {:authorize => {:model => {:only => :index}}} }
592
+ params.merge!(:action => "index")
593
+ expect(CanCan::ControllerResource.new(controller).skip?(:authorize)).to be_false
594
+ expect(CanCan::ControllerResource.new(controller, :model).skip?(:authorize)).to be_true
595
+ params.merge!(:action => "other_action")
596
+ expect(CanCan::ControllerResource.new(controller, :model).skip?(:authorize)).to be_false
466
597
  end
467
598
 
468
599
  it "skips resource behavior :except actions in array" do
469
- allow(@controller_class).to receive(:cancan_skipper) { {:load => {nil => {:except => [:index, :show]}}} }
470
- @params.merge!(:action => "index")
471
- expect(CanCan::ControllerResource.new(@controller).skip?(:load)).to be_false
472
- @params.merge!(:action => "show")
473
- expect(CanCan::ControllerResource.new(@controller).skip?(:load)).to be_false
474
- @params.merge!(:action => "other_action")
475
- expect(CanCan::ControllerResource.new(@controller).skip?(:load)).to be_true
476
- expect(CanCan::ControllerResource.new(@controller, :some_resource).skip?(:load)).to be_false
600
+ allow(controller_class).to receive(:cancan_skipper) { {:load => {nil => {:except => [:index, :show]}}} }
601
+ params.merge!(:action => "index")
602
+ expect(CanCan::ControllerResource.new(controller).skip?(:load)).to be_false
603
+ params.merge!(:action => "show")
604
+ expect(CanCan::ControllerResource.new(controller).skip?(:load)).to be_false
605
+ params.merge!(:action => "other_action")
606
+ expect(CanCan::ControllerResource.new(controller).skip?(:load)).to be_true
607
+ expect(CanCan::ControllerResource.new(controller, :some_resource).skip?(:load)).to be_false
477
608
  end
478
609
 
479
610
  it "skips resource behavior :except one action on resource" do
480
- allow(@controller_class).to receive(:cancan_skipper) { {:authorize => {:project => {:except => :index}}} }
481
- @params.merge!(:action => "index")
482
- expect(CanCan::ControllerResource.new(@controller, :project).skip?(:authorize)).to be_false
483
- @params.merge!(:action => "other_action")
484
- expect(CanCan::ControllerResource.new(@controller).skip?(:authorize)).to be_false
485
- expect(CanCan::ControllerResource.new(@controller, :project).skip?(:authorize)).to be_true
611
+ allow(controller_class).to receive(:cancan_skipper) { {:authorize => {:model => {:except => :index}}} }
612
+ params.merge!(:action => "index")
613
+ expect(CanCan::ControllerResource.new(controller, :model).skip?(:authorize)).to be_false
614
+ params.merge!(:action => "other_action")
615
+ expect(CanCan::ControllerResource.new(controller).skip?(:authorize)).to be_false
616
+ expect(CanCan::ControllerResource.new(controller, :model).skip?(:authorize)).to be_true
486
617
  end
487
618
 
488
619
  it "skips loading and authorization" do
489
- allow(@controller_class).to receive(:cancan_skipper) { {:authorize => {nil => {}}, :load => {nil => {}}} }
490
- @params.merge!(:action => "new")
491
- resource = CanCan::ControllerResource.new(@controller)
620
+ allow(controller_class).to receive(:cancan_skipper) { {:authorize => {nil => {}}, :load => {nil => {}}} }
621
+ params.merge!(:action => "new")
622
+ resource = CanCan::ControllerResource.new(controller)
492
623
  expect { resource.load_and_authorize_resource }.not_to raise_error
493
- expect(@controller.instance_variable_get(:@project)).to be_nil
494
- end
495
-
496
- context "with a strong parameters method" do
497
-
498
- it "only calls the santitize method with actions matching param_actions" do
499
- @params.merge!(:controller => "project", :action => "update")
500
- @controller.stub(:resource_params).and_return(:resource => 'params')
501
- resource = CanCan::ControllerResource.new(@controller)
502
- resource.stub(:param_actions => [:create])
503
-
504
- @controller.should_not_receive(:send).with(:resource_params)
505
- resource.send("resource_params")
506
- end
507
-
508
- it "uses the specified option for santitizing input" do
509
- @params.merge!(:controller => "project", :action => "create")
510
- @controller.stub(:resource_params).and_return(:resource => 'params')
511
- @controller.stub(:project_params).and_return(:model => 'params')
512
- @controller.stub(:create_params).and_return(:create => 'params')
513
- @controller.stub(:custom_params).and_return(:custom => 'params')
514
- resource = CanCan::ControllerResource.new(@controller, {:param_method => :custom_params})
515
- expect(resource.send("resource_params")).to eq(:custom => 'params')
516
- end
517
-
518
- it "prefers to use the create_params method for santitizing input" do
519
- @params.merge!(:controller => "project", :action => "create")
520
- @controller.stub(:resource_params).and_return(:resource => 'params')
521
- @controller.stub(:project_params).and_return(:model => 'params')
522
- @controller.stub(:create_params).and_return(:create => 'params')
523
- @controller.stub(:custom_params).and_return(:custom => 'params')
524
- resource = CanCan::ControllerResource.new(@controller)
525
- expect(resource.send("resource_params")).to eq(:create => 'params')
526
- end
527
-
528
- it "uses the proper action param based on the action" do
529
- @params.merge!(:controller => "project", :action => "update")
530
- @controller.stub(:create_params).and_return(:create => 'params')
531
- @controller.stub(:update_params).and_return(:update => 'params')
532
- resource = CanCan::ControllerResource.new(@controller)
533
- expect(resource.send("resource_params")).to eq(:update => 'params')
534
- end
535
-
536
- it "prefers to use the <model_name>_params method for santitizing input if create is not found" do
537
- @params.merge!(:controller => "project", :action => "create")
538
- @controller.stub(:resource_params).and_return(:resource => 'params')
539
- @controller.stub(:project_params).and_return(:model => 'params')
540
- @controller.stub(:custom_params).and_return(:custom => 'params')
541
- resource = CanCan::ControllerResource.new(@controller)
542
- expect(resource.send("resource_params")).to eq(:model => 'params')
543
- end
544
-
545
- it "prefers to use the resource_params method for santitizing input if create or model is not found" do
546
- @params.merge!(:controller => "project", :action => "create")
547
- @controller.stub(:resource_params).and_return(:resource => 'params')
548
- @controller.stub(:custom_params).and_return(:custom => 'params')
549
- resource = CanCan::ControllerResource.new(@controller)
550
- expect(resource.send("resource_params")).to eq(:resource => 'params')
551
- end
624
+ expect(controller.instance_variable_get(:@model)).to be_nil
552
625
  end
553
626
  end