Hi, Sling uses the JSONObject and friends for json parsing. Its fine for small JSON fragments but not as fast for huge streams as its memory based.
JSONObject o = new JSONObject(jsonString) will parse a JSON string into an object. Thanks for doing the other things. Best Regards Ian On 22 September 2013 19:03, Dishara Wijewardana <[email protected]> wrote: > Hi Ian, > I changed the logic to correct with my misunderstandings as suggested and > refactored all code to use apache commons Base64 for encoding and rest of > the code review suggestions. And it works. Hence I commited the code. And > also upload the code to the JIRA at this milestone (without json parser). > > I also spent more time trying to move the existing logics to json parser > since there are several places that I need to change and need to make sure > my working impl not break from this refactor. I am new to json. I used > com.google.gson.*; > > {"key1":{"principal":"dishara","grant":"false","permission":"1"},"key2":{"principal":"dishara2","grant":"false","permission":"7"}} > > Here when I add a ACE, I have to add a property along with the json child > object. i.e key1. So above is the json file of a ACL from the code what I > tried. But in this case, when try to update a ACL with a new ACE, we have > to provide a unique key always. I believe there will be a better ways of > doing this. Does sling has a JSON parser ? I saw only JSONObject. > > > On Sat, Sep 21, 2013 at 10:16 AM, Ian Boston <[email protected]> wrote: > >> Hi, >> You need to publish the comments before anyone else can see them, >> thats why they say draft on them. >> >> I'll reply here, but if you could publish them it would keep the >> thread going on code review. >> >> On 21 September 2013 02:53, Dishara Wijewardana <[email protected]> >> wrote: >> > Hi Ian, >> > I could see them when I logged in. Did you checked whether you logged in >> to >> > the google project? When I was not logged in I was not able to see my >> > comments also. >> > >> > But in case if it is still not viewable, I am copying the 3 comments I >> > added. >> > >> > * >> > Ian Boston* 2013/09/18 08:18:57 >> > I think this should be getPrivilegeFromACE. READ, WRITE, DELETE are >> > privileges >> > *Reply* *Done* >> > *(Draft)* 2013/09/18 18:36:19 >> > As per explanation of yours in the mail, read=0x01,write=0x02 and etc are >> > categorized under Principals. Can you confirm that? >> >> no. >> >> read, write, delete are categorised as privileges. >> ieb, dishara are categorised as principals. >> >> >> On 2013/09/18 08:18:57, >> > Ian Boston wrote: >> > Show quoted text >> > *Edit >> > >> > * >> > *Ian Boston* 2013/09/18 08:18:57 >> > I think this impl might be wrong ? Principals are things like ieb, >> > dishara, I cant see how you could represent those in an int. I think it >> > should be boolean isUserHasPrincipal(String principal, Set<String> >> > userPrincipals) { return userPrincipals.contains(principal); } >> > *Reply* *Done* >> > *(Draft)* 2013/09/18 18:43:48 >> > According to the mail ACE is something like >> > <order>_<principal>_<allow|deny> where i.e 0_everyone_allow : 0x01. As >> per >> > you, user name is the "principle" ? Initially I did it in that way, but I >> > thought principle is read, write etc as per last comment of mine, and >> hence >> > the values of it can be represent in a integer bitmap. But if READ,WRITE >> > are priviledges my impl is wrong. Can you please confirm what I think is >> > correct or not ? >> >> If the ACE is 0_everyone_allow: 0x01 then the fields are >> <order>_<principal>_<grant|deny> : <privileges> >> >> >> >> >> On 2013/09/18 08:18:57, Ian Boston wrote: >> > Show quoted text >> > *Edit >> > >> > * >> > *Ian Boston* 2013/09/18 08:18:57 >> > the int userPrinciples should be a Set<String> userPrincipals >> > *Reply* *Done* >> > *(Draft)* 2013/09/19 21:29:51 >> > Can you provide an example set of principals. According to me, it can be >> > the values of i.e read=0x01, write=0x02 so the set should be <0x01> >> <0x02> >> > or a single integer 0x03 which represents above. But as per you I am >> wrong. >> >> >> Principals identify a user or a group, and every user or group has a >> principal. >> >> eg >> ieb is the Principal that identifies the user ieb. >> everyone is the Principal that identifies the group everyone, >> containing all users. >> anonymous is the Principal that identifies the anonymous user who has >> not logged in. >> >> The concept of principal is widely used and in java is frequently >> implemented extending the interface java.lang.Principal. If you go >> into Eclipse and show Type Hierarchy you will see some examples, >> including the Jackrabbit User and Group implementations. >> >> In the ACEs we just need the name of the principal to identify who the >> ACE applies to. >> >> Finally, in addition to a User and Group being identified by a >> Principal, they will have Principals by virtue of the fact they are >> members of other groups. A user will always have the principal >> "everyone" since they are part of the group "everyone". >> >> HTH >> >> Best Regards >> Ian >> >> >> >> > So please correct me. On 2013/09/18 08:18:57, Ian Boston wrote: >> > Show quoted text >> > *Edit >> > >> > >> > ** >> > >> > * >> > >> > >> > On Fri, Sep 20, 2013 at 4:44 PM, Ian Boston <[email protected]> wrote: >> > >> >> Hi, >> >> >> >> Did you publish your replies to my comments at [1]. I cant see >> >> anything other than what I said. Be sure to click on each comment to >> >> exand it to full size. >> >> >> >> Ian >> >> >> >> 1 >> >> >> https://codereview.appspot.com/13396052/diff/1/main/cassandra/src/main/java/org/apache/sling/cassandra/resource/provider/security/AccessControlUtil.java >> >> >> >> On 19 September 2013 22:38, Dishara Wijewardana < >> [email protected]> >> >> wrote: >> >> > Hi Ian, >> >> > If you find some time, appreciate if you can add some reply to the >> >> comments >> >> > on the code review basically regarding the issues you raised in the >> >> > algorithm and the conflict of principals and privileges as per my >> >> > understanding. I will cleanup the code with your clarification in this >> >> > weekend. >> >> > >> >> > >> >> > On Thu, Sep 19, 2013 at 12:16 AM, Dishara Wijewardana < >> >> > [email protected]> wrote: >> >> > >> >> >> Hi Ian, >> >> >> Thank you very much for the valuable feedbacks as always. I added >> some >> >> >> comments to clarify couple of cases. Appreciate your response, so >> that I >> >> >> can proceed with the improvements. >> >> >> >> >> >> >> >> >> On Wed, Sep 18, 2013 at 1:19 PM, Ian Boston <[email protected]> wrote: >> >> >> >> >> >>> Hi Dishara, >> >> >>> Looking good, I have some comments, so I did a code review at [1] >> >> >>> Ian >> >> >>> >> >> >>> 1 https://codereview.appspot.com/13396052/ >> >> >>> >> >> >>> >> >> >>> On 18 September 2013 04:13, Dishara Wijewardana < >> >> [email protected]> >> >> >>> wrote: >> >> >>> > Hi Ian, >> >> >>> > Sorry for the delay of updating the thread. I had to to some >> >> >>> > experiment(writing dummy tests iteratively) to figure out what you >> >> >>> exactly >> >> >>> > meant. And finally was able to implement what you said. I have >> >> commited >> >> >>> the >> >> >>> > src under a new package called "security". Currently it is a util >> >> class >> >> >>> > which is executable and we can test. >> >> >>> > >> >> >>> > Now i.e following can be done and returns accurate results. >> >> >>> > >> >> >>> > String path = "/content/cassandra/p2/c2"; >> >> >>> > String policy="0_dishara_allow :0x01 "; >> >> >>> > >> >> >>> > accessControlUtil.addACE(path,policy); >> >> >>> > >> >> >>> > int results[] = >> >> accessControlUtil.buildAtLevel(0x04,path); >> >> >>> > System.out.println("GRANT" +results[0]); >> >> >>> > System.out.println("DENY" +results[1]); >> >> >>> > >> >> >>> > >> >> >>> > >> >> >>> > >> >> >>> > >> >> >>> > On Mon, Sep 16, 2013 at 2:28 PM, Ian Boston <[email protected]> >> wrote: >> >> >>> > >> >> >>> >> 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 < >> >> >>> [email protected]> >> >> >>> >> wrote: >> >> >>> >> > On Thu, Sep 12, 2013 at 8:37 PM, Ian Boston <[email protected]> >> >> wrote: >> >> >>> >> > >> >> >>> >> >> Hi >> >> >>> >> >> >> >> >>> >> >> On 12 September 2013 13:24, Dishara Wijewardana < >> >> >>> >> [email protected]> >> >> >>> >> >> wrote: >> >> >>> >> >> > Hi Ian >> >> >>> >> >> > >> >> >>> >> >> > >> >> >>> >> >> > On Thu, Sep 12, 2013 at 1:50 PM, Ian Boston <[email protected]> >> >> >>> 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 >> >> >>> >> >> >> >>> > >> >> >>> > >> >> >>> > >> >> >>> > -- >> >> >>> > Thanks >> >> >>> > /Dishara >> >> >>> >> >> >> >> >> >> >> >> >> >> >> >> -- >> >> >> Thanks >> >> >> /Dishara >> >> >> >> >> > >> >> > >> >> > >> >> > -- >> >> > Thanks >> >> > /Dishara >> >> >> > >> > >> > >> > -- >> > Thanks >> > /Dishara >> > > > > -- > Thanks > /Dishara
