Poliakoff
Poliakoff

Reputation: 1672

How to implement custom user access permissions in neo4j?

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 Users who are members of Groups, which have Permissions. 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

Answers (1)

InverseFalcon
InverseFalcon

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

Related Questions