Reputation: 1672
Need to define access policies hierarchically in the application that uses Neo4j.
Model: application uses a tree structure (each Node
has exactly one parent), which can be represented as
(:Node)-[:SUBTREE_OF]->(:Node)
for simplicity.
There are also User
s who are members of Group
s, which have Permission
s. Permissions contain two properties: objectId
that contains ID of the object for which permission is set and accessLevel
, which can be WRITE
, READ
or NONE
.
so
(u:User)-[:MEMBER_OF]->(g:Group)
(g:Group)-[:WITH]->(p:Permission{objectID:"99", accessLevel:"WRITE"})
Problem: need to make possible specifying permission access level for the object that can be inherited by all its children. That means a need to determine access level of particular User
to particular Node
at runtime using specified permission for this object in case it is specified or the nearest specified parent permission if the permission is not specified.
How to write the query that will return accessLevel
property of specified Node
or the nearest parent Node
?
Upvotes: 1
Views: 795
Reputation: 30407
Right now your :Permissions
are associated to :Nodes
and :Applications
by an objectID property, but for effective queries using graph relationships, this should probably be modeled as a relationship to the object itself. At that point it's a question of if the accessLevel
should be stored in the :Permission
nodes, or if you want to get rid of :Permission
nodes entirely and model it with a :HasPermission
relationship with an accessLevel
property.
I'll assume that you'll want to keep your :Permission
nodes for now.
Assuming that a :User
can only be a member of one :Group
, your query to get the permission for a :Node
(or its :Node
ancestors) becomes something like:
MATCH (:User{ID:123})-[:MEMBER_OF]->(group:Group), (node:Node{ID:123})
WITH group, node
// looking for a :Node going up the chain with a permission from your group
OPTIONAL MATCH (node)-[jumps:SUBTREE_OF*0..]->(target:Node)<-[:HasPermission]-(perm:Permission)<-[:WITH]-(group)
WITH node, target, perm, jumps
ORDER BY SIZE(jumps) LIMIT 1
RETURN target, COALESCE(target = node, false) as same_node, COALESCE(perm.accessLevel, "WRITE") as accessLevel
The return includes the target
:Node we eventually matched on (it will be null if we couldn't find one in the hierarchy and defaulted to the "WRITE" access level), same_node
for whether the :Node we wanted to get permission for was the one with the permission on it (false means we found an ancestor with a permission or defaulted), and the accessLevel
is the access level of the associated permission. You probably don't need all of these, but it's useful to have for testing that it's working properly.
EDIT
Remembered that we could easily enforce the ordering to select the nearest parent node (or the node itself) with your group permission by sorting on the number of relationships we traverse up the chain. Altered my query above to handle this.
Upvotes: 4