ACL Fast Passing Strategies
We are currently working on a project where the main domain objects are content nodes and we are using an ACL-like system where each node in the hierarchy can contain rules that override or complement those of their parents. For example, everything is also based on roles and actions.
Node 1 - {Deny All, Allow Role1 View}
\- Node 2 - {Allow Role2 View}
\- Node 3 - {Deny Role1 View}
In this case, the rules will be read in order from top to bottom so that node 3 can only be viewed by Role2. It's not very difficult in concept.
Getting the rules for one node can lead to some queries, get all the parents, and then recreate the list of rules and evaluate them, but the process can be cumbersome as the hierarchy can get quite deep and there can be many rules for each node.
I was thinking about preparing a table with pre-calculated rules for each node that could be recreated whenever the resolution changes and propagates to all leaf nodes of the updated one.
Do you think of any other strategy for speeding up your search and calculation of rules? Ideally this should be done in a single query, but trees are not the best structures for them.
a source to share
I would have thought the observer pattern could be adapted .
The idea would be that everyone Node
maintains a precomputed list and is simply notified by their parent of any update so that they can recheck that list.
This can be done in two different ways:
- report that the change happened, but don't recompile anything
- recompose on every update
I would advise going with 1
if possible, as it does not involve re-linking the whole world when the root directory is updated, and only recalculating when needed (lazy eval actually), but you may prefer the second option if you update rarely but a fast search is required (there are concurrency issues).
Let's denote Solution 1:
Root ___ Node1 ___ Node1A
\ \__ Node1B
\_ Node2 ___ Node2A
\__ Node2B
Now, to begin with, none of them have subtracted anything (everything is in a dirty state) if I ask for the rules Node2A
:
-
Node2A
realizes that he is dirty: he asks forNode2
rules -
Node2
realizes that he is dirty: he asksRoot
-
Root
has no parent, so it can't be messy, it sends its rules toNode2
-
Node2
caches the response fromRoot
, concatenates its rules with those received fromRoot
, and cleans up the dirty bit, it sends the merge result (cached now) toNode2A
-
Node2A
caches, concatenates, cleans up the dirty bit and returns the result
If I then ask for the rules Node2B
:
-
Node2B
dirty, he asksNode2
-
Node2
clean, he answers -
Node2B
caches, concatenates, cleans up the dirty bit and returns the result
Please note that Node2
nothing changed.
In case of update:
- I update
Node1
: I am using caching rulesRoot
to recheck new rules and send a bit inNode1A
andNode1B
out to notify them that their cache is out of date. -
Node1A
andNode1B
set their dirty bit, they also spread this notice if they had kids
Note that since I have cached the rules Root
, I do not need to query the object Root
, if it is a simple enough operation, you may not cache them at all: if you are not playing with distributed here, and the query Root
only includes memory feedback you might choose not to duplicate to save memory and bookkeeping.
I hope you succeed.
a source to share
Your precomputed version stores all the permissions specific to each role in each node. You can save a bit of time and space by traversing the tree, specifying nodes as you reach them, and creating for each role an array of node numbers and permission changes only for the nodes on which the permissions are related to that role change. This produces output only linear to the size of the input tree (including its annotations). Then, when you start checking the permission for a role in a node, use the number of that node to search the array to find the point in the array that represents the most recent resolution change when you visited that node during the tour.
It might be related in some way from http://en.wikipedia.org/wiki/Range_Minimum_Query and http://en.wikipedia.org/wiki/Lowest_common_ancestor , but I really don't know if these links will help or not.
a source to share