biz.netcentric.cq.tools.actool.helper.AcHelper.java Source code

Java tutorial

Introduction

Here is the source code for biz.netcentric.cq.tools.actool.helper.AcHelper.java

Source

/*
 * (C) Copyright 2015 Netcentric AG.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 */
package biz.netcentric.cq.tools.actool.helper;

import java.security.Principal;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

import javax.jcr.AccessDeniedException;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.UnsupportedRepositoryOperationException;
import javax.jcr.Value;
import javax.jcr.ValueFormatException;
import javax.jcr.query.InvalidQueryException;
import javax.jcr.query.Query;
import javax.jcr.query.QueryResult;

import org.apache.commons.lang.StringUtils;
import org.apache.jackrabbit.api.JackrabbitSession;
import org.apache.jackrabbit.api.security.user.Authorizable;
import org.apache.jackrabbit.api.security.user.UserManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import biz.netcentric.cq.tools.actool.comparators.AcePermissionComparator;
import biz.netcentric.cq.tools.actool.configmodel.AceBean;
import biz.netcentric.cq.tools.actool.configmodel.Restriction;

public class AcHelper {
    public static final Logger LOG = LoggerFactory.getLogger(AcHelper.class);

    private AcHelper() {
    }

    /** By default ACEs with denies are sorted up to the top of the list, this follows the best practice to order denies always before
     * allows - this makes by default allows always take precedence over denies.
     *
     * Denies should be used sparsely: Normally there is exactly one group that includes all deny-ACEs for to-be-secured content and many
     * groups with allow-ACEs, that selectively allow what has been denied by the "global deny" group.
     *
     * For some special cases (e.g. when working with restrictions that limit a preceding allow) it is possible to specify "keepOrder=true",
     * for those cases the natural order from the config file is kept when {@link #ACE_ORDER_ACTOOL_BEST_PRACTICE} is used. */
    public static int ACE_ORDER_ACTOOL_BEST_PRACTICE = 1;

    /** Retains order of ACEs in ACLs. */
    public static int ACE_ORDER_NONE = 2;

    /** Sorts ACEs in ACLs alphabetical. */
    public static int ACE_ORDER_ALPHABETICAL = 3;

    public static int PRINCIPAL_BASED_ORDER = 1;
    public static int PATH_BASED_ORDER = 2;

    public static AceBean getAceBean(final AceWrapper ace)
            throws ValueFormatException, IllegalStateException, RepositoryException {
        final AceBean aceBean = new AceBean();

        aceBean.setPermission(ace.isAllow() ? "allow" : "deny");
        aceBean.setJcrPath(ace.getJcrPath());
        aceBean.setPrincipal(ace.getPrincipal().getName());
        aceBean.setPrivilegesString(ace.getPrivilegesString());

        List<Restriction> restrictions = buildRestrictionsMap(ace);
        aceBean.setRestrictions(restrictions);
        return aceBean;
    }

    private static List<Restriction> buildRestrictionsMap(final AceWrapper ace)
            throws RepositoryException, ValueFormatException {
        final String[] restrictionNames = ace.getRestrictionNames();
        final List<Restriction> restrictionsList = new ArrayList<Restriction>();
        for (final String restrictionName : restrictionNames) {
            final Value[] values = ace.getRestrictions(restrictionName);
            String[] strValues = new String[values.length];
            for (int i = 0; i < strValues.length; i++) {
                strValues[i] = values[i].getString();
            }
            restrictionsList.add(new Restriction(restrictionName, strValues));
        }
        return restrictionsList;
    }

    public static String getBlankString(final int nrOfBlanks) {
        return StringUtils.repeat(" ", nrOfBlanks);
    }

    /** Method that searches a group by nodename or by ldap attribute 'cn' inside the rep:principal property of a group node. Serves as a
     * fallback, in case a group can't be resolved by principal manager by its name provided in config file after ldap import
     *
     * @param session
     * @param aceBean
     * @return found Principal or null
     * @throws InvalidQueryException
     * @throws RepositoryException */
    public static Principal getPrincipal(final Session session, final AceBean aceBean)
            throws InvalidQueryException, RepositoryException {
        Principal principal = null;
        final String principalName = aceBean.getPrincipalName();

        principal = getPrincipalForName(session, principalName);

        if (principal == null) {
            final String query = "/jcr:root" + Constants.GROUPS_ROOT
                    + "//*[(@jcr:primaryType = 'rep:Group') and jcr:like(@rep:principalName, 'cn=" + principalName
                    + "%')]";
            LOG.debug(
                    "Fallback query did not return results for principalName={}, using second fallback query (ldap name): {}",
                    principalName, query);
            principal = getPrincipalByQuery(query, session);
        }

        LOG.debug("Returning {} for principal {}", principal, principalName);
        return principal;
    }

    private static Principal getPrincipalForName(final Session session, String principalName)
            throws AccessDeniedException, UnsupportedRepositoryOperationException, RepositoryException {
        Principal principal = null;
        // AEM 6.1 has potentially a delayed visibility of just created groups when using PrincipalManager, therefore using UserManager
        // Also see https://issues.apache.org/jira/browse/OAK-3228
        final JackrabbitSession js = (JackrabbitSession) session;
        final UserManager userManager = js.getUserManager();
        final Authorizable authorizable = userManager.getAuthorizable(principalName);
        principal = authorizable != null ? authorizable.getPrincipal() : null;
        return principal;
    }

    private static Principal getPrincipalByQuery(final String queryStringGroups, final Session session)
            throws InvalidQueryException, RepositoryException {

        final Query queryGroups = session.getWorkspace().getQueryManager().createQuery(queryStringGroups,
                Query.XPATH);
        final QueryResult queryResultGroups = queryGroups.execute();
        final NodeIterator nitGroups = queryResultGroups.getNodes();
        String principalName;
        if (!nitGroups.hasNext()) {
            LOG.debug("Executing query '{}' did not have any results", queryStringGroups);
            return null;
        }
        final Node node = nitGroups.nextNode();

        if (node.hasProperty("rep:principalName")) {
            principalName = node.getProperty("rep:principalName").getString();
            final Principal principal = getPrincipalForName(session, principalName);
            return principal;
        }
        LOG.debug("Group '{}' did not have a rep:principalName property", node.getPath());

        return null;
    }

    /** changes a group based ACE map into a path based ACE map
     *
     * @param groupBasedAceMap the group based ace map
     * @param sorting specifies whether ACEs get sorted by permissions (all denies followed by all allows)
     * @return the path based ace map */
    public static Map<String, Set<AceBean>> getPathBasedAceMap(final Map<String, Set<AceBean>> groupBasedAceMap,
            final int sorting) {
        final Map<String, Set<AceBean>> pathBasedAceMap = new TreeMap<String, Set<AceBean>>();

        // loop through all Sets of groupBasedAceMap
        for (final Entry<String, Set<AceBean>> entry : groupBasedAceMap.entrySet()) {
            final String principal = entry.getKey();

            // get current Set of current principal
            final Set<AceBean> tmpSet = entry.getValue();

            for (final AceBean bean : tmpSet) {

                // set current principal
                bean.setPrincipal(principal);

                // if there isn't already a path key in pathBasedAceMap create a
                // new one and add new Set
                // with current ACE as first entry
                if (pathBasedAceMap.get(bean.getJcrPath()) == null) {

                    Set<AceBean> aceSet = null;
                    if (sorting == AcHelper.ACE_ORDER_NONE) {
                        aceSet = new LinkedHashSet<AceBean>();
                    } else if (sorting == AcHelper.ACE_ORDER_ACTOOL_BEST_PRACTICE) {
                        aceSet = new TreeSet<AceBean>(new AcePermissionComparator());
                    }

                    aceSet.add(bean);
                    pathBasedAceMap.put(bean.getJcrPath(), aceSet);
                    // add current ACE to Set
                } else {
                    pathBasedAceMap.get(bean.getJcrPath()).add(bean);
                }
            }
        }
        return pathBasedAceMap;
    }

}