Permissions Implementation

The permissions have been implemented for a little while now and have proven to be working very well even in a rather complicated environment where various new pages are only accessible to a small group of users.

When a user or a page are linked to permission page, the permission gets added recursively. That means that one page and all of its children, children of children, etc. recursively, all get added.

1) Gathering of User Rights

When gathering the user rights, the permissions plugin first determines the user login capability. The user does not need to currently be logged in. That way we can check permissions from backends, or an administrator could see whether a certain users (or user group) could access a page in various ways.

User permissions depend:

  • User (spammer, visitor, logged in, etc.)
  • Permissions linked to that user
  • Action being checked when user is logged in

In other words, the page against a user permissions are to be checked does not alter the list of permissions for that user. This means user permissions can be cached without consideration of the page being checked (i.e. specific to that user depending on his login status and the action being checked.)

1.1) User is a Spammer

If the user is considered to be a spammer, then only the spammer right is added:

types/permissions/groups/root/administrator/editor/moderator/author/commenter/registered-user/returning-registered-user/returning-visitor/visitor/spammer

This means you cannot add dynamic permissions to those users.

1.2) User is a Returning Visitor

A returning visitor is someone who visited at least one page a while back. Such users get only one right which is a little better than a visitor.

types/permissions/groups/root/administrator/editor/moderator/author/commenter/registered-user/returning-registered-user/returning-visitor

This means you cannot add dynamic permissions to those users.

1.3) User is a Visitor

A visitor is someone who visits the website for the first time. Such a user is viewed as an anonymous user and he gets only one right.

types/permissions/groups/root/administrator/editor/moderator/author/commenter/registered-user/returning-registered-user/returning-visitor/visitor

This means you cannot add dynamic permissions to those users.

1.4) Registered User Returning

A registered user is someone who has an account. The users plugin detects returning users and defines their user key so programmers have full access to the user data. However, a user who is only marked as returning is not currently logged in. The main difference is that most private data is not accessible to returning users and actions are limited to a few things one can do such as change a page title, but not edit the content (what is allowed and not allowed changes on a per website basis, depending on what the owners like to make it.)

Registered users who are returning get a few permissions as follow:

user/<user-identifier> (non-recursive)
types/permissions/groups/root/administrator/editor/moderator/author/commenter/registered-user/returning-registered-user
links matching [permissions::direct::group::returning_registered_user]

The group allows you to have fully dynamic permissions attached to such users.

1.5) Registered User who is Logged In

Currently logged in users get all the permissions assigned to them.

user/<user-identifier> (non-recursive)
types/permissions/groups/root/administrator/editor/moderator/author/commenter/registered-user
rights matching [permissions::direct::group]
links matching [permissions::direct::action::<action-name>]

Group rights are links to pages defining rights. The group links themselves are not considered rights.

The <action-name> is the name of the action being checked. This is because users can be assigned direct rights.

1.6) Other Plugins

That's it for Core. Other plugins may capture the get_user_rights() signal in order to add more rights to the sets. This is not specified here. Remember that permissions gathering is done often and need to be as fast as possible.

2) Gathering of Page Rights

When gathering the page rights, the permissions plugin first determines the direct permissions and then various group permissions. Group permissions are also taken from the page type. In other words, by making a certain page a certain type you give that page a certain set of permissions.

Page permissions depend:

  • Direct permissions linked to that page
  • Non-direct permissions defined in the Page Type
  • Action being checked when testing group permissions

In other words, the user being checked has no effect on the list of rights the permissions plugin is to find for a page. This means page permissions can be cached without consideration of the user being checked (i.e. specific to that page depending on its type and the action being checked.)

2.1) Special Pages: user/###

When accessing a user page, the page is automatically assigned that right. This right is specific to that user and no other users should ever get that specific right so we consider this as being really save.

user/###

Note: registered users, returning or logged in, get this right.

2.2) Renamed Paths / Other Paths

2.2.1) Renamed Paths

The permissions of the path of the page being checked must be read from somewhere. Some plugins will rename the path in which case that very path is used for permission purposes. (search on set_plugin_if_renamed() for examples in existing core plugins; the attachment plugin, for example, understands a path with .gz, for example, and the renamed path could be the same path without the .gz)

The path to be checked is the renamed path, not the original path.

2.2.2) Other Paths

When the path does not get renamed, the page must exist in the database. However, the permissions plugin will test all the parents of that page, counting the number of parents it goes up, and if the found parent allows fully dynamic children at the specified depth, then that parent page permissions are to be gathered.

For example, the permissions of the "char-chart/0" dynamic page (the char_chart plugin offers the complete list of all Unicode characters) are found in "char-chart". When we define the "char-chart" page we add a permission::dynamic field as follow:

<param name="permissions::dynamic" type="int8" revision="global">1</param>

The value 1 represents the depth at which pages are accepted by "char-chart". So a page such as "char-chart/extra/7" is not going to match and will generate a Page Not Found (unless another plugin adds a permission, somehow.)

So, for other paths we are to check the exact path unless it does not exist. If it does not exist, try with a parent path instead. If neither match, another plugin may have additional heuristic but the permissions plugin is done here.

2.3) Direct Permissions with Action

Once we have a specific path determine, we can check whether that page has a set of direct permissions and add those to the list of rights for that page.

These rights are specific to an action. They are checked using the following links:

links matching [permissions::direct::action::<action-name>]

This is actually required because otherwise we could not give any permissions to permission pages themselves.

2.4) Page Type Rights

All pages are assigned a type. That type is used to determine additional rights that a page was given. The page types are viewed as having group rights.

These rights are read in the same way as direct rights, only without the direct namespace.

links matching [permissions::action::<action-name>]

2.5) Page Type Groups

Finally the permission groups are added. These are similar to other rights, only groups means the rights within those pages are added and not the groups themselves.

rights matching [permissions::groups]

2.6) Other Plugins

That's it for Core. Other plugins may capture the get_plugin_permissions() signal in order to add more rights to the sets. This is not specified here. Remember that permissions gathering is done often and need to be as fast as possible.

3) Optimizations

At this time, there are two main optimizations implemented in the gathering of rights.

3.1) Ending Rights With a Slash

In order to quickly compare pages between each others we make sure that all URIs of rights end with a slash. That allows for comparing quickly.

3.2) Shorter Rights

Having two rights, one of which is a parent of another, means that only the parent is required. So for example a user who has the following two rights:

types/permissions/groups/root/administrator/editor
types/permissions/groups/root/administrator/editor/moderator

really only needs to keep the shortest version:

types/permissions/groups/root/administrator/editor

This is done immediately while gathering rights. It then allows for faster computation of the intersection between the user and page rights.

Snap! Websites
An Open Source CMS System in C++

Contact Us Directly