Confluence Permissions Architecture
Permissions checking overview
In Confluence, a permission check is a question like does user U have permission to do action A to content C? The way we answer that question deserves a brief overview of the logical operations:
- First, Confluence checks that the user is allowed to access the application. This involves user and group checks for user U against the defined global permissions.
- Second, Confluence checks space permissions. This involves user and group checks for user U against the space permissions for action A for the space containing content C.
- Finally, Confluence checks content level restrictions like page permissions. This involves user and group checks for user U against the content level permissions for action A on content C.
- If all three checks succeed, user U is permitted to do action A to content C by Confluence.
The logical operations involved in a "user and group check for user U" look like this, taking space permissions as an example:
- First, Confluence retrieves all the space permissions for the space containing content C from the database.
- Next, it gets the groups that user U is a member of and checks if each of the group has permission required to do action A.
- Next, it checks whether user U is one of the individual users that has been granted the permissions required to do action A.
- If the membership status of user U and the group isn't cached already, Confluence determines which user repository (database, LDAP, Crowd) owns the group. Confluence checks in the user repository whether user U is a member of the group, and caches the result for subsequent checks.
- If either check succeeds – that is, if either user U or one of the her groups has permission for the action – user U is permitted to do action A to content C by Confluence.
The API used for performing all these checks is described in more detail below.
The PermissionManager API
The core API for checking permissions in Confluence is through the
PermissionManager (javadoc). The two most important methods on this interface are:
hasPermission– does user U have permission P on object O?
hasCreatePermission– does user U have permission to create object of type T inside container C?
So, for example. If you have a page, and want to determine if a user is able to edit it:
Or, if you want to know if user is permitted to comment on a page:
- Permissions are defined as constants on the
Permissioninterface (javadoc). They are VIEW, EDIT, EXPORT, REMOVE, SET_PERMISSIONS and ADMINISTER.
- If the supplied user is null, the anonymous permission is checked
- For the purpose of checking create permissions, the "containing" object is not the same as the parent. You test if a page can be created in a space, and a comment within a page, not within its parent page or comment.
- There is a special object –
PermissionManager.TARGET_APPLICATION– that represents Confluence itself and is used for checking global permissions
- Some permission checks don't make sense, for example checking if you can
TARGET_APPLICATION, or checking if you can administer a page. Checking a nonsensical permission will result in an
- Similarly, if you check permissions against a type of object that the
PermissionManagerdoesn't know how to check permissions against (i.e. it doesn't have a delegate for that class, see below), it will throw an
The system does not cater for any inheritance of permissions. having
Permission.ADMINISTER against an object does not imply that you also have
However, certain permissions are considered "guard permissions". For example, permission to
TARGET_APPLICATION is required to do anything in Confluence (it's generally referred to as "Use Confluence" permission). Similarly, permission to
VIEW a particular space is required to do anything else in that space. If you are modifying Confluence permissions through the UI, removing a guard permission from a user or group will also remove any dependent permissions that user/group might have. If you are modifying Confluence permissions programatically, you are responsible for making sure they end up in a sensible state w.r.t guard permissions.
- The PermissionManager always checks to ensure a user is not deactivated, and that a user has the "Use Confluence" guard permission.
- The PermissionManager does not check if the user is a member of the super-user
confluence-administratorsgroup. If you want super-users to override your permission check, you have to do it manually.
For every type of target object (or container in the case of create permissions) there is a corresponding
PermissionDelegate (javadoc) that performs the actual checks. The code should be reasonably self-explanatory
Getting all viewable/editable spaces for a user
Finding all spaces for which the user has a particular permission is a common, and reasonably expensive operation in instances with large numbers of spaces. For this reason we have a number of shortcut methods on
SpaceManager that go straight to the database:
- getPermittedSpaces – get all spaces for which a user has
- getPermittedSpacesByType – get all spaces of a certain
SpaceTypefor which the user has
- getSpacesEditableByUser – get all spaces in which the user can create or edit pages
- getEditableSpacesByType – get all spaces of a certain
SpaceTypein which the user can create or edit pages
Note: These operations are still not cheap, especially in situations where the user being checked may be a member of a large number of groups.
Searching / Lucene
The Lucene index contains enough information for searches to determine if particular results are visible to the user performing the search. So long as you're not going direct to the Lucene index yourself, and use one of Confluence's search APIs to find content, the content returned should not require any more tests for VIEW permission.
Checking Permissions from Velocity
It might be difficult (or even impossible) to construct a required
PermissionManager call from velocity code, especially for calls to the
hasCreatePermission() method. For this reason there is an object called
permissionHelper (javadoc) in the default velocity context with a number of helper methods to perform common permission checks.
If you can not find an appropriate method on the
PermissionHelper, your best course of action is to write a Velocity context plugin to encapsulate your permission checking code (or if you're an Atlassian developer, obviously, just add it to the helper).
Other Permission-related APIs
PermissionCheckDispatcher allows you to check if a particular user has access to a certain Confluence URL. It will only work if the target of the URL is a WebWork action (it works by instantiating the action referred to by that URL, filling in all the relevant form values, and calling
isPermitted on the action).
PermissionCheckDispatcher used to be the preferred way of testing whether or not to display a link in the web UI. However, its use is being phased out because it can be very slow. Do not use the
PermissionCheckDispatcher for new code. Instead, use the
PermissionManager directly. If you are in UI code, use the
PermissionHelper (javadoc), a convenience class that is placed in the Velocity context to make permission checks more Velocity-friendly.
SpacePermissionManager is a low-level API for directly manipulating user permissions. You should not use the
SpacePermissionManager for checking permissions, because it tightly couples your permission check to the internal representation of permissions in the database. Use the
PermissionManager for all permission checks.
SpacePermissionManager should only be used:
- By a
PermissionDelegateto translate between a logical permission check, and the back-end implementation of that permission
- By permissions management code (i.e. granting, revoking or displaying user permissions)
Adding New Permissionable Objects
To make it possible to use a new type of object as the subject of a permissions check, you will need to:
- write a
PermissionDelegatefor that object's class.
- instantiate the delegate in Spring (delegates are defined in
- add that object to
Adding New Permissions
- Ask if this permission is really necessary? For example a lot of things that look like they should be permissions are really "create" permissions (like "can comment on page" is really "can create (comment, page)"
- Add a new method to the
- For each existing
PermissionDelegate, implement your new method. Throw
IllegalStateExceptionif the permission is not relevant to that delegate's object
- Add a new constant to the
Permissioninterface to represent your permission (see the existing examples)
- Permissions checking on labels (add, remove) are broken, and still being done via page permission checks
- We should probably throw
UnsupportedOperationExceptionfor bogus checks instead of
- Currently create permissions are tested against container, not parent. a
hasCreatePermission(user, container, parent, klass)could be useful