blob: 64042719e362d57528a8803bf39abf6a5d2bdec0 [file] [log] [blame]
/*
* 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.jackrabbit.accessmanager.impl;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.Workspace;
import javax.jcr.security.Privilege;
import org.apache.jackrabbit.api.JackrabbitWorkspace;
import org.apache.jackrabbit.api.security.authorization.PrivilegeManager;
import org.apache.sling.jcr.jackrabbit.accessmanager.LocalPrivilege;
import org.apache.sling.jcr.jackrabbit.accessmanager.LocalRestriction;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Contains utility methods related to handling privileges
*/
public final class PrivilegesHelper {
private PrivilegesHelper() {
}
/**
* If the privilege is contained in multiple aggregate privileges, then
* calculate the instance with the greatest depth.
*/
private static void toLongestDepth(int parentDepth, Privilege parentPrivilege, Map<Privilege, Integer> privilegeToLongestDepth) {
Privilege[] declaredAggregatePrivileges = parentPrivilege.getDeclaredAggregatePrivileges();
for (Privilege privilege : declaredAggregatePrivileges) {
Integer oldValue = privilegeToLongestDepth.get(privilege);
int candidateDepth = parentDepth + 1;
if (oldValue == null || oldValue.intValue() < candidateDepth) {
privilegeToLongestDepth.put(privilege, candidateDepth);
// continue drilling down to the leaf privileges
toLongestDepth(candidateDepth, privilege, privilegeToLongestDepth);
}
}
}
/**
* Calculate the longest path for each of the possible privileges
*
* @param jcrSession the current users JCR session
* @return map where the key is the privilege and the value is the longest path
*/
public static Map<Privilege, Integer> buildPrivilegeLongestDepthMap(Privilege jcrAll) {
Map<Privilege, Integer> privilegeToLongestPath = new HashMap<>();
privilegeToLongestPath.put(jcrAll, 1);
toLongestDepth(1, jcrAll, privilegeToLongestPath);
return privilegeToLongestPath;
}
/**
* Populates a local allow privilege in the privilegeToLocalPrivilegesMap
*
* @param privilegeToLocalPrivilegesMap the map containing the declared LocalPrivilege items
* @param privilege the privilege to allow
* @param isAllow true or false to set the allow value of the LocalPrivilege
* @param restrictions if isAllow is true, the set of restrictions
* @return the LocalPrivileges that was populated
*/
public static LocalPrivilege localAllowPriv(Map<Privilege, LocalPrivilege> privilegeToLocalPrivilegesMap,
Privilege privilege, boolean isAllow, Set<LocalRestriction> restrictions) {
LocalPrivilege localPrivilege = privilegeToLocalPrivilegesMap.computeIfAbsent(privilege, LocalPrivilege::new);
localPrivilege.setAllow(isAllow);
if (isAllow) {
localPrivilege.setAllowRestrictions(restrictions);
if (localPrivilege.isDeny() && localPrivilege.sameAllowAndDenyRestrictions()) {
// same restrictions to we can unset the other one
localPrivilege.setDeny(false);
localPrivilege.setDenyRestrictions(Collections.emptySet());
}
} else {
localPrivilege.setAllowRestrictions(Collections.emptySet());
}
return localPrivilege;
}
/**
* Populates a local deny privilege in the privilegeToLocalPrivilegesMap
*
* @param privilegeToLocalPrivilegesMap the map containing the declared LocalPrivilege items
* @param privilege the privilege to deny
* @param isDeny true or false to set the deny value of the LocalPrivilege
* @param restrictions if isDeny is true, the set of restrictions
* @return the LocalPrivileges that was populated
*/
public static LocalPrivilege localDenyPriv(Map<Privilege, LocalPrivilege> privilegeToLocalPrivilegesMap,
Privilege privilege, boolean isDeny, Set<LocalRestriction> restrictions) {
LocalPrivilege localPrivilege = privilegeToLocalPrivilegesMap.computeIfAbsent(privilege, LocalPrivilege::new);
localPrivilege.setDeny(isDeny);
if (isDeny) {
localPrivilege.setDenyRestrictions(restrictions);
if (localPrivilege.sameAllowAndDenyRestrictions()) {
// same restrictions to we can unset the other one
localPrivilege.setAllow(false);
localPrivilege.setAllowRestrictions(Collections.emptySet());
}
} else {
localPrivilege.setDenyRestrictions(Collections.emptySet());
}
return localPrivilege;
}
/**
* Populates each of the local allow privilege in the privilegeToLocalPrivilegesMap. If the supplied
* privilege is an aggregate then the data is populated for each of non-aggregate privileges contained in
* the aggregate privilege. Otherwise, the data is populated for the privilege itself.
*
* @param privilegeToLocalPrivilegesMap the map containing the declared LocalPrivilege items
* @param p the privilege to update
* @param isAllow true or false to set the allow value of the LocalPrivilege
* @param restrictions if isAllow is true, the set of restrictions
*/
private static void expandAllowPrivWithoutAggregates(Map<Privilege, LocalPrivilege> privilegeToLocalPrivilegesMap,
Privilege p, boolean isAllow, Set<LocalRestriction> restrictions) throws RepositoryException {
if (p.isAggregate()) {
Privilege[] aggregatePrivileges = p.getDeclaredAggregatePrivileges();
for (Privilege aggregatePrivilege : aggregatePrivileges) {
if (aggregatePrivilege.isAggregate()) {
expandAllowPrivWithoutAggregates(privilegeToLocalPrivilegesMap, aggregatePrivilege, isAllow, restrictions);
} else {
localAllowPriv(privilegeToLocalPrivilegesMap, aggregatePrivilege, isAllow, restrictions);
}
}
} else {
localAllowPriv(privilegeToLocalPrivilegesMap, p, isAllow, restrictions);
}
}
/**
* Populates each of the local deny privilege in the privilegeToLocalPrivilegesMap. If the supplied
* privilege is an aggregate then the data is populated for each of non-aggregate privileges contained in
* the aggregate privilege. Otherwise, the data is populated for the privilege itself.
*
* @param privilegeToLocalPrivilegesMap the map containing the declared LocalPrivilege items
* @param p the privilege to update
* @param isDeny true or false to set the allow value of the LocalPrivilege
* @param restrictions if isDeny is true, the set of restrictions
*/
private static void expandDenyPrivWithoutAggregates(Map<Privilege, LocalPrivilege> privilegeToLocalPrivilegesMap,
Privilege p, boolean isDeny, Set<LocalRestriction> restrictions) throws RepositoryException {
if (p.isAggregate()) {
Privilege[] aggregatePrivileges = p.getDeclaredAggregatePrivileges();
for (Privilege aggregatePrivilege : aggregatePrivileges) {
if (aggregatePrivilege.isAggregate()) {
expandDenyPrivWithoutAggregates(privilegeToLocalPrivilegesMap, aggregatePrivilege, isDeny, restrictions);
} else {
localDenyPriv(privilegeToLocalPrivilegesMap, aggregatePrivilege, isDeny, restrictions);
}
}
} else {
localDenyPriv(privilegeToLocalPrivilegesMap, p, isDeny, restrictions);
}
}
/**
* Populates each of the allow privilege in the privilegeToLocalPrivilegesMap.
*
* @param privilegeToLocalPrivilegesMap the map containing the declared LocalPrivilege items
* @param restrictions the set of restrictions (possibly empty)
* @param privileges the privilege to update
*/
public static void allow(@NotNull Map<Privilege, LocalPrivilege> privilegeToLocalPrivilegesMap,
@NotNull Set<LocalRestriction> restrictions, @NotNull Collection<Privilege> privileges) throws RepositoryException {
for (Privilege privilege : privileges) {
expandAllowPrivWithoutAggregates(privilegeToLocalPrivilegesMap, privilege, true, restrictions);
}
}
/**
* Unset each of the allow privilege in the privilegeToLocalPrivilegesMap.
*
* @param privilegeToLocalPrivilegesMap the map containing the declared LocalPrivilege items
* @param privileges the privilege to update
*/
public static void unallow(@NotNull Map<Privilege, LocalPrivilege> privilegeToLocalPrivilegesMap,
@NotNull Collection<Privilege> privileges) throws RepositoryException {
for (Privilege privilege : privileges) {
expandAllowPrivWithoutAggregates(privilegeToLocalPrivilegesMap, privilege, false, Collections.emptySet());
}
}
/**
* Populates each of the deny privilege in the privilegeToLocalPrivilegesMap.
*
* @param privilegeToLocalPrivilegesMap the map containing the declared LocalPrivilege items
* @param restrictions the set of restrictions (possibly empty)
* @param privileges the privilege to update
*/
public static void deny(@NotNull Map<Privilege, LocalPrivilege> privilegeToLocalPrivilegesMap,
@NotNull Set<LocalRestriction> restrictions, @NotNull Collection<Privilege> privileges) throws RepositoryException {
for (Privilege privilege : privileges) {
expandDenyPrivWithoutAggregates(privilegeToLocalPrivilegesMap, privilege, true, restrictions);
}
}
/**
* Unset each of the deny privilege in the privilegeToLocalPrivilegesMap.
*
* @param privilegeToLocalPrivilegesMap the map containing the declared LocalPrivilege items
* @param privileges the privilege to update
*/
public static void undeny(@NotNull Map<Privilege, LocalPrivilege> privilegeToLocalPrivilegesMap,
@NotNull Collection<Privilege> privileges) throws RepositoryException {
for (Privilege privilege : privileges) {
expandDenyPrivWithoutAggregates(privilegeToLocalPrivilegesMap, privilege, false, Collections.emptySet());
}
}
/**
* Unset each of the allow and deny privilege in the privilegeToLocalPrivilegesMap.
*
* @param privilegeToLocalPrivilegesMap the map containing the declared LocalPrivilege items
* @param privileges the privilege to update
*/
public static void none(@NotNull Map<Privilege, LocalPrivilege> privilegeToLocalPrivilegesMap, @NotNull Collection<Privilege> privileges) throws RepositoryException {
for (Privilege privilege : privileges) {
expandAllowPrivWithoutAggregates(privilegeToLocalPrivilegesMap, privilege, false, Collections.emptySet());
expandDenyPrivWithoutAggregates(privilegeToLocalPrivilegesMap, privilege, false, Collections.emptySet());
}
}
/**
* Remove the specified restrictions from the LocalPrivilege
*
* @param privilegeToLocalPrivilegesMap the map containing the declared LocalPrivilege items
* @param privileges the privilege to update
* @param forAllow true to remove allow restrictions
* @param forDeny true to remove deny restrictions
* @param restrictionNames the set of restriction names to remove
* @return the local privilege that was populated
*/
private static LocalPrivilege localPrivRemoveRestrictions(@NotNull Map<Privilege, LocalPrivilege> privilegeToLocalPrivilegesMap,
@NotNull Privilege privilege, boolean forAllow, boolean forDeny, @NotNull Collection<String> restrictionNames) {
LocalPrivilege localPrivilege = privilegeToLocalPrivilegesMap.computeIfAbsent(privilege, LocalPrivilege::new);
// make sure allow/deny already exists
forAllow &= localPrivilege.isAllow();
forDeny &= localPrivilege.isDeny();
if (forAllow) {
localPrivilege.unsetAllowRestrictions(restrictionNames);
}
if (forDeny) {
localPrivilege.unsetDenyRestrictions(restrictionNames);
}
if (localPrivilege.sameAllowAndDenyRestrictions()) {
// same restrictions so we can unset one of them
if (forAllow) {
localPrivilege.setDeny(false);
localPrivilege.clearDenyRestrictions();
} else if (forDeny) {
localPrivilege.setAllow(false);
localPrivilege.clearAllowRestrictions();
}
}
return localPrivilege;
}
/**
* Remove the specified restrictions from each of the local privilege in the privilegeToLocalPrivilegesMap.
* If the supplied privilege is an aggregate then the data is populated for each of non-aggregate privileges contained in
* the aggregate privilege. Otherwise, the data is populated for the privilege itself.
*
* @param privilegeToLocalPrivilegesMap the map containing the declared LocalPrivilege items
* @param privileges the privilege to update
* @param forAllow true to remove allow restrictions
* @param forDeny true to remove deny restrictions
* @param restrictionNames the set of restriction names to remove
* @return the local privilege that was populated
*/
private static void removeRestrictionsWithoutAggregates(@NotNull Map<Privilege, LocalPrivilege> privilegeToLocalPrivilegesMap,
@NotNull Privilege p, boolean forAllow, boolean forDeny, @NotNull Collection<String> restrictionNames) throws RepositoryException {
if (p.isAggregate()) {
Privilege[] aggregatePrivileges = p.getDeclaredAggregatePrivileges();
for (Privilege aggregatePrivilege : aggregatePrivileges) {
if (aggregatePrivilege.isAggregate()) {
removeRestrictionsWithoutAggregates(privilegeToLocalPrivilegesMap, aggregatePrivilege, forAllow, forDeny, restrictionNames);
} else {
localPrivRemoveRestrictions(privilegeToLocalPrivilegesMap, aggregatePrivilege, forAllow, forDeny, restrictionNames);
}
}
} else {
localPrivRemoveRestrictions(privilegeToLocalPrivilegesMap, p, forAllow, forDeny, restrictionNames);
}
}
/**
* Add the specified restriction to the LocalPrivilege
*
* @param privilegeToLocalPrivilegesMap the map containing the declared LocalPrivilege items
* @param privileges the privilege to update
* @param forAllow true to remove allow restrictions
* @param forDeny true to remove deny restrictions
* @param restriction the restriction to add
* @param requireAllowOrDenyAlreadySet if true, only do work if the allow/deny state is already set to true
* @return the local privilege that was populated
*/
private static LocalPrivilege localPrivAddRestriction(@NotNull Map<Privilege, LocalPrivilege> privilegeToLocalPrivilegesMap,
@NotNull Privilege privilege, boolean forAllow, boolean forDeny, @NotNull LocalRestriction restriction,
boolean requireAllowOrDenyAlreadySet) {
LocalPrivilege localPrivilege = privilegeToLocalPrivilegesMap.computeIfAbsent(privilege, LocalPrivilege::new);
if (forDeny) {
if (requireAllowOrDenyAlreadySet && !localPrivilege.isDeny()) {
//skip it
} else {
localPrivilege.setDeny(true);
localPrivilege.unsetDenyRestrictions(Collections.singleton(restriction.getName()));
localPrivilege.setDenyRestrictions(Collections.singleton(restriction));
}
}
if (forAllow) {
if (requireAllowOrDenyAlreadySet && !localPrivilege.isAllow()) {
//skip it
} else {
localPrivilege.setAllow(true);
localPrivilege.unsetAllowRestrictions(Collections.singleton(restriction.getName()));
localPrivilege.setAllowRestrictions(Collections.singleton(restriction));
}
}
if (localPrivilege.sameAllowAndDenyRestrictions()) {
// same restrictions so we can unset one of them
if (forAllow) {
localPrivilege.setDeny(false);
localPrivilege.setDenyRestrictions(Collections.emptySet());
} else if (forDeny) {
localPrivilege.setAllow(false);
localPrivilege.setAllowRestrictions(Collections.emptySet());
}
}
return localPrivilege;
}
/**
* Add the specified restrictions to each of the local privilege in the privilegeToLocalPrivilegesMap.
* If the supplied privilege is an aggregate then the data is populated for each of non-aggregate privileges contained in
* the aggregate privilege. Otherwise, the data is populated for the privilege itself.
*
* @param privilegeToLocalPrivilegesMap the map containing the declared LocalPrivilege items
* @param privileges the privilege to update
* @param forAllow true to remove allow restrictions
* @param forDeny true to remove deny restrictions
* @param restrictionNames the set of restriction names to remove
* @return the local privilege that was populated
*/
private static void addRestrictionWithoutAggregates(Map<Privilege, LocalPrivilege> privilegeToLocalPrivilegesMap,
Privilege p, boolean forAllow, boolean forDeny, LocalRestriction restriction, boolean requireAllowOrDenyAlreadySet) throws RepositoryException {
if (p.isAggregate()) {
Privilege[] aggregatePrivileges = p.getDeclaredAggregatePrivileges();
for (Privilege aggregatePrivilege : aggregatePrivileges) {
if (aggregatePrivilege.isAggregate()) {
addRestrictionWithoutAggregates(privilegeToLocalPrivilegesMap, aggregatePrivilege, forAllow, forDeny, restriction, requireAllowOrDenyAlreadySet);
} else {
localPrivAddRestriction(privilegeToLocalPrivilegesMap, aggregatePrivilege, forAllow, forDeny, restriction, requireAllowOrDenyAlreadySet);
}
}
} else {
localPrivAddRestriction(privilegeToLocalPrivilegesMap, p, forAllow, forDeny, restriction, requireAllowOrDenyAlreadySet);
}
}
/**
* Adds the restriction for each of the supplied allow privilege in the privilegeToLocalPrivilegesMap.
*
* @param privilegeToLocalPrivilegesMap the map containing the declared LocalPrivilege items
* @param restriction the restrictions to add
* @param privileges the privilege to update
*/
public static void allowRestriction(@NotNull Map<Privilege, LocalPrivilege> privilegeToLocalPrivilegesMap,
@NotNull LocalRestriction restriction, @NotNull Collection<Privilege> privileges) throws RepositoryException {
for (Privilege privilege : privileges) {
addRestrictionWithoutAggregates(privilegeToLocalPrivilegesMap, privilege, true, false, restriction, false);
}
}
/**
* Remove the restriction for each of the supplied allow privilege in the privilegeToLocalPrivilegesMap.
*
* @param privilegeToLocalPrivilegesMap the map containing the declared LocalPrivilege items
* @param restrictionName the restriction name to remove
* @param privileges the privilege to update
*/
public static void unallowRestriction(@NotNull Map<Privilege, LocalPrivilege> privilegeToLocalPrivilegesMap,
@NotNull String restrictionName, @NotNull Collection<Privilege> privileges) throws RepositoryException {
unallowRestrictions(privilegeToLocalPrivilegesMap, Collections.singleton(restrictionName), privileges);
}
/**
* Remove the restrictions for each of the supplied allow privilege in the privilegeToLocalPrivilegesMap.
*
* @param privilegeToLocalPrivilegesMap the map containing the declared LocalPrivilege items
* @param restrictionNames the collection of restriction names to remove
* @param privileges the privilege to update
*/
public static void unallowRestrictions(@NotNull Map<Privilege, LocalPrivilege> privilegeToLocalPrivilegesMap,
@NotNull Collection<String> restrictionNames, @NotNull Collection<Privilege> privileges) throws RepositoryException {
for (Privilege privilege : privileges) {
removeRestrictionsWithoutAggregates(privilegeToLocalPrivilegesMap, privilege, true, false, restrictionNames);
}
}
/**
* Adds the restriction for each of the supplied deny privilege in the privilegeToLocalPrivilegesMap.
*
* @param privilegeToLocalPrivilegesMap the map containing the declared LocalPrivilege items
* @param restriction the restrictions to add
* @param privileges the privilege to update
*/
public static void denyRestriction(@NotNull Map<Privilege, LocalPrivilege> privilegeToLocalPrivilegesMap,
@NotNull LocalRestriction restriction, @NotNull Collection<Privilege> privileges) throws RepositoryException {
for (Privilege privilege : privileges) {
addRestrictionWithoutAggregates(privilegeToLocalPrivilegesMap, privilege, false, true, restriction, false);
}
}
/**
* Remove the restriction for each of the supplied deny privilege in the privilegeToLocalPrivilegesMap.
*
* @param privilegeToLocalPrivilegesMap the map containing the declared LocalPrivilege items
* @param restriction the restrictions to add
* @param privileges the privilege to update
*/
public static void undenyRestriction(@NotNull Map<Privilege, LocalPrivilege> privilegeToLocalPrivilegesMap,
@NotNull String restrictionName, @NotNull Collection<Privilege> privileges) throws RepositoryException {
undenyRestrictions(privilegeToLocalPrivilegesMap, Collections.singleton(restrictionName), privileges);
}
/**
* Remove the restrictions for each of the supplied deny privilege in the privilegeToLocalPrivilegesMap.
*
* @param privilegeToLocalPrivilegesMap the map containing the declared LocalPrivilege items
* @param restrictionNames the collection of restriction names to remove
* @param privileges the privilege to update
*/
public static void undenyRestrictions(@NotNull Map<Privilege, LocalPrivilege> privilegeToLocalPrivilegesMap,
@NotNull Collection<String> restrictionNames, @NotNull Collection<Privilege> privileges) throws RepositoryException {
for (Privilege privilege : privileges) {
removeRestrictionsWithoutAggregates(privilegeToLocalPrivilegesMap, privilege, false, true, restrictionNames);
}
}
/**
* Adds the restriction for each of the supplied privilege in the privilegeToLocalPrivilegesMap that
* is already has allow or deny set to true.
*
* @param privilegeToLocalPrivilegesMap the map containing the declared LocalPrivilege items
* @param restriction the restrictions to add
* @param privileges the privilege to update
*/
public static void allowOrDenyRestriction(@NotNull Map<Privilege, LocalPrivilege> privilegeToLocalPrivilegesMap,
@NotNull LocalRestriction restriction, @NotNull Collection<Privilege> privileges) throws RepositoryException {
for (Privilege privilege : privileges) {
addRestrictionWithoutAggregates(privilegeToLocalPrivilegesMap, privilege, true, true, restriction, true);
}
}
/**
* Remove the restriction for each of the supplied privilege in the privilegeToLocalPrivilegesMap that
* is already has allow or deny set to true.
*
* @param privilegeToLocalPrivilegesMap the map containing the declared LocalPrivilege items
* @param restriction the restrictions to add
* @param privileges the privilege to update
*/
public static void unallowOrUndenyRestriction(@NotNull Map<Privilege, LocalPrivilege> privilegeToLocalPrivilegesMap,
@NotNull String restrictionName, @NotNull Collection<Privilege> privileges) throws RepositoryException {
unallowOrUndenyRestrictions(privilegeToLocalPrivilegesMap, Collections.singleton(restrictionName), privileges);
}
/**
* Remove the restrictions for each of the supplied privilege in the privilegeToLocalPrivilegesMap that
* is already has allow or deny set to true.
*
* @param privilegeToLocalPrivilegesMap the map containing the declared LocalPrivilege items
* @param restrictionNames the collection of restriction names to remove
* @param privileges the privilege to update
*/
public static void unallowOrUndenyRestrictions(@NotNull Map<Privilege, LocalPrivilege> privilegeToLocalPrivilegesMap,
@NotNull Collection<String> restrictionNames, @NotNull Collection<Privilege> privileges) throws RepositoryException {
for (Privilege privilege : privileges) {
removeRestrictionsWithoutAggregates(privilegeToLocalPrivilegesMap, privilege, true, true, restrictionNames);
}
}
/**
* Calculates the supported privileges in the resource path exists, or the registered
* privileges if the resource path does not exist
*
* @param jcrSession the current session
* @param resourcePath the resource path to consider
* @return
* @throws RepositoryException
*/
private static @NotNull Privilege[] getSupportedOrRegisteredPrivileges(@NotNull Session jcrSession, @Nullable String resourcePath)
throws RepositoryException {
Privilege[] supportedPrivileges = null;
if (resourcePath != null && jcrSession.nodeExists(resourcePath)) {
supportedPrivileges = jcrSession.getAccessControlManager().getSupportedPrivileges(resourcePath);
} else {
// non-existing path. We can't determine what is supported there, so consider all registered privileges
Workspace workspace = jcrSession.getWorkspace();
if (workspace instanceof JackrabbitWorkspace) {
PrivilegeManager privilegeManager = ((JackrabbitWorkspace)workspace).getPrivilegeManager();
supportedPrivileges = privilegeManager.getRegisteredPrivileges();
}
}
return supportedPrivileges == null ? new Privilege[0] : supportedPrivileges;
}
/**
* Process the supplied privileges and consolidate each aggregate whenever the state of all the
* aggregated direct child privileges are allow or deny
*
* @param jcrSession the current session
* @param resourcePath the path of the resource
* @param privilegeToLocalPrivilegesMap map of privileges to process. The map entry key is the
* privilege and value is the associated LocalPrivilege.
* @param privilegeLongestDepthMap map of privileges to the longest depth. See {@link #buildPrivilegeLongestDepthMap(Privilege)}
*/
public static void consolidateAggregates(Session jcrSession, String resourcePath,
Map<Privilege, LocalPrivilege> privilegeToLocalPrivilegesMap,
Map<Privilege, Integer> privilegeLongestDepthMap) throws RepositoryException {
Privilege[] supportedPrivileges = getSupportedOrRegisteredPrivileges(jcrSession, resourcePath);
// sort the aggregates to process the deepest first
Privilege[] supportedAggregatePrivileges = Stream.of(supportedPrivileges)
.filter(Privilege::isAggregate)
.sorted((p1, p2) -> privilegeLongestDepthMap.get(p2).compareTo(privilegeLongestDepthMap.get(p1)))
.toArray(size -> new Privilege[size]);
// loop to consider each aggregate privilege
for (Privilege aggregatePrivilege : supportedAggregatePrivileges) {
// filter the declared aggregate privileges in case some are not a
// direct child. For example, the jcr:all aggregate privileges list
// contains too many for this use case.
int childDepth = privilegeLongestDepthMap.getOrDefault(aggregatePrivilege, -1) + 1;
Privilege[] childPrivileges = Stream.of(aggregatePrivilege.getDeclaredAggregatePrivileges())
.filter(p -> privilegeLongestDepthMap.getOrDefault(p, -1) == childDepth)
.toArray(size -> new Privilege[size]);
// map to LocalPrivileges if we have them
List<LocalPrivilege> childLocalPrivileges = Stream.of(childPrivileges)
.filter(privilegeToLocalPrivilegesMap::containsKey)
.map(privilegeToLocalPrivilegesMap::get)
.collect(Collectors.toList());
if (childPrivileges.length == childLocalPrivileges.size()) {
boolean allAllow = childLocalPrivileges.stream().allMatch(LocalPrivilege::isAllow);
if (allAllow) {
// all the child privileges are allow so we can mark the parent as allow
LocalPrivilege alp = privilegeToLocalPrivilegesMap.computeIfAbsent(aggregatePrivilege, LocalPrivilege::new);
alp.setAllow(true);
// if the restrictions of all the items is the same then we should copy it up
// and unset the data from each child
Set<LocalRestriction> firstAllowRestrictions = childLocalPrivileges.get(0).getAllowRestrictions();
boolean allRestrictionsSame = childLocalPrivileges.stream().allMatch(lp -> firstAllowRestrictions.equals(lp.getAllowRestrictions()));
if (allRestrictionsSame) {
alp.setAllowRestrictions(firstAllowRestrictions);
}
// each child with the same restrictions can be unset
for (LocalPrivilege lp : childLocalPrivileges) {
if (lp.sameAllowRestrictions(alp.getAllowRestrictions())) {
lp.setAllow(false);
lp.setAllowRestrictions(Collections.emptySet());
}
}
}
boolean allDeny = childLocalPrivileges.stream().allMatch(LocalPrivilege::isDeny);
if (allDeny) {
// all the child privileges are deny so we can mark the parent as deny
LocalPrivilege alp = privilegeToLocalPrivilegesMap.computeIfAbsent(aggregatePrivilege, LocalPrivilege::new);
alp.setDeny(true);
// if the restrictions of all the items is the same then we should copy it up
// and unset the data from each child
Set<LocalRestriction> firstDenyRestrictions = childLocalPrivileges.get(0).getDenyRestrictions();
boolean allRestrictionsSame = childLocalPrivileges.stream().allMatch(lp -> firstDenyRestrictions.equals(lp.getDenyRestrictions()));
if (allRestrictionsSame) {
alp.setDenyRestrictions(firstDenyRestrictions);
}
// each child with the same restrictions can be unset
for (LocalPrivilege lp : childLocalPrivileges) {
if (lp.sameDenyRestrictions(alp.getDenyRestrictions())) {
lp.setDeny(false);
lp.setDenyRestrictions(Collections.emptySet());
}
}
}
}
}
// remove any entries that are neither allow nor deny
privilegeToLocalPrivilegesMap.entrySet().removeIf(entry -> entry.getValue().isNone());
}
}