From Sling to Sakai 3 Groups

WARNING: These are working notes and sketches and may be torn up or scrawled over at any time.

The foundation

Jackrabbit is a content repository and Sling is a web framework. They aren't social networking application suites. (That's our job.) When we talk about a Jackrabbit or Sling "user", we don't mean a person with a name and an age and an address: we mean that we can get some opaque identifier from some sort of authentication, and we can use that thing to assign and check access rights (and as a container for properties of our own). A Jackrabbit "group" is only an identifiable pointer to those kinds of "users" and to other "groups", and its only inherent use is also as a way to assign and check access rights (although again we can attach properties of our own). Jackrabbit "access rights" don't include "revise the standard syllabus" or "read final grades" or "broadcast a message to the teaching assistants" or "moderate a chat room". They're a static set of file-system-like actions that can be taken on file-system-like nodes: read, update, delete, add children, remove children, read the access rights, and change the access rights.

Obviously there's a big distance from this bare authentication-and-access-rights skeleton to real-world people and communities. But I don't want to assume that the only way to model the richer domain is by warping that perfectly useful skeleton. Let me start with a relatively minor example:

Personal contacts

Update: KERN-763 asks for a Jackrabbit authz group corresponding to all of a person's personal contacts, but does not allow for more fine-grained access control by contact relationship type. Since relationship types are not being removed, we still have a version of the gap described below.

In the demo's current implementation, the list of my personal contacts and their "types" (meaning their relationship to me) is not stored as a Jackrabbit-group under "/system/userManager/group/" but as a folder at "/_user/contacts/BIG_STORE_USER_ID/". However we want to deliver some scenarios which use contacts and their relationships in the same way as other groups of people: "Let my friends see this blog post"; "Send this message to my supervisors."

So is this a problem? Not necessarily. As we can see at the demo site, the 3akai UX has no trouble displaying your contact list as a "group of people" even though it's not a "Jackrabbit-group of Jackrabbit-users". The only time we're really pushed to make something a Jackrabbit-group is when we want to assign Jackrabbit-access-rights, and even there Ian's dynamic principal resolver lets us incorporate other sorts of criteria. There may turn out to be solid technical reasons to store personal contact lists as Jackrabbit-groups, but there's no theoretical requirement.

Composite groups vs. dependent groups

A Jackrabbit-group hierarchy as shown in /wiki/spaces/KERNDOC/pages/22660743360 is a composite tree: a group's membership is the sum of all member-group's memberships (plus any extras). A Sakai group hierarchy as we've been discussing it is a dependency tree: a child-group's membership is restricted to members of its parent, and if a member disappears from the parent, then it also disappears from the child-group. To put it another way, a Jackrabbit-group tree describes the sources of the top group's membership, whereas the Sakai-group tree describes named selections from the top group.

Functional groups without authz interest

Because vanilla "site membership" without a role doesn't give specific information on access rights, site memberships aren't currently directly visible as a Jackrabbit-group. (Remember, Jackrabbit and Sling themselves only use groups to check permissions on repository nodes.) Instead, the site node's access-control-list includes two top-level Jackrabbit groups, one ("g-SITENAME-collaborators") handling the "collaborator" permissions and the other ("g-SITENAME-viewers") handling the "viewer" permissions. (Would this have to be changed to enable "Give all members of the Chess Club site access to the Chess Championship newsfeed"?)

Group management permissions

The JCR standard restricts itself to normal Java security Principal objects when discussing access rights, allowing for a legal implementation which relies entirely on external systems for user/group identities. As a stand-alone implementation of the JCR standard, Jackrabbit supplies internally-created and maintained Principals via Authorizable nodes. The Jackrabbit Authorizable API exposes membership control and the ability to set and get arbitrary properties, but does not expose any way to set normal Jackrabbit ACLs on an Authorizable.

Instead of checking Authorizable-node ACLs, Jackrabbit's UserAccessControlProvider implements the following default policy: "everybody has READ permission to all items, every known user is allowed to modify it's own properties except for her/his group membership, members of the 'User administrator' group are allowed to create, modify and remove users, members of the 'Group administrator' group are allowed to create, modify and remove groups, group membership can only be edited by members of the 'Group administrator' and the 'User administrator' group."

Going with the flow, Sling's jackrabbit-usermanager servlets don't let normal users create groups or change properties of a group (whether they're members or not), and Sling's jackrabbit-accessmanager servlets don't work against users or groups.

To work around these limitations, Nakamura UpdateSakaiGroupServlet handleOperation checks a pseudo-privilege to decide whether the current user can modify group properties.

More recently, KERN-759 has led Ian to create a new Nakamura-specific user access controller which uses two new pseudo-privileges to determine management and read access for groups.

For comparison, a pair of more specialized products:

  • uPortal Groups Manager permissions on a group or group member:
    • canAssignPermissions
    • canCreateGroup (i.e., a sub-group)
    • canDelete
    • canManageMembers
    • canSelect
    • canUpdate
    • canView
    • canViewProperties
  • Grouper privileges on a group:
    • ADMIN - Can assign privileges and do anything else.
    • OPTIN - Can add self as a member
    • OPTOUT - Can remove self as a member
    • READ - Can see group and membership
    • UPDATE - Can see and change membership
    • VIEW - Can see group (meaning just the name? all properties except membership?)

Site role implementation

Update: See this description of the current implementation.

For functional permissions, so far K2/3akai relies (almost) exclusively on normal Jackrabbit access control lists, and roles are (almost) absent from server-side code. At time of writing, there's one leak into the Discussion servlet at DiscussionUtils.java and Post.java.

The SiteMembersServlet currently finds site members via the SiteService, which finds them by using a private method called "getMembershipTree" which gets the site node's "sakai:authorizables" properties and then resolves Groups to individual users. In other words, SiteService knows where the user's membership comes from. But the public "getMembers" method strips that tree-information out and returns a flat list of users. The servlet then does a second fetch of the site's "sakai:authorizables", looks at each user's list of group memberships, and copies the ones which A) are included in the "sakai:authorizables" and B) begin with the string "g-" into a new user property, "member:groups".

Within generic people-picking member-viewing functionality, "site role" might be thought as a special type of Group Category with the following properties:

  • Each container-member must be in one-and-and-only-one group of that category. A person cannot be added to the membership-pool without having a role assigned.
  • The list of groups in the category is set at site creation time.
  • When the groups are given special default treatment in managing software permissions, this needs to be conveyed somehow (even if only by choice of role/relationship name).
  • Roles and relationships tend to be more tightly associated with an individual. For example, we tend to use the language "PERSON is a SINGULAR-NOUN" rather than "PERSON belongs to PLURAL-NAME", and we're more likely to display them under thumbnails or in a directory line.

In implementation terms, "site role" currently involves a mix of complex client-side JavaScript, string parsing, JCR node properties, and Jackrabbit-authz-groups. The UX needs to support a distinction between site-independent Jackrabbit-groups that have been given access to the site and the site-specific role-defining Jackrabbit-groups that divide up a site's membership. The current demo code mostly does this by parsing the Jackrabbit-group name; it looks like that dependency might also be decideable based on the existence of a "sakai:site" property in the "/system/userManager/group/GROUPNAME" node.

Dealing with non-standard permissions

Sakai 3 developers have thus far relied as much as possible on standard Jackrabbit access control lists and JCR privileges to determine runtime access rights. In a number of places, however, a component-specific pseudo-privilege mechanism has shown up. It has not yet been generalized, but works roughly as follows:

  1. Choose a property name. This can't be configurable because the service needs to know which property to look for.
  2. Set a node property with that name to a list of Principal (user and group) IDs.
  3. Later, have your service check that node property. If the current user matches a user or group on the list, then start an administrative session to bypass normal authz checks.

Here are two examples:

  • sakai:sitegroupcreate - Used by the create-site servlet to decide whether the user can create a self-owned site node under "/sites" (or any other location which would otherwise be writeable only by admins)
  • sakai:delegatedGroupAdmin - Used by the group-handling servlets to decide whether the user has rights to change the group's properties and membership.

Along very similar lines, the user access controller added by KERN-759 checks Jackrabbit group nodes for the following two properties, which again point to an list of Principal IDs:

  • rep:group-managers - Determines write access to the group
  • rep:group-viewers - Restricts read access to the group