Managing roles, permissions and ACLs

October 8, 2024
Jahia Authorization system is aimed to provide out-of-the-box set of roles and permissions to facilitate fine-grained access control and separation of concerns.

Roles concepts

Permissions

Jahia’s authorization system is based on very granular and low-level permissions. Permission (or Privilege) defines an atomic action that can be performed on a node like reading, writing, deleting, publishing, but also permissions for specific UI like accessing the edit mode, using a specific tab in an engine, or using a specific template to create a page.

Permissions are defined and used by the application or the modules when they need to check if the user is allowed to do an action. They cannot be modified or customized by the administrator.

Permissions are stored as a tree of permission nodes. Top level permissions are aggregates of all sub permissions. Leaves are atomic permissions. For example, the “Edit mode” permission groups all permission of the edit mode UI, including the “edit mode actions” permissions, “edit selector” permissions, … “Edit selector” permission is also a group of more granular permissions, one for each tab of the edit mode selector.

rolestree.png

Basic permissions

The JCR defines a default set of permissions that match the low level operations available on any node. jcr:read, jcr:addChildNodes , jcr:modifyProperties, ... are examples of JCR system permissions.

Jahia uses different permissions for the different workspaces - for every workspace (default and live), jahia provides a distinct set of permissions. So, a user being able to read the live workspace may not be able to read the default (staging) workspace. Permissions are suffixed with the workspace name. For example, in order to add nodes in live workspace, the user should have the jcr:addChildNodes_live permission.

Jahia provides also distinct permission per language, for updating properties. The jcr:modifyProperties is split in multiple sub-permissions, one for every language. The jcr:modifyProperties_default will allow to change any property in the default workspace, but the jcr:modifyProperties_default_en will allow only modification of internationalized properties in English.

In addition, Jahia provides a “Publish” permission which allows to publish 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 permission, that will be 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

These permissions are relative to edit, contribute or studio UI. Every manager, selector tab, engine tab or action item can have an associated permission, that will be required 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 he creates a new page by granting some template permissions. It is also possible to define which component 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 sub-section.

Site administration

All permissions related to site administration panel.

Server administration

All permissions related to server administration panel.

Roles

Roles are groups of permissions, created by the Jahia administrator. They allow to give a predefined set of permissions to users. Roles can be created and modified through the roles manager. Roles can then be assigned to users and/or groups at any location in the content repository. For example, you may define a role “editor” to a specific group in a specific section of the website. They will be able to act as that role only in that specific location in the content repository, and nowhere else. This makes it easy to delegate responsibilities in order to collaborate on content editing, reviewing and overall content management.

Roles organization

Roles are shared for all sites on the platform. They are organized as a tree : a role can have sub roles, which will inherit all the permissions. Sub-roles can define more permission than their parent. For example, the role “editor-in-chief” is a sub role of “editor”, and adds more permissions on the edit UI.

 

Role types and node types association

Roles are organized in “role types”. Roles of the same type share different properties, like the nodes on which they can be applied, the access they may give on shared nodes, and the permissions you may put in them.

By default, 4 role types are defined :

  • live role - give permission for viewing live content, or interact with user generated content
  • edit role - roles for editors and contributors
  • site role - site-level administration roles
  • server role - global administration roles

Live and edit roles can be applied on any node through the permission tabs, whereas site roles can be applied only on site nodes, and server roles only on the server.
For each live and edit roles, it’s possible to limit on which node types they can be applied by changing the “nodetypes” property within the role edition page. These roles will appear only for the specified node types.

roletypes.png

Privileged roles

Any user who needs to access edit or contribute mode need to have an access on the default workspace of the repository. This is required in order to be able able to read the templates, files, and other resources of the site from the default workspace. This is called privileged access.

For every site, a group site-privileged is created - this group has read access on the root node of the site, through the privileged system role. This role is inherited to all sub-nodes of the site, so If a section or a folder need to be set as private - the "break all inheritance" flag should be used.

The shared privileged group has read access on all shared resources, like the system site, categories, templates, .. Every site-privileged group is member of this group - so any user member of a site-privileged group can also get shared resources.

Depending on its role type, a role can define the property j:privilegedAccess . Any user which is granted a role with privileged access will be added as a member of a site-privileged group.

The “edit-role”, “site-role” and “server-role” types have a privileged access. As long as a user is granted a privileged access role on any node, he will be part of the site-privileged group and will have privileged access.

In the following example, the user is author of section 1. The user is then member of the site-privileged group of the site siteA, and have read access on siteA. He also have read access on shared resources.

Role scopes and external permissions

A role is always granted on a specific node and gives permissions to the user on that node, the context node. However, a role can also have additional scopes and give global permissions on the associated site or on the server. Depending on the role type, additional scopes are pre-configured. For example, in an edit-role, you’ll find 2 scopes : current node and current site. Basic permissions are given on the context node. As the UI permissions are only valid on a site, they will be given on the context site. These UI permissions, which are not applied on the context node but on another node, are called external permissions.

 

Access control lists

An ACL (access control list) defines which roles are granted to a user or group on a specific node. It defines the access policy on a node and its sub-tree : ACLs are inherited on all sub-nodes. ACL are structured in multiple Access Control Entries - one entry for every user and every group that need to have a role.

ACL are stored as a jnt:acl sub-node on the node where it is set. It contains a list of jnt:ace.

Roles are assigned to users and groups by the editor, through the Roles tab of the node engines. Each line in the table corresponds to an entry in the ACL.

A user will have a specific permission if he (or a group he's member of) has been assigned at least one role containing that permission.

It is possible to partially break the role inheritance at one level by "removing" the role to a user or a group. In that case, the ACE node will contain "DENY" in the j:aceType property.

In order to completely redefine ACL on a node, the "Break all inheritance" can be used. The property j:inherit is set to false, and the permission check will not look into the ancestors ACLs.

 

Granting roles

Granting live/edit roles to nodes

Live and edit roles are granted to individual nodes by using the “live roles” or “edit roles” tab in the edit engine.

 

Granting server or site roles

Granting site roles is done in the site settings selector, in the site roles panel. The same interface is available for server roles in server administration, Users and Roles / Server roles.

Roles inheritance

Roles are automatically inherited on sub nodes. When editing roles on sub node, the last column shows from where the role is inherited. It’s possible to remove the granted role at any level by clicking on the red X :

The user/group name appear in strikethrough text, and restore icon allows to put the role back.

In order to fully break all inheritance for all roles - including the one that are not visible, like privileged roles, you need to use the “break all inheritance” button. The current user will be automatically granted editor in chief role, and nobody else will be able to access the content.

Roles/permissions related rules

Several rules are provided for an event-driven role/permission management in an automated way. The list of possible rule consequences (actions) can be found in the WEB-INF/etc/repository/rules/rules.dsl file, which contains rule consequences in JBoss Rules Domain Specific Language, like:

  • Grant role {role} on the {node} to the current user * Grant role {role} on the {node} to the user {user} * Revoke role {role} from everybody on the {node}

Checking permissions

Jahia provides ways to use the role/permission concept in JSP templates as well as in custom Java services.

JSP templates

With functions included in the <Jahia JCR> taglib (uri: http://www.jahia.org/tags/jcr) template developers can restrict access to particular areas of their 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 will automatically attach 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 property requiredPermission is set on an action, the Jahia controller will check the permission before trying to call the doExecute method.

For instance, the action to create a new blog entry is defined with the requiredPermission property :

<bean class="org.jahia.modules.blog.AddBlogEntryAction">
    <property name="name" value="addBlogEntry">
    <property name="requiredPermission" value="addBlogEntry">
</bean>

Views

The properties file associated to a view can contain a requirePermissions property. If this property is set, the RenderService will check that permission before calling the script.

In the example module contact, the file contactForm.properties associated to contactForm.jsp contains the following line :

requirePermissions=viewContacts

Technical details

Permissions tree

Core permissions are stored in the /permissions node. Module which defines permissions have a sub-node permissions which contains them, and are only available when the module is deployed and started.

Although they are organized as a tree, a permission is always referred to by using its name - which means there should never be multiple permissions with the same name, even in different modules. If a module declares an already existing permission it will be ignored.

Roles are all stored in the /roles node, organized in a tree as explained earlier. Roles can also have jnt:externalPermissions sub-nodes, which contains the additional scopes and the associated permissions. Roles can be modified by the administrator with the “roles and permissions” administration panel.

Roles and permissions are only visible in the default workspace, and are never published.

In order to give a role to a user on a node, the node needs to have the jmix:accessControlled mixin type, which define a j:acl subnode. The access control list contains a list of entries (jnt:ace), one for each user/group, listing the roles that are granted or removed to the user/group.

If the permission was granted through an external permission, the entry has the type jnt:externalAce and maintain a link to the associated ace node, which was set by the user.

Permission check algorithm

The permission check happens in the JahiaAccessManager class. This class is called by jackrabbit when checking a permissions. It internally checks repository permissions like read, write when the JCR API is used. The method isGranted checks if the current user has a set permissions for a given path. In order to do that, the method will iterate 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 of giving or removing privileged access when roles are granted or removed, and of settings external permissions based on the roles that were granted.

If the editor role is granted to john, the listener do the following :

  • editor role is a privileged role : the listener will add john to the site-privileged group if he’s not already in it.
  • 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 to a user, the listener checks if the user still have 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 will be privileged, on which node types it can apply, what are the predefined additional scopes and which permissions will be available.