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.
- data/CONTRIBUTING.md +35 -0
- data/Gemfile +2 -0
- data/MIT-LICENSE +20 -0
- data/README.md +98 -0
- data/lib/generators/policy_machine/policy_machine_generator.rb +13 -0
- data/lib/generators/policy_machine/templates/migration.rb +40 -0
- data/lib/policy_machine.rb +236 -0
- data/lib/policy_machine/association.rb +73 -0
- data/lib/policy_machine/policy_element.rb +269 -0
- data/lib/policy_machine/version.rb +3 -0
- data/lib/policy_machine_storage_adapters/active_record.rb +306 -0
- data/lib/policy_machine_storage_adapters/in_memory.rb +266 -0
- data/lib/policy_machine_storage_adapters/neography.rb +236 -0
- data/lib/policy_machine_storage_adapters/template.rb +169 -0
- data/lib/tasks/policy_machine_tasks.rake +4 -0
- data/policy_machine.gemspec +23 -0
- data/spec/policy_machine/association_spec.rb +61 -0
- data/spec/policy_machine/policy_element_spec.rb +20 -0
- data/spec/policy_machine_spec.rb +7 -0
- data/spec/policy_machine_storage_adapters/active_record_spec.rb +54 -0
- data/spec/policy_machine_storage_adapters/in_memory_spec.rb +13 -0
- data/spec/policy_machine_storage_adapters/neography_spec.rb +42 -0
- data/spec/policy_machine_storage_adapters/template_spec.rb +6 -0
- data/spec/spec_helper.rb +24 -0
- data/spec/support/neography_helpers.rb +39 -0
- data/spec/support/policy_machine_helpers.rb +22 -0
- data/spec/support/shared_examples_policy_machine_spec.rb +697 -0
- data/spec/support/shared_examples_policy_machine_storage_adapter_spec.rb +278 -0
- data/spec/support/shared_examples_storage_adapter_public_methods.rb +20 -0
- data/spec/support/storage_adapter_helpers.rb +7 -0
- data/test/dummy/Rakefile +7 -0
- data/test/dummy/app/controllers/application_controller.rb +3 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/models/.gitkeep +0 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +65 -0
- data/test/dummy/config/boot.rb +10 -0
- data/test/dummy/config/database.yml +42 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +37 -0
- data/test/dummy/config/environments/test.rb +37 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/inflections.rb +15 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +7 -0
- data/test/dummy/config/initializers/session_store.rb +8 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/routes.rb +58 -0
- data/test/dummy/db/migrate/20131015214828_generate_policy_machine.rb +40 -0
- data/test/dummy/db/migrate/20131021221759_add_color_to_policy_element.rb +5 -0
- data/test/dummy/db/schema.rb +57 -0
- data/test/dummy/lib/assets/.gitkeep +0 -0
- data/test/dummy/script/rails +6 -0
- data/test/policy_machine_test.rb +7 -0
- data/test/test_helper.rb +15 -0
- metadata +270 -0
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# Contributing
|
2
|
+
|
3
|
+
If you find a bug:
|
4
|
+
|
5
|
+
* Check the "GitHub issue tracker" to see if anyone else has reported issue.
|
6
|
+
* If you don't see anything, create an issue with information on how to reproduce it.
|
7
|
+
|
8
|
+
If you want to contribute an enhancement or a fix:
|
9
|
+
|
10
|
+
* Fork the project on GitHub.
|
11
|
+
* bundle install
|
12
|
+
* Make your changes with tests.
|
13
|
+
* Run all automated tests to see if your change broke anything or is providing anything less than 100% code coverage (see below).
|
14
|
+
* Commit the changes without making changes to any other files that aren't related to your enhancement or fix.
|
15
|
+
* Send a pull request.
|
16
|
+
|
17
|
+
## Running Automated Tests
|
18
|
+
|
19
|
+
Run all rspec with:
|
20
|
+
|
21
|
+
```
|
22
|
+
[bundle exec] rspec
|
23
|
+
```
|
24
|
+
|
25
|
+
Simplecov code coverage is generated automatically. Any changes you make to this repository should
|
26
|
+
ensure that code coverage remains at 100%. **No pull request will be merged unless proof is given
|
27
|
+
(i.e. a PR comment) that code coverage remains at 100% in the PR branch.**
|
28
|
+
|
29
|
+
## Making Your Own Policy Machine Storage Adapter
|
30
|
+
|
31
|
+
A template storage adapter is provided in `lib/policy_machine_storage_adapters/template.rb`. Copy this
|
32
|
+
storage adapter as a starting point for making your own; implement all methods contained therein.
|
33
|
+
|
34
|
+
To test your storage adapter, adapt the tests in either `spec/policy_machine_storage_adapters/in_memory_spec.rb` or
|
35
|
+
`spec/policy_machine_storage_adapters/neography_spec.rb`.
|
data/Gemfile
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2013 YOURNAME
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
policy_machine
|
2
|
+
==============
|
3
|
+
|
4
|
+
A ruby implementation of the Policy Machine authorization formalism. You can find the NIST specification for Policy
|
5
|
+
Machines [here](http://csrc.nist.gov/pm/documents/pm_report-rev-x_final.pdf).
|
6
|
+
|
7
|
+
Note that prohibitions, obligations and multiple policy classes have not yet been implemented. These aspects of the Policy Machine
|
8
|
+
will be included in future versions of this gem.
|
9
|
+
|
10
|
+
# Installation
|
11
|
+
|
12
|
+
Add the following to your Gemfile:
|
13
|
+
```
|
14
|
+
gem 'policy_machine'
|
15
|
+
```
|
16
|
+
|
17
|
+
# Usage
|
18
|
+
```
|
19
|
+
require 'policy_machine'
|
20
|
+
require 'policy_machine_storage_adapters/in_memory'
|
21
|
+
|
22
|
+
policy_machine = PolicyMachine.new('my_policy_machine', ::PolicyMachineStorageAdapter::InMemory)
|
23
|
+
|
24
|
+
# This PM is taken from the policy machine spec at http://csrc.nist.gov/pm/documents/pm_report-rev-x_final.pdf,
|
25
|
+
# Figure 4. (pg. 19)
|
26
|
+
|
27
|
+
# Users
|
28
|
+
u1 = policy_machine.create_user('u1')
|
29
|
+
u2 = policy_machine.create_user('u2')
|
30
|
+
u3 = policy_machine.create_user('u3')
|
31
|
+
|
32
|
+
# Objects
|
33
|
+
o1 = policy_machine.create_object('o1')
|
34
|
+
o2 = policy_machine.create_object('o2')
|
35
|
+
o3 = policy_machine.create_object('o3')
|
36
|
+
|
37
|
+
# User Attributes
|
38
|
+
group1 = policy_machine.create_user_attribute('Group1')
|
39
|
+
group2 = policy_machine.create_user_attribute('Group2')
|
40
|
+
division = policy_machine.create_user_attribute('Division')
|
41
|
+
|
42
|
+
# Object Attributes
|
43
|
+
project1 = policy_machine.create_object_attribute('Project1')
|
44
|
+
project2 = policy_machine.create_object_attribute('Project2')
|
45
|
+
projects = policy_machine.create_object_attribute('Projects')
|
46
|
+
|
47
|
+
# Operations
|
48
|
+
r = policy_machine.create_operation('read')
|
49
|
+
w = policy_machine.create_operation('write')
|
50
|
+
|
51
|
+
# Assignments
|
52
|
+
policy_machine.add_assignment(u1, group1)
|
53
|
+
policy_machine.add_assignment(u2, group2)
|
54
|
+
policy_machine.add_assignment(u3, division)
|
55
|
+
policy_machine.add_assignment(group1, division)
|
56
|
+
policy_machine.add_assignment(group2, division)
|
57
|
+
policy_machine.add_assignment(o1, project1)
|
58
|
+
policy_machine.add_assignment(o2, project1)
|
59
|
+
policy_machine.add_assignment(o3, project2)
|
60
|
+
policy_machine.add_assignment(project1, projects)
|
61
|
+
policy_machine.add_assignment(project2, projects)
|
62
|
+
|
63
|
+
# Associations
|
64
|
+
policy_machine.add_association(group1, Set.new([w]), project1)
|
65
|
+
policy_machine.add_association(group2, Set.new([w]), project2)
|
66
|
+
policy_machine.add_association(division, Set.new([r]), projects)
|
67
|
+
|
68
|
+
# List all privileges encoded in the policy machine
|
69
|
+
policy_machine.privileges
|
70
|
+
|
71
|
+
# Returns true
|
72
|
+
policy_machine.is_privilege?(u1, w, o1)
|
73
|
+
|
74
|
+
# Returns false
|
75
|
+
policy_machine.is_privilege?(u3, w, o3)
|
76
|
+
```
|
77
|
+
|
78
|
+
# Storage Adapters
|
79
|
+
|
80
|
+
Note that the Policy Machine in the above example stores policy elements in memory. Other persistent
|
81
|
+
storage options are available in `lib/policy_machine_storage_adapters`.
|
82
|
+
|
83
|
+
*Neography*
|
84
|
+
|
85
|
+
The Neography storage adapter uses the neo4j graph database, which must be installed separately,
|
86
|
+
and `gem 'neography'`. This should not be used in production since the interface is slow.
|
87
|
+
|
88
|
+
*ActiveRecord*
|
89
|
+
|
90
|
+
The ActiveRecord storage adapter talks to your existing MySQL database via your preconfigured
|
91
|
+
ActiveRecord. You'll need to run `rails generate policy_machine migration` to add the necessary
|
92
|
+
tables to your database.
|
93
|
+
|
94
|
+
If you'd like to make your own storage adapter, see See [CONTRIBUTING.md](CONTRIBUTING.md).
|
95
|
+
|
96
|
+
# Contributing
|
97
|
+
|
98
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'rails/generators/active_record/migration/migration_generator'
|
2
|
+
|
3
|
+
class PolicyMachineGenerator < ::ActiveRecord::Generators::MigrationGenerator
|
4
|
+
desc "Create a migration to store Policy Machine elements in your database"
|
5
|
+
|
6
|
+
source_root File.expand_path('../templates', __FILE__)
|
7
|
+
|
8
|
+
def initialize(*args)
|
9
|
+
args[0] = ['generate_policy_machine']
|
10
|
+
super(*args)
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
class GeneratePolicyMachine < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
|
4
|
+
create_table :policy_elements do |t|
|
5
|
+
t.string :unique_identifier, null: false
|
6
|
+
t.string :policy_machine_uuid
|
7
|
+
t.string :type, null: false
|
8
|
+
t.text :extra_attributes
|
9
|
+
end
|
10
|
+
add_index :policy_elements, [:unique_identifier], unique: true
|
11
|
+
add_index :policy_elements, [:type]
|
12
|
+
|
13
|
+
create_table :policy_element_associations do |t|
|
14
|
+
t.integer :user_attribute_id, null: false
|
15
|
+
t.integer :object_attribute_id, null: false
|
16
|
+
end
|
17
|
+
add_index :policy_element_associations, [:user_attribute_id, :object_attribute_id], name: 'index_pe_assocs_on_ua_and_oa'
|
18
|
+
|
19
|
+
create_table :transitive_closure, id: false do |t|
|
20
|
+
t.integer :ancestor_id, null: false
|
21
|
+
t.integer :descendant_id, null: false
|
22
|
+
end
|
23
|
+
add_index :transitive_closure, [:ancestor_id, :descendant_id], unique: true
|
24
|
+
add_index :transitive_closure, [:descendant_id]
|
25
|
+
|
26
|
+
create_table :assignments do |t|
|
27
|
+
t.integer :parent_id, null: false
|
28
|
+
t.integer :child_id, null: false
|
29
|
+
end
|
30
|
+
add_index :assignments, [:parent_id, :child_id], unique: true
|
31
|
+
add_index :assignments, [:child_id]
|
32
|
+
|
33
|
+
create_table :operations_policy_element_associations, id: false do |t|
|
34
|
+
t.integer :policy_element_association_id, null: false
|
35
|
+
t.integer :operation_id, null: false
|
36
|
+
end
|
37
|
+
add_index :operations_policy_element_associations, [:policy_element_association_id, :operation_id], unique: true, name: 'index_pe_assoc_os_on_assoc_and_o'
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,236 @@
|
|
1
|
+
require 'policy_machine/policy_element'
|
2
|
+
require 'policy_machine/association'
|
3
|
+
require 'securerandom'
|
4
|
+
require 'active_support/inflector'
|
5
|
+
require 'set'
|
6
|
+
|
7
|
+
# require all adapters
|
8
|
+
Dir.glob(File.dirname(File.absolute_path(__FILE__)) + '/policy_machine_storage_adapters/*.rb').each{ |f| require f }
|
9
|
+
|
10
|
+
class PolicyMachine
|
11
|
+
POLICY_ELEMENT_TYPES = %w(user user_attribute object object_attribute operation policy_class)
|
12
|
+
|
13
|
+
attr_accessor :name
|
14
|
+
attr_reader :uuid
|
15
|
+
attr_reader :policy_machine_storage_adapter
|
16
|
+
|
17
|
+
def initialize(options = {})
|
18
|
+
@name = (options[:name] || options['name'] || 'default_policy_machine').to_s.strip
|
19
|
+
@uuid = (options[:uuid] || options['uuid'] || SecureRandom.uuid).to_s.strip
|
20
|
+
policy_machine_storage_adapter_class = options[:storage_adapter] || options['storage_adapter'] || ::PolicyMachineStorageAdapter::InMemory
|
21
|
+
@policy_machine_storage_adapter = policy_machine_storage_adapter_class.new
|
22
|
+
|
23
|
+
raise(ArgumentError, "uuid cannot be blank") if @uuid.empty?
|
24
|
+
end
|
25
|
+
|
26
|
+
##
|
27
|
+
# Persist an assignment in this policy machine.
|
28
|
+
# An assignment is a binary relation between two existing policy elements.
|
29
|
+
# Some policy element types cannot be assigned to other types. See the NIST
|
30
|
+
# spec for details.
|
31
|
+
#
|
32
|
+
def add_assignment(src_policy_element, dst_policy_element)
|
33
|
+
assert_policy_element_in_machine(src_policy_element)
|
34
|
+
assert_policy_element_in_machine(dst_policy_element)
|
35
|
+
|
36
|
+
src_policy_element.assign_to(dst_policy_element)
|
37
|
+
end
|
38
|
+
|
39
|
+
##
|
40
|
+
# Remove an assignment in this policy machine.
|
41
|
+
#
|
42
|
+
def remove_assignment(src_policy_element, dst_policy_element)
|
43
|
+
assert_policy_element_in_machine(src_policy_element)
|
44
|
+
assert_policy_element_in_machine(dst_policy_element)
|
45
|
+
|
46
|
+
src_policy_element.unassign(dst_policy_element)
|
47
|
+
end
|
48
|
+
|
49
|
+
##
|
50
|
+
# Add an association between a user_attribute, an operation_set and an object_attribute
|
51
|
+
# in this policy machine.
|
52
|
+
#
|
53
|
+
def add_association(user_attribute_pe, operation_set, object_attribute_pe)
|
54
|
+
assert_policy_element_in_machine(user_attribute_pe)
|
55
|
+
operation_set.each{ |op| assert_policy_element_in_machine(op) }
|
56
|
+
assert_policy_element_in_machine(object_attribute_pe)
|
57
|
+
|
58
|
+
PM::Association.create(user_attribute_pe, operation_set, object_attribute_pe, @uuid, @policy_machine_storage_adapter)
|
59
|
+
end
|
60
|
+
|
61
|
+
##
|
62
|
+
# Can we derive a privilege of the form (u, op, o) from this policy machine?
|
63
|
+
# user_or_attribute is a user or user_attribute.
|
64
|
+
# operation is an operation.
|
65
|
+
# object_or_attribute is an object or object attribute.
|
66
|
+
#
|
67
|
+
# TODO: add option to ignore policy classes to allow consumer to speed up this method.
|
68
|
+
def is_privilege?(user_or_attribute, operation, object_or_attribute, options = {})
|
69
|
+
unless user_or_attribute.is_a?(PM::User) || user_or_attribute.is_a?(PM::UserAttribute)
|
70
|
+
raise(ArgumentError, "user_attribute_pe must be a User or UserAttribute.")
|
71
|
+
end
|
72
|
+
|
73
|
+
unless operation.is_a?(PM::Operation)
|
74
|
+
raise(ArgumentError, "operation must be an Operation.")
|
75
|
+
end
|
76
|
+
|
77
|
+
unless object_or_attribute.is_a?(PM::Object) || object_or_attribute.is_a?(PM::ObjectAttribute)
|
78
|
+
raise(ArgumentError, "object_or_attribute must either be an Object or ObjectAttribute.")
|
79
|
+
end
|
80
|
+
|
81
|
+
# Try to get associations to check from options
|
82
|
+
associations = options[:associations] || options['associations']
|
83
|
+
if associations
|
84
|
+
raise(ArgumentError, "expected options[:associations] to be an Array; got #{associations.class}") unless associations.is_a?(Array)
|
85
|
+
raise(ArgumentError, "options[:associations] cannot be empty") if associations.empty?
|
86
|
+
raise(ArgumentError, "expected each element of options[:associations] to be a PM::Association") unless associations.all?{|a| a.is_a?(PM::Association)}
|
87
|
+
|
88
|
+
associations.keep_if{ |assoc| assoc.includes_operation?(operation) }
|
89
|
+
return false if associations.empty?
|
90
|
+
else
|
91
|
+
associations = operation.associations
|
92
|
+
end
|
93
|
+
|
94
|
+
# Is a privilege iff options[:in_user_attribute] is involved (given options[:in_user_attribute] is not nil)
|
95
|
+
in_user_attribute = options[:in_user_attribute] || options['in_user_attribute']
|
96
|
+
if in_user_attribute
|
97
|
+
unless in_user_attribute.is_a?(PM::UserAttribute)
|
98
|
+
raise(ArgumentError, "expected options[:in_user_attribute] to be a PM::UserAttribute; got #{in_user_attribute.class}")
|
99
|
+
end
|
100
|
+
if user_or_attribute.connected?(in_user_attribute)
|
101
|
+
user_or_attribute = in_user_attribute
|
102
|
+
else
|
103
|
+
return false
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# Is a privilege iff options[:in_object_attribute] is involved (given options[:in_object_attribute] is not nil)
|
108
|
+
in_object_attribute = options[:in_object_attribute] || options['in_object_attribute']
|
109
|
+
if in_object_attribute
|
110
|
+
unless in_object_attribute.is_a?(PM::ObjectAttribute)
|
111
|
+
raise(ArgumentError, "expected options[:in_object_attribute] to be a PM::ObjectAttribute; got #{in_object_attribute.class}")
|
112
|
+
end
|
113
|
+
if object_or_attribute.connected?(in_object_attribute)
|
114
|
+
object_or_attribute = in_object_attribute
|
115
|
+
else
|
116
|
+
return false
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
policy_classes_containing_object = object_or_attribute.policy_classes
|
121
|
+
if policy_classes_containing_object.empty?
|
122
|
+
is_privilege_single_policy_class(user_or_attribute, object_or_attribute, associations)
|
123
|
+
else
|
124
|
+
is_privilege_multiple_policy_classes(user_or_attribute, object_or_attribute, associations, policy_classes_containing_object)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
##
|
129
|
+
# Returns an array of all privileges encoded in this
|
130
|
+
# policy machine. Each privilege is of the form:
|
131
|
+
# [PM::User, PM::Operation, PM::Object]
|
132
|
+
#
|
133
|
+
# TODO: might make privilege a class of its own
|
134
|
+
def privileges
|
135
|
+
privileges = []
|
136
|
+
|
137
|
+
users.each do |user|
|
138
|
+
operations.each do |operation|
|
139
|
+
objects.each do |object|
|
140
|
+
if is_privilege?(user, operation, object)
|
141
|
+
privileges << [user, operation, object]
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
privileges
|
148
|
+
end
|
149
|
+
|
150
|
+
##
|
151
|
+
# Returns an array of all user_attributes a PM::User is assigned to,
|
152
|
+
# directly or indirectly.
|
153
|
+
def list_user_attributes(user)
|
154
|
+
unless user.is_a?(PM::User)
|
155
|
+
raise(ArgumentError, "Expected a PM::User, got a #{user.class}")
|
156
|
+
end
|
157
|
+
assert_policy_element_in_machine(user)
|
158
|
+
user.user_attributes(@policy_machine_storage_adapter)
|
159
|
+
end
|
160
|
+
|
161
|
+
POLICY_ELEMENT_TYPES.each do |pe_type|
|
162
|
+
pm_class = "PM::#{pe_type.camelize}".constantize
|
163
|
+
|
164
|
+
##
|
165
|
+
# Define a create method for each policy element type, as in create_user
|
166
|
+
# Each method takes one argument, the unique_identifier of the policy element.
|
167
|
+
#
|
168
|
+
define_method("create_#{pe_type}") do |unique_identifier, extra_attributes = {}|
|
169
|
+
# when creating a policy element, we provide a unique_identifier, the uuid of this policy machine
|
170
|
+
# and a policy machine storage adapter to allow us to persist the policy element.
|
171
|
+
pm_class.send(:create, unique_identifier, @uuid, @policy_machine_storage_adapter, extra_attributes)
|
172
|
+
end
|
173
|
+
|
174
|
+
##
|
175
|
+
# Define an "all" method for each policy element type, as in .users or .object_attributes
|
176
|
+
# This will return all persisted of the elements of this type. If an options hash is passed
|
177
|
+
# then only elements that match all specified attributes will be returned.
|
178
|
+
#
|
179
|
+
define_method(pe_type.pluralize) do |options = {}|
|
180
|
+
# TODO: We might want to scope by the uuid of this policy machine in the request to the persistent store, rather than
|
181
|
+
# here, after records have already been retrieved.
|
182
|
+
# TODO: When the policy machine raises a NoMethoError, we should log a nice message
|
183
|
+
# saying that the underlying policy element class doesn't implement 'all'. Do
|
184
|
+
# it when we have a logger, though.
|
185
|
+
all_found = pm_class.send(:all, @policy_machine_storage_adapter, options)
|
186
|
+
all_found.select{ |pe| pe.policy_machine_uuid == uuid }
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
##
|
191
|
+
# Execute the passed-in block transactionally: any error raised out of the block causes
|
192
|
+
# all the block's changes to be rolled back.
|
193
|
+
# TODO: Possibly rescue NotImplementError and warn.
|
194
|
+
def transaction(&block)
|
195
|
+
policy_machine_storage_adapter.transaction(&block)
|
196
|
+
end
|
197
|
+
|
198
|
+
private
|
199
|
+
|
200
|
+
# Raise unless the argument is a policy element.
|
201
|
+
def assert_policy_element_in_machine(arg_pe)
|
202
|
+
unless arg_pe.is_a?(PM::PolicyElement)
|
203
|
+
raise(ArgumentError, "arg must each be a kind of PolicyElement; got #{arg_pe.class.name} instead")
|
204
|
+
end
|
205
|
+
unless arg_pe.policy_machine_uuid == self.uuid
|
206
|
+
raise(ArgumentError, "#{arg_pe.unique_identifier} is not in policy machine with uuid #{self.uuid}")
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# According to the NIST spec: "the triple (u, op, o) is a privilege, iff there
|
211
|
+
# exists an association (ua, ops, oa), such that user u→+ua, op ∈ ops, and o→*oa."
|
212
|
+
# Note: this method assumes that the caller has already checked that the given operation is in the operation_set
|
213
|
+
# for all associations provided.
|
214
|
+
def is_privilege_single_policy_class(user_or_attribute, object_or_attribute, associations)
|
215
|
+
# does there exist an association (ua, ops, oa), such that user u→+ua, op ∈ ops, and o→*oa?
|
216
|
+
associations.any? do |assoc|
|
217
|
+
user_or_attribute.connected?(assoc.user_attribute) && object_or_attribute.connected?(assoc.object_attribute)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
# According to the NIST spec: "In multiple policy class situations, the triple (u, op, o) is a PM privilege, iff for
|
222
|
+
# each policy class pcl that contains o, there exists an association (uai, opsj, oak),
|
223
|
+
# such that user u→+uai, op ∈ opsj, o→*oak, and oak→+pcl."
|
224
|
+
# Note: this method assumes that the caller has already checked that the given operation is in the operation_set
|
225
|
+
# for all associations provided.
|
226
|
+
def is_privilege_multiple_policy_classes(user_or_attribute, object_or_attribute, associations, policy_classes_containing_object)
|
227
|
+
policy_classes_containing_object.all? do |pc|
|
228
|
+
associations.any? do |assoc|
|
229
|
+
user_or_attribute.connected?(assoc.user_attribute) &&
|
230
|
+
object_or_attribute.connected?(assoc.object_attribute) &&
|
231
|
+
assoc.object_attribute.connected?(pc)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
end
|