cancancan 1.7.1 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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