policy_machine 0.0.1

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 (56) hide show
  1. data/CONTRIBUTING.md +35 -0
  2. data/Gemfile +2 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.md +98 -0
  5. data/lib/generators/policy_machine/policy_machine_generator.rb +13 -0
  6. data/lib/generators/policy_machine/templates/migration.rb +40 -0
  7. data/lib/policy_machine.rb +236 -0
  8. data/lib/policy_machine/association.rb +73 -0
  9. data/lib/policy_machine/policy_element.rb +269 -0
  10. data/lib/policy_machine/version.rb +3 -0
  11. data/lib/policy_machine_storage_adapters/active_record.rb +306 -0
  12. data/lib/policy_machine_storage_adapters/in_memory.rb +266 -0
  13. data/lib/policy_machine_storage_adapters/neography.rb +236 -0
  14. data/lib/policy_machine_storage_adapters/template.rb +169 -0
  15. data/lib/tasks/policy_machine_tasks.rake +4 -0
  16. data/policy_machine.gemspec +23 -0
  17. data/spec/policy_machine/association_spec.rb +61 -0
  18. data/spec/policy_machine/policy_element_spec.rb +20 -0
  19. data/spec/policy_machine_spec.rb +7 -0
  20. data/spec/policy_machine_storage_adapters/active_record_spec.rb +54 -0
  21. data/spec/policy_machine_storage_adapters/in_memory_spec.rb +13 -0
  22. data/spec/policy_machine_storage_adapters/neography_spec.rb +42 -0
  23. data/spec/policy_machine_storage_adapters/template_spec.rb +6 -0
  24. data/spec/spec_helper.rb +24 -0
  25. data/spec/support/neography_helpers.rb +39 -0
  26. data/spec/support/policy_machine_helpers.rb +22 -0
  27. data/spec/support/shared_examples_policy_machine_spec.rb +697 -0
  28. data/spec/support/shared_examples_policy_machine_storage_adapter_spec.rb +278 -0
  29. data/spec/support/shared_examples_storage_adapter_public_methods.rb +20 -0
  30. data/spec/support/storage_adapter_helpers.rb +7 -0
  31. data/test/dummy/Rakefile +7 -0
  32. data/test/dummy/app/controllers/application_controller.rb +3 -0
  33. data/test/dummy/app/helpers/application_helper.rb +2 -0
  34. data/test/dummy/app/models/.gitkeep +0 -0
  35. data/test/dummy/config.ru +4 -0
  36. data/test/dummy/config/application.rb +65 -0
  37. data/test/dummy/config/boot.rb +10 -0
  38. data/test/dummy/config/database.yml +42 -0
  39. data/test/dummy/config/environment.rb +5 -0
  40. data/test/dummy/config/environments/development.rb +37 -0
  41. data/test/dummy/config/environments/test.rb +37 -0
  42. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  43. data/test/dummy/config/initializers/inflections.rb +15 -0
  44. data/test/dummy/config/initializers/mime_types.rb +5 -0
  45. data/test/dummy/config/initializers/secret_token.rb +7 -0
  46. data/test/dummy/config/initializers/session_store.rb +8 -0
  47. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  48. data/test/dummy/config/routes.rb +58 -0
  49. data/test/dummy/db/migrate/20131015214828_generate_policy_machine.rb +40 -0
  50. data/test/dummy/db/migrate/20131021221759_add_color_to_policy_element.rb +5 -0
  51. data/test/dummy/db/schema.rb +57 -0
  52. data/test/dummy/lib/assets/.gitkeep +0 -0
  53. data/test/dummy/script/rails +6 -0
  54. data/test/policy_machine_test.rb +7 -0
  55. data/test/test_helper.rb +15 -0
  56. metadata +270 -0
@@ -0,0 +1,22 @@
1
+ # This file contains helper methods to test assertions on policy machines
2
+
3
+ # Make sure each expected privilege has been returned
4
+ def assert_pm_privilege_expectations(actual_privileges, expected_privileges)
5
+ expected_privileges.each do |ep|
6
+ u_id = ep[0].unique_identifier
7
+ op_id = ep[1].unique_identifier
8
+ obj_id = ep[2].unique_identifier
9
+
10
+ found_actual_priv = actual_privileges.find do |priv|
11
+ priv[0].unique_identifier == u_id &&
12
+ priv[1].unique_identifier == op_id &&
13
+ priv[2].unique_identifier == obj_id
14
+ end
15
+
16
+ pp("expected to find #{[u_id, op_id, obj_id]}") if found_actual_priv.nil?
17
+
18
+ found_actual_priv.should_not be_nil
19
+ end
20
+
21
+ actual_privileges.count.should == expected_privileges.size
22
+ end
@@ -0,0 +1,697 @@
1
+ require 'spec_helper'
2
+ require_relative 'storage_adapter_helpers.rb'
3
+
4
+ policy_element_types = ::PolicyMachine::POLICY_ELEMENT_TYPES
5
+
6
+ shared_examples "a policy machine" do
7
+ describe 'instantiation' do
8
+ it 'has a default name' do
9
+ PolicyMachine.new.name.length.should_not == 0
10
+ end
11
+
12
+ it 'can be named' do
13
+ ['name', :name].each do |key|
14
+ PolicyMachine.new(key => 'my name').name.should == 'my name'
15
+ end
16
+ end
17
+
18
+ it 'sets the uuid if not specified' do
19
+ PolicyMachine.new.uuid.length.should_not == 0
20
+ end
21
+
22
+ it 'allows uuid to be specified' do
23
+ ['uuid', :uuid].each do |key|
24
+ PolicyMachine.new(key => 'my uuid').uuid.should == 'my uuid'
25
+ end
26
+ end
27
+
28
+ it 'raises when uuid is blank' do
29
+ ['', ' '].each do |blank_value|
30
+ expect{ PolicyMachine.new(:uuid => blank_value) }.
31
+ to raise_error(ArgumentError, 'uuid cannot be blank')
32
+ end
33
+ end
34
+
35
+ it 'defaults to in-memory storage adapter' do
36
+ PolicyMachine.new.policy_machine_storage_adapter.should be_a(::PolicyMachineStorageAdapter::InMemory)
37
+ end
38
+
39
+ it 'allows user to set storage adapter' do
40
+ ['storage_adapter', :storage_adapter].each do |key|
41
+ PolicyMachine.new(key => ::PolicyMachineStorageAdapter::Neography).policy_machine_storage_adapter.
42
+ should be_a(::PolicyMachineStorageAdapter::Neography)
43
+ end
44
+ end
45
+
46
+ end
47
+
48
+ describe 'Assignments' do
49
+ allowed_assignments = [
50
+ ['object', 'object'],
51
+ ['object', 'object_attribute'],
52
+ ['object_attribute', 'object_attribute'],
53
+ ['object_attribute', 'object'],
54
+ ['user', 'user_attribute'],
55
+ ['user_attribute', 'user_attribute'],
56
+ ['user_attribute', 'policy_class'],
57
+ ['object_attribute', 'policy_class']
58
+ ]
59
+
60
+ # Add an assignment e.g. o -> oa or oa -> oa or u -> ua or ua -> ua.
61
+ describe 'Adding' do
62
+ allowed_assignments.each do |allowed_assignment|
63
+ it "allows a #{allowed_assignment[0]} to be assigned a #{allowed_assignment[1]} (returns true)" do
64
+ pe0 = policy_machine.send("create_#{allowed_assignment[0]}", SecureRandom.uuid)
65
+ pe1 = policy_machine.send("create_#{allowed_assignment[1]}", SecureRandom.uuid)
66
+
67
+ policy_machine.add_assignment(pe0, pe1).should be_true
68
+ end
69
+ end
70
+
71
+ disallowed_assignments = policy_element_types.product(policy_element_types) - allowed_assignments
72
+ disallowed_assignments.each do |disallowed_assignment|
73
+ it "does not allow a #{disallowed_assignment[0]} to be assigned a #{disallowed_assignment[1]} (raises)" do
74
+ pe0 = policy_machine.send("create_#{disallowed_assignment[0]}", SecureRandom.uuid)
75
+ pe1 = policy_machine.send("create_#{disallowed_assignment[1]}", SecureRandom.uuid)
76
+
77
+ expect{ policy_machine.add_assignment(pe0, pe1) }.to raise_error(ArgumentError)
78
+ end
79
+ end
80
+
81
+ it 'raises when first argument is not a policy element' do
82
+ pe = policy_machine.create_user_attribute(SecureRandom.uuid)
83
+ expect{ policy_machine.add_assignment(1, pe) }.
84
+ to raise_error(ArgumentError, "arg must each be a kind of PolicyElement; got Fixnum instead")
85
+ end
86
+
87
+ it 'raises when first argument is not in policy machine' do
88
+ pm2 = PolicyMachine.new
89
+ pe0 = pm2.create_user_attribute(SecureRandom.uuid)
90
+ pe1 = policy_machine.create_user_attribute(SecureRandom.uuid)
91
+ expect{ policy_machine.add_assignment(pe0, pe1) }.
92
+ to raise_error(ArgumentError, "#{pe0.unique_identifier} is not in policy machine with uuid #{policy_machine.uuid}")
93
+ end
94
+
95
+ it 'raises when second argument is not a policy element' do
96
+ pe = policy_machine.create_user_attribute(SecureRandom.uuid)
97
+ expect{ policy_machine.add_assignment(pe, "hello") }.
98
+ to raise_error(ArgumentError, "arg must each be a kind of PolicyElement; got String instead")
99
+ end
100
+
101
+ it 'raises when second argument is not in policy machine' do
102
+ pm2 = PolicyMachine.new
103
+ pe0 = policy_machine.create_user_attribute(SecureRandom.uuid)
104
+ pe1 = pm2.create_user_attribute(SecureRandom.uuid)
105
+ expect{ policy_machine.add_assignment(pe0, pe1) }.
106
+ to raise_error(ArgumentError, "#{pe1.unique_identifier} is not in policy machine with uuid #{policy_machine.uuid}")
107
+ end
108
+ end
109
+
110
+ describe 'Removing' do
111
+ before do
112
+ @pe0 = policy_machine.create_user(SecureRandom.uuid)
113
+ @pe1 = policy_machine.create_user_attribute(SecureRandom.uuid)
114
+ end
115
+
116
+ it 'removes an existing assignment (returns true)' do
117
+ policy_machine.add_assignment(@pe0, @pe1)
118
+ policy_machine.remove_assignment(@pe0, @pe1).should be_true
119
+ end
120
+
121
+ it 'does not remove a non-existant assignment (returns false)' do
122
+ policy_machine.remove_assignment(@pe0, @pe1).should be_false
123
+ end
124
+
125
+ it 'raises when first argument is not a policy element' do
126
+ expect{ policy_machine.add_assignment(1, @pe1) }.
127
+ to raise_error(ArgumentError, "arg must each be a kind of PolicyElement; got Fixnum instead")
128
+ end
129
+
130
+ it 'raises when first argument is not in policy machine' do
131
+ pm2 = PolicyMachine.new
132
+ pe0 = pm2.create_user_attribute(SecureRandom.uuid)
133
+ pe1 = policy_machine.create_user_attribute(SecureRandom.uuid)
134
+ expect{ policy_machine.remove_assignment(pe0, pe1) }.
135
+ to raise_error(ArgumentError, "#{pe0.unique_identifier} is not in policy machine with uuid #{policy_machine.uuid}")
136
+ end
137
+
138
+ it 'raises when second argument is not a policy element' do
139
+ expect{ policy_machine.add_assignment(@pe0, "hello") }.
140
+ to raise_error(ArgumentError, "arg must each be a kind of PolicyElement; got String instead")
141
+ end
142
+
143
+ it 'raises when second argument is not in policy machine' do
144
+ pm2 = PolicyMachine.new
145
+ pe0 = policy_machine.create_user_attribute(SecureRandom.uuid)
146
+ pe1 = pm2.create_user_attribute(SecureRandom.uuid)
147
+ expect{ policy_machine.remove_assignment(pe0, pe1) }.
148
+ to raise_error(ArgumentError, "#{pe1.unique_identifier} is not in policy machine with uuid #{policy_machine.uuid}")
149
+ end
150
+ end
151
+ end
152
+
153
+ describe 'Associations' do
154
+ describe 'Adding' do
155
+ before do
156
+ @object_attribute = policy_machine.create_object_attribute('OA name')
157
+ @operation1 = policy_machine.create_operation('read')
158
+ @operation2 = policy_machine.create_operation('write')
159
+ @operation_set = Set.new [@operation1, @operation2]
160
+ @user_attribute = policy_machine.create_user_attribute('UA name')
161
+ end
162
+
163
+ it 'raises when first argument is not a PolicyElement' do
164
+ expect{ policy_machine.add_association("234", @operation_set, @object_attribute) }
165
+ .to raise_error(ArgumentError, "arg must each be a kind of PolicyElement; got String instead")
166
+ end
167
+
168
+ it 'raises when first argument is not in policy machine' do
169
+ pm2 = PolicyMachine.new
170
+ ua = pm2.create_user_attribute(SecureRandom.uuid)
171
+ expect{ policy_machine.add_association(ua, @operation_set, @object_attribute) }.
172
+ to raise_error(ArgumentError, "#{ua.unique_identifier} is not in policy machine with uuid #{policy_machine.uuid}")
173
+ end
174
+
175
+ it 'raises when third argument is not a PolicyElement' do
176
+ expect{ policy_machine.add_association(@user_attribute, @operation_set, 3) }
177
+ .to raise_error(ArgumentError, "arg must each be a kind of PolicyElement; got Fixnum instead")
178
+ end
179
+
180
+ it 'raises when third argument is not in policy machine' do
181
+ pm2 = PolicyMachine.new
182
+ oa = pm2.create_object_attribute(SecureRandom.uuid)
183
+ expect{ policy_machine.add_association(@user_attribute, @operation_set, oa) }.
184
+ to raise_error(ArgumentError, "#{oa.unique_identifier} is not in policy machine with uuid #{policy_machine.uuid}")
185
+ end
186
+
187
+ it 'allows an association to be made between an existing user_attribute, operation set and object attribute (returns true)' do
188
+ policy_machine.add_association(@user_attribute, @operation_set, @object_attribute).should be_true
189
+ end
190
+ end
191
+ end
192
+
193
+ describe 'All methods for policy elements' do
194
+ (PolicyMachine::POLICY_ELEMENT_TYPES - %w(policy_class)).each do |pe_type|
195
+ it "returns an array of all #{pe_type.to_s.pluralize}" do
196
+ pe = policy_machine.send("create_#{pe_type}", 'some name')
197
+ policy_machine.send(pe_type.to_s.pluralize).should == [pe]
198
+ end
199
+
200
+ it "scopes by policy machine when finding an array of #{pe_type.to_s.pluralize}" do
201
+ pe = policy_machine.send("create_#{pe_type}", 'some name')
202
+ other_pm = PolicyMachine.new
203
+ pe_in_other_machine = other_pm.send("create_#{pe_type}", 'some name')
204
+ policy_machine.send(pe_type.to_s.pluralize).should == [pe]
205
+ end
206
+ end
207
+
208
+ (PolicyMachine::POLICY_ELEMENT_TYPES - %w(user user_attribute object object_attribute operation)).each do |pe_type|
209
+ it "raises when calling #{pe_type.to_s.pluralize}" do
210
+ pe = policy_machine.send("create_#{pe_type}", 'some name')
211
+ expect{ policy_machine.send(pe_type.to_s.pluralize) }.
212
+ to raise_error(NoMethodError)
213
+ end
214
+ end
215
+ end
216
+
217
+ describe 'User Attributes' do
218
+
219
+ describe '#extra_attributes' do
220
+
221
+ it 'accepts and persists arbitrary extra attributes' do
222
+ @ua = policy_machine.create_user_attribute('ua1', foo: 'bar')
223
+ @ua.foo.should == 'bar'
224
+ policy_machine.user_attributes.last.foo.should == 'bar'
225
+ end
226
+
227
+ end
228
+
229
+ describe '#delete' do
230
+
231
+ it 'successfully deletes itself' do
232
+ @ua = policy_machine.create_user_attribute('ua1')
233
+ @ua.delete
234
+ policy_machine.user_attributes.should_not include(@ua)
235
+ end
236
+
237
+ end
238
+
239
+ end
240
+
241
+ describe 'Users' do
242
+
243
+ describe '#extra_attributes' do
244
+
245
+ it 'accepts and persists arbitrary extra attributes' do
246
+ @u = policy_machine.create_user('u1', foo: 'bar')
247
+ @u.foo.should == 'bar'
248
+ policy_machine.users.last.foo.should == 'bar'
249
+ end
250
+
251
+ it 'updates persisted extra attributes' do
252
+ @u = policy_machine.create_user('u1', foo: 'bar')
253
+ @u.update(foo: 'baz')
254
+ @u.foo.should == 'baz'
255
+ policy_machine.users.last.foo.should == 'baz'
256
+ end
257
+
258
+ it 'does not remove old attributes when adding new ones' do
259
+ @u = policy_machine.create_user('u1', foo: 'bar')
260
+ @u.update(deleted: true)
261
+ @u.foo.should == 'bar'
262
+ policy_machine.users.last.foo.should == 'bar'
263
+ end
264
+
265
+ it 'allows searching on any extra attribute keys' do
266
+ policy_machine.create_user('u1', foo: 'bar')
267
+ policy_machine.create_user('u2', foo: nil, attitude: 'sassy')
268
+ silence_warnings do
269
+ policy_machine.users(foo: 'bar').should be_one
270
+ policy_machine.users(foo: nil).should be_one
271
+ policy_machine.users(foo: 'baz').should be_none
272
+ policy_machine.users(foo: 'bar', attitude: 'sassy').should be_none
273
+ end
274
+ end
275
+
276
+ end
277
+
278
+ end
279
+
280
+ describe '#is_privilege?' do
281
+ before do
282
+ # Define policy elements
283
+ @u1 = policy_machine.create_user('u1')
284
+ @o1 = policy_machine.create_object('o1')
285
+ @o2 = policy_machine.create_object('o2')
286
+ @group1 = policy_machine.create_user_attribute('Group1')
287
+ @project1 = policy_machine.create_object_attribute('Project1')
288
+ @w = policy_machine.create_operation('write')
289
+
290
+ # Assignments
291
+ policy_machine.add_assignment(@u1, @group1)
292
+ policy_machine.add_assignment(@o1, @project1)
293
+
294
+ # Associations
295
+ policy_machine.add_association(@group1, Set.new([@w]), @project1)
296
+ end
297
+
298
+ it 'raises when the first argument is not a user or user_attribute' do
299
+ expect{ policy_machine.is_privilege?(@o1, @w, @o1)}.
300
+ to raise_error(ArgumentError, "user_attribute_pe must be a User or UserAttribute.")
301
+ end
302
+
303
+ it 'raises when the second argument is not an operation' do
304
+ expect{ policy_machine.is_privilege?(@u1, @u1, @o1)}.
305
+ to raise_error(ArgumentError, "operation must be an Operation.")
306
+ end
307
+
308
+ it 'raises when the third argument is not an object or object_attribute' do
309
+ expect{ policy_machine.is_privilege?(@u1, @w, @u1)}.
310
+ to raise_error(ArgumentError, "object_or_attribute must either be an Object or ObjectAttribute.")
311
+ end
312
+
313
+ it 'returns true if privilege can be inferred from user, operation and object' do
314
+ policy_machine.is_privilege?(@u1, @w, @o1).should be_true
315
+ end
316
+
317
+ it 'returns true if privilege can be inferred from user_attribute, operation and object' do
318
+ policy_machine.is_privilege?(@group1, @w, @o1).should be_true
319
+ end
320
+
321
+ it 'returns true if privilege can be inferred from user, operation and object_attribute' do
322
+ policy_machine.is_privilege?(@u1, @w, @project1).should be_true
323
+ end
324
+
325
+ it 'returns false if privilege cannot be inferred from arguments' do
326
+ policy_machine.is_privilege?(@u1, @w, @o2).should be_false
327
+ end
328
+
329
+ it 'does not infer privileges from deleted attributes' do
330
+ @group1.delete
331
+ policy_machine.is_privilege?(@u1, @w, @o1).should be_false
332
+ end
333
+
334
+ describe 'options' do
335
+ describe 'associations' do
336
+ it 'raises unless options[:associations] is an Array' do
337
+ expect{ policy_machine.is_privilege?(@u1, @w, @o2, :associations => 4) }.
338
+ to raise_error(ArgumentError, "expected options[:associations] to be an Array; got Fixnum")
339
+ end
340
+
341
+ it 'raises if options[:associations] is an empty array' do
342
+ expect{ policy_machine.is_privilege?(@u1, @w, @o2, :associations => []) }.
343
+ to raise_error(ArgumentError, "options[:associations] cannot be empty")
344
+ end
345
+
346
+ it 'raises unless every element of options[:associations] is a PM::Association' do
347
+ expect{ policy_machine.is_privilege?(@u1, @w, @o2, :associations => [4]) }.
348
+ to raise_error(ArgumentError, "expected each element of options[:associations] to be a PM::Association")
349
+ end
350
+
351
+ it 'raises if no element of options[:associations] contains the given operation' do
352
+ e = policy_machine.create_operation('execute')
353
+ policy_machine.add_association(@group1, Set.new([e]), @project1)
354
+ policy_machine.is_privilege?(@u1, @w, @o2, :associations => e.associations).should be_false
355
+ end
356
+
357
+ it 'accepts associations in options[:associations]' do
358
+ policy_machine.is_privilege?(@u1, @w, @o1, :associations => @w.associations).should be_true
359
+ end
360
+
361
+ it "accepts associations in options['associations']" do
362
+ policy_machine.is_privilege?(@u1, @w, @o1, 'associations' => @w.associations).should be_true
363
+ end
364
+
365
+ it 'returns true when given association is part of the granting of a given privilege' do
366
+ policy_machine.is_privilege?(@u1, @w, @o1, 'associations' => @w.associations).should be_true
367
+ end
368
+
369
+ it 'returns false when given association is not part of the granting of a given privilege' do
370
+ group2 = policy_machine.create_user_attribute('Group2')
371
+
372
+ policy_machine.add_association(group2, Set.new([@w]), @project1)
373
+ policy_machine.is_privilege?(@u1, @w, @o1, 'associations' => [@w.associations.last]).should be_false
374
+ end
375
+ end
376
+
377
+ describe 'in_user_attribute' do
378
+ it 'raises unless options[:in_user_attribute] is a PM::UserAttribute' do
379
+ expect{ policy_machine.is_privilege?(@u1, @w, @o2, :in_user_attribute => 4) }.
380
+ to raise_error(ArgumentError, 'expected options[:in_user_attribute] to be a PM::UserAttribute; got Fixnum')
381
+ end
382
+
383
+ it 'accepts in_user_attribute in options[:in_user_attribute]' do
384
+ policy_machine.is_privilege?(@u1, @w, @o1, :in_user_attribute => @group1).should be_true
385
+ end
386
+
387
+ it "accepts in_user_attribute in options['in_user_attribute']" do
388
+ policy_machine.is_privilege?(@group1, @w, @o1, 'in_user_attribute' => @group1).should be_true
389
+ end
390
+
391
+ it 'returns false if given user is not in given in_user_attribute' do
392
+ group2 = policy_machine.create_user_attribute('Group2')
393
+ policy_machine.is_privilege?(@u1, @w, @o1, 'in_user_attribute' => group2).should be_false
394
+ end
395
+ end
396
+
397
+ describe 'in_object_attribute' do
398
+ it 'raises unless options[:in_object_attribute] is a PM::ObjectAttribute' do
399
+ expect{ policy_machine.is_privilege?(@u1, @w, @o2, :in_object_attribute => 4) }.
400
+ to raise_error(ArgumentError, 'expected options[:in_object_attribute] to be a PM::ObjectAttribute; got Fixnum')
401
+ end
402
+
403
+ it 'accepts in_object_attribute in options[:in_object_attribute]' do
404
+ policy_machine.is_privilege?(@u1, @w, @o1, :in_object_attribute => @project1).should be_true
405
+ end
406
+
407
+ it "accepts in_object_attribute in options['in_object_attribute']" do
408
+ policy_machine.is_privilege?(@u1, @w, @project1, 'in_object_attribute' => @project1).should be_true
409
+ end
410
+
411
+ it 'returns false if given user is not in given in_object_attribute' do
412
+ project2 = policy_machine.create_object_attribute('Project2')
413
+ policy_machine.is_privilege?(@u1, @w, @o1, 'in_object_attribute' => project2).should be_false
414
+ end
415
+
416
+ it 'accepts both in_user_attribute and in_object_attribute' do
417
+ project2 = policy_machine.create_object_attribute('Project2')
418
+ policy_machine.is_privilege?(@u1, @w, @o1, 'in_user_attribute' => @group1, 'in_object_attribute' => project2).
419
+ should be_false
420
+ end
421
+
422
+ end
423
+
424
+ end
425
+ end
426
+
427
+ describe '#list_user_attributes' do
428
+ before do
429
+ # Define policy elements
430
+ @u1 = policy_machine.create_user('u1')
431
+ @u2 = policy_machine.create_user('u2')
432
+ @group1 = policy_machine.create_user_attribute('Group1')
433
+ @group2 = policy_machine.create_user_attribute('Group2')
434
+ @subgroup1a = policy_machine.create_user_attribute('Subgroup1a')
435
+
436
+ # Assignments
437
+ policy_machine.add_assignment(@u1, @subgroup1a)
438
+ policy_machine.add_assignment(@subgroup1a, @group1)
439
+ policy_machine.add_assignment(@u2, @group2)
440
+ end
441
+
442
+ it 'lists the user attributes for a user' do
443
+ policy_machine.list_user_attributes(@u2).should == [@group2]
444
+ end
445
+
446
+ it 'searches multiple hops deep' do
447
+ policy_machine.list_user_attributes(@u1).should =~ [@group1, @subgroup1a]
448
+ end
449
+
450
+ end
451
+
452
+ describe '#transaction' do
453
+ it 'executes the block' do
454
+ if_implements(policy_machine.policy_machine_storage_adapter, :transaction){}
455
+ policy_machine.transaction do
456
+ @oa = policy_machine.create_object_attribute('some_oa')
457
+ end
458
+ policy_machine.object_attributes.should == [@oa]
459
+ end
460
+
461
+ it 'rolls back the block on error' do
462
+ if_implements(policy_machine.policy_machine_storage_adapter, :transaction){}
463
+ @oa1 = policy_machine.create_object_attribute('some_oa')
464
+ expect do
465
+ policy_machine.transaction do
466
+ @oa2 = policy_machine.create_object_attribute('some_other_oa')
467
+ policy_machine.add_assignment(@oa2, :invalid_policy_class)
468
+ end
469
+ end.to raise_error(ArgumentError)
470
+ policy_machine.object_attributes.should == [@oa1]
471
+ end
472
+ end
473
+
474
+ describe '#privileges' do
475
+
476
+ # This PM is taken from the policy machine spec, Figure 4. (pg. 19)
477
+ describe 'Simple Example: Figure 4. (pg. 19)' do
478
+ before do
479
+ # Users
480
+ @u1 = policy_machine.create_user('u1')
481
+ @u2 = policy_machine.create_user('u2')
482
+ @u3 = policy_machine.create_user('u3')
483
+
484
+ # Objects
485
+ @o1 = policy_machine.create_object('o1')
486
+ @o2 = policy_machine.create_object('o2')
487
+ @o3 = policy_machine.create_object('o3')
488
+
489
+ # User Attributes
490
+ @group1 = policy_machine.create_user_attribute('Group1')
491
+ @group2 = policy_machine.create_user_attribute('Group2')
492
+ @division = policy_machine.create_user_attribute('Division')
493
+
494
+ # Object Attributes
495
+ @project1 = policy_machine.create_object_attribute('Project1')
496
+ @project2 = policy_machine.create_object_attribute('Project2')
497
+ @projects = policy_machine.create_object_attribute('Projects')
498
+
499
+ # Operations
500
+ @r = policy_machine.create_operation('read')
501
+ @w = policy_machine.create_operation('write')
502
+
503
+ # Policy Classes
504
+ @ou = policy_machine.create_policy_class("OU")
505
+
506
+ # Assignments
507
+ policy_machine.add_assignment(@u1, @group1)
508
+ policy_machine.add_assignment(@u2, @group2)
509
+ policy_machine.add_assignment(@u3, @division)
510
+ policy_machine.add_assignment(@group1, @division)
511
+ policy_machine.add_assignment(@group2, @division)
512
+ policy_machine.add_assignment(@o1, @project1)
513
+ policy_machine.add_assignment(@o2, @project1)
514
+ policy_machine.add_assignment(@o3, @project2)
515
+ policy_machine.add_assignment(@project1, @projects)
516
+ policy_machine.add_assignment(@project2, @projects)
517
+ policy_machine.add_assignment(@division, @ou)
518
+ policy_machine.add_assignment(@projects, @ou)
519
+
520
+ # Associations
521
+ policy_machine.add_association(@group1, Set.new([@w]), @project1)
522
+ policy_machine.add_association(@group2, Set.new([@w]), @project2)
523
+ policy_machine.add_association(@division, Set.new([@r]), @projects)
524
+ end
525
+
526
+ it 'returns all and only these privileges encoded by the policy machine' do
527
+ expected_privileges = [
528
+ [@u1, @w, @o1], [@u1, @w, @o2], [@u1, @r, @o1], [@u1, @r, @o2], [@u1, @r, @o3],
529
+ [@u2, @w, @o3], [@u2, @r, @o1], [@u2, @r, @o2], [@u2, @r, @o3],
530
+ [@u3, @r, @o1], [@u3, @r, @o2], [@u3, @r, @o3]
531
+ ]
532
+
533
+ assert_pm_privilege_expectations(policy_machine.privileges, expected_privileges)
534
+ end
535
+ end
536
+ end
537
+
538
+ describe 'The Mail System: Figure 8. (pg. 43)' do
539
+ before do
540
+ # Users
541
+ @u2 = policy_machine.create_user('u2')
542
+
543
+ # Objects
544
+ @in_u2 = policy_machine.create_object('In u2')
545
+ @out_u2 = policy_machine.create_object('Out u2')
546
+ @draft_u2 = policy_machine.create_object('Draft u2')
547
+ @trash_u2 = policy_machine.create_object('Trash u2')
548
+
549
+ # User Attributes
550
+ @id_u2 = policy_machine.create_user_attribute('ID u2')
551
+ @users = policy_machine.create_user_attribute('Users')
552
+
553
+ # Object Attributes
554
+ @inboxes = policy_machine.create_object_attribute('Inboxes')
555
+ @outboxes = policy_machine.create_object_attribute('Outboxes')
556
+ @other_u2 = policy_machine.create_object_attribute('Other u2')
557
+ @objects = policy_machine.create_object_attribute('Objects')
558
+
559
+ # Operations
560
+ @r = policy_machine.create_operation('read')
561
+ @w = policy_machine.create_operation('write')
562
+
563
+ # Policy Classes
564
+ @mail_system = policy_machine.create_policy_class('Mail System')
565
+
566
+ # Assignments
567
+ policy_machine.add_assignment(@u2, @id_u2)
568
+ policy_machine.add_assignment(@id_u2, @users)
569
+ policy_machine.add_assignment(@in_u2, @inboxes)
570
+ policy_machine.add_assignment(@out_u2, @outboxes)
571
+ policy_machine.add_assignment(@draft_u2, @other_u2)
572
+ policy_machine.add_assignment(@trash_u2, @other_u2)
573
+ policy_machine.add_assignment(@inboxes, @objects)
574
+ policy_machine.add_assignment(@outboxes, @objects)
575
+ policy_machine.add_assignment(@users, @mail_system)
576
+ policy_machine.add_assignment(@objects, @mail_system)
577
+
578
+ # Associations
579
+ policy_machine.add_association(@id_u2, Set.new([@r]), @in_u2)
580
+ policy_machine.add_association(@id_u2, Set.new([@r, @w]), @out_u2)
581
+ policy_machine.add_association(@id_u2, Set.new([@w]), @inboxes)
582
+ policy_machine.add_association(@id_u2, Set.new([@r, @w]), @other_u2)
583
+ end
584
+
585
+ it 'returns all and only these privileges encoded by the policy machine' do
586
+ expected_privileges = [
587
+ [@u2, @r, @in_u2], [@u2, @r, @out_u2], [@u2, @w, @out_u2], [@u2, @r, @draft_u2],
588
+ [@u2, @w, @draft_u2], [@u2, @r, @trash_u2], [@u2, @w, @trash_u2]
589
+ ]
590
+
591
+ # TODO: remove the expected privilege below once prohibitions are put in place.
592
+ # In the example, @u2 is prohibited from writing to @in_u2
593
+ expected_privileges << [@u2, @w, @in_u2]
594
+
595
+ assert_pm_privilege_expectations(policy_machine.privileges, expected_privileges)
596
+ end
597
+ end
598
+
599
+ describe 'The DAC Operating System: Figure 11. (pg. 47)' do
600
+ before do
601
+ # Users
602
+ @u1 = policy_machine.create_user('u1')
603
+ @u2 = policy_machine.create_user('u2')
604
+
605
+ # Objects
606
+ @o11 = policy_machine.create_object('o11')
607
+ @o12 = policy_machine.create_object('o12')
608
+
609
+ # User Attributes
610
+ @id_u1 = policy_machine.create_user_attribute('ID u1')
611
+ @id_u2 = policy_machine.create_user_attribute('ID u2')
612
+ @users = policy_machine.create_user_attribute('Users')
613
+
614
+ # Object Attributes
615
+ @home_u1 = policy_machine.create_object_attribute('Home u1')
616
+ @home_u2 = policy_machine.create_object_attribute('Home u2')
617
+ @objects = policy_machine.create_object_attribute('Objects')
618
+
619
+ # Operations
620
+ @r = policy_machine.create_operation('read')
621
+ @w = policy_machine.create_operation('write')
622
+ @e = policy_machine.create_operation('execute')
623
+
624
+ # Policy Classes
625
+ @dac = policy_machine.create_policy_class('DAC')
626
+
627
+ # Assignments
628
+ policy_machine.add_assignment(@u1, @id_u1)
629
+ policy_machine.add_assignment(@u2, @id_u2)
630
+ policy_machine.add_assignment(@id_u1, @users)
631
+ policy_machine.add_assignment(@id_u2, @users)
632
+ policy_machine.add_assignment(@o11, @home_u1)
633
+ policy_machine.add_assignment(@o12, @home_u1)
634
+ policy_machine.add_assignment(@home_u1, @objects)
635
+ policy_machine.add_assignment(@home_u2, @objects)
636
+ policy_machine.add_assignment(@users, @dac)
637
+ policy_machine.add_assignment(@objects, @dac)
638
+
639
+ # Associations
640
+ policy_machine.add_association(@id_u1, Set.new([@r, @w, @e]), @home_u1)
641
+ policy_machine.add_association(@id_u2, Set.new([@r, @w, @e]), @home_u2)
642
+ end
643
+
644
+ it 'returns all and only these privileges encoded by the policy machine' do
645
+ expected_privileges = [
646
+ [@u1, @r, @o11], [@u1, @w, @o11], [@u1, @e, @o11],
647
+ [@u1, @r, @o12], [@u1, @w, @o12], [@u1, @e, @o12],
648
+ ]
649
+
650
+ assert_pm_privilege_expectations(policy_machine.privileges, expected_privileges)
651
+ end
652
+ end
653
+
654
+ describe 'simple multiple policy class machine' do
655
+ before do
656
+ # Users
657
+ @u1 = policy_machine.create_user('u1')
658
+
659
+ # Objects
660
+ @o1 = policy_machine.create_object('o1')
661
+
662
+ # User Attributes
663
+ @ua = policy_machine.create_user_attribute('UA')
664
+
665
+ # Object Attributes
666
+ @oa1 = policy_machine.create_object_attribute('OA1')
667
+ @oa2 = policy_machine.create_object_attribute('OA2')
668
+
669
+ # Operations
670
+ @r = policy_machine.create_operation('read')
671
+ @w = policy_machine.create_operation('write')
672
+
673
+ # Policy Classes
674
+ @pc1 = policy_machine.create_policy_class('pc1')
675
+ @pc2 = policy_machine.create_policy_class('pc2')
676
+
677
+ # Assignments
678
+ policy_machine.add_assignment(@u1, @ua)
679
+ policy_machine.add_assignment(@o1, @oa1)
680
+ policy_machine.add_assignment(@o1, @oa2)
681
+ policy_machine.add_assignment(@oa1, @pc1)
682
+ policy_machine.add_assignment(@oa2, @pc2)
683
+
684
+ # Associations
685
+ policy_machine.add_association(@ua, Set.new([@r]), @oa1)
686
+ policy_machine.add_association(@ua, Set.new([@r, @w]), @oa2)
687
+ end
688
+
689
+ it 'returns all and only these privileges encoded by the policy machine' do
690
+ expected_privileges = [
691
+ [@u1, @r, @o1]
692
+ ]
693
+ assert_pm_privilege_expectations(policy_machine.privileges, expected_privileges)
694
+ end
695
+ end
696
+
697
+ end