/* * (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 static biz.netcentric.cq.tools.actool.history.AcInstallationLog.msHumanReadable; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import java.util.TreeSet; import javax.jcr.AccessDeniedException; import javax.jcr.ItemNotFoundException; import javax.jcr.Node; import javax.jcr.NodeIterator; import javax.jcr.PathNotFoundException; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.UnsupportedRepositoryOperationException; import javax.jcr.query.InvalidQueryException; import javax.jcr.query.Query; import javax.jcr.query.QueryResult; import javax.jcr.security.AccessControlList; import javax.jcr.security.AccessControlManager; import org.apache.commons.lang.time.StopWatch; import org.apache.jackrabbit.api.security.JackrabbitAccessControlList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class QueryHelper { public static final Logger LOG = LoggerFactory.getLogger(QueryHelper.class); /** Method that returns a set containing all rep:policy nodes from repository excluding those contained in paths which are excluded from * search * * @param session the jcr session * @param excludePaths paths which are excluded from search * @return all rep:policy nodes delivered by query */ public static Set<Node> getRepPolicyNodes(final Session session, final List<String> excludePaths) { NodeIterator nodeIt = null; try { nodeIt = session.getRootNode().getNodes(); } catch (RepositoryException e) { AcHelper.LOG.error("Exception: {}", e); } Set<String> paths = new TreeSet<String>(); while (nodeIt.hasNext()) { String currentPath = null; Node currentNode = nodeIt.nextNode(); try { currentPath = currentNode.getPath(); } catch (RepositoryException e) { AcHelper.LOG.error("Exception: {}", e); } try { if (!currentNode.hasProperty("rep:AuthorizableFolder")) { if (!excludePaths.contains(currentPath)) { paths.add(currentPath); } } } catch (RepositoryException e) { AcHelper.LOG.error("Exception: {}", e); } } Set<Node> nodes = new LinkedHashSet<Node>(); try { // get the rep:policy node of "/", if existing if (session.nodeExists("/rep:policy")) { nodes.add(session.getNode("/rep:policy")); } if (session.nodeExists("/" + Constants.REPO_POLICY_NODE)) { nodes.add(session.getNode("/" + Constants.REPO_POLICY_NODE)); } // get the rep:policy node of "/home", if existing if (session.nodeExists("/home/rep:policy")) { nodes.add(session.getNode("/home/rep:policy")); } for (String path : paths) { String query = "SELECT * FROM [rep:ACL] WHERE ISDESCENDANTNODE([" + path + "])"; nodes.addAll(QueryHelper.getNodes(session, query, Query.JCR_SQL2)); } } catch (InvalidQueryException e) { AcHelper.LOG.error("InvalidQueryException: {}", e); } catch (RepositoryException e) { AcHelper.LOG.error("RepositoryException: {}", e); } return nodes; } /** Get Nodes with XPATH Query. */ public static Set<Node> getNodes(final Session session, final String xpathQuery) throws InvalidQueryException, RepositoryException { Set<Node> nodes = getNodes(session, xpathQuery, Query.XPATH); return nodes; } /** @param session the jcr session * @param queryStatement - ex. "SELECT * FROM [rep:ACL]" * @param queryLanguageType - ex. Query.JCR_SQL2 */ public static Set<Node> getNodes(final Session session, final String queryStatement, String queryLanguageType) throws InvalidQueryException, RepositoryException { Set<Node> nodes = new HashSet<Node>(); Query query = session.getWorkspace().getQueryManager() .createQuery(queryStatement, queryLanguageType); QueryResult queryResult = query.execute(); NodeIterator nit = queryResult.getNodes(); List<String> paths = new ArrayList<String>(); while (nit.hasNext()) { // get the next rep:policy node Node node = nit.nextNode(); // AcHelper.LOG.debug("adding node: {} to node set", node.getPath()); paths.add(node.getPath()); nodes.add(node); } return nodes; } public static Set<AclBean> getAuthorizablesAcls(final Session session, final Set<String> authorizableIds, Set<String> principalIdsToBeFilled) throws InvalidQueryException, RepositoryException { LOG.debug("Querying AclBeans for {} authorizables", authorizableIds.size()); StopWatch sw = new StopWatch(); sw.start(); Set<Node> nodeSet = new LinkedHashSet<Node>(); Iterator<String> authorizablesIdIterator = authorizableIds.iterator(); while (authorizablesIdIterator.hasNext()) { StringBuilder queryStringBuilder = new StringBuilder(); queryStringBuilder.append( "SELECT ace.* FROM [rep:ACE] AS ace INNER JOIN [rep:Authorizable] AS authorizable " + "ON ace.[rep:principalName] = authorizable.[rep:principalName] WHERE "); queryStringBuilder.append(getAuthorizablesQueryStringBuilder(authorizablesIdIterator, 100)); String query = queryStringBuilder.toString(); Set<Node> resultNodes = getNodes(session, query, Query.JCR_SQL2); LOG.trace("Querying AclBeans with {} returned {} results", query, resultNodes.size()); nodeSet.addAll(resultNodes); } Set<AclBean> resultBeans = buildAclBeansFromNodeSet(session, nodeSet, principalIdsToBeFilled); sw.stop(); LOG.debug("Found {} AclBeans in {}", resultBeans.size(), msHumanReadable(sw.getTime())); return resultBeans; } private static Set<AclBean> buildAclBeansFromNodeSet(final Session session, Set<Node> nodeSet, Set<String> principalIdsToBeFilled) throws UnsupportedRepositoryOperationException, RepositoryException, PathNotFoundException, AccessDeniedException, ItemNotFoundException { AccessControlManager aMgr = session.getAccessControlManager(); AccessControlList acl; Set<AclBean> aclSet = new TreeSet<AclBean>(); // use natural ordering for (Node allowOrDenyNode : nodeSet) { String principalId = allowOrDenyNode.getProperty("rep:principalName").getValue().getString(); principalIdsToBeFilled.add(principalId); Node aclNode = allowOrDenyNode.getParent(); String aclEffectiveOnPath; String jcrPathAcl; if (!Constants.REPO_POLICY_NODE.equals(aclNode.getName())) { // default aclEffectiveOnPath = aclNode.getParent().getPath(); jcrPathAcl = aclNode.getPath(); } else { // repo policy aclEffectiveOnPath = null; jcrPathAcl = "/" + Constants.REPO_POLICY_NODE; } acl = (AccessControlList) aMgr.getPolicies(aclEffectiveOnPath)[0]; if (acl == null) { LOG.warn("Path " + aclEffectiveOnPath + " unexpectedly does not have a ACL"); continue; } AclBean aclBean = new AclBean(); aclBean.setParentPath(aclEffectiveOnPath); aclBean.setAcl((JackrabbitAccessControlList) acl); aclBean.setJcrPath(jcrPathAcl); aclSet.add(aclBean); } return aclSet; } private static StringBuilder getAuthorizablesQueryStringBuilder(final Iterator<String> authorizablesIdIterator, final int authorizbalesLimitPerQuery) { int authorizableCounter = 0; StringBuilder querySb = new StringBuilder(); if (!authorizablesIdIterator.hasNext()) { return querySb; } while (true) { querySb.append("authorizable.[rep:authorizableId] = '" + authorizablesIdIterator.next() + "'"); authorizableCounter++; if (authorizableCounter < authorizbalesLimitPerQuery && authorizablesIdIterator.hasNext()) { querySb.append(" or "); } else { return querySb; } } } }