Hi Dishara,
To make the Cassandra Resource Provider really useful I think we need
to add access control. I think the best way of doing this is to borrow
some concepts from Jackrabbit access control.
Take a deep breath, and you will see why I left this till last.
I think we should provide path base access control, which inherits
from parent resources in the path. At every level there is a an
ordered list of access control entries each access control entry (ACE)
being either an allow entry or a deny entry. What is allowed or denied
is defined in a 32bit bitmap with each bit representing 1 permission,
so we can have upto 32 permissions. Each ACE specifies a single
principal. So an ACL consists of a ordered list of ACE's each one
bound to a principal.
A user has a set of principals, so to resolve the ACL at any one path
for a user the global ACL is filtered to contain only the ACE's with
principals that the user has.
Computing a final access control bitmap for a user at a location
requires ordered processing of all the ACEs relevant to the user at
the current path and then all ancestors.
The pseudo algorithm to calculate the a grant bitmap and a deny bitmap
at any level is:
function getCurrentLevelBitmaps(currentPath):
int grants = 0;
int denies = 0;
for all ACEs in the ACL at the currentPath:
if the user has the principal of the current ACE:
int toGrant = 0;
int toDeny = 0;
if the ACE is a grant:
toGrant = the ACE bitmap;
else:
toDeny = the ACE bitmap;
toGrant = toGrant & ~denies;
toDeny = toDeny & ~grants;
grants = grants | toGrant;
denied = denies | toDenies;
return (grants, denies);
To combine what is granted at the child level with what is granted at
a parent level we need to mask the parent level with the deny at the
child level.
eg
toGrant = grantedAtParent & ~denies;
toDeny = deniedAtParent & ~grants;
grants = grants | toGrant;
denied = denies | toDenies;
The simplest way of achieving this is to use recursion again in pseudo code:
function buildAtLevel():
if not root level:
(grantedAtParent, deniedAtParent) =
buildAtLevel(getParentLevel(currentLevel));
(grants, denies) = getCurrentLevelBitmaps(currentLevel);
toGrant = grantedAtParent & ~denies;
toDeny = deniedAtParent & ~grants;
grants = grants | toGrant;
denied = denies | toDenies;
return (grants, denied);
There are some optimisations you can apply here, and there are plenty
of opportunities to cache intermediate bitmaps in memory. Just caching
the ACL reduces resolution to bitwise operations.
Principals
----------------
Initially keep it simple.
read = 0x01
write = 0x02
delete = 0x04
Storage of ACLs.
-------------------------
I suggest you store ACLs in their own Column Family, where the rowID is
base64(sha1(path)) or whatever path -> rowid encoding you have currently.
IIRC Cassandra columns come out in the natural order of Strings
<order>_<principal>_<allow|deny> and the value is the bitmap of permissions.
Where <order> is 000 to 999 ( I really doubt that a single ACL will
have 1000 ACEs ever)
Once you have this working, we can wire it into the ResourceProvider
or another Sling API.
Does that make sense ?
Ian