Security Policy Service

Table of Contents

13.1. Introduction
13.2. Architecture
13.3. Policy contributions
13.3.1. Core policy contribution
13.3.2. Search policy contribution

13.1. Introduction

The Security Service provides an extension point to plug custom security policies that do not rely on the document security settings. For instance, it can be used to define permissions according to the document metadata, or to the logger user information.

13.2. Architecture

Policies are set in two places:

  • in the core where custom checks of permission can be performed before resolving the document security using its ACP.

  • in the search where the query can be patched with custom constraints before processing it.

Policies are checked in the order given as a parameter when registering them.

On the core side, they are used when checking permissions: they can grant or deny access, in case following policies - as well as the default security check relying on the ACP set on the document - will be ignored. They can also return an undefined access, in case following policy checks will continue.

When defining a custom policy for the Read permission, queries to the search service have to be adapted to have the same constraints: otherwise some queries will not return documents that the user can see, or will return documents that the user cannot see. Search policies are used when performing any search.

These policies are set on different services and follow different interfaces. When deploying on multi machines environment, where search and core may be hosted on different machines, the core and search policies will have to be deployed on the corresponding machine.

13.3. Policy contributions

13.3.1. Core policy contribution

To register a core security policy, you need to write a contribution specifying the class name of your implementation.

<?xml version="1.0"?>
<component name="org.nuxeo.ecm.core.securityPolicy.accessLevelContrib">

  <extension target="org.nuxeo.ecm.core.security.SecurityService"
    point="policies">

    <policy name="accessLevel"
      class="org.nuxeo.ecm.core.security.AccessLevelSecurityPolicy" order="0" />

  </extension>

</component>

Example 13.1. Sample core security policy contribution


Here is a sample contribution: core policies have to follow the org.nuxeo.ecm.core.security.SecurityPolicy interface.

public class AccessLevelSecurityPolicy extends AbstractSecurityPolicy {

    public Access checkPermission(Document doc, ACP mergedAcp,
            Principal principal, String permission,
            String[] resolvedPermissions, String[] additionalPrincipals) {
        Access access = Access.UNKNOWN;
        try {
            if (principal instanceof NuxeoPrincipal) {
                DocumentModel userModel = ((NuxeoPrincipal) principal).getModel();
                if (userModel != null) {
                    Long accessLevel = (Long) userModel.getPropertyValue("user:accessLevel");
                    Long securityLevel = (Long) doc.getPropertyValue("sp:securityLevel");
                    if (accessLevel < securityLevel) {
                        access = Access.DENY;
                    }
                }
            }
        } catch (Exception e) {
        }
        return access;
    }
}

Here a specific check is done on all documents, regardless of the permission being checked. A property on the document (securityLevel) is checked against a property set on the user model (accessLevel).

Note that the "unknown" access is returned when the policy should not interfere with the standard security check.

13.3.2. Search policy contribution

To register a search policy contribution, you need to write a contribution specifying the class name of your implementation.

<?xml version="1.0"?>
<component name="org.nuxeo.ecm.platform.searchPolicy.accessLevelContrib">

  <extension target="org.nuxeo.ecm.core.search.service.SearchServiceImpl"
    point="policies">

    <policy name="access"
      class="org.nuxeo.ecm.core.search.security.AccessLevelSearchPolicy"
      order="20" />

  </extension>

</component>

Example 13.2. Sample search policy contribution


Here is a sample contribution: search policies have to follow the org.nuxeo.ecm.core.search.api.security.SearchPolicy interface.

public class AccessLevelSearchPolicy extends AbstractSearchPolicy {

    private static final Log log = LogFactory.getLog(AccessLevelSearchPolicy.class);

    public ComposedNXQuery applyPolicy(ComposedNXQuery nxqlQuery)
            throws SearchException {
        SQLQuery query = nxqlQuery.getQuery();
        NuxeoPrincipal principal = null;
        SearchPrincipal sPrincipal = nxqlQuery.getSearchPrincipal();
        if (sPrincipal != null) {
            principal = (NuxeoPrincipal) sPrincipal.getOriginalPrincipal();
        }
        if (principal == null) {
            return nxqlQuery;
        }
        Predicate add = new Predicate(new Reference("sp:securityLevel"),
                Operator.LTEQ, new IntegerLiteral(
                        (Long) principal.getModel().getProperty("user",
                                "accessLevel")));
        if (!query.getWhereClause().toString().contains(add.toString())) {
            WhereClause wc;
            if (query.getWhereClause() != null) {
                wc = new WhereClause(new Predicate(
                        query.getWhereClause().predicate, Operator.AND, add));
            } else {
                wc = new WhereClause(add);
            }
            query = new SQLQuery(query.getSelectClause(),
                    query.getFromClause(), wc, query.groupBy, query.having,
                    query.getOrderByClause());
        }
        return new ComposedNXQueryImpl(query, sPrincipal);
    }
}

As a check is done on the core side regardless of the permission, it will also apply for the "Read" permission which is checked when navigating to a document. Without this policy, searches would return documents that the user may not be able to see, and woul generate a security exception when the user clicks on the document link.

The same check than performed on the core is added here as a contraint to the search query. Note that the principal attached to the query is kept: it is used to perform standard security checks, as the search indexes the document ACP.

For additional information about NXQL queries, please refer to the Section 10.3, “Programmatic Searching” section.