All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.sling.jcr.base.util.AccessControlUtil Maven / Gradle / Ivy

There is a newer version: 2025.3.19823.20250304T101418Z-250200
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */
package org.apache.sling.jcr.base.util;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.jcr.AccessDeniedException;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.UnsupportedRepositoryOperationException;
import javax.jcr.Value;
import javax.jcr.ValueFormatException;
import javax.jcr.security.AccessControlEntry;
import javax.jcr.security.AccessControlException;
import javax.jcr.security.AccessControlList;
import javax.jcr.security.AccessControlManager;
import javax.jcr.security.AccessControlPolicy;
import javax.jcr.security.AccessControlPolicyIterator;
import javax.jcr.security.Privilege;

import org.apache.jackrabbit.api.JackrabbitSession;
import org.apache.jackrabbit.api.security.JackrabbitAccessControlEntry;
import org.apache.jackrabbit.api.security.JackrabbitAccessControlList;
import org.apache.jackrabbit.api.security.principal.PrincipalManager;
import org.apache.jackrabbit.api.security.user.UserManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A simple utility class providing utilities with respect to
 * access control over repositories.
 */
public class AccessControlUtil {

	// the name of the accessor method for the AccessControlManager
    private static final String METHOD_GET_ACCESS_CONTROL_MANAGER = "getAccessControlManager";
    // the name of the accessor method for the UserManager
    private static final String METHOD_GET_USER_MANAGER = "getUserManager";
    // the name of the accessor method for the PrincipalManager
    private static final String METHOD_GET_PRINCIPAL_MANAGER = "getPrincipalManager";
    // the name of the JackrabbitAccessControlList method getPath
    private static final String METHOD_JACKRABBIT_ACL_GET_PATH = "getPath";
    // the name of the JackrabbitAccessControlList method
    private static final String METHOD_JACKRABBIT_ACL_IS_EMPTY = "isEmpty";
    // the name of the JackrabbitAccessControlList method
    private static final String METHOD_JACKRABBIT_ACL_SIZE = "size";
    // the name of the JackrabbitAccessControlList method
    private static final String METHOD_JACKRABBIT_ACL_ADD_ENTRY = "addEntry";
    // the name of the JackrabbitAccessControlEntry method
    private static final String METHOD_JACKRABBIT_ACE_IS_ALLOW = "isAllow";
    // the name of the JackrabbitAccessControlEntry method
    private static final String METHOD_JACKRABBIT_ACE_GET_RESTRICTIONS = "getRestrictions";

    private static final Logger log = LoggerFactory.getLogger(AccessControlUtil.class);

    // ---------- SessionImpl methods -----------------------------------------------------

	/**
     * Returns the AccessControlManager for the given
     * session. If the session does not have a
     * getAccessControlManager method, a
     * UnsupportedRepositoryOperationException is thrown. Otherwise
     * the AccessControlManager is returned or if the call fails,
     * the respective exception is thrown.
     *
     * @param session The JCR Session whose AccessControlManager is
     *            to be returned. If the session is a pooled session, the
     *            session underlying the pooled session is actually used.
     * @return The AccessControlManager of the session
     * @throws UnsupportedRepositoryOperationException If the session has no
     *             getAccessControlManager method or the exception
     *             thrown by the method.
     * @throws RepositoryException Forwarded from the
     *             getAccessControlManager method call.
     */
	public static AccessControlManager getAccessControlManager(Session session)
											throws UnsupportedRepositoryOperationException, RepositoryException {
        return safeInvokeRepoMethod(session, METHOD_GET_ACCESS_CONTROL_MANAGER, AccessControlManager.class);
	}

	// ---------- JackrabbitSession methods -----------------------------------------------

	/**
	 * Returns the UserManager for the given
     * session. If the session does not have a
     * getUserManager method, a
     * UnsupportedRepositoryOperationException is thrown. Otherwise
     * the UserManager is returned or if the call fails,
     * the respective exception is thrown.
	 *
	 * @param session  The JCR Session whose UserManager is
     *            to be returned. If the session is not a JackrabbitSession
     *            uses reflection to retrive the manager from the repository.
	 * @return The UserManager of the session.
	 * @throws AccessDeniedException If this session is not allowed
	 * 			  to access user data.
	 * @throws UnsupportedRepositoryOperationException If the session has no
     *            getUserManager method or the exception
     *            thrown by the method.
	 * @throws RepositoryException Forwarded from the
     *             getUserManager method call.
	 */
	public static UserManager getUserManager(Session session)
										throws AccessDeniedException, UnsupportedRepositoryOperationException, RepositoryException {
		JackrabbitSession jackrabbitSession = getJackrabbitSession(session);
		if(jackrabbitSession != null) {
			return jackrabbitSession.getUserManager();
		} else {
			return safeInvokeRepoMethod(session, METHOD_GET_USER_MANAGER, UserManager.class);
		}
	}

	/**
	 * Returns the PrincipalManager for the given
     * session. If the session does not have a
     * PrincipalManager method, a
     * UnsupportedRepositoryOperationException is thrown. Otherwise
     * the PrincipalManager is returned or if the call fails,
     * the respective exception is thrown.
	 *
	 * @param session  The JCR Session whose PrincipalManager is
     *            to be returned. If the session is not a JackrabbitSession
     *            uses reflection to retrive the manager from the repository.
	 * @return The PrincipalManager of the session.
	 * @throws AccessDeniedException If the current user lacks sufficient privileges
	 * @throws UnsupportedRepositoryOperationException If the session has no
	 * 				PrincipalManager method or the exception
     *             	thrown by the method.
	 * @throws RepositoryException Forwarded from the
     *             PrincipalManager method call.
	 */
	public static PrincipalManager getPrincipalManager(Session session)
										throws AccessDeniedException, UnsupportedRepositoryOperationException, RepositoryException {
		JackrabbitSession jackrabbitSession = getJackrabbitSession(session);
		if(jackrabbitSession != null) {
			return jackrabbitSession.getPrincipalManager();
		} else {
			return safeInvokeRepoMethod(session, METHOD_GET_PRINCIPAL_MANAGER, PrincipalManager.class);
		}
	}

	// ---------- AccessControlList methods -----------------------------------------------

	/**
	 * Returns the path of the node AccessControlList acl
	 * has been created for.
	 * @param acl The acl to get the path for
	 * @return the path for the acl
	 * @throws RepositoryException Forwarded from the
     *             getPath method call.
	 */
	public static String getPath(AccessControlList acl) throws RepositoryException {
			return safeInvokeRepoMethod(acl, METHOD_JACKRABBIT_ACL_GET_PATH, String.class);
	}

	/**
	 * Returns true if AccessControlList acl
	 * does not yet define any entries.
	 * @param acl The acl to check
	 * @return true if the acl is empty, false otherwise
	 * @throws RepositoryException Forwarded from the
     *             isEmpty method call.
	 */
    public static boolean isEmpty(AccessControlList acl) throws RepositoryException {
		return safeInvokeRepoMethod(acl, METHOD_JACKRABBIT_ACL_IS_EMPTY, Boolean.class);
    }

    /**
     * Returns the number of acl entries or 0 if the acl is empty.
	 * @param acl The acl to get the size of
	 * @return the size of the acl
	 * @throws RepositoryException Forwarded from the
     *             size method call.
     */
    public static int size(AccessControlList acl) throws RepositoryException {
		return safeInvokeRepoMethod(acl, METHOD_JACKRABBIT_ACL_SIZE, Integer.class);
    }

    /**
     * Same as {@link #addEntry(AccessControlList, Principal, Privilege[], boolean, Map)} using
     * some implementation specific restrictions.
     * 
     * @param acl the list to add the new entry to
     * @param principal the principal for the user or group to add the entry for
     * @param privileges the set of privileges to grant or deny 
     * @param isAllow try to grant the privileges or false to deny the privileges
     * 
     * @return true if this policy was modified,
     * false otherwise.
     * 
     * @throws AccessControlException If any of the given parameter is invalid
     * or cannot be handled by the implementation.
     * @throws RepositoryException if any other error occurs.
     */
	public static boolean addEntry(AccessControlList acl, Principal principal, Privilege privileges[], boolean isAllow)
        							throws AccessControlException, RepositoryException {
    	Object[] args = new Object[] {principal, privileges, isAllow};
    	@SuppressWarnings("rawtypes")
        Class[] types = new Class[] {Principal.class, Privilege[].class, boolean.class};
		return safeInvokeRepoMethod(acl, METHOD_JACKRABBIT_ACL_ADD_ENTRY, Boolean.class, args, types);
    }

    /**
     * Adds an access control entry to the acl consisting of the specified
     * principal, the specified privileges, the
     * isAllow flag and an optional map containing additional
     * restrictions.
     * 
     * @param acl the list to add the new entry to
     * @param principal the principal for the user or group to add the entry for
     * @param privileges the set of privileges to grant or deny 
     * @param isAllow try to grant the privileges or false to deny the privileges
     * @param restrictions (optional) additional restrictions to filter the scope of the added entry.  The value of the map must be a {@link Value} or {@link Value[]}
     * 
     * @return true if this policy was modified,
     * false otherwise.
     * 
     * @throws UnsupportedRepositoryOperationException if the repository doesn't support adding access control entries
     * @throws RepositoryException if any other error occurs.
     */
	public static boolean addEntry(AccessControlList acl, Principal principal, Privilege privileges[], boolean isAllow, @SuppressWarnings("rawtypes") Map restrictions)
    															throws UnsupportedRepositoryOperationException, RepositoryException {
		Map restrictionsMap = null;
		Map mvRestrictionsMap = null;
		if (restrictions != null) {
			// restrictions arg expected to be Map
			// mvRestrictions arg expected to be Map
			Set entrySet = ((Map)restrictions).entrySet();
			for (Object entry : entrySet) {
				Map.Entry me = (Map.Entry)entry;
				Object value = me.getValue();
				if (value != null) {
					String key = String.valueOf(me.getKey());
					if (value instanceof Value) {
						if (restrictionsMap == null) {
							restrictionsMap = new HashMap<>();
						}
						restrictionsMap.put(key, (Value)value);
					} else if (value instanceof Value[]) {
						if (mvRestrictionsMap == null) {
							mvRestrictionsMap = new HashMap<>();
						}
						mvRestrictionsMap.put(key, (Value[])value);
					} else {
						//some other data type?
						throw new IllegalArgumentException("the values in the restrictions argument must be either a Value or Value[] object");
					}
				}
			}
		}
		
		return addEntry(acl, principal, privileges, isAllow, restrictionsMap, mvRestrictionsMap);
    }

    /**
     * Adds an access control entry to the acl consisting of the specified
     * principal, the specified privileges, the
     * isAllow flag and an optional map containing additional
     * restrictions.
     * 
     * @param acl the list to add the new entry to
     * @param principal the principal for the user or group to add the entry for
     * @param privileges the set of privileges to grant or deny 
     * @param isAllow try to grant the privileges or false to deny the privileges
     * @param restrictions (optional) additional single-value restrictions to filter the scope of the added entry
     * @param mvRestrictions (optional) additional multi-value restrictions to filter the scope of the added entry
     * 
     * @return true if this policy was modified,
     * false otherwise.
     * @throws UnsupportedRepositoryOperationException if the repository doesn't support adding access control entries
     * @throws RepositoryException if any other error occurs.
     */
	public static boolean addEntry(AccessControlList acl, Principal principal, Privilege privileges[], boolean isAllow, 
			Map restrictions, Map mvRestrictions)
    															throws UnsupportedRepositoryOperationException, RepositoryException {
		Object[] args = null;
		Class[] types = null;
		if (mvRestrictions == null && restrictions == null) {
			//no restrictions specified
			args = new Object[] {principal, privileges, isAllow};
			types = new Class[] {Principal.class, Privilege[].class, boolean.class};
		} else if (mvRestrictions == null) {
			//only single-value restrictions (valid with jackrabbit-api version 2.0.0 or later)
			args = new Object[] {principal, privileges, isAllow, restrictions};
			types = new Class[] {Principal.class, Privilege[].class, boolean.class, Map.class};
		} else {
			//both single-value and multi-value restrictions (valid with jackrabbit-api version 2.8.0 or later)_
			args = new Object[] {principal, privileges, isAllow, restrictions, mvRestrictions};
			types = new Class[] {Principal.class, Privilege[].class, boolean.class, Map.class, Map.class};
		}
		
		return safeInvokeRepoMethod(acl, METHOD_JACKRABBIT_ACL_ADD_ENTRY, Boolean.class, args, types);
    }

    /**
     * Replaces existing access control entries in the ACL for the specified
     * principal and resourcePath. Any existing granted
     * or denied privileges which do not conflict with the specified privileges
     * are maintained. Where conflicts exist, existing privileges are dropped.
     * The end result will be at most two ACEs for the principal: one for grants
     * and one for denies. Aggregate privileges are disaggregated before checking
     * for conflicts.
     * @param session the JCR session of the user doing the work
     * @param resourcePath the path of the resource to replace the entry on
     * @param principal the principal for the user or group to add the entry for
     * @param grantedPrivilegeNames the names of the privileges to grant
     * @param deniedPrivilegeNames the names of the privileges to deny
     * @param removedPrivilegeNames privileges which, if they exist, should be
     * removed for this principal and resource
     * @throws RepositoryException if any error occurs.
     * @deprecated use @link {@link #replaceAccessControlEntry(Session, String, Principal, String[], String[], String[], String)} instead.
     */
    @Deprecated
    public static void replaceAccessControlEntry(Session session, String resourcePath, Principal principal,
			String[] grantedPrivilegeNames, String[] deniedPrivilegeNames, String[] removedPrivilegeNames)
    		throws RepositoryException {
    	replaceAccessControlEntry(session,
    			resourcePath,
    			principal,
    			grantedPrivilegeNames,
    			deniedPrivilegeNames,
    			removedPrivilegeNames,
    			null);
    }

    /**
     * Replaces existing access control entries in the ACL for the specified
     * principal and resourcePath. Any existing granted
     * or denied privileges which do not conflict with the specified privileges
     * are maintained. Where conflicts exist, existing privileges are dropped.
     * The end result will be at most two ACEs for the principal: one for grants
     * and one for denies. Aggregate privileges are disaggregated before checking
     * for conflicts.
     * @param session the JCR session of the user doing the work
     * @param resourcePath the path of the resource to replace the entry on
     * @param principal the principal for the user or group to add the entry for
     * @param grantedPrivilegeNames the names of the privileges to grant
     * @param deniedPrivilegeNames the names of the privileges to deny
     * @param removedPrivilegeNames privileges which, if they exist, should be
     * removed for this principal and resource
     * @param order where the access control entry should go in the list.
     *         Value should be one of these:
     *         
     *          
     *          
     * 			
	 *			
	 * 			
	 * 			
	 * 			
	 *         
Values
nullIf the ACE for the principal doesn't exist add at the end, otherwise leave the ACE at it's current position.
firstPlace the target ACE as the first amongst its siblings
lastPlace the target ACE as the last amongst its siblings
before xyzPlace the target ACE immediately before the sibling whose name is xyz
after xyzPlace the target ACE immediately after the sibling whose name is xyz
numericPlace the target ACE at the specified numeric index
* @throws RepositoryException if any error occurs. */ public static void replaceAccessControlEntry(Session session, String resourcePath, Principal principal, String[] grantedPrivilegeNames, String[] deniedPrivilegeNames, String[] removedPrivilegeNames, String order) throws RepositoryException { replaceAccessControlEntry(session, resourcePath, principal, grantedPrivilegeNames, deniedPrivilegeNames, removedPrivilegeNames, order, null, null, null); } /** * Replaces existing access control entries in the ACL for the specified * principal and resourcePath. Any existing granted * or denied privileges which do not conflict with the specified privileges * are maintained. Where conflicts exist, existing privileges are dropped. * The end result will be at most two ACEs for the principal: one for grants * and one for denies. Aggregate privileges are disaggregated before checking * for conflicts. * @param session the JCR session of the user doing the work * @param resourcePath the path of the resource to replace the entry on * @param principal the principal for the user or group to add the entry for * @param grantedPrivilegeNames the names of the privileges to grant * @param deniedPrivilegeNames the names of the privileges to deny * @param removedPrivilegeNames privileges which, if they exist, should be * removed for this principal and resource * @param order where the access control entry should go in the list. * Value should be one of these: * * * * * * * * *
Values
nullIf the ACE for the principal doesn't exist add at the end, otherwise leave the ACE at it's current position.
firstPlace the target ACE as the first amongst its siblings
lastPlace the target ACE as the last amongst its siblings
before xyzPlace the target ACE immediately before the sibling whose name is xyz
after xyzPlace the target ACE immediately after the sibling whose name is xyz
numericPlace the target ACE at the specified numeric index
* @param restrictions (optional) additional single-value restrictions to filter the scope of the replaced entry * @param mvRestrictions (optional) additional multi-value restrictions to filter the scope of the replaced entry * @param removedRestrictionNames optional set of restriction names that should be removed (if they already exist). * @throws RepositoryException if any error occurs. */ public static void replaceAccessControlEntry(Session session, String resourcePath, Principal principal, String[] grantedPrivilegeNames, String[] deniedPrivilegeNames, String[] removedPrivilegeNames, String order, Map restrictions, Map mvRestrictions, Set removedRestrictionNames) throws RepositoryException { AccessControlManager accessControlManager = getAccessControlManager(session); Set specifiedPrivilegeNames = new HashSet(); Set newGrantedPrivilegeNames = disaggregateToPrivilegeNames(accessControlManager, grantedPrivilegeNames, specifiedPrivilegeNames); Set newDeniedPrivilegeNames = disaggregateToPrivilegeNames(accessControlManager, deniedPrivilegeNames, specifiedPrivilegeNames); disaggregateToPrivilegeNames(accessControlManager, removedPrivilegeNames, specifiedPrivilegeNames); //make a copy since we may need to merge and change the map before applying them Map newRestrictions = new HashMap<>(); Map newMvRestrictions = new HashMap<>(); if (restrictions != null) { Set> entrySet = restrictions.entrySet(); for (Entry entry : entrySet) { String key = entry.getKey(); if (removedRestrictionNames != null && removedRestrictionNames.contains(key)) { // new restriction is also in the removed set, so no need to try to remove it removedRestrictionNames.remove(key); } newRestrictions.put(key, entry.getValue()); } } if (mvRestrictions != null) { Set> entrySet = mvRestrictions.entrySet(); for (Entry entry : entrySet) { String key = entry.getKey(); if (removedRestrictionNames != null && removedRestrictionNames.contains(key)) { // new restriction is also in the removed set, so no need to try to remove it removedRestrictionNames.remove(key); } newMvRestrictions.put(key, entry.getValue()); } } // Get or create the ACL for the node. AccessControlList acl = null; AccessControlPolicy[] policies = accessControlManager.getPolicies(resourcePath); for (AccessControlPolicy policy : policies) { if (policy instanceof AccessControlList) { acl = (AccessControlList) policy; break; } } if (acl == null) { AccessControlPolicyIterator applicablePolicies = accessControlManager.getApplicablePolicies(resourcePath); while (applicablePolicies.hasNext()) { AccessControlPolicy policy = applicablePolicies.nextAccessControlPolicy(); if (policy instanceof AccessControlList) { acl = (AccessControlList) policy; break; } } } if (acl == null) { throw new RepositoryException("Could not obtain ACL for resource " + resourcePath); } // Used only for logging. Set oldGrants = null; Set oldDenies = null; if (log.isDebugEnabled()) { oldGrants = new HashSet(); oldDenies = new HashSet(); } // Combine all existing ACEs for the target principal. AccessControlEntry[] accessControlEntries = acl.getAccessControlEntries(); for (int i=0; i < accessControlEntries.length; i++) { AccessControlEntry ace = accessControlEntries[i]; if (principal.equals(ace.getPrincipal())) { if (log.isDebugEnabled()) { log.debug("Found Existing ACE for principal {} on resource {}", new Object[] {principal.getName(), resourcePath}); } if (order == null || order.length() == 0) { //order not specified, so keep track of the original ACE position. order = String.valueOf(i); } boolean isAllow = isAllow(ace); Privilege[] privileges = ace.getPrivileges(); if (log.isDebugEnabled()) { if (isAllow) { oldGrants.addAll(Arrays.asList(privileges)); } else { oldDenies.addAll(Arrays.asList(privileges)); } } for (Privilege privilege : privileges) { Set maintainedPrivileges = disaggregateToPrivilegeNames(privilege); // If there is any overlap with the newly specified privileges, then // break the existing privilege down; otherwise, maintain as is. if (!maintainedPrivileges.removeAll(specifiedPrivilegeNames)) { // No conflicts, so preserve the original. maintainedPrivileges.clear(); maintainedPrivileges.add(privilege.getName()); } if (!maintainedPrivileges.isEmpty()) { if (isAllow) { newGrantedPrivilegeNames.addAll(maintainedPrivileges); } else { newDeniedPrivilegeNames.addAll(maintainedPrivileges); } } } // If there is any existing restrictions that are not specified with the newly specified arguments, // then maintain the original restriction values as they were. if (ace instanceof JackrabbitAccessControlEntry) { JackrabbitAccessControlEntry jace = (JackrabbitAccessControlEntry)ace; String[] restrictionNames = jace.getRestrictionNames(); if (restrictionNames != null) { for (String rname : restrictionNames) { if (!(newRestrictions.containsKey(rname) || newMvRestrictions.containsKey(rname))) { // the restriction doesn't have a new value, so remember the old value try { Value restriction = jace.getRestriction(rname); newRestrictions.put(rname, restriction); } catch (Exception vfe) { if (vfe instanceof ValueFormatException) { //try multival variant (only valid for jackrabbit-api 2.8.0 or later) Value[] rvalue = safeInvokeRepoMethod(ace, METHOD_JACKRABBIT_ACE_GET_RESTRICTIONS, Value[].class, new Object[] {rname}, new Class[] {String.class}); newMvRestrictions.put(rname, rvalue); } else { //some other exception, so just re-throw the original throw vfe; } } } } } } // Remove the old ACE. acl.removeAccessControlEntry(ace); } } //remove the map entry for any restrictions were requested to be removed and no new value was // supplied if (removedRestrictionNames != null) { for (String rname : removedRestrictionNames) { // remove if a new value was not also supplied if (!((restrictions != null && restrictions.containsKey(rname)) || (mvRestrictions != null && mvRestrictions.containsKey(rname)))) { newRestrictions.remove(rname); newMvRestrictions.remove(rname); } } } //add a fresh ACE with the granted privileges List grantedPrivilegeList = new ArrayList(); for (String name : newGrantedPrivilegeNames) { Privilege privilege = accessControlManager.privilegeFromName(name); grantedPrivilegeList.add(privilege); } if (grantedPrivilegeList.size() > 0) { addEntry(acl, principal, grantedPrivilegeList.toArray(new Privilege[grantedPrivilegeList.size()]), true, newRestrictions, newMvRestrictions); } //add a fresh ACE with the denied privileges List deniedPrivilegeList = new ArrayList(); for (String name : newDeniedPrivilegeNames) { Privilege privilege = accessControlManager.privilegeFromName(name); deniedPrivilegeList.add(privilege); } if (deniedPrivilegeList.size() > 0) { addEntry(acl, principal, deniedPrivilegeList.toArray(new Privilege[deniedPrivilegeList.size()]), false, newRestrictions, newMvRestrictions); } //order the ACL reorderAccessControlEntries(acl, principal, order); accessControlManager.setPolicy(resourcePath, acl); if (log.isDebugEnabled()) { List oldGrantedNames = new ArrayList(oldGrants.size()); for (Privilege privilege : oldGrants) { oldGrantedNames.add(privilege.getName()); } List oldDeniedNames = new ArrayList(oldDenies.size()); for (Privilege privilege : oldDenies) { oldDeniedNames.add(privilege.getName()); } log.debug("Updated ACE for principalName {} for resource {} from grants {}, denies {} to grants {}, denies {}", new Object [] { principal.getName(), resourcePath, oldGrantedNames, oldDeniedNames, newGrantedPrivilegeNames, newDeniedPrivilegeNames }); } } // ---------- AccessControlEntry methods ----------------------------------------------- /** * Returns true if the AccessControlEntry represents 'allowed' rights or false * it it represents 'denied' rights. * * @param ace the access control entry to check * @return true if the entry represents allowed rights of ralse otherwise * @throws RepositoryException Forwarded from the * isAllow method call. */ public static boolean isAllow(AccessControlEntry ace) throws RepositoryException { return safeInvokeRepoMethod(ace, METHOD_JACKRABBIT_ACE_IS_ALLOW, Boolean.class); } // ---------- internal ----------------------------------------------------- /** * Use reflection to invoke a repository method. */ @SuppressWarnings("unchecked") private static T safeInvokeRepoMethod(Object target, String methodName, Class returnType, Object[] args, @SuppressWarnings("rawtypes") Class[] argsTypes) throws UnsupportedRepositoryOperationException, RepositoryException { try { Method m = target.getClass().getMethod(methodName, argsTypes); if (!m.isAccessible()) { m.setAccessible(true); } return (T) m.invoke(target, args); } catch (InvocationTargetException ite) { // wraps the exception thrown by the method Throwable t = ite.getCause(); if (t instanceof UnsupportedRepositoryOperationException) { throw (UnsupportedRepositoryOperationException) t; } else if (t instanceof AccessDeniedException) { throw (AccessDeniedException) t; } else if (t instanceof AccessControlException) { throw (AccessControlException) t; } else if (t instanceof RepositoryException) { throw (RepositoryException) t; } else if (t instanceof RuntimeException) { throw (RuntimeException) t; } else if (t instanceof Error) { throw (Error) t; } else { throw new RepositoryException(methodName, t); } } catch (Throwable t) { // any other problem is just encapsulated throw new RepositoryException(methodName, t); } } private static T safeInvokeRepoMethod(Object target, String methodName, Class returnType, Object... args) throws UnsupportedRepositoryOperationException, RepositoryException { return safeInvokeRepoMethod(target, methodName, returnType, args, new Class[0]); } /** * Unwrap the jackrabbit session. */ private static JackrabbitSession getJackrabbitSession(Session session) { if (session instanceof JackrabbitSession) return (JackrabbitSession) session; else return null; } /** * Helper routine to transform an input array of privilege names into a set in * a null-safe way while also adding its disaggregated privileges to an input set. */ private static Set disaggregateToPrivilegeNames(AccessControlManager accessControlManager, String[] privilegeNames, Set disaggregatedPrivilegeNames) throws RepositoryException { Set originalPrivilegeNames = new HashSet(); if (privilegeNames != null) { for (String privilegeName : privilegeNames) { originalPrivilegeNames.add(privilegeName); Privilege privilege = accessControlManager.privilegeFromName(privilegeName); disaggregatedPrivilegeNames.addAll(disaggregateToPrivilegeNames(privilege)); } } return originalPrivilegeNames; } /** * Transform an aggregated privilege into a set of disaggregated privilege * names. If the privilege is not an aggregate, the set will contain the * original name. */ private static Set disaggregateToPrivilegeNames(Privilege privilege) { Set disaggregatedPrivilegeNames = new HashSet(); if (privilege.isAggregate()) { Privilege[] privileges = privilege.getAggregatePrivileges(); for (Privilege disaggregate : privileges) { if (disaggregate.isAggregate()) { continue; //nested aggregate, so skip it since the privileges are already included. } disaggregatedPrivilegeNames.add(disaggregate.getName()); } } else { disaggregatedPrivilegeNames.add(privilege.getName()); } return disaggregatedPrivilegeNames; } /** * Move the ACE(s) for the specified principal to the position specified by the 'order' * parameter. * * @param acl the acl of the node containing the ACE to position * @param principal the user or group of the ACE to position * @param order where the access control entry should go in the list. * Value should be one of these: * * * * * * * *
Values
firstPlace the target ACE as the first amongst its siblings
lastPlace the target ACE as the last amongst its siblings
before xyzPlace the target ACE immediately before the sibling whose name is xyz
after xyzPlace the target ACE immediately after the sibling whose name is xyz
numericPlace the target ACE at the specified index
* @throws RepositoryException * @throws UnsupportedRepositoryOperationException * @throws AccessControlException */ private static void reorderAccessControlEntries(AccessControlList acl, Principal principal, String order) throws RepositoryException { if (order == null || order.length() == 0) { return; //nothing to do } if (acl instanceof JackrabbitAccessControlList) { JackrabbitAccessControlList jacl = (JackrabbitAccessControlList)acl; AccessControlEntry[] accessControlEntries = jacl.getAccessControlEntries(); if (accessControlEntries.length <= 1) { return; //only one ACE, so nothing to reorder. } AccessControlEntry beforeEntry = null; if ("first".equals(order)) { beforeEntry = accessControlEntries[0]; } else if ("last".equals(order)) { beforeEntry = null; } else if (order.startsWith("before ")) { String beforePrincipalName = order.substring(7); //find the index of the ACE of the 'before' principal for (int i=0; i < accessControlEntries.length; i++) { if (beforePrincipalName.equals(accessControlEntries[i].getPrincipal().getName())) { //found it! beforeEntry = accessControlEntries[i]; break; } } if (beforeEntry == null) { //didn't find an ACE that matched the 'before' principal throw new IllegalArgumentException("No ACE was found for the specified principal: " + beforePrincipalName); } } else if (order.startsWith("after ")) { String afterPrincipalName = order.substring(6); //find the index of the ACE of the 'after' principal for (int i = accessControlEntries.length - 1; i >= 0; i--) { if (afterPrincipalName.equals(accessControlEntries[i].getPrincipal().getName())) { //found it! // the 'before' ACE is the next one after the 'after' ACE if (i >= accessControlEntries.length - 1) { //the after is the last one in the list beforeEntry = null; } else { beforeEntry = accessControlEntries[i + 1]; } break; } } if (beforeEntry == null) { //didn't find an ACE that matched the 'after' principal throw new IllegalArgumentException("No ACE was found for the specified principal: " + afterPrincipalName); } } else { try { int index = Integer.parseInt(order); if (index > accessControlEntries.length) { //invalid index throw new IndexOutOfBoundsException("Index value is too large: " + index); } if (index == 0) { beforeEntry = accessControlEntries[0]; } else { //the index value is the index of the principal. A principal may have more // than one ACEs (deny + grant), so we need to compensate. Set processedPrincipals = new HashSet(); for (int i = 0; i < accessControlEntries.length; i++) { Principal principal2 = accessControlEntries[i].getPrincipal(); if (processedPrincipals.size() == index && !processedPrincipals.contains(principal2)) { //we are now at the correct position in the list beforeEntry = accessControlEntries[i]; break; } processedPrincipals.add(principal2); } } } catch (NumberFormatException nfe) { //not a number. throw new IllegalArgumentException("Illegal value for the order parameter: " + order); } } //now loop through the entries to move the affected ACEs to the specified // position. for (int i = accessControlEntries.length - 1; i >= 0; i--) { AccessControlEntry ace = accessControlEntries[i]; if (principal.equals(ace.getPrincipal())) { //this ACE is for the specified principal. jacl.orderBefore(ace, beforeEntry); } } } else { throw new IllegalArgumentException("The acl must be an instance of JackrabbitAccessControlList"); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy