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.
Files changed (42) hide show
  1. data/.gitignore +5 -0
  2. data/.yardopts +3 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.mkd +238 -0
  5. data/Rakefile +69 -0
  6. data/VERSION.yml +5 -0
  7. data/generators/permit/USAGE +40 -0
  8. data/generators/permit/permit_generator.rb +25 -0
  9. data/generators/permit/templates/authorization.rb +2 -0
  10. data/generators/permit/templates/initializer.rb +37 -0
  11. data/generators/permit/templates/migration.rb +28 -0
  12. data/generators/permit/templates/role.rb +2 -0
  13. data/init.rb +1 -0
  14. data/install.rb +1 -0
  15. data/lib/models/association.rb +89 -0
  16. data/lib/models/authorizable.rb +31 -0
  17. data/lib/models/authorization.rb +54 -0
  18. data/lib/models/person.rb +148 -0
  19. data/lib/models/role.rb +59 -0
  20. data/lib/permit/controller.rb +132 -0
  21. data/lib/permit/permit_rule.rb +198 -0
  22. data/lib/permit/permit_rules.rb +141 -0
  23. data/lib/permit/support.rb +67 -0
  24. data/lib/permit.rb +134 -0
  25. data/permit.gemspec +91 -0
  26. data/rails/init.rb +7 -0
  27. data/spec/models/alternate_models_spec.rb +54 -0
  28. data/spec/models/authorizable_spec.rb +78 -0
  29. data/spec/models/authorization_spec.rb +77 -0
  30. data/spec/models/person_spec.rb +278 -0
  31. data/spec/models/role_spec.rb +121 -0
  32. data/spec/permit/controller_spec.rb +308 -0
  33. data/spec/permit/permit_rule_spec.rb +452 -0
  34. data/spec/permit/permit_rules_spec.rb +273 -0
  35. data/spec/permit_spec.rb +58 -0
  36. data/spec/spec_helper.rb +73 -0
  37. data/spec/support/helpers.rb +13 -0
  38. data/spec/support/models.rb +38 -0
  39. data/spec/support/permits_controller.rb +7 -0
  40. data/tasks/permit_tasks.rake +4 -0
  41. data/uninstall.rb +1 -0
  42. 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