cancancan 1.7.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.
- 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
|