permit 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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