This example gives a generic overview of an approach to handling ACLs in graphs, and a simplified example with concrete queries.
In many scenarios, an application needs to handle security on some form of managed objects. This example describes one pattern to handle that through use a the graph structure and traversers that build a full permissions-structure for any managed object with exclude and include overriding possibilities. This results in a dynamic construction of Access Control Lists (ACLs) based on the position and context of the managed object.
The result is a complex security scheme that can easily be implemented in a graph structure, supporting permissions overriding, principal and content composition, and does not duplicate data anywhere.
As seen in the example graph layout, there are some key concepts in this domain model:
HAS_CHILD_CONTENT
relationships
PRINCIPAL
relationships.
IS_MEMBER_OF
relationship. One principal (user or group) can be part of many groups at the same time.
SECURITY
- relationships, connecting the content composite structure to the principal composite structure, containing a addition/removal modifier property ("+RW
")
The calculation of the effective permissions (e.g. Read, Write, Execute) for a principal for any given ACL-managed node (content) follows a number of rules that will be encoded into the permissions-traversal:
This approach will let you define a generic permission pattern on the root content, and then refine that for specific sub-content nodes and specific principals.
111
in a bit encoded ReadWriteExecute case)
or 000
if you like pessimistic security handling (everything is forbidden unless explicitly allowed).
SECURITY
relationships on it.
SECURITY
relationship.
+
" permission modifiers to the existing permission pattern, revoke the "-
" permission modifiers from the pattern.
The same algorithm is applicable for the bottom-up approach, basically just traversing from the target content node upwards and applying the security modifiers dynamically as the traverser goes up.
Now, to get the resulting access rights for e.g. "user 1
" on the "My File.pdf
" in a Top-Down
approach on the model in the graph above would go like:
Root folder
", and set the permissions to 11
initially (only considering Read, Write).
SECURITY
relationships to that folder.
User 1 is contained in both of them, but "root
" is more generic, so apply it first then "All principals
" +W
+R
→ 11
.
Home
" has no SECURITY
instructions, continue.
user1 Home
" has SECURITY
.
First apply "Regular Users
" (-R
-W
) → 00
, Then "user 1
" (+R
+W
) → 11
.
My File.pdf
" has no SECURITY
modifiers on it, so the effective permissions for "User 1
" on "My File.pdf
" are ReadWrite
→ 11
.
In this example, we are going to examine a tree structure of directories
and
files
. Also, there are users that own files and roles that can be assigned to
users. Roles can have permissions on directory or files structures (here we model
only canRead
, as opposed to full rwx
Unix permissions) and be nested. A more thorough
example of modeling ACL structures can be found at
How to Build Role-Based Access Control in SQL.
In order to find all files contained in this structure, we need a variable length
query that follows all contains
relationships and retrieves the nodes at the other
end of the leaf
relationships.
START root=node:node_auto_index(name = 'FileRoot') MATCH root-[:contains*0..]->(parentDir)-[:leaf]->file RETURN file
resulting in:
file |
---|
2 rows, 5 ms |
|
|
If we introduce the concept of ownership on files, we then can ask for the owners of the files we find — connected via owns
relationships to file nodes.
START root=node:node_auto_index(name = 'FileRoot') MATCH root-[:contains*0..]->()-[:leaf]->file<-[:owns]-user RETURN file, user
Returning the owners of all files below the FileRoot
node.
file | user |
---|---|
2 rows, 5 ms | |
|
|
|
|
If we now want to check what users have read access to all Files, and define our ACL as
canRead
access to one of the parent folders of a File has read access.
In order to find users that can read any part of the parent folder hierarchy above the files, Cypher provides optional variable length path.
START file=node:node_auto_index('name:File*') MATCH file<-[:leaf]-()<-[:contains*0..]-dir<-[?:canRead]-role-[:member]->readUser RETURN file.name, dir.name, role.name, readUser.name
This will return the file
, and the directory where the user has the canRead
permission along
with the user
and their role
.
file.name | dir.name | role.name | readUser.name |
---|---|---|---|
9 rows, 50 ms | |||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The results listed above contain null
values for optional path segments, which can be mitigated by either asking several
queries or returning just the really needed values.
Copyright © 2012 Neo Technology