permit 0.9.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.
- data/.gitignore +5 -0
- data/.yardopts +3 -0
- data/MIT-LICENSE +20 -0
- data/README.mkd +238 -0
- data/Rakefile +69 -0
- data/VERSION.yml +5 -0
- data/generators/permit/USAGE +40 -0
- data/generators/permit/permit_generator.rb +25 -0
- data/generators/permit/templates/authorization.rb +2 -0
- data/generators/permit/templates/initializer.rb +37 -0
- data/generators/permit/templates/migration.rb +28 -0
- data/generators/permit/templates/role.rb +2 -0
- data/init.rb +1 -0
- data/install.rb +1 -0
- data/lib/models/association.rb +89 -0
- data/lib/models/authorizable.rb +31 -0
- data/lib/models/authorization.rb +54 -0
- data/lib/models/person.rb +148 -0
- data/lib/models/role.rb +59 -0
- data/lib/permit/controller.rb +132 -0
- data/lib/permit/permit_rule.rb +198 -0
- data/lib/permit/permit_rules.rb +141 -0
- data/lib/permit/support.rb +67 -0
- data/lib/permit.rb +134 -0
- data/permit.gemspec +91 -0
- data/rails/init.rb +7 -0
- data/spec/models/alternate_models_spec.rb +54 -0
- data/spec/models/authorizable_spec.rb +78 -0
- data/spec/models/authorization_spec.rb +77 -0
- data/spec/models/person_spec.rb +278 -0
- data/spec/models/role_spec.rb +121 -0
- data/spec/permit/controller_spec.rb +308 -0
- data/spec/permit/permit_rule_spec.rb +452 -0
- data/spec/permit/permit_rules_spec.rb +273 -0
- data/spec/permit_spec.rb +58 -0
- data/spec/spec_helper.rb +73 -0
- data/spec/support/helpers.rb +13 -0
- data/spec/support/models.rb +38 -0
- data/spec/support/permits_controller.rb +7 -0
- data/tasks/permit_tasks.rake +4 -0
- data/uninstall.rb +1 -0
- metadata +107 -0
@@ -0,0 +1,452 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
include Permit
|
3
|
+
|
4
|
+
def allow_rule(options = {})
|
5
|
+
PermitRule.new((options.delete(:roles) || :admin), options)
|
6
|
+
end
|
7
|
+
|
8
|
+
def allow_person_rule(options = {})
|
9
|
+
options[:roles] = :person
|
10
|
+
allow_rule options
|
11
|
+
end
|
12
|
+
|
13
|
+
module Permit::Specs
|
14
|
+
describe PermitRule, "initialization" do
|
15
|
+
context "of roles" do
|
16
|
+
it "should raise an error with no roles" do
|
17
|
+
lambda {
|
18
|
+
PermitRule.new nil
|
19
|
+
}.should raise_error(PermitConfigurationError, "At least one role must be specified.")
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should accept one custom role" do
|
23
|
+
r = nil
|
24
|
+
lambda {r = PermitRule.new :admin}.should_not raise_error
|
25
|
+
r.roles.should == [:admin]
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should accept multiple custom roles" do
|
29
|
+
r = nil
|
30
|
+
lambda {r = PermitRule.new [:admin, :member]}.should_not raise_error
|
31
|
+
r.roles.should == [:admin, :member]
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should accept one builtin role" do
|
35
|
+
r = nil
|
36
|
+
[:everyone, :person, :guest].each do |role|
|
37
|
+
lambda {r = PermitRule.new role}.should_not raise_error
|
38
|
+
r.roles.should == [role]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should raise an error with multiple builtin roles" do
|
43
|
+
lambda {PermitRule.new [:everyone, :person, :guest]}.should raise_error(PermitConfigurationError, "Only one role may be specified when using :person, :guest, or :everyone")
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should raise an error with mixed builtin and custom roles" do
|
47
|
+
lambda {PermitRule.new [:person, :admin]}.should raise_error(PermitConfigurationError, "Only one role may be specified when using :person, :guest, or :everyone")
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should not be modifiable" do
|
51
|
+
r = PermitRule.new :person
|
52
|
+
lambda {r.roles << :hackyrole}.should raise_error(TypeError)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context "of resource" do
|
57
|
+
it "should raise an error if both :of and :on are given" do
|
58
|
+
lambda {
|
59
|
+
allow_rule :of => :thing, :on => :something
|
60
|
+
}.should raise_error(PermitConfigurationError, "Either :of or :on may be specified, but not both.")
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should store accept the resource through :of" do
|
64
|
+
r = allow_rule :of => :team
|
65
|
+
r.target_var.should == :team
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should accept the resource through :on" do
|
69
|
+
r = allow_rule :on => :project
|
70
|
+
r.target_var.should == :project
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should not be modifiable" do
|
74
|
+
r = allow_rule :on => :project
|
75
|
+
lambda {r.target_var = :other}.should raise_error(NoMethodError)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context "of method" do
|
80
|
+
it "should raise an error if both :who and :that are given" do
|
81
|
+
lambda {
|
82
|
+
allow_person_rule :who => :is_member, :that => :is_owner, :on => :team
|
83
|
+
}.should raise_error(PermitConfigurationError, "Either :who or :that may be specified, but not both.")
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should not be modifiable" do
|
87
|
+
r = allow_person_rule :who => :is_member, :of => :team
|
88
|
+
lambda { r.method = :monkey }.should raise_error(NoMethodError)
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should raise an error if method is given for a non :person role" do
|
92
|
+
[:everyone, :guest, :manager].each do |role|
|
93
|
+
[:who, :that].each do |key|
|
94
|
+
lambda {
|
95
|
+
allow_rule :roles => role, key => :something, :on => :team
|
96
|
+
}.should raise_error(PermitConfigurationError, "The :who and :that options are only valid for the :person role.")
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
context "when given for :person role" do
|
102
|
+
it "should raise an error if the resource is not specified" do
|
103
|
+
lambda {
|
104
|
+
allow_person_rule :who => :is_member
|
105
|
+
}.should raise_error(PermitConfigurationError, "When :who or :that is specified a corresponding :of or :on must be given")
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should accept the resource and method" do
|
109
|
+
r = allow_person_rule :who => :is_member, :of => :team
|
110
|
+
r.target_var.should == :team
|
111
|
+
r.method.should == :is_member
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
context "of :if condition" do
|
117
|
+
it "should allow an :if condition" do
|
118
|
+
r = allow_rule :if => :some_method
|
119
|
+
r.if.should == :some_method
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should not be modifiable" do
|
123
|
+
r = allow_rule :if => :something
|
124
|
+
lambda {r.if = :other}.should raise_error(NoMethodError)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
context "of :unless condition" do
|
129
|
+
it "should allow an :unless condition" do
|
130
|
+
r = allow_rule :unless => :something_happened
|
131
|
+
r.unless.should == :something_happened
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should not be modifiable" do
|
135
|
+
r = allow_rule :unless=> :something
|
136
|
+
lambda {r.unless = :other}.should raise_error(NoMethodError)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
|
142
|
+
describe PermitRule, "#matches?" do
|
143
|
+
context "for the :everyone role" do
|
144
|
+
it "should match for a guest" do
|
145
|
+
r = allow_rule :roles => :everyone
|
146
|
+
r.matches?(Guest.new, binding).should be_true
|
147
|
+
end
|
148
|
+
|
149
|
+
it "should match for a non-guest" do
|
150
|
+
p = Person.create :name => 'bob'
|
151
|
+
r = allow_rule :roles => :everyone
|
152
|
+
r.matches?(p, binding).should be_true
|
153
|
+
end
|
154
|
+
|
155
|
+
it "should allow any action for :all" do
|
156
|
+
r = allow_rule :roles => :everyone
|
157
|
+
r.matches?(Guest.new, binding).should be_true
|
158
|
+
r.matches?(Guest.new, binding).should be_true
|
159
|
+
r.matches?(Guest.new, binding).should be_true
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
context "for the :guest role" do
|
164
|
+
it "should match for a guest" do
|
165
|
+
r = allow_rule :roles => :guest
|
166
|
+
r.matches?(Guest.new, binding).should be_true
|
167
|
+
end
|
168
|
+
|
169
|
+
it "should not match for a non-guest" do
|
170
|
+
p = Person.create :name => 'bob'
|
171
|
+
r = allow_rule :roles => :guest
|
172
|
+
r.matches?(p, binding).should be_false
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
context "for the :person role" do
|
177
|
+
before do
|
178
|
+
@team = Team.new
|
179
|
+
@person = Person.create :name => 'bob'
|
180
|
+
end
|
181
|
+
|
182
|
+
context "with no target to evaluate against" do
|
183
|
+
it "should not match for a guest" do
|
184
|
+
r = allow_person_rule
|
185
|
+
r.matches?(Guest.new, binding).should be_false
|
186
|
+
end
|
187
|
+
|
188
|
+
it "should match for a non-guest" do
|
189
|
+
r = allow_person_rule
|
190
|
+
r.matches?(@person, binding).should be_true
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
context "using an is_* method" do
|
195
|
+
before {@rule = allow_person_rule :who => :is_owner, :on => :team}
|
196
|
+
|
197
|
+
context "attempting #is_owner" do
|
198
|
+
it "should call #is_owner on the resource" do
|
199
|
+
@team.should_receive(:is_owner).with(@person).and_return(true)
|
200
|
+
@rule.matches?(@person, binding)
|
201
|
+
end
|
202
|
+
|
203
|
+
it "should return the result of the resource call" do
|
204
|
+
@team.stub!(:is_owner).and_return(true)
|
205
|
+
@rule.matches?(@person, binding).should be_true
|
206
|
+
@team.stub!(:is_owner).and_return(false)
|
207
|
+
@rule.matches?(@person, binding).should be_false
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
context "attempting #is_owner?" do
|
212
|
+
it "should call #is_owner? on the resource" do
|
213
|
+
@team.should_receive(:is_owner?).with(@person).and_return(true)
|
214
|
+
@rule.matches?(@person, binding).should be_true
|
215
|
+
end
|
216
|
+
|
217
|
+
it "should return the result of the resource call" do
|
218
|
+
@team.stub!(:is_owner?).and_return(true)
|
219
|
+
@rule.matches?(@person, binding).should be_true
|
220
|
+
@team.stub!(:is_owner?).and_return(false)
|
221
|
+
@rule.matches?(@person, binding).should be_false
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
context "attempting #owner?" do
|
226
|
+
it "should call #owner? on the resource" do
|
227
|
+
@team.should_receive(:owner?).with(@person).and_return(false)
|
228
|
+
@rule.matches?(@person, binding).should be_false
|
229
|
+
end
|
230
|
+
|
231
|
+
it "should return the result of the resource call" do
|
232
|
+
@team.stub!(:owner?).and_return(true)
|
233
|
+
@rule.matches?(@person, binding).should be_true
|
234
|
+
@team.stub!(:owner?).and_return(false)
|
235
|
+
@rule.matches?(@person, binding).should be_false
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
context "attempting #owner" do
|
240
|
+
it "should call #owner on the resource" do
|
241
|
+
@team.should_receive(:owner).and_return(@person)
|
242
|
+
@rule.matches?(@person, binding).should be_true
|
243
|
+
end
|
244
|
+
|
245
|
+
it "should return the result of the comparison of the resource call with the current person" do
|
246
|
+
@team.stub!(:owner).and_return(@person)
|
247
|
+
@rule.matches?(@person, binding).should be_true
|
248
|
+
jim = Person.create :name => 'jim'
|
249
|
+
@team.stub!(:owner).and_return(jim)
|
250
|
+
@rule.matches?(@person, binding).should be_false
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
context "attempting #owners.exists?" do
|
255
|
+
it "should call #owners.exists? on the resource" do
|
256
|
+
owners = mock("owners")
|
257
|
+
owners.should_receive(:exists?).with(@person).and_return(true)
|
258
|
+
@team.stub!(:owners).and_return(owners)
|
259
|
+
@rule.matches?(@person, binding).should be_true
|
260
|
+
end
|
261
|
+
|
262
|
+
it "should return the result of the resource call" do
|
263
|
+
owners = mock("owners")
|
264
|
+
@team.stub!(:owners).and_return(owners)
|
265
|
+
owners.stub!(:exists?).and_return(true)
|
266
|
+
@rule.matches?(@person, binding).should be_true
|
267
|
+
owners.stub!(:exists?).and_return(false)
|
268
|
+
@rule.matches?(@person, binding).should be_false
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
it "should raise an error if none of the attempted calls responded" do
|
273
|
+
@team.stub!(:respond_to?).and_return(false)
|
274
|
+
lambda {
|
275
|
+
@rule.matches?(@person, binding)
|
276
|
+
}.should raise_error(PermitEvaluationError, "Target object ':team' evaluated as #{@team.inspect} did not respond to any of the following: is_owner, is_owner?, owner, owner?, owners")
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
context "using an is_*? method" do
|
281
|
+
before {@rule = allow_person_rule :who => :is_manager?, :on => :team}
|
282
|
+
|
283
|
+
context "attempting #is_manager?" do
|
284
|
+
it "should call #is_manager? on the resource" do
|
285
|
+
@team.should_receive(:is_manager?).with(@person).and_return(true)
|
286
|
+
@rule.matches?(@person, binding)
|
287
|
+
end
|
288
|
+
|
289
|
+
it "should return the result from the resource call" do
|
290
|
+
@team.stub!(:is_manager?).and_return(true)
|
291
|
+
@rule.matches?(@person, binding).should be_true
|
292
|
+
@team.stub!(:is_manager?).and_return(false)
|
293
|
+
@rule.matches?(@person, binding).should be_false
|
294
|
+
end
|
295
|
+
|
296
|
+
it "should not call #manager? if resource responds to #is_manager?" do
|
297
|
+
@team.stub!(:is_manager?).and_return(true)
|
298
|
+
@team.should_not_receive(:manager?)
|
299
|
+
@rule.matches?(@person, binding)
|
300
|
+
end
|
301
|
+
|
302
|
+
end
|
303
|
+
|
304
|
+
context "attempting #manager?" do
|
305
|
+
it "should call #manager? on the resource" do
|
306
|
+
@team.should_receive(:manager?).with(@person).and_return(false)
|
307
|
+
@rule.matches?(@person, binding).should be_false
|
308
|
+
end
|
309
|
+
|
310
|
+
it "should return the result from the resource call" do
|
311
|
+
@team.stub!(:manager?).and_return(true)
|
312
|
+
@rule.matches?(@person, binding).should be_true
|
313
|
+
@team.stub!(:manager?).and_return(false)
|
314
|
+
@rule.matches?(@person, binding).should be_false
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
it "should raise an error if none of the attempted calls responded" do
|
319
|
+
@team.stub!(:respond_to?).and_return(false)
|
320
|
+
lambda {
|
321
|
+
@rule.matches?(@person, binding)
|
322
|
+
}.should raise_error(PermitEvaluationError, "Target object ':team' evaluated as #{@team.inspect} did not respond to any of the following: is_manager?, manager?")
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
context "using any other method" do
|
327
|
+
before {@rule = allow_person_rule :who => :has_permission, :on => :team}
|
328
|
+
it "should call the method on the resource" do
|
329
|
+
@team.should_receive(:has_permission).with(@person)
|
330
|
+
@rule.matches?(@person, binding)
|
331
|
+
end
|
332
|
+
|
333
|
+
it "should raise an error if the attempted call did not respond" do
|
334
|
+
@team.stub!(:respond_to?).and_return(false)
|
335
|
+
lambda {
|
336
|
+
@rule.matches?(@person, binding)
|
337
|
+
}.should raise_error(PermitEvaluationError, "Target object ':team' evaluated as #{@team.inspect} did not respond to any of the following: has_permission")
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
end
|
342
|
+
|
343
|
+
context "for a named authorization" do
|
344
|
+
before do
|
345
|
+
@bob = Person.create :name => "bob"
|
346
|
+
@tom = Person.create :name => "tom"
|
347
|
+
@hotness = Project.create(:name => "hotness")
|
348
|
+
@maintenance = Project.create(:name => "maintenance")
|
349
|
+
Role.create :key => :site_admin, :name => 'site admin', :authorize_resource => false, :requires_resource => false
|
350
|
+
new_authz @bob, :site_admin, nil
|
351
|
+
new_authz @bob, :admin, @maintenance
|
352
|
+
new_authz @bob, :developer, @maintenance
|
353
|
+
new_authz @bob, :team_lead, @hotness
|
354
|
+
|
355
|
+
new_authz @tom, :admin, @hotness
|
356
|
+
end
|
357
|
+
|
358
|
+
it "should return false for a guest" do
|
359
|
+
r = PermitRule.new :admin
|
360
|
+
r.matches?(Guest.new, binding).should be_false
|
361
|
+
end
|
362
|
+
|
363
|
+
context "with a resource" do
|
364
|
+
it "should return true if the person is authorized for the role and resource" do
|
365
|
+
r = allow_rule :roles => :admin, :of => :maintenance
|
366
|
+
r.matches?(@bob, binding).should be_true
|
367
|
+
end
|
368
|
+
|
369
|
+
it "should return false if the person is not authorized for the role and resource" do
|
370
|
+
r = allow_rule :roles => :monkey_tech, :of => :maintenance
|
371
|
+
r.matches?(@bob, binding).should be_false
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
context "without a resource" do
|
376
|
+
it "should return true if the person is authorized for the role" do
|
377
|
+
r = allow_rule :roles => :site_admin
|
378
|
+
r.matches?(@bob, binding).should be_true
|
379
|
+
end
|
380
|
+
|
381
|
+
it "should return false if the person is not authorized for the role" do
|
382
|
+
r = allow_rule :roles => :admin
|
383
|
+
r.matches?(@bob, binding).should be_false
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
context "with any resource" do
|
388
|
+
it "should return true if the person is authorized for the role and a resource" do
|
389
|
+
r = allow_rule :roles => :admin, :of => :any
|
390
|
+
r.matches?(@bob, binding).should be_true
|
391
|
+
end
|
392
|
+
|
393
|
+
it "should return false if the person is not authorized for the role and a resource" do
|
394
|
+
r = allow_rule :roles => :developer, :of => :any
|
395
|
+
r.matches?(@tom, binding).should be_false
|
396
|
+
end
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
context "for multiple named authorizations" do
|
401
|
+
before do
|
402
|
+
@bob = Person.create :name => "bob"
|
403
|
+
@tom = Person.create :name => "tom"
|
404
|
+
@hotness = Project.create(:name => "hotness")
|
405
|
+
@maintenance = Project.create(:name => "maintenance")
|
406
|
+
Role.create :key => :site_admin, :name => 'site admin', :authorize_resource => false, :requires_resource => false
|
407
|
+
new_authz @bob, :site_admin, nil
|
408
|
+
new_authz @bob, :admin, @maintenance
|
409
|
+
new_authz @bob, :developer, @maintenance
|
410
|
+
new_authz @bob, :team_lead, @hotness
|
411
|
+
|
412
|
+
new_authz @tom, :admin, @hotness
|
413
|
+
end
|
414
|
+
|
415
|
+
context "with a resource" do
|
416
|
+
it "should return true if the person is authorized for any of the roles and resource" do
|
417
|
+
r = allow_rule :roles => [:admin, :team_lead], :of => :maintenance
|
418
|
+
r.matches?(@bob, binding).should be_true
|
419
|
+
end
|
420
|
+
|
421
|
+
it "should return false if the person is not authorized for any of the roles and resource" do
|
422
|
+
r = allow_rule :roles => [:site_admin, :monkey_tech], :of => :maintenance
|
423
|
+
r.matches?(@bob, binding).should be_false
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
context "without a resource" do
|
428
|
+
it "should return true if the person is authorized for any of the roles" do
|
429
|
+
r = allow_rule :roles => [:site_admin, :team_lead]
|
430
|
+
r.matches?(@bob, binding).should be_true
|
431
|
+
end
|
432
|
+
|
433
|
+
it "should return false if the person is not authorized for any of the roles" do
|
434
|
+
r = allow_rule :roles => [:admin, :team_lead]
|
435
|
+
r.matches?(@bob, binding).should be_false
|
436
|
+
end
|
437
|
+
end
|
438
|
+
|
439
|
+
context "with any resource" do
|
440
|
+
it "should return true if the person is authorized for any of the roles and a resource" do
|
441
|
+
r = allow_rule :roles => [:admin, :team_lead], :of => :any
|
442
|
+
r.matches?(@bob, binding).should be_true
|
443
|
+
end
|
444
|
+
|
445
|
+
it "should return false if the person is not authorized for any of the roles and a resource" do
|
446
|
+
r = allow_rule :roles => [:developer, :site_admin], :of => :any
|
447
|
+
r.matches?(@tom, binding).should be_false
|
448
|
+
end
|
449
|
+
end
|
450
|
+
end
|
451
|
+
end
|
452
|
+
end
|