/* * (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.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.RepositoryException; import javax.jcr.Value; import javax.jcr.ValueFormatException; import javax.jcr.security.AccessControlEntry; import javax.jcr.security.AccessControlList; import org.apache.commons.lang.StringUtils; import org.apache.jackrabbit.api.security.JackrabbitAccessControlEntry; import org.apache.jackrabbit.api.security.JackrabbitAccessControlList; 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(AccessControlEntry ace, AccessControlList acl) throws RepositoryException { AceWrapper aceWrapper = new AceWrapper((JackrabbitAccessControlEntry) ace, ( (JackrabbitAccessControlList) acl).getPath()); AceBean aceBean = AcHelper.getAceBean(aceWrapper); return aceBean; } 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); } public static Map<String, Set<AceBean>> getPathBasedAceMap(Set<AceBean> aceBeansFromConfig, int sorting) { final Map<String, Set<AceBean>> pathBasedAceMap = new TreeMap<String, Set<AceBean>>(); for (final AceBean bean : aceBeansFromConfig) { // 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; } /** 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; } public static String valuesToString(Value[] propertyValues) throws RepositoryException { if (propertyValues == null) { return null; } else if (propertyValues.length == 0) { return null; } else if (propertyValues.length == 1) { return propertyValues[0].getString(); } else { throw new IllegalArgumentException( "Unexpectedly received more than one value for a property that is expected to be non-multiple"); } } }