Java tutorial
/* * #%L * ACS AEM Commons Bundle * %% * Copyright (C) 2015 Adobe * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * #L% */ package com.adobe.acs.commons.users.impl; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.jcr.PropertyType; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.Value; import javax.jcr.ValueFactory; import javax.jcr.ValueFormatException; import javax.jcr.security.AccessControlPolicy; import javax.jcr.security.Privilege; import com.adobe.acs.commons.users.impl.AbstractAuthorizable; import com.day.cq.search.result.Hit; import org.apache.commons.lang.StringUtils; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.Service; import org.apache.jackrabbit.api.security.JackrabbitAccessControlEntry; import org.apache.jackrabbit.api.security.JackrabbitAccessControlList; import org.apache.jackrabbit.api.security.JackrabbitAccessControlManager; import org.apache.jackrabbit.api.security.user.Authorizable; import org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils; import org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.AccessControlConstants; import org.apache.sling.api.resource.Resource; import org.apache.sling.api.resource.ResourceResolver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.day.cq.search.PredicateGroup; import com.day.cq.search.Query; import com.day.cq.search.QueryBuilder; @Component @Service(EnsureAce.class) public class EnsureAce { private static final Logger log = LoggerFactory.getLogger(EnsureAce.class); @Reference private QueryBuilder queryBuilder; /** * Ensures the ACEs for the Service User exists. Any extra ACEs for the Service User will be removed. * * @param resourceResolver * the resource resolver to perform the user management * @param jcrAuthorizable * the Jackrabbit Authorizable Object (User or Group) the Authorizable represents * @param authorizable * the Authorizable to ensure * @return The number of ace entries that could not be processed * @throws RepositoryException */ @SuppressWarnings("squid:S3776") public int ensureAces(ResourceResolver resourceResolver, Authorizable jcrAuthorizable, AbstractAuthorizable authorizable) throws RepositoryException { int failures = 0; final Session session = resourceResolver.adaptTo(Session.class); final JackrabbitAccessControlManager accessControlManager = (JackrabbitAccessControlManager) session .getAccessControlManager(); final List<JackrabbitAccessControlList> acls = findAcls(resourceResolver, authorizable.getPrincipalName(), accessControlManager); // For each rep:policy (ACL) this service user participates in ... for (final JackrabbitAccessControlList acl : acls) { final JackrabbitAccessControlEntry[] aces = (JackrabbitAccessControlEntry[]) acl .getAccessControlEntries(); final boolean serviceUserCoversThisPath = authorizable.hasAceAt(acl.getPath()); for (final JackrabbitAccessControlEntry ace : aces) { if (!StringUtils.equals(authorizable.getPrincipalName(), ace.getPrincipal().getName())) { // Only care about ACEs that this service user participates in continue; } // Pertains to this service user if (StringUtils.startsWith(acl.getPath(), jcrAuthorizable.getPath())) { // Skip the corner case of ACL's under the system user itself; Do nothing to these. } else if (!serviceUserCoversThisPath) { // Remove all ACE's for this user from this ACL since this Service User is not configured to cover // this path log.debug("Service user does NOT cover the path yet has an ACE; ensure removal of the ace! {}", ace.toString()); acl.removeAccessControlEntry(ace); } else { final Ace serviceUserAce = authorizable.getAce(ace, acl.getPath()); if (serviceUserAce == null) { acl.removeAccessControlEntry(ace); log.debug("Removed System ACE as it doesn't exist in Service User [ {} ] configuration", authorizable.getPrincipalName()); } else { serviceUserAce.setExists(true); log.debug("No-op on System ACE as it already matches Service User [ {} ] configuration", authorizable.getPrincipalName()); } } } accessControlManager.setPolicy(acl.getPath(), acl); } // Create an ACEs that do not yet exist for (Ace ace : authorizable.getMissingAces()) { if (resourceResolver.getResource(ace.getContentPath()) == null) { log.warn( "Unable to apply Service User [ {} ] privileges due to missing path at [ {} ]. Please create the path and re-ensure this service user.", authorizable.getPrincipalName(), ace.getContentPath()); failures++; continue; } final JackrabbitAccessControlList acl = AccessControlUtils.getAccessControlList(session, ace.getContentPath()); final Map<String, Value> restrictions = new HashMap<String, Value>(); final Map<String, Value[]> multiRestrictions = new HashMap<String, Value[]>(); final ValueFactory valueFactory = session.getValueFactory(); // Add rep:glob restriction if (ace.hasRepGlob()) { restrictions.put(AccessControlConstants.REP_GLOB, valueFactory.createValue(ace.getRepGlob(), PropertyType.STRING)); } // Add rep:ntNames restriction if (ace.hasRepNtNames()) { multiRestrictions.put(AccessControlConstants.REP_NT_NAMES, getMultiValues(valueFactory, ace.getRepNtNames(), PropertyType.NAME)); } // Add rep:itemNames if (ace.hasRepItemNames()) { multiRestrictions.put(AccessControlConstants.REP_ITEM_NAMES, getMultiValues(valueFactory, ace.getRepItemNames(), PropertyType.NAME)); } // Add rep:prefixes if (ace.hasRepPrefixes()) { multiRestrictions.put(AccessControlConstants.REP_PREFIXES, getMultiValues(valueFactory, ace.getRepPrefixes(), PropertyType.STRING)); } // Add ACE to the ACL acl.addEntry(jcrAuthorizable.getPrincipal(), ace.getPrivileges(accessControlManager).toArray(new Privilege[] {}), ace.isAllow(), restrictions, multiRestrictions); // Update the ACL on the content accessControlManager.setPolicy(ace.getContentPath(), acl); log.debug("Added Service User ACE for [ {} ] to [ {} ]", authorizable.getPrincipalName(), ace.getContentPath()); } return failures; } /** * Removes all ACEs for the Service User principal (except those that live beneath the System User's rep:User node) * * @param resourceResolver * the resource resolver to perform the user management * @param jcrAuthorizable * the Jackrabbit Authorizable Object (User or Group) the Authorizable represents * @param authorizable * the Authorizable to remove * @throws RepositoryException */ public void removeAces(ResourceResolver resourceResolver, Authorizable jcrAuthorizable, AbstractAuthorizable authorizable) throws RepositoryException { final Session session = resourceResolver.adaptTo(Session.class); final JackrabbitAccessControlManager accessControlManager = (JackrabbitAccessControlManager) session .getAccessControlManager(); final List<JackrabbitAccessControlList> acls = findAcls(resourceResolver, authorizable.getPrincipalName(), accessControlManager); for (final JackrabbitAccessControlList acl : acls) { final JackrabbitAccessControlEntry[] aces = (JackrabbitAccessControlEntry[]) acl .getAccessControlEntries(); // Check all the existing ACEs in the ACL for (JackrabbitAccessControlEntry ace : aces) { if (StringUtils.equals(authorizable.getPrincipalName(), ace.getPrincipal().getName())) { if (jcrAuthorizable != null && StringUtils.startsWith(acl.getPath(), jcrAuthorizable.getPath())) { // Skip! Don't ensureRemoval ACE's from the system user itself! } else { acl.removeAccessControlEntry(ace); } } } accessControlManager.setPolicy(acl.getPath(), acl); log.debug("Removed ACE from ACL at [ {} ] for [ {} ]", acl.getPath(), authorizable.getPrincipalName()); } } /** * Locates by query all the ACLs that the principal participates in. * * @param resourceResolver * the resource resolver to perform the user management * @param principalName * the principal name * @param accessControlManager * Jackrabbit access control manager * @return a list of ACLs that principal participates in. * @throws RepositoryException */ private List<JackrabbitAccessControlList> findAcls(ResourceResolver resourceResolver, String principalName, JackrabbitAccessControlManager accessControlManager) throws RepositoryException { final Set<String> paths = new HashSet<String>(); final List<JackrabbitAccessControlList> acls = new ArrayList<JackrabbitAccessControlList>(); final Map<String, String> params = new HashMap<String, String>(); params.put("type", AccessControlConstants.NT_REP_ACE); params.put("property", AccessControlConstants.REP_PRINCIPAL_NAME); params.put("property.value", principalName); params.put("p.limit", "-1"); final Query query = queryBuilder.createQuery(PredicateGroup.create(params), resourceResolver.adaptTo(Session.class)); // Handle leaking resource resolver in AEM QueryBuilder ResourceResolver leakingResourceResolver = null; for (final Hit hit : query.getResult().getHits()) { try { // Handle leaking resource resolver in AEM QueryBuilder if (leakingResourceResolver == null) { leakingResourceResolver = hit.getResource().getResourceResolver(); } final Resource aceResource = resourceResolver.getResource(hit.getPath()); // first parent is the rep:policy node // second parent (grand-parent) is the content node this ACE controls // that is the node we need to use the JackrabbitAccessControlManager api final Resource contentResource = aceResource.getParent().getParent(); if (!paths.contains(contentResource.getPath())) { for (AccessControlPolicy policy : accessControlManager.getPolicies(contentResource.getPath())) { if (policy instanceof JackrabbitAccessControlList) { acls.add((JackrabbitAccessControlList) policy); break; } } } } catch (RepositoryException e) { log.error("Failed to get resource for query result.", e); } } // Handle leaking resource resolver in AEM QueryBuilder if (leakingResourceResolver != null) { leakingResourceResolver.close(); } return acls; } /** * Helper function that returns a list of Strings into an Array of Value's * * @param valueFactory * the valueFactory to create the Value * @param valueStrs * the string values to convert * @return the array of Values derives from the valueStrs */ private Value[] getMultiValues(ValueFactory valueFactory, List<String> valueStrs, int propertyType) throws ValueFormatException { final List<Value> result = new ArrayList<Value>(); for (final String valueStr : valueStrs) { result.add(valueFactory.createValue(valueStr, propertyType)); } return result.toArray(new Value[result.size()]); } }