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

Reply via email to