Hi,

Yes you could store the ACL (ordered list of ACE's) with the resource
itself, although you will then have to add additional code to protect
access to that property which will complicate the CassandraProvider,
which is why I was sugesting that you do ACL storage in a completely
separate Column Family.

If you choose to do it that way (storing ACLs with the resource), then
you might consider storing the ACL as a JSON string.

By default, permissions inherit from the permissions on the parent
resource, so there is no need to create an ACL on creation, but the
implementation of the ResourceAccessGate will need compute access
control including parent ACLs.

How you implement ACL crud is entirely upto you. I suggest you use
Hector directly to do this.

This will make your CassandraResourceAccessGate completely separate
from you CassandraResourceProvider.

Best Regards
Ian

On 13 September 2013 13:21, Dishara Wijewardana <ddwijeward...@gmail.com> wrote:
> On Thu, Sep 12, 2013 at 8:37 PM, Ian Boston <i...@tfd.co.uk> wrote:
>
>> Hi
>>
>> On 12 September 2013 13:24, Dishara Wijewardana <ddwijeward...@gmail.com>
>> wrote:
>> > Hi Ian
>> >
>> >
>> > On Thu, Sep 12, 2013 at 1:50 PM, Ian Boston <i...@tfd.co.uk> wrote:
>> >
>> >> 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.
>> >>
>> >>
>> > The following algorithms, and etc does sling already have any
>> > implementation of it. If so I can reuse them. Since sling has few
>> providers
>> > I believe they probably have some common interface.
>>
>>
>>
>> There is not, or at least, not in a way that is exposed. There is an
>> implementation inside Jackrabbit, but it is tightly coupled to
>> Jackrabbit and more complex than the simple system below.
>>
>> Once implemented, this access control system may be exposed as a
>> ResourceAccessGate, but since (IIRC) imposing anything other than read
>> access control has not been done, the CassandraResourceProvider may
>> use the implementation directly to impose write and delete.
>>
>> >
>> >
>> >> 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);
>> >>
>> >
>> > - Can you please tell me how to calculate the ACE bitmap ?
>>
>> That is just the 32bit integer stored in the cassandra column
>> representing the grant or deny for the principal
>>
>> eg
>> Cassandra rowID : base64(sha1(/content/cassandra/foo/bar ))
>> Columns: 0_everyone_allow : 0x01, 1_admin_allow : 0x07
>>
>> Allows everyone read and admin everything at the path /cassandra
>>
>> > - Also I will be more clear if you can provide a sample value for the
>> input
>> > and output of this function ? i.e When currentPath=
>> > /content/cassandra/foo/bar  it returns grants=? denies=? some actual
>> values
>> > just for my understanding.
>>
>> Using the above for a random user (ie has the everyone principal):
>> grants = 0x01
>> denies - 0x0
>>
>> For the following values
>> Cassandra rowID : base64(sha1(/content/cassandra/foo/bar ))
>> Columns: 0_everyone_allow : 0x01, 1_admin_allow : 0x07, 2_ieb_deny : 0x01
>>
>> results in
>> dishara : grants = 0x01, denies = 0x00
>> ieb : grants = 0x00, denies = 0x01
>> admin: grants = 0x07, denies = 0x00
>>
>>
>>
>> >
>> >
>> >> 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.
>> >>
>> >> - If I understand you correctly is   <order>_<principal>_<allow|deny> is
>> > one ACE ?
>>
>> yes
>>
>> If per row there can be a ACL, there should be one additional
>> > column by default called "ACL" and it will have a comma separated string
>> > which are set of ACEs.
>>
>> not necessary as the order of columns is returned in the natural
>> string sort order (IIRC) so 0_* will be the first followed by 1_
>>
>> However, if thats not the case a column called ACL will be required to
>> list the ACLs in order.
>>
>>  Correct me if I am wrong.
>> > - Who stores these ACEs ? any API?
>>
>> Not determined yet. It would need an API able to modify ACLs
>>
>
> OK, I am trying to understand. Can you please clarify following.
>
> To start with, as I feel I should start storing data to cassandra with an
> updated column which stores a string which is a comma separated ACE list.
>  i.e when the provider stores a resource at /content/cassandra/foo/bar the
> ACL column can be empty. Does permissions added upon node creation ? Or
> there is some interface,and when it get called, (path,ACE-list) I will
> update the row with the new value for the ACL column. If that is the case
> permission should be granted to a path only of the path exists. OR upon
> node creation does  it suppose to store parent resolved ACE list by
> default. If that is so it will be more complex for Provider and have to
> change that to do so.
>
>
>
>> > - i.e <order> is a auto increment number we have to do a additional read
>> > before storing ACE to check what is the last number for <order>.
>>
>>
>> Ok, that indicates having an ACE column might be better, which would
>> indicate that no count is required.
>>
>> Best Regards
>> Ian
>>
>> >
>> > 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
>> >>
>> >
>> >
>> >
>> > --
>> > Thanks
>> > /Dishara
>>
>
>
>
> --
> Thanks
> /Dishara

Reply via email to