Sunday 20 November 2011

Permissioning

Introduction #

This article explains the inner details of how the permissions system works in Liferay. And it does so by examining each of the tables (and therefore the entities) and the relationships that are involved in the security logic. The following ERD diagram shows the relationships among all the entities that are going to be explained:

Basic entities #

Let's start by examining a few tables called "entity" tables. In other words, they define entities in the system that can have permissions/roles attached to them.

User_ #

The most obvious entity is the User. In the simplest terms, permissions is all about asking the question, "Does this user have permission to do this action on this thing?"

Organization_ #

Users can belong to only one Organization and one Location. The confusing thing about the data model is that we use the same table to represent both. Basically, if the parentOrganizationId column has a -1, then it's an Organization, and if it's any other number, it's a Location. The "only one Organization and one Location" rule is maintained through logic in our code...not through data integrity. The logic also enforces that the Organization that a User belongs to must be the parent of the Location that the user belongs to. Users inherit permissions/roles from the Organization and Location that they belong to.

UserGroups #

Users -> UserGroups (Collection of Users)
Users can inherit permissions/roles from UserGroups. In Liferay 4.4, parentUserGroupId column is not yet being used.

Group_ #

Group is the old name for what are now called Communities. Users can belong to any number of Communities and inherit permissions/roles from them. Notice that in the Group_ table, there is a className and classPK column. If className and classPK are blank, then the record is a Community. If className is com.liferay.portal.model.User, then the record represents a private user community (only applies to Power Users). If className is com.liferay.portal.model.Organization, then the record represents an Organization or Location. If className is com.liferay.portal.model.UserGroup, then the record represents a UserGroup.
The reason for having Group records representing Organizations/Locations and UserGroups is to have one entry in this table for each entity in the system that represents a set of users. This simplifies relationships of other entities (such as permissions or roles) with these sets of users.

Permissions and Roles #

A permission is defined as an ACTION acting on a RESOURCE. Permissions can be assigned directly to a User or inherited through different means. A collection of permissions is known as a Role.

Resource_ #

A resource is a representation of an object in the portal...anything that you'd want to slap a permission on. It could be a portlet, a community, a user, a message board topic, a blog entry...whatever. Let's take a quick look at each of the columns in the Resource_ table:
  • resourceId = The unique id of the resource
  • companyId = The company that this resource belongs to
  • name = The "name" of the object that this resource represents. If the object is a portlet, it's the portletId. If it's a class, it's the fully qualified package name of the class
  • typeId = For the time being, we're only concerned with permissioning classes, so this value will always be "class". However, in the future, we might start permissioning files or folders, so typeId might take on the values of "file" or "folder".
  • scope = The possible values are "company" (we refer to it as "Enterprise Scope"), "group" (we refer to it as "Community Scope"), or "individual" (we refer to it as "Individual Scope"). Why the different naming conventions? Because things change... The numeric values for scope (as found in the database) can be found in the class com.liferay.portal.model.impl.ResourceImpl

Permission_ #

As stated earlier, a permission is an action acting on a resource. Therefore, the Permission_ table has an actionId and resourceId column. As expected, the resourceId column references the resourceId column from the Resource_ table. However, the actionId column doesn't have a corresponding Action_ table. All actionIds are defined in the ActionKeys class. Wanna know how to define new actions for resources? Read Implementing Permissions

Role_ #

This is the table where a role is defined. Really, the only important column is the name column because roles must have unique names.

Roles_Permissions #

This is the relational table that links a permission to a role. Without these links, roles would be useless...they'd just be empty containers.

Assignment of users to communities and organizations #

Users_Groups #

This is the relational table that links a User to a Community. You're probably wondering why we don't have a className and classPK column in this table so we could handle all entities (e.g., Communites, Organization/Locations, UserGroups) in one table. Again, too hard to explain, but trust us...it's better this way.

Users_Orgs #

This is the relational table that links a User to an Organization/Location.

Users_UserGroups #

This is the relational table that links a User to a UserGroup.

Assignment of roles and permissions #

Users_Permissions #

This is the relational table that directly links a permission to a user.

Users_Roles #

This is the relational table that links a role to a user. The user then inherits all the permissions linked to that role (via Roles_Permissions).

Groups_Permissions #

This is the relational table that links a permission to a Group. Remember earlier in our discussion how we said a Group_ record could either represent a Community, Organization/Location, or UserGroup? Well, this is the table that directly links those permissions to each of these entities. And then of course, users that belong to these entities would inherit the permissions. Notice there is no Orgs_Permissions or UserGroups_Permissions tables. Groups_Permissions is enough to handle all cases. Maybe now it makes more sense why this is simpler.

Groups_Roles #

This is the relational table that links a role to a Group. Just like for Groups_Permissions, a Group could either refer to a Community, Organization/Location, or UserGroup. Users that belong to these "Groups" would then inherit the permissions from the corresponding roles.

Groups_Orgs #

This is the relational table that links Organizations/Locations to Communities. In other words, an admin can assign every User that is part of a particular Organization or Location to a Community. As a result, any permissions or roles assigned to that Community would now be inherited by every User in the selected Organization/Location

Groups_UserGroups #

Exactly the same reasoning as Groups_Orgs, except for UserGroups

OrgGroupPermission #

Here's the oddball of the bunch. This table is used for "Exclusive Permissions". Basically, a user has to belong to a particular Location (or Organization since Liferay v4.4) AND a particular Community to receive this permission. By the way, though the OrgGroupRole table exists, it is never used in our code.
Let's see why this option is useful with an example. Within a community there is a message boards, and the administrator wants to set up a category of the message boards so that only the users that belong to a given location can post to it. So he clicks the 'Permissions' icon for that category and selects the appropriate location. Now ALL users of the location will be able to post, regardless of whether they are members of the community or not.
In some circumstances, the administrator may want to restrict this further, saying that the user must also belong to the organization to post to that category. This is done by setting "Exclusive Permissions."

No comments:

Post a Comment