cancancan 1.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/CHANGELOG.rdoc +427 -0
- data/CONTRIBUTING.md +11 -0
- data/Gemfile +23 -0
- data/LICENSE +20 -0
- data/README.rdoc +161 -0
- data/Rakefile +18 -0
- data/init.rb +1 -0
- data/lib/cancan.rb +13 -0
- data/lib/cancan/ability.rb +324 -0
- data/lib/cancan/controller_additions.rb +397 -0
- data/lib/cancan/controller_resource.rb +286 -0
- data/lib/cancan/exceptions.rb +50 -0
- data/lib/cancan/inherited_resource.rb +20 -0
- data/lib/cancan/matchers.rb +14 -0
- data/lib/cancan/model_adapters/abstract_adapter.rb +56 -0
- data/lib/cancan/model_adapters/active_record_adapter.rb +180 -0
- data/lib/cancan/model_adapters/data_mapper_adapter.rb +34 -0
- data/lib/cancan/model_adapters/default_adapter.rb +7 -0
- data/lib/cancan/model_adapters/mongoid_adapter.rb +54 -0
- data/lib/cancan/model_additions.rb +31 -0
- data/lib/cancan/rule.rb +147 -0
- data/lib/cancancan.rb +1 -0
- data/lib/generators/cancan/ability/USAGE +4 -0
- data/lib/generators/cancan/ability/ability_generator.rb +11 -0
- data/lib/generators/cancan/ability/templates/ability.rb +32 -0
- data/spec/README.rdoc +28 -0
- data/spec/cancan/ability_spec.rb +455 -0
- data/spec/cancan/controller_additions_spec.rb +141 -0
- data/spec/cancan/controller_resource_spec.rb +553 -0
- data/spec/cancan/exceptions_spec.rb +58 -0
- data/spec/cancan/inherited_resource_spec.rb +60 -0
- data/spec/cancan/matchers_spec.rb +29 -0
- data/spec/cancan/model_adapters/active_record_adapter_spec.rb +358 -0
- data/spec/cancan/model_adapters/data_mapper_adapter_spec.rb +118 -0
- data/spec/cancan/model_adapters/default_adapter_spec.rb +7 -0
- data/spec/cancan/model_adapters/mongoid_adapter_spec.rb +226 -0
- data/spec/cancan/rule_spec.rb +52 -0
- data/spec/matchers.rb +13 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +77 -0
- metadata +126 -0
@@ -0,0 +1,58 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe CanCan::AccessDenied do
|
4
|
+
describe "with action and subject" do
|
5
|
+
before(:each) do
|
6
|
+
@exception = CanCan::AccessDenied.new(nil, :some_action, :some_subject)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "has action and subject accessors" do
|
10
|
+
expect(@exception.action).to eq(:some_action)
|
11
|
+
expect(@exception.subject).to eq(:some_subject)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "has a changable default message" do
|
15
|
+
expect(@exception.message).to eq("You are not authorized to access this page.")
|
16
|
+
@exception.default_message = "Unauthorized!"
|
17
|
+
expect(@exception.message).to eq("Unauthorized!")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "with only a message" do
|
22
|
+
before(:each) do
|
23
|
+
@exception = CanCan::AccessDenied.new("Access denied!")
|
24
|
+
end
|
25
|
+
|
26
|
+
it "has nil action and subject" do
|
27
|
+
expect(@exception.action).to be_nil
|
28
|
+
expect(@exception.subject).to be_nil
|
29
|
+
end
|
30
|
+
|
31
|
+
it "has passed message" do
|
32
|
+
expect(@exception.message).to eq("Access denied!")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "i18n in the default message" do
|
37
|
+
after(:each) do
|
38
|
+
I18n.backend = nil
|
39
|
+
end
|
40
|
+
|
41
|
+
it "uses i18n for the default message" do
|
42
|
+
I18n.backend.store_translations :en, :unauthorized => {:default => "This is a different message"}
|
43
|
+
@exception = CanCan::AccessDenied.new
|
44
|
+
expect(@exception.message).to eq("This is a different message")
|
45
|
+
end
|
46
|
+
|
47
|
+
it "defaults to a nice message" do
|
48
|
+
@exception = CanCan::AccessDenied.new
|
49
|
+
expect(@exception.message).to eq("You are not authorized to access this page.")
|
50
|
+
end
|
51
|
+
|
52
|
+
it "does not use translation if a message is given" do
|
53
|
+
@exception = CanCan::AccessDenied.new("Hey! You're not welcome here")
|
54
|
+
expect(@exception.message).to eq("Hey! You're not welcome here")
|
55
|
+
expect(@exception.message).to_not eq("You are not authorized to access this page.")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe CanCan::InheritedResource do
|
4
|
+
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).and_return { @params }
|
10
|
+
allow(@controller).to receive(:current_ability) { @ability }
|
11
|
+
allow(@controller_class).to receive(:cancan_skipper) { {:authorize => {}, :load => {}} }
|
12
|
+
end
|
13
|
+
|
14
|
+
it "show loads resource through @controller.resource" do
|
15
|
+
@params.merge!(:action => "show", :id => 123)
|
16
|
+
allow(@controller).to receive(:resource) { :project_resource }
|
17
|
+
CanCan::InheritedResource.new(@controller).load_resource
|
18
|
+
expect(@controller.instance_variable_get(:@project)).to eq(:project_resource)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "new loads through @controller.build_resource" do
|
22
|
+
@params[:action] = "new"
|
23
|
+
allow(@controller).to receive(:build_resource) { :project_resource }
|
24
|
+
CanCan::InheritedResource.new(@controller).load_resource
|
25
|
+
expect(@controller.instance_variable_get(:@project)).to eq(:project_resource)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "index loads through @controller.association_chain when parent" do
|
29
|
+
@params[:action] = "index"
|
30
|
+
allow(@controller).to receive(:association_chain) { @controller.instance_variable_set(:@project, :project_resource) }
|
31
|
+
CanCan::InheritedResource.new(@controller, :parent => true).load_resource
|
32
|
+
expect(@controller.instance_variable_get(:@project)).to eq(:project_resource)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "index loads through @controller.end_of_association_chain" do
|
36
|
+
@params[:action] = "index"
|
37
|
+
allow(Project).to receive(:accessible_by).with(@ability, :index) { :projects }
|
38
|
+
allow(@controller).to receive(:end_of_association_chain) { Project }
|
39
|
+
CanCan::InheritedResource.new(@controller).load_resource
|
40
|
+
expect(@controller.instance_variable_get(:@projects)).to eq(:projects)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "builds a new resource with attributes from current ability" do
|
44
|
+
@params[:action] = "new"
|
45
|
+
@ability.can(:create, Project, :name => "from conditions")
|
46
|
+
allow(@controller).to receive(:build_resource) { Struct.new(:name).new }
|
47
|
+
resource = CanCan::InheritedResource.new(@controller)
|
48
|
+
resource.load_resource
|
49
|
+
expect(@controller.instance_variable_get(:@project).name).to eq("from conditions")
|
50
|
+
end
|
51
|
+
|
52
|
+
it "overrides initial attributes with params" do
|
53
|
+
@params.merge!(:action => "new", :project => {:name => "from params"})
|
54
|
+
@ability.can(:create, Project, :name => "from conditions")
|
55
|
+
allow(@controller).to receive(:build_resource) { Struct.new(:name).new }
|
56
|
+
resource = CanCan::ControllerResource.new(@controller)
|
57
|
+
resource.load_resource
|
58
|
+
expect(@controller.instance_variable_get(:@project).name).to eq("from params")
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "be_able_to" do
|
4
|
+
it "delegates to can?" do
|
5
|
+
expect(object = double).to receive(:can?).with(:read, 123) { true }
|
6
|
+
expect(object).to be_able_to(:read, 123)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "reports a nice failure message for should" do
|
10
|
+
expect(object = double).to receive(:can?).with(:read, 123) { false }
|
11
|
+
expect {
|
12
|
+
expect(object).to be_able_to(:read, 123)
|
13
|
+
}.to raise_error('expected to be able to :read 123')
|
14
|
+
end
|
15
|
+
|
16
|
+
it "reports a nice failure message for should not" do
|
17
|
+
expect(object = double).to receive(:can?).with(:read, 123) { true }
|
18
|
+
expect {
|
19
|
+
expect(object).to_not be_able_to(:read, 123)
|
20
|
+
}.to raise_error('expected not to be able to :read 123')
|
21
|
+
end
|
22
|
+
|
23
|
+
it "delegates additional arguments to can? and reports in failure message" do
|
24
|
+
expect(object = double).to receive(:can?).with(:read, 123, 456) { false }
|
25
|
+
expect {
|
26
|
+
expect(object).to be_able_to(:read, 123, 456)
|
27
|
+
}.to raise_error('expected to be able to :read 123 456')
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,358 @@
|
|
1
|
+
if ENV["MODEL_ADAPTER"].nil? || ENV["MODEL_ADAPTER"] == "active_record"
|
2
|
+
require "spec_helper"
|
3
|
+
|
4
|
+
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
|
5
|
+
|
6
|
+
describe CanCan::ModelAdapters::ActiveRecordAdapter do
|
7
|
+
with_model :category do
|
8
|
+
table do |t|
|
9
|
+
t.string "name"
|
10
|
+
t.boolean "visible"
|
11
|
+
end
|
12
|
+
model do
|
13
|
+
has_many :articles
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
with_model :article do
|
18
|
+
table do |t|
|
19
|
+
t.string "name"
|
20
|
+
t.boolean "published"
|
21
|
+
t.boolean "secret"
|
22
|
+
t.integer "priority"
|
23
|
+
t.integer "category_id"
|
24
|
+
t.integer "user_id"
|
25
|
+
end
|
26
|
+
model do
|
27
|
+
belongs_to :category
|
28
|
+
has_many :comments
|
29
|
+
belongs_to :user
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
with_model :comment do
|
34
|
+
table do |t|
|
35
|
+
t.boolean "spam"
|
36
|
+
t.integer "article_id"
|
37
|
+
end
|
38
|
+
model do
|
39
|
+
belongs_to :article
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
with_model :user do
|
44
|
+
table do |t|
|
45
|
+
|
46
|
+
end
|
47
|
+
model do
|
48
|
+
has_many :articles
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
before(:each) do
|
53
|
+
Article.delete_all
|
54
|
+
Comment.delete_all
|
55
|
+
(@ability = double).extend(CanCan::Ability)
|
56
|
+
@article_table = Article.table_name
|
57
|
+
@comment_table = Comment.table_name
|
58
|
+
end
|
59
|
+
|
60
|
+
it "is for only active record classes" do
|
61
|
+
expect(CanCan::ModelAdapters::ActiveRecordAdapter).to_not be_for_class(Object)
|
62
|
+
expect(CanCan::ModelAdapters::ActiveRecordAdapter).to be_for_class(Article)
|
63
|
+
expect(CanCan::ModelAdapters::AbstractAdapter.adapter_class(Article)).to eq(CanCan::ModelAdapters::ActiveRecordAdapter)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "finds record" do
|
67
|
+
article = Article.create!
|
68
|
+
expect(CanCan::ModelAdapters::ActiveRecordAdapter.find(Article, article.id)).to eq(article)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "does not fetch any records when no abilities are defined" do
|
72
|
+
Article.create!
|
73
|
+
expect(Article.accessible_by(@ability)).to be_empty
|
74
|
+
end
|
75
|
+
|
76
|
+
it "fetches all articles when one can read all" do
|
77
|
+
@ability.can :read, Article
|
78
|
+
article = Article.create!
|
79
|
+
expect(Article.accessible_by(@ability)).to eq([article])
|
80
|
+
end
|
81
|
+
|
82
|
+
it "fetches only the articles that are published" do
|
83
|
+
@ability.can :read, Article, :published => true
|
84
|
+
article1 = Article.create!(:published => true)
|
85
|
+
article2 = Article.create!(:published => false)
|
86
|
+
expect(Article.accessible_by(@ability)).to eq([article1])
|
87
|
+
end
|
88
|
+
|
89
|
+
it "fetches any articles which are published or secret" do
|
90
|
+
@ability.can :read, Article, :published => true
|
91
|
+
@ability.can :read, Article, :secret => true
|
92
|
+
article1 = Article.create!(:published => true, :secret => false)
|
93
|
+
article2 = Article.create!(:published => true, :secret => true)
|
94
|
+
article3 = Article.create!(:published => false, :secret => true)
|
95
|
+
article4 = Article.create!(:published => false, :secret => false)
|
96
|
+
expect(Article.accessible_by(@ability)).to eq([article1, article2, article3])
|
97
|
+
end
|
98
|
+
|
99
|
+
it "fetches only the articles that are published and not secret" do
|
100
|
+
@ability.can :read, Article, :published => true
|
101
|
+
@ability.cannot :read, Article, :secret => true
|
102
|
+
article1 = Article.create!(:published => true, :secret => false)
|
103
|
+
article2 = Article.create!(:published => true, :secret => true)
|
104
|
+
article3 = Article.create!(:published => false, :secret => true)
|
105
|
+
article4 = Article.create!(:published => false, :secret => false)
|
106
|
+
expect(Article.accessible_by(@ability)).to eq([article1])
|
107
|
+
end
|
108
|
+
|
109
|
+
it "only reads comments for articles which are published" do
|
110
|
+
@ability.can :read, Comment, :article => { :published => true }
|
111
|
+
comment1 = Comment.create!(:article => Article.create!(:published => true))
|
112
|
+
comment2 = Comment.create!(:article => Article.create!(:published => false))
|
113
|
+
expect(Comment.accessible_by(@ability)).to eq([comment1])
|
114
|
+
end
|
115
|
+
|
116
|
+
it "should only read articles which are published or in visible categories" do
|
117
|
+
@ability.can :read, Article, :category => { :visible => true }
|
118
|
+
@ability.can :read, Article, :published => true
|
119
|
+
article1 = Article.create!(:published => true)
|
120
|
+
article2 = Article.create!(:published => false)
|
121
|
+
article3 = Article.create!(:published => false, :category => Category.create!(:visible => true))
|
122
|
+
expect(Article.accessible_by(@ability)).to eq([article1, article3])
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should only read categories once even if they have multiple articles" do
|
126
|
+
@ability.can :read, Category, :articles => { :published => true }
|
127
|
+
@ability.can :read, Article, :published => true
|
128
|
+
category = Category.create!
|
129
|
+
Article.create!(:published => true, :category => category)
|
130
|
+
Article.create!(:published => true, :category => category)
|
131
|
+
expect(Category.accessible_by(@ability)).to eq([category])
|
132
|
+
end
|
133
|
+
|
134
|
+
it "only reads comments for visible categories through articles" do
|
135
|
+
@ability.can :read, Comment, :article => { :category => { :visible => true } }
|
136
|
+
comment1 = Comment.create!(:article => Article.create!(:category => Category.create!(:visible => true)))
|
137
|
+
comment2 = Comment.create!(:article => Article.create!(:category => Category.create!(:visible => false)))
|
138
|
+
expect(Comment.accessible_by(@ability)).to eq([comment1])
|
139
|
+
end
|
140
|
+
|
141
|
+
it "allows conditions in SQL and merge with hash conditions" do
|
142
|
+
@ability.can :read, Article, :published => true
|
143
|
+
@ability.can :read, Article, ["secret=?", true]
|
144
|
+
article1 = Article.create!(:published => true, :secret => false)
|
145
|
+
article2 = Article.create!(:published => true, :secret => true)
|
146
|
+
article3 = Article.create!(:published => false, :secret => true)
|
147
|
+
article4 = Article.create!(:published => false, :secret => false)
|
148
|
+
expect(Article.accessible_by(@ability)).to eq([article1, article2, article3])
|
149
|
+
end
|
150
|
+
|
151
|
+
it "allows a scope for conditions" do
|
152
|
+
@ability.can :read, Article, Article.where(:secret => true)
|
153
|
+
article1 = Article.create!(:secret => true)
|
154
|
+
article2 = Article.create!(:secret => false)
|
155
|
+
expect(Article.accessible_by(@ability)).to eq([article1])
|
156
|
+
end
|
157
|
+
|
158
|
+
it "fetches only associated records when using with a scope for conditions" do
|
159
|
+
@ability.can :read, Article, Article.where(:secret => true)
|
160
|
+
category1 = Category.create!(:visible => false)
|
161
|
+
category2 = Category.create!(:visible => true)
|
162
|
+
article1 = Article.create!(:secret => true, :category => category1)
|
163
|
+
article2 = Article.create!(:secret => true, :category => category2)
|
164
|
+
expect(category1.articles.accessible_by(@ability)).to eq([article1])
|
165
|
+
end
|
166
|
+
|
167
|
+
it "raises an exception when trying to merge scope with other conditions" do
|
168
|
+
@ability.can :read, Article, :published => true
|
169
|
+
@ability.can :read, Article, Article.where(:secret => true)
|
170
|
+
expect(lambda { Article.accessible_by(@ability) }).to raise_error(CanCan::Error, "Unable to merge an Active Record scope with other conditions. Instead use a hash or SQL for read Article ability.")
|
171
|
+
end
|
172
|
+
|
173
|
+
it "does not allow to fetch records when ability with just block present" do
|
174
|
+
@ability.can :read, Article do
|
175
|
+
false
|
176
|
+
end
|
177
|
+
expect(lambda { Article.accessible_by(@ability) }).to raise_error(CanCan::Error)
|
178
|
+
end
|
179
|
+
|
180
|
+
it "should support more than one deeply nested conditions" do
|
181
|
+
@ability.can :read, Comment, :article => {
|
182
|
+
:category => {
|
183
|
+
:name => 'foo', :visible => true
|
184
|
+
}
|
185
|
+
}
|
186
|
+
expect { Comment.accessible_by(@ability) }.to_not raise_error
|
187
|
+
end
|
188
|
+
|
189
|
+
it "does not allow to check ability on object against SQL conditions without block" do
|
190
|
+
@ability.can :read, Article, ["secret=?", true]
|
191
|
+
expect(lambda { @ability.can? :read, Article.new }).to raise_error(CanCan::Error)
|
192
|
+
end
|
193
|
+
|
194
|
+
it "has false conditions if no abilities match" do
|
195
|
+
expect(@ability.model_adapter(Article, :read).conditions).to eq("'t'='f'")
|
196
|
+
end
|
197
|
+
|
198
|
+
it "returns false conditions for cannot clause" do
|
199
|
+
@ability.cannot :read, Article
|
200
|
+
expect(@ability.model_adapter(Article, :read).conditions).to eq("'t'='f'")
|
201
|
+
end
|
202
|
+
|
203
|
+
it "returns SQL for single `can` definition in front of default `cannot` condition" do
|
204
|
+
@ability.cannot :read, Article
|
205
|
+
@ability.can :read, Article, :published => false, :secret => true
|
206
|
+
expect(@ability.model_adapter(Article, :read).conditions).to orderlessly_match(%Q["#{@article_table}"."published" = 'f' AND "#{@article_table}"."secret" = 't'])
|
207
|
+
end
|
208
|
+
|
209
|
+
it "returns true condition for single `can` definition in front of default `can` condition" do
|
210
|
+
@ability.can :read, Article
|
211
|
+
@ability.can :read, Article, :published => false, :secret => true
|
212
|
+
expect(@ability.model_adapter(Article, :read).conditions).to eq("'t'='t'")
|
213
|
+
end
|
214
|
+
|
215
|
+
it "returns `false condition` for single `cannot` definition in front of default `cannot` condition" do
|
216
|
+
@ability.cannot :read, Article
|
217
|
+
@ability.cannot :read, Article, :published => false, :secret => true
|
218
|
+
expect(@ability.model_adapter(Article, :read).conditions).to eq("'t'='f'")
|
219
|
+
end
|
220
|
+
|
221
|
+
it "returns `not (sql)` for single `cannot` definition in front of default `can` condition" do
|
222
|
+
@ability.can :read, Article
|
223
|
+
@ability.cannot :read, Article, :published => false, :secret => true
|
224
|
+
expect(@ability.model_adapter(Article, :read).conditions).to orderlessly_match(%Q["not (#{@article_table}"."published" = 'f' AND "#{@article_table}"."secret" = 't')])
|
225
|
+
end
|
226
|
+
|
227
|
+
it "returns appropriate sql conditions in complex case" do
|
228
|
+
@ability.can :read, Article
|
229
|
+
@ability.can :manage, Article, :id => 1
|
230
|
+
@ability.can :update, Article, :published => true
|
231
|
+
@ability.cannot :update, Article, :secret => true
|
232
|
+
expect(@ability.model_adapter(Article, :update).conditions).to eq(%Q[not ("#{@article_table}"."secret" = 't') AND (("#{@article_table}"."published" = 't') OR ("#{@article_table}"."id" = 1))])
|
233
|
+
expect(@ability.model_adapter(Article, :manage).conditions).to eq({:id => 1})
|
234
|
+
expect(@ability.model_adapter(Article, :read).conditions).to eq("'t'='t'")
|
235
|
+
end
|
236
|
+
|
237
|
+
it "returns appropriate sql conditions in complex case with nested joins" do
|
238
|
+
@ability.can :read, Comment, :article => { :category => { :visible => true } }
|
239
|
+
expect(@ability.model_adapter(Comment, :read).conditions).to eq({ Category.table_name.to_sym => { :visible => true } })
|
240
|
+
end
|
241
|
+
|
242
|
+
it "returns appropriate sql conditions in complex case with nested joins of different depth" do
|
243
|
+
@ability.can :read, Comment, :article => { :published => true, :category => { :visible => true } }
|
244
|
+
expect(@ability.model_adapter(Comment, :read).conditions).to eq({ Article.table_name.to_sym => { :published => true }, Category.table_name.to_sym => { :visible => true } })
|
245
|
+
end
|
246
|
+
|
247
|
+
it "does not forget conditions when calling with SQL string" do
|
248
|
+
@ability.can :read, Article, :published => true
|
249
|
+
@ability.can :read, Article, ['secret=?', false]
|
250
|
+
adapter = @ability.model_adapter(Article, :read)
|
251
|
+
2.times do
|
252
|
+
expect(adapter.conditions).to eq(%Q[(secret='f') OR ("#{@article_table}"."published" = 't')])
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
it "has nil joins if no rules" do
|
257
|
+
expect(@ability.model_adapter(Article, :read).joins).to be_nil
|
258
|
+
end
|
259
|
+
|
260
|
+
it "has nil joins if no nested hashes specified in conditions" do
|
261
|
+
@ability.can :read, Article, :published => false
|
262
|
+
@ability.can :read, Article, :secret => true
|
263
|
+
expect(@ability.model_adapter(Article, :read).joins).to be_nil
|
264
|
+
end
|
265
|
+
|
266
|
+
it "merges separate joins into a single array" do
|
267
|
+
@ability.can :read, Article, :project => { :blocked => false }
|
268
|
+
@ability.can :read, Article, :company => { :admin => true }
|
269
|
+
expect(@ability.model_adapter(Article, :read).joins.inspect).to orderlessly_match([:company, :project].inspect)
|
270
|
+
end
|
271
|
+
|
272
|
+
it "merges same joins into a single array" do
|
273
|
+
@ability.can :read, Article, :project => { :blocked => false }
|
274
|
+
@ability.can :read, Article, :project => { :admin => true }
|
275
|
+
expect(@ability.model_adapter(Article, :read).joins).to eq([:project])
|
276
|
+
end
|
277
|
+
|
278
|
+
it "merges nested and non-nested joins" do
|
279
|
+
@ability.can :read, Article, :project => { :blocked => false }
|
280
|
+
@ability.can :read, Article, :project => { :comments => { :spam => true } }
|
281
|
+
expect(@ability.model_adapter(Article, :read).joins).to eq([{:project=>[:comments]}])
|
282
|
+
end
|
283
|
+
|
284
|
+
it "merges :all conditions with other conditions" do
|
285
|
+
user = User.create!
|
286
|
+
article = Article.create!(:user => user)
|
287
|
+
ability = Ability.new(user)
|
288
|
+
ability.can :manage, :all
|
289
|
+
ability.can :manage, Article, :user_id => user.id
|
290
|
+
expect(Article.accessible_by(ability)).to eq([article])
|
291
|
+
end
|
292
|
+
|
293
|
+
it "restricts articles given a MetaWhere condition" do
|
294
|
+
@ability.can :read, Article, :priority.lt => 2
|
295
|
+
article1 = Article.create!(:priority => 1)
|
296
|
+
article2 = Article.create!(:priority => 3)
|
297
|
+
expect(Article.accessible_by(@ability)).to eq([article1])
|
298
|
+
expect(@ability).to be_able_to(:read, article1)
|
299
|
+
expect(@ability).to_not be_able_to(:read, article2)
|
300
|
+
end
|
301
|
+
|
302
|
+
it "merges MetaWhere and non-MetaWhere conditions" do
|
303
|
+
@ability.can :read, Article, :priority.lt => 2
|
304
|
+
@ability.can :read, Article, :priority => 1
|
305
|
+
article1 = Article.create!(:priority => 1)
|
306
|
+
article2 = Article.create!(:priority => 3)
|
307
|
+
expect(Article.accessible_by(@ability)).to eq([article1])
|
308
|
+
expect(@ability).to be_able_to(:read, article1)
|
309
|
+
expect(@ability).to_not be_able_to(:read, article2)
|
310
|
+
end
|
311
|
+
|
312
|
+
it "matches any MetaWhere condition" do
|
313
|
+
adapter = CanCan::ModelAdapters::ActiveRecordAdapter
|
314
|
+
article1 = Article.new(:priority => 1, :name => "Hello World")
|
315
|
+
expect(adapter.matches_condition?(article1, :priority.eq, 1)).to be_true
|
316
|
+
expect(adapter.matches_condition?(article1, :priority.eq, 2)).to be_false
|
317
|
+
expect(adapter.matches_condition?(article1, :priority.eq_any, [1, 2])).to be_true
|
318
|
+
expect(adapter.matches_condition?(article1, :priority.eq_any, [2, 3])).to be_false
|
319
|
+
expect(adapter.matches_condition?(article1, :priority.eq_all, [1, 1])).to be_true
|
320
|
+
expect(adapter.matches_condition?(article1, :priority.eq_all, [1, 2])).to be_false
|
321
|
+
expect(adapter.matches_condition?(article1, :priority.ne, 2)).to be_true
|
322
|
+
expect(adapter.matches_condition?(article1, :priority.ne, 1)).to be_false
|
323
|
+
expect(adapter.matches_condition?(article1, :priority.in, [1, 2])).to be_true
|
324
|
+
expect(adapter.matches_condition?(article1, :priority.in, [2, 3])).to be_false
|
325
|
+
expect(adapter.matches_condition?(article1, :priority.nin, [2, 3])).to be_true
|
326
|
+
expect(adapter.matches_condition?(article1, :priority.nin, [1, 2])).to be_false
|
327
|
+
expect(adapter.matches_condition?(article1, :priority.lt, 2)).to be_true
|
328
|
+
expect(adapter.matches_condition?(article1, :priority.lt, 1)).to be_false
|
329
|
+
expect(adapter.matches_condition?(article1, :priority.lteq, 1)).to be_true
|
330
|
+
expect(adapter.matches_condition?(article1, :priority.lteq, 0)).to be_false
|
331
|
+
expect(adapter.matches_condition?(article1, :priority.gt, 0)).to be_true
|
332
|
+
expect(adapter.matches_condition?(article1, :priority.gt, 1)).to be_false
|
333
|
+
expect(adapter.matches_condition?(article1, :priority.gteq, 1)).to be_true
|
334
|
+
expect(adapter.matches_condition?(article1, :priority.gteq, 2)).to be_false
|
335
|
+
expect(adapter.matches_condition?(article1, :name.like, "%ello worl%")).to be_true
|
336
|
+
expect(adapter.matches_condition?(article1, :name.like, "hello world")).to be_true
|
337
|
+
expect(adapter.matches_condition?(article1, :name.like, "hello%")).to be_true
|
338
|
+
expect(adapter.matches_condition?(article1, :name.like, "h%d")).to be_true
|
339
|
+
expect(adapter.matches_condition?(article1, :name.like, "%helo%")).to be_false
|
340
|
+
expect(adapter.matches_condition?(article1, :name.like, "hello")).to be_false
|
341
|
+
expect(adapter.matches_condition?(article1, :name.like, "hello.world")).to be_false
|
342
|
+
# For some reason this is reporting "The not_matches MetaWhere condition is not supported."
|
343
|
+
# expect(adapter.matches_condition?(article1, :name.nlike, "%helo%")).to be_true
|
344
|
+
# expect(adapter.matches_condition?(article1, :name.nlike, "%ello worl%")).to be_false
|
345
|
+
end
|
346
|
+
|
347
|
+
it 'should not execute a scope when checking ability on the class' do
|
348
|
+
relation = Article.where(:secret => true)
|
349
|
+
@ability.can :read, Article, relation do |article|
|
350
|
+
article.secret == true
|
351
|
+
end
|
352
|
+
|
353
|
+
relation.stub(:count).and_raise('Unexpected scope execution.')
|
354
|
+
|
355
|
+
expect { @ability.can? :read, Article }.not_to raise_error
|
356
|
+
end
|
357
|
+
end
|
358
|
+
end
|