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
@@ -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
|