/* * (C) Copyright 2016 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.aceinstaller; import static biz.netcentric.cq.tools.actool.history.AcInstallationLog.msHumanReadable; import java.security.Principal; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.UnsupportedRepositoryOperationException; import javax.jcr.ValueFormatException; import javax.jcr.security.AccessControlManager; import javax.jcr.security.Privilege; import org.apache.commons.lang.time.StopWatch; 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; import biz.netcentric.cq.tools.actool.helper.AccessControlUtils; import biz.netcentric.cq.tools.actool.helper.ContentHelper; import biz.netcentric.cq.tools.actool.helper.RestrictionsHolder; import biz.netcentric.cq.tools.actool.history.AcInstallationLog; /** Base Class */ public abstract class BaseAceBeanInstaller implements AceBeanInstaller { private static final Logger LOG = LoggerFactory.getLogger(BaseAceBeanInstaller.class); @Override public void installPathBasedACEs( final Map<String, Set<AceBean>> pathBasedAceMapFromConfig, final Session session, final AcInstallationLog history, Set<String> principalsToRemoveAcesFor, boolean intermediateSaves) throws Exception { StopWatch stopWatch = new StopWatch(); stopWatch.start(); final Set<String> paths = pathBasedAceMapFromConfig.keySet(); history.addVerboseMessage(LOG, "Found " + paths.size() + " paths in config"); LOG.trace("Paths with ACEs: {}", paths); if (intermediateSaves) { history.addMessage(LOG, "Will save ACL for each path to session due to configuration option intermediateSaves=true - " + "rollback functionality is disabled."); } // loop through all nodes from config for (final String path : paths) { final Set<AceBean> aceBeanSetFromConfig = pathBasedAceMapFromConfig .get(path); // Set which holds the AceBeans of the current path in configuration // check if the path even exists final boolean pathExits = AccessControlUtils.getModifiableAcl(session.getAccessControlManager(), path) != null; if (!pathExits) { if (!ContentHelper.createInitialContent(session, history, path, aceBeanSetFromConfig)) { history.addVerboseMessage(LOG, "Skipped installing privileges/actions for non existing path: " + path); history.incCountAclsPathDoesNotExist(); continue; } } // order entries (denies in front of allows) final Set<AceBean> orderedAceBeanSetFromConfig = new TreeSet<AceBean>( new AcePermissionComparator()); orderedAceBeanSetFromConfig.addAll(aceBeanSetFromConfig); installAcl(orderedAceBeanSetFromConfig, path, principalsToRemoveAcesFor, session, history); if (intermediateSaves && session.hasPendingChanges()) { history.addVerboseMessage(LOG, "Saved session for path " + path); session.save(); } } history.addMessage(LOG, "Finished installation of " + paths.size() + " ACLs in " + msHumanReadable(stopWatch.getTime()) + " (changed ACLs=" + history.getCountAclsChanged() + " unchanged ACLs=" + history.getCountAclsUnchanged() + " path does not exist=" + history.getCountAclsPathDoesNotExist() + " action cache hit/miss=" + history.getCountActionCacheHit() + "/" + history.getCountActionCacheMiss() + ")"); } /** Installs a full set of ACE beans that form an ACL for the path * * @throws RepositoryException */ protected abstract void installAcl(Set<AceBean> aceBeanSetFromConfig, String path, Set<String> authorizablesToRemoveAcesFor, Session session, AcInstallationLog history) throws RepositoryException; protected boolean installPrivileges(AceBean aceBean, Principal principal, JackrabbitAccessControlList acl, Session session, AccessControlManager acMgr) throws RepositoryException { final Set<Privilege> privileges = getPrivilegeSet(aceBean.getPrivileges(), acMgr); if (!privileges.isEmpty()) { final RestrictionsHolder restrictions = getRestrictions(aceBean, session, acl); if (!restrictions.isEmpty()) { acl.addEntry(principal, privileges .toArray(new Privilege[privileges.size()]), aceBean.isAllow(), restrictions.getSingleValuedRestrictionsMap(), restrictions.getMultiValuedRestrictionsMap()); } else { acl.addEntry(principal, privileges .toArray(new Privilege[privileges.size()]), aceBean.isAllow()); } return true; } return false; } /** Creates a RestrictionHolder object containing 2 restriction maps being used in * {@link JackrabbitAccessControlList#addEntry(Principal, Privilege[], boolean, Map, Map)} out of the set actions on this bean. * * @param session the session * @param acl the access control list for which this restriction map should be used * @return RestrictionMapsHolder containing 2 maps with restriction names as keys and restriction values as values * (singleValuedRestrictionsMap) and values[] (multiValuedRestrictionsMap). * @throws ValueFormatException * @throws UnsupportedRepositoryOperationException * @throws RepositoryException */ protected RestrictionsHolder getRestrictions(AceBean aceBean, Session session, JackrabbitAccessControlList acl) throws ValueFormatException, UnsupportedRepositoryOperationException, RepositoryException { final Collection<String> supportedRestrictionNames = Arrays.asList(acl.getRestrictionNames()); if (aceBean.getRestrictions().isEmpty()) { return RestrictionsHolder.empty(); } List<Restriction> restrictions = aceBean.getRestrictions(); for (Restriction restriction : restrictions) { if (!supportedRestrictionNames.contains(restriction.getName())) { throw new IllegalStateException( "The AccessControlList at " + acl.getPath() + " does not support setting " + restriction.getName() + " restrictions!"); } } RestrictionsHolder restrictionsHolder = new RestrictionsHolder(restrictions, session.getValueFactory(), acl); return restrictionsHolder; } /** Converts the given privilege names into a set of privilege objects. * * @param privNames (may be {@code null} * @param acMgr * @return a set of privileges (never {@code null}, but may be empty set) * @throws RepositoryException */ public Set<Privilege> getPrivilegeSet(String[] privNames, AccessControlManager acMgr) throws RepositoryException { if (privNames == null) { return Collections.emptySet(); } final Set<Privilege> privileges = new HashSet<Privilege>(privNames.length); for (final String name : privNames) { final Privilege p = acMgr.privilegeFromName(name); if (p.isAggregate()) { privileges.addAll(Arrays.asList(p.getAggregatePrivileges())); } else { privileges.add(p); } } return privileges; } }