
org.apache.sling.jcr.base.util.AccessControlUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aem-sdk-api Show documentation
Show all versions of aem-sdk-api Show documentation
The Adobe Experience Manager SDK
/*
* 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
* null If the ACE for the principal doesn't exist add at the end, otherwise leave the ACE at it's current position.
* first Place the target ACE as the first amongst its siblings
* last Place the target ACE as the last amongst its siblings
* before xyz Place the target ACE immediately before the sibling whose name is xyz
* after xyz Place the target ACE immediately after the sibling whose name is xyz
* numeric Place 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
* null If the ACE for the principal doesn't exist add at the end, otherwise leave the ACE at it's current position.
* first Place the target ACE as the first amongst its siblings
* last Place the target ACE as the last amongst its siblings
* before xyz Place the target ACE immediately before the sibling whose name is xyz
* after xyz Place the target ACE immediately after the sibling whose name is xyz
* numeric Place 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
* first Place the target ACE as the first amongst its siblings
* last Place the target ACE as the last amongst its siblings
* before xyz Place the target ACE immediately before the sibling whose name is xyz
* after xyz Place the target ACE immediately after the sibling whose name is xyz
* numeric Place 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