Permissions and roles deep dive
This topic shows how to check for permissions in JSP templates, custom Java services, and actions, and describes permissions types. The topic also provides technical details on the permissions tree and permissions check algorithm, and on the ACL listener.
Implementing permission checks
Jahia provides ways to check for permissions in JSP templates, custom Java services, and actions.
JSP templates
With functions included in the <Jahia JCR> taglib template (see Downloading Javadoc and Taglib doc for Jahia), you can restrict access to particular areas of your templates. Here is a simple example that show the usage of such a function.
<c:if test="${jcr:hasPermission(currentNode,'jcr:write')}">
The user is allowed to modify the node
</c:if>
Notice that the permission is used without the workspace name suffix. This is because Jahia automatically attaches the current workspace name when resolving the permission.
Java API
A permission is always checked for a user on a specific node. The JCR AccessControlManager
API provides a method to check permissions on a node.
boolean AccessControlManager.hasPrivileges(String absPath, Privilege[] privileges)
A convenient method is also provided in JCRNodeWrapper
to check a single permission.
boolean hasPermission(String permission)
Global permissions are checked on the root node. Site permissions can be checked on the site node.
Custom actions
If the requiredPermission
property is set on an action, the Jahia controller checks the permission before trying to call the doExecute
method. For example, the action for creating a new blog entry is defined with the setRequiredPermission
method.
@Component(service = RateContent.class)
public class AddBlogEntryAction extends Action {
JCRTemplate jcrTemplate;
@Activate
public void activate() {
setName("addBlogEntry");
setRequiredPermission("addBlogEntry");
}
@Reference
public void setJcrTemplate(JCRTemplate jcrTemplate) {
this.jcrTemplate = jcrTemplate;
}
@Override
public ActionResult doExecute(HttpServletRequest req, RenderContext renderContext, final Resource resource, JCRSessionWrapper session, final Map<String, List<String>> parameters, URLResolver urlResolver) throws Exception {
return (ActionResult) jcrTemplate.doExecuteWithSystemSession(null,session.getWorkspace().getName(),session.getLocale(),new JCRCallback<Object>() {
public Object doInJCR(JCRSessionWrapper session) throws RepositoryException {
// .... logic to add blog entry
}
});
}
}
Views
The properties file associated with a view can contain a requirePermissions
property. If this property is set, the RenderService checks that permission before calling the script. In the example module contact, the contactForm.properties
file associated with the contactForm.jsp
contains the following line.
requirePermissions=viewContacts
About permission sets
Permissions are stored as a tree of permission nodes. Top level permissions are aggregates of all subpermissions. For example, the Edit mode permission includes all the granular permissions related to the edit mode UI, including Edit mode access, Edit mode actions, Edit selector and more.
Basic permissions
The JCR defines a default set of permissions that match the low level operations available on any node. For example, jcr:read
, jcr:addChildNodes
, and jcr:modifyProperties
are examples of JCR system permissions.
Jahia provides a distinct set of permissions for the default and live workspaces. A user who can read the live workspace may not be able to read the default (staging) workspace. Permissions are suffixed with the workspace name. For example, to add nodes in live workspace, a user requires the jcr:addChildNodes_live permission
.
Jahia provides also distinct permission by language for updating properties. The jcr:modifyProperties
is split in multiple subpermissions, one for each language. jcr:modifyProperties_default
will allow users to change any property in the default workspace, but jcr:modifyProperties_default_en
will allow only modification of internationalized properties in English.
In addition, Jahia provides a Publish
permission which allows publishing content in live. Note that you may not need to give this permission to any user if you use the publication workflow to validate content before publishing.
Permissions on modules
A module with a complex application can define any number of specific permissions, which are checked by module specific actions, or directly by the JSPs.
Workflow permissions
Each workflow task is mapped to a permission. Any user who has the associated permission when a task is created will have the right to take and complete the task.
UI permissions
UI permissions are relative to the edit, contribute, or studio UIs. Every manager, selector tab, engine tab, or action item can have an associated permission that is required to be able to use it. All UI permissions are checked at the site level, meaning that an access to the edit mode cannot be limited to a specific section of the site.
Template and components permission
You can refine which template a user can use when they creates a new page by granting template permissions. You can also define which components a user can manipulate by giving components permissions. Like the UI permissions, these permissions are checked on the site level. You cannot constrain some templates on a site subsection.
Site administration
Site administration permissions are related to the site administration panel.
Server administration
Site administration permissions are related to the server administration panel.
Technical details
Permissions tree
Core permissions are stored in the /permissions
node. Modules that define permissions have subnode permissions that contain them. They are only available when the module is deployed and started.
Although they are organized as a tree, a permission is always referred by its name. This means that there should never be multiple permissions with the same name, even in different modules. If a module declares a permission that already exists, it will be ignored.
Roles are all stored in the /roles
node, organized in a tree. Roles can also have the jnt:externalPermissions
subnode that contain additional scopes and associated permissions. An administrator can modify roles in Administration>Users and Roles>Roles and permissions. Roles and permissions are only visible in the default workspace and are never published.
For a role to be available to a user on a node, the node needs to have the jmix:accessControlled
mixin type, which defines a j:acl
subnode. The access control list contains a list of entries (jnt:ace
), one for each user and group, that define the roles that are granted or denied for the user or group.
If a permission was granted through an external permission, the entry has the jnt:externalAce
type and maintains a link to the associated ACE node that was set by the user.
Permission check algorithm
The permission check occurs in the JahiaAccessManager
class. This class is called by Jackrabbit when checking a permission. It checks internal repository permissions, like read and write, when the JCR API is used. The method isGranted
checks if the current user has a set permission for a given path. To do so, the method iterates on the node and its parent until all requested permissions find a match in the different ACEs. The iteration is done in the recurseOnACPs()
method. Here’s the detailed algorithm.
def foundRoles
while (true)
if (node has an acl) do
for each (ace in acl) do
if (ace matches current user) do
for each (role in ace) do
if (role is not foundRoles) do
add role in foundRoles
if (ace type is "granted") do
remove all permission in role from permissions
if (permissions is empty) do
return true
end if
end if
end if
end for
end if
end for
if (acl breaks all inheritance) do
return false
end if
end if
if (node is root node)
return false
end if
node = parent of node
end while
ACL listener
ACL listener is a JCR listener responsible for granting or removing privileged access when roles are granted or removed and for setting external permissions based on the roles that were granted. For example, if the editor role is granted to John, the listener does the following when:
- the editor role is a privileged role
the listener adds John to the site-privileged group if he’s not already in it. - the editor role has an external permission on the current site
the listener checks if the current site already has an external ACE for the role editor and the user John. If not, it creates it with the external permission defined in the editor role.
When removing a role for a user, the listener checks if the user still has the role anywhere else on the site with a query before removing it from the site-privileged group and deleting the external ACE.
Defining role types
Roles types are defined in mod-rolesmanager.xml
file in the Roles Manager module. For each role type, an entry defines if the roles are privileged, on which node types it can apply, what the predefined additional scopes are, and which permissions are available.