policy_machine 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -0,0 +1,266 @@
|
|
1
|
+
require 'policy_machine'
|
2
|
+
|
3
|
+
# This class stores policy elements in memory and
|
4
|
+
# exposes required operations for managing/querying these elements.
|
5
|
+
|
6
|
+
module PolicyMachineStorageAdapter
|
7
|
+
class InMemory
|
8
|
+
|
9
|
+
POLICY_ELEMENT_TYPES = %w(user user_attribute object object_attribute operation policy_class)
|
10
|
+
|
11
|
+
POLICY_ELEMENT_TYPES.each do |pe_type|
|
12
|
+
##
|
13
|
+
# Store a policy element of type pe_type.
|
14
|
+
# The unique_identifier identifies the element within the policy machine.
|
15
|
+
# The policy_machine_uuid is the uuid of the containing policy machine.
|
16
|
+
#
|
17
|
+
# TODO: add optional check to determine if unique_identifier is truly unique within
|
18
|
+
# given policy_machine.
|
19
|
+
#
|
20
|
+
define_method("add_#{pe_type}") do |unique_identifier, policy_machine_uuid, extra_attributes = {}|
|
21
|
+
persisted_pe = PersistedPolicyElement.new(unique_identifier, policy_machine_uuid, pe_type, extra_attributes)
|
22
|
+
persisted_pe.persisted = true
|
23
|
+
policy_elements << persisted_pe
|
24
|
+
persisted_pe
|
25
|
+
end
|
26
|
+
|
27
|
+
define_method("find_all_of_type_#{pe_type}") do |options = {}|
|
28
|
+
conditions = options.merge(pe_type: pe_type)
|
29
|
+
policy_elements.select do |pe|
|
30
|
+
conditions.all? do |k,v|
|
31
|
+
if v.nil?
|
32
|
+
!pe.respond_to?(k) || pe.send(k) == nil
|
33
|
+
else
|
34
|
+
pe.respond_to?(k) && pe.send(k) == v
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
##
|
42
|
+
# Assign src to dst in policy machine
|
43
|
+
#
|
44
|
+
def assign(src, dst)
|
45
|
+
assert_persisted_policy_element(src)
|
46
|
+
assert_persisted_policy_element(dst)
|
47
|
+
|
48
|
+
assignments << [src, dst]
|
49
|
+
true
|
50
|
+
end
|
51
|
+
|
52
|
+
##
|
53
|
+
# Determine if there is a path from src to dst in the policy machine
|
54
|
+
#
|
55
|
+
def connected?(src, dst)
|
56
|
+
assert_persisted_policy_element(src)
|
57
|
+
assert_persisted_policy_element(dst)
|
58
|
+
|
59
|
+
return true if src == dst
|
60
|
+
|
61
|
+
distances = dijkstra(src, dst)
|
62
|
+
distances.nil? ? false : true
|
63
|
+
end
|
64
|
+
|
65
|
+
##
|
66
|
+
# Disconnect two policy elements in the machine
|
67
|
+
#
|
68
|
+
def unassign(src, dst)
|
69
|
+
assert_persisted_policy_element(src)
|
70
|
+
assert_persisted_policy_element(dst)
|
71
|
+
|
72
|
+
assignment = assignments.find{|assgn| assgn[0] == src && assgn[1] == dst}
|
73
|
+
if assignment
|
74
|
+
assignments.delete(assignment)
|
75
|
+
true
|
76
|
+
else
|
77
|
+
false
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
##
|
82
|
+
# Remove a persisted policy element
|
83
|
+
#
|
84
|
+
def delete(element)
|
85
|
+
assignments.delete_if{ |assgn| assgn.include?(element) }
|
86
|
+
associations.delete_if { |_,assoc| assoc.include?(element) }
|
87
|
+
policy_elements.delete(element)
|
88
|
+
end
|
89
|
+
|
90
|
+
##
|
91
|
+
# Update a persisted policy element
|
92
|
+
#
|
93
|
+
def update(element, changes_hash)
|
94
|
+
element.send(:extra_attributes).merge!(changes_hash)
|
95
|
+
end
|
96
|
+
|
97
|
+
##
|
98
|
+
# Determine if the given node is in the policy machine or not.
|
99
|
+
def element_in_machine?(pe)
|
100
|
+
policy_elements.member?( pe )
|
101
|
+
end
|
102
|
+
|
103
|
+
##
|
104
|
+
# Add the given association to the policy map. If an association between user_attribute
|
105
|
+
# and object_attribute already exists, then replace it with that given in the arguments.
|
106
|
+
def add_association(user_attribute, operation_set, object_attribute, policy_machine_uuid)
|
107
|
+
# TODO: scope by policy machine uuid
|
108
|
+
associations[user_attribute.unique_identifier + object_attribute.unique_identifier] =
|
109
|
+
[user_attribute, operation_set, object_attribute]
|
110
|
+
|
111
|
+
true
|
112
|
+
end
|
113
|
+
|
114
|
+
##
|
115
|
+
# Return all associations in which the given operation is included
|
116
|
+
# Returns an array of arrays. Each sub-array is of the form
|
117
|
+
# [user_attribute, operation_set, object_attribute]
|
118
|
+
def associations_with(operation)
|
119
|
+
matching = associations.values.select do |assoc|
|
120
|
+
assoc[1].include?(operation)
|
121
|
+
end
|
122
|
+
|
123
|
+
matching.map{ |m| [m[0], m[1], m[2]] }
|
124
|
+
end
|
125
|
+
|
126
|
+
##
|
127
|
+
# Return array of all policy classes which contain the given object_attribute (or object).
|
128
|
+
# Return empty array if no such policy classes found.
|
129
|
+
def policy_classes_for_object_attribute(object_attribute)
|
130
|
+
find_all_of_type_policy_class.select do |pc|
|
131
|
+
connected?(object_attribute, pc)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
##
|
136
|
+
# Return array of all user attributes which contain the given user.
|
137
|
+
# Return empty array if no such user attributes are found.
|
138
|
+
def user_attributes_for_user(user)
|
139
|
+
find_all_of_type_user_attribute.select do |user_attribute|
|
140
|
+
connected?(user, user_attribute)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
##
|
145
|
+
# Execute the passed-in block transactionally: any error raised out of the block causes
|
146
|
+
# all the block's changes to be rolled back.
|
147
|
+
def transaction
|
148
|
+
old_state = dup
|
149
|
+
instance_variables.each do |var|
|
150
|
+
value = instance_variable_get(var)
|
151
|
+
|
152
|
+
if (value.respond_to?(:dup))
|
153
|
+
old_state.instance_variable_set(var, value.dup)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
begin
|
158
|
+
yield
|
159
|
+
rescue Exception
|
160
|
+
instance_variables.each do |var|
|
161
|
+
value = old_state.instance_variable_get(var)
|
162
|
+
instance_variable_set(var, value)
|
163
|
+
end
|
164
|
+
raise
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
|
169
|
+
private
|
170
|
+
|
171
|
+
# Raise argument error if argument is not suitable for consumption in
|
172
|
+
# public methods.
|
173
|
+
def assert_persisted_policy_element(arg)
|
174
|
+
raise(ArgumentError, "arg must be a PersistedPolicyElement; got #{arg.class.name}") unless arg.is_a?(PersistedPolicyElement)
|
175
|
+
raise(ArgumentError, "arg must be persisted") unless element_in_machine?(arg)
|
176
|
+
end
|
177
|
+
|
178
|
+
# The policy elements in the persisted policy machine.
|
179
|
+
def policy_elements
|
180
|
+
@policy_elements ||= []
|
181
|
+
end
|
182
|
+
|
183
|
+
# The policy element assignments in the persisted policy machine.
|
184
|
+
def assignments
|
185
|
+
@assignments ||= []
|
186
|
+
end
|
187
|
+
|
188
|
+
# All persisted associations
|
189
|
+
def associations
|
190
|
+
@associations ||= {}
|
191
|
+
end
|
192
|
+
|
193
|
+
def dijkstra(src, dst = nil)
|
194
|
+
nodes = policy_elements
|
195
|
+
|
196
|
+
distances = {}
|
197
|
+
previouses = {}
|
198
|
+
nodes.each do |vertex|
|
199
|
+
distances[vertex] = nil # Infinity
|
200
|
+
previouses[vertex] = nil
|
201
|
+
end
|
202
|
+
distances[src] = 0
|
203
|
+
vertices = nodes.clone
|
204
|
+
until vertices.empty?
|
205
|
+
nearest_vertex = vertices.inject do |a, b|
|
206
|
+
next b unless distances[a]
|
207
|
+
next a unless distances[b]
|
208
|
+
next a if distances[a] < distances[b]
|
209
|
+
b
|
210
|
+
end
|
211
|
+
break unless distances[nearest_vertex] # Infinity
|
212
|
+
if dst and nearest_vertex == dst
|
213
|
+
return distances[dst]
|
214
|
+
end
|
215
|
+
neighbors = neighbors(nearest_vertex)
|
216
|
+
neighbors.each do |vertex|
|
217
|
+
alt = distances[nearest_vertex] + 1
|
218
|
+
if distances[vertex].nil? or alt < distances[vertex]
|
219
|
+
distances[vertex] = alt
|
220
|
+
previouses[vertices] = nearest_vertex
|
221
|
+
# decrease-key v in Q # ???
|
222
|
+
end
|
223
|
+
end
|
224
|
+
vertices.delete nearest_vertex
|
225
|
+
end
|
226
|
+
|
227
|
+
return nil
|
228
|
+
end
|
229
|
+
|
230
|
+
# Find all nodes which are directly connected to
|
231
|
+
# +node+
|
232
|
+
def neighbors(pe)
|
233
|
+
neighbors = []
|
234
|
+
assignments.each do |assignment|
|
235
|
+
neighbors.push assignment[1] if assignment[0] == pe
|
236
|
+
end
|
237
|
+
return neighbors.uniq
|
238
|
+
end
|
239
|
+
|
240
|
+
# Class to represent policy elements
|
241
|
+
class PersistedPolicyElement
|
242
|
+
attr_accessor :persisted
|
243
|
+
attr_reader :unique_identifier, :policy_machine_uuid, :pe_type, :extra_attributes
|
244
|
+
|
245
|
+
# Ensure that attr keys are strings
|
246
|
+
def initialize(unique_identifier, policy_machine_uuid, pe_type, extra_attributes)
|
247
|
+
@unique_identifier = unique_identifier
|
248
|
+
@policy_machine_uuid = policy_machine_uuid
|
249
|
+
@pe_type = pe_type
|
250
|
+
@persisted = false
|
251
|
+
@extra_attributes = extra_attributes
|
252
|
+
extra_attributes.each do |key, value|
|
253
|
+
define_singleton_method key, lambda {@extra_attributes[key]}
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def ==(other)
|
258
|
+
return false unless other.is_a?(PersistedPolicyElement)
|
259
|
+
self.unique_identifier == other.unique_identifier &&
|
260
|
+
self.policy_machine_uuid == other.policy_machine_uuid &&
|
261
|
+
self.pe_type == other.pe_type
|
262
|
+
end
|
263
|
+
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
@@ -0,0 +1,236 @@
|
|
1
|
+
begin
|
2
|
+
require 'neography'
|
3
|
+
rescue LoadError
|
4
|
+
neography_unavailable = true
|
5
|
+
end
|
6
|
+
|
7
|
+
# This class stores policy elements in a neo4j graph db using the neography client and
|
8
|
+
# exposes required operations for managing/querying these elements.
|
9
|
+
# Note that this adapter shouldn't be used in production for high-performance needs as Neography
|
10
|
+
# is inherently slower than more direct NEO4J access.
|
11
|
+
module PolicyMachineStorageAdapter
|
12
|
+
class Neography
|
13
|
+
|
14
|
+
POLICY_ELEMENT_TYPES = %w(user user_attribute object object_attribute operation policy_class)
|
15
|
+
|
16
|
+
POLICY_ELEMENT_TYPES.each do |pe_type|
|
17
|
+
##
|
18
|
+
# Store a policy element of type pe_type.
|
19
|
+
# The unique_identifier identifies the element within the policy machine.
|
20
|
+
# The policy_machine_uuid is the uuid of the containing policy machine.
|
21
|
+
#
|
22
|
+
# TODO: add optional check to determine if unique_identifier is truly unique within
|
23
|
+
# given policy_machine.
|
24
|
+
#
|
25
|
+
define_method("add_#{pe_type}") do |unique_identifier, policy_machine_uuid, extra_attributes = {}|
|
26
|
+
node_attrs = {
|
27
|
+
:unique_identifier => unique_identifier,
|
28
|
+
:policy_machine_uuid => policy_machine_uuid,
|
29
|
+
:pe_type => pe_type,
|
30
|
+
:persisted => true
|
31
|
+
}.merge(extra_attributes)
|
32
|
+
persisted_pe = ::Neography::Node.create(node_attrs)
|
33
|
+
persisted_pe.add_to_index('nodes', 'unique_identifier', unique_identifier)
|
34
|
+
persisted_pe.add_to_index('policy_element_types', 'pe_type', pe_type)
|
35
|
+
persisted_pe
|
36
|
+
end
|
37
|
+
|
38
|
+
define_method("find_all_of_type_#{pe_type}") do |options = {}|
|
39
|
+
found_elts = ::Neography::Node.find('policy_element_types', 'pe_type', pe_type)
|
40
|
+
found_elts = found_elts.nil? ? [] : [found_elts].flatten
|
41
|
+
found_elts.select do |elt|
|
42
|
+
options.all? do |k,v|
|
43
|
+
if v.nil?
|
44
|
+
!elt.respond_to?(k)
|
45
|
+
else
|
46
|
+
elt.respond_to?(k) && elt.send(k) == v
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
##
|
54
|
+
# Assign src to dst in policy machine
|
55
|
+
#
|
56
|
+
def assign(src, dst)
|
57
|
+
assert_persisted_policy_element(src)
|
58
|
+
assert_persisted_policy_element(dst)
|
59
|
+
|
60
|
+
e = ::Neography::Relationship.create(:outgoing, src, dst)
|
61
|
+
|
62
|
+
if e.nil?
|
63
|
+
false
|
64
|
+
else
|
65
|
+
unique_identifier = src.unique_identifier + dst.unique_identifier
|
66
|
+
e.add_to_index('edges', 'unique_identifier', unique_identifier)
|
67
|
+
true
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
##
|
72
|
+
# Determine if there is a path from src to dst in the policy machine
|
73
|
+
#
|
74
|
+
def connected?(src, dst)
|
75
|
+
assert_persisted_policy_element(src)
|
76
|
+
assert_persisted_policy_element(dst)
|
77
|
+
|
78
|
+
return true if src == dst
|
79
|
+
|
80
|
+
neo_connection.execute_query("start n=node({id1}),m=node({id2}) return (n)-[*]->(m)",
|
81
|
+
{:id1 => src.neo_id.to_i, :id2 => dst.neo_id.to_i})['data'] != [[[]]]
|
82
|
+
end
|
83
|
+
|
84
|
+
##
|
85
|
+
# Disconnect two policy elements in the machine
|
86
|
+
#
|
87
|
+
def unassign(src, dst)
|
88
|
+
assert_persisted_policy_element(src)
|
89
|
+
assert_persisted_policy_element(dst)
|
90
|
+
|
91
|
+
unique_identifier = src.unique_identifier + dst.unique_identifier
|
92
|
+
found_edges = ::Neography::Relationship.find('edges', 'unique_identifier', unique_identifier)
|
93
|
+
|
94
|
+
if found_edges
|
95
|
+
# Neography::Relationship doesn't respond to .to_a
|
96
|
+
found_edges = [found_edges] unless found_edges.is_a?(Array)
|
97
|
+
found_edges.each do |found_edge|
|
98
|
+
# Unfortunately, we have to reload the edge as find isn't deserializing it properly.
|
99
|
+
e = ::Neography::Relationship.load(found_edge.neo_id.to_i)
|
100
|
+
e.del unless e.nil?
|
101
|
+
end
|
102
|
+
true
|
103
|
+
else
|
104
|
+
false
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
##
|
109
|
+
# Remove a persisted policy element
|
110
|
+
#
|
111
|
+
def delete(element)
|
112
|
+
if %w[user_attribute object_attribute].include?(element.pe_type)
|
113
|
+
element.outgoing(:in_association).each do |assoc|
|
114
|
+
assoc.del
|
115
|
+
end
|
116
|
+
end
|
117
|
+
element.del
|
118
|
+
end
|
119
|
+
|
120
|
+
##
|
121
|
+
# Update a persisted policy element
|
122
|
+
#
|
123
|
+
def update(element, changes_hash)
|
124
|
+
element.neo_server.set_node_properties(element.neo_id, changes_hash)
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
##
|
129
|
+
# Determine if the given node is in the policy machine or not.
|
130
|
+
def element_in_machine?(pe)
|
131
|
+
found_node = ::Neography::Node.find('nodes', 'unique_identifier', pe.unique_identifier)
|
132
|
+
!found_node.nil?
|
133
|
+
end
|
134
|
+
|
135
|
+
##
|
136
|
+
# Add the given association to the policy map. If an association between user_attribute
|
137
|
+
# and object_attribute already exists, then replace it with that given in the arguments.
|
138
|
+
def add_association(user_attribute, operation_set, object_attribute, policy_machine_uuid)
|
139
|
+
remove_association(user_attribute, object_attribute, policy_machine_uuid)
|
140
|
+
|
141
|
+
# TODO: scope by policy machine uuid
|
142
|
+
unique_identifier = user_attribute.unique_identifier + object_attribute.unique_identifier
|
143
|
+
node_attrs = {
|
144
|
+
:unique_identifier => unique_identifier,
|
145
|
+
:policy_machine_uuid => policy_machine_uuid,
|
146
|
+
:user_attribute_unique_identifier => user_attribute.unique_identifier,
|
147
|
+
:object_attribute_unique_identifier => object_attribute.unique_identifier,
|
148
|
+
:operations => operation_set.map(&:unique_identifier).to_json,
|
149
|
+
}
|
150
|
+
persisted_assoc = ::Neography::Node.create(node_attrs)
|
151
|
+
persisted_assoc.add_to_index('associations', 'unique_identifier', unique_identifier)
|
152
|
+
|
153
|
+
[user_attribute, object_attribute, *operation_set].each do |element|
|
154
|
+
::Neography::Relationship.create(:in_association, element, persisted_assoc)
|
155
|
+
end
|
156
|
+
|
157
|
+
true
|
158
|
+
end
|
159
|
+
|
160
|
+
##
|
161
|
+
# Return all associations in which the given operation is included
|
162
|
+
# Returns an array of arrays. Each sub-array is of the form
|
163
|
+
# [user_attribute, operation_set, object_attribute]
|
164
|
+
#
|
165
|
+
def associations_with(operation)
|
166
|
+
operation.outgoing(:in_association).map do |association|
|
167
|
+
user_attribute = ::Neography::Node.find('nodes', 'unique_identifier', association.user_attribute_unique_identifier)
|
168
|
+
object_attribute = ::Neography::Node.find('nodes', 'unique_identifier', association.object_attribute_unique_identifier)
|
169
|
+
|
170
|
+
operation_set = Set.new
|
171
|
+
JSON.parse(association.operations).each do |op_unique_id|
|
172
|
+
op_node = ::Neography::Node.find('nodes', 'unique_identifier', op_unique_id)
|
173
|
+
operation_set << op_node
|
174
|
+
end
|
175
|
+
|
176
|
+
[user_attribute, operation_set, object_attribute]
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
##
|
181
|
+
# Remove an existing association. Return true if the association was removed and false if
|
182
|
+
# it didn't exist in the first place.
|
183
|
+
def remove_association(user_attribute, object_attribute, policy_machine_uuid)
|
184
|
+
unique_identifier = user_attribute.unique_identifier + object_attribute.unique_identifier
|
185
|
+
|
186
|
+
begin
|
187
|
+
assoc_node = ::Neography::Node.find('associations', 'unique_identifier', unique_identifier)
|
188
|
+
return false unless assoc_node
|
189
|
+
assoc_node.del
|
190
|
+
true
|
191
|
+
rescue ::Neography::NotFoundException
|
192
|
+
false
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
##
|
197
|
+
# Return array of all policy classes which contain the given object_attribute (or object).
|
198
|
+
# Return empty array if no such policy classes found.
|
199
|
+
def policy_classes_for_object_attribute(object_attribute)
|
200
|
+
find_all_of_type_policy_class.select do |pc|
|
201
|
+
connected?(object_attribute, pc)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
##
|
206
|
+
# Return array of all user attributes which contain the given user.
|
207
|
+
# Return empty array if no such user attributes are found.
|
208
|
+
def user_attributes_for_user(user)
|
209
|
+
#Don't use this kind of query plan in a for-production adapter.
|
210
|
+
find_all_of_type_user_attribute.select do |user_attribute|
|
211
|
+
connected?(user, user_attribute)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
##
|
216
|
+
# Execute the passed-in block transactionally: any error raised out of the block causes
|
217
|
+
# all the block's changes to be rolled back.
|
218
|
+
def transaction
|
219
|
+
raise NotImplementedError, "transactions are only available in neo4j 2.0 which #{self.class} is not compatible with"
|
220
|
+
end
|
221
|
+
|
222
|
+
private
|
223
|
+
|
224
|
+
# Raise argument error if argument is not suitable for consumption in
|
225
|
+
# public methods.
|
226
|
+
def assert_persisted_policy_element(arg)
|
227
|
+
raise(ArgumentError, "arg must be a Neography::Node; got #{arg.class.name}") unless arg.is_a?(::Neography::Node)
|
228
|
+
raise(ArgumentError, "arg must be persisted") unless element_in_machine?(arg)
|
229
|
+
end
|
230
|
+
|
231
|
+
# Neo4j client
|
232
|
+
def neo_connection
|
233
|
+
@neo_connection ||= ::Neography::Rest.new
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end unless neography_unavailable
|