Index: oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeAccessControlManager.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeAccessControlManager.java (revision 1859856) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeAccessControlManager.java (date 1558687126000) @@ -38,6 +38,7 @@ import org.apache.jackrabbit.oak.spi.security.SecurityProvider; import org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.AbstractAccessControlManager; import org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.PolicyOwner; +import org.apache.jackrabbit.oak.spi.security.authorization.permission.AggregationFilter; import org.jetbrains.annotations.NotNull; /** @@ -50,13 +51,16 @@ class CompositeAccessControlManager extends AbstractAccessControlManager { private final List acMgrs; + private final AggregationFilter aggregationFilter; public CompositeAccessControlManager(@NotNull Root root, @NotNull NamePathMapper namePathMapper, @NotNull SecurityProvider securityProvider, - @NotNull List acMgrs) { + @NotNull List acMgrs, + @NotNull AggregationFilter aggregationFilter) { super(root, namePathMapper, securityProvider); this.acMgrs = acMgrs; + this.aggregationFilter = aggregationFilter; } //-----------------------------------------------< AccessControlManager >--- @@ -86,6 +90,9 @@ ImmutableList.Builder policies = ImmutableList.builder(); for (AccessControlManager acMgr : acMgrs) { policies.add(acMgr.getEffectivePolicies(absPath)); + if (aggregationFilter.stop(acMgr, absPath)) { + break; + } } List l = policies.build(); return l.toArray(new AccessControlPolicy[0]); @@ -154,7 +161,11 @@ ImmutableList.Builder policies = ImmutableList.builder(); for (AccessControlManager acMgr : acMgrs) { if (acMgr instanceof JackrabbitAccessControlManager) { - policies.add(((JackrabbitAccessControlManager) acMgr).getEffectivePolicies(principals)); + JackrabbitAccessControlManager jAcMgr = (JackrabbitAccessControlManager) acMgr; + policies.add(jAcMgr.getEffectivePolicies(principals)); + if (aggregationFilter.stop(jAcMgr, principals)) { + break; + } } } List l = policies.build(); Index: oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeAuthorizationConfiguration.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeAuthorizationConfiguration.java (revision 1859856) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeAuthorizationConfiguration.java (date 1558687310000) @@ -30,6 +30,7 @@ import org.apache.jackrabbit.oak.spi.security.SecurityProvider; import org.apache.jackrabbit.oak.spi.security.authorization.AuthorizationConfiguration; import org.apache.jackrabbit.oak.spi.security.authorization.permission.AggregatedPermissionProvider; +import org.apache.jackrabbit.oak.spi.security.authorization.permission.AggregationFilter; import org.apache.jackrabbit.oak.spi.security.authorization.permission.EmptyPermissionProvider; import org.apache.jackrabbit.oak.spi.security.authorization.permission.PermissionProvider; import org.apache.jackrabbit.oak.spi.security.authorization.restriction.CompositeRestrictionProvider; @@ -112,6 +113,8 @@ private CompositionType compositionType = CompositionType.AND; + private AggregationFilter aggregationFilter = AggregationFilter.DEFAULT; + public CompositeAuthorizationConfiguration() { super(AuthorizationConfiguration.NAME); } @@ -124,6 +127,10 @@ this.compositionType = CompositionType.fromString(ct); } + public void withAggregationFilter(@NotNull AggregationFilter aggregationFilter) { + this.aggregationFilter = aggregationFilter; + } + @NotNull @Override public AccessControlManager getAccessControlManager(@NotNull final Root root, @@ -134,8 +141,7 @@ case 1: return configurations.get(0).getAccessControlManager(root, namePathMapper); default: List mgrs = Lists.transform(configurations, authorizationConfiguration -> authorizationConfiguration.getAccessControlManager(root, namePathMapper)); - return new CompositeAccessControlManager(root, namePathMapper, getSecurityProvider(), mgrs); - + return new CompositeAccessControlManager(root, namePathMapper, getSecurityProvider(), mgrs, aggregationFilter); } } @@ -172,7 +178,11 @@ for (AuthorizationConfiguration conf : configurations) { PermissionProvider pProvider = conf.getPermissionProvider(root, workspaceName, principals); if (pProvider instanceof AggregatedPermissionProvider) { - aggrPermissionProviders.add((AggregatedPermissionProvider) pProvider); + AggregatedPermissionProvider aggrProvider = (AggregatedPermissionProvider) pProvider; + aggrPermissionProviders.add(aggrProvider); + if (aggregationFilter.stop(aggrProvider, principals)) { + break; + } } else { log.debug("Ignoring permission provider of '{}': Not an AggregatedPermissionProvider", conf.getClass().getName()); } Index: oak-core/src/main/java/org/apache/jackrabbit/oak/security/internal/SecurityProviderRegistration.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/security/internal/SecurityProviderRegistration.java (revision 1859856) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/security/internal/SecurityProviderRegistration.java (date 1558688294000) @@ -17,15 +17,18 @@ package org.apache.jackrabbit.oak.security.internal; import java.io.Closeable; +import java.security.Principal; import java.util.Collection; import java.util.Collections; import java.util.Dictionary; import java.util.Hashtable; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; +import org.apache.jackrabbit.api.security.JackrabbitAccessControlManager; import org.apache.jackrabbit.oak.commons.PropertiesUtil; import org.apache.jackrabbit.oak.osgi.OsgiWhiteboard; import org.apache.jackrabbit.oak.plugins.tree.RootProvider; @@ -49,6 +52,8 @@ import org.apache.jackrabbit.oak.spi.security.authentication.token.TokenConfiguration; import org.apache.jackrabbit.oak.spi.security.authorization.AuthorizationConfiguration; import org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.AccessControlConstants; +import org.apache.jackrabbit.oak.spi.security.authorization.permission.AggregatedPermissionProvider; +import org.apache.jackrabbit.oak.spi.security.authorization.permission.AggregationFilter; import org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionProvider; import org.apache.jackrabbit.oak.spi.security.principal.CompositePrincipalConfiguration; import org.apache.jackrabbit.oak.spi.security.principal.PrincipalConfiguration; @@ -62,6 +67,7 @@ import org.apache.jackrabbit.oak.spi.whiteboard.Whiteboard; import org.apache.jackrabbit.oak.stats.StatisticsProvider; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.osgi.framework.BundleContext; import org.osgi.framework.Constants; import org.osgi.framework.ServiceReference; @@ -82,6 +88,8 @@ import com.google.common.io.Closer; +import javax.jcr.security.AccessControlManager; + import static com.google.common.collect.Lists.newArrayList; import static org.apache.jackrabbit.oak.spi.security.RegistrationConstants.OAK_SECURITY_NAME; import static org.apache.jackrabbit.oak.commons.IOUtils.closeQuietly; @@ -156,6 +164,7 @@ private final SortedMap authorizableActionProviders = Collections.synchronizedSortedMap(new TreeMap<>()); private final SortedMap restrictionProviders = Collections.synchronizedSortedMap(new TreeMap<>()); private final SortedMap userAuthenticationFactories = Collections.synchronizedSortedMap(new TreeMap<>()); + private final SortedMap aggregationFilters = Collections.synchronizedSortedMap(new TreeMap<>()); private RootProvider rootProvider; private TreeProvider treeProvider; @@ -424,6 +433,26 @@ maybeUnregister(); } + @Reference( + name = "aggregationFilters", service = AggregationFilter.class, + cardinality = ReferenceCardinality.MULTIPLE, + policy = ReferencePolicy.DYNAMIC) + public void bindAggregationFilter(@NotNull ServiceReference serviceReference, @NotNull AggregationFilter aggregationFilter) { + synchronized (this) { + aggregationFilters.put(serviceReference, aggregationFilter); + addCandidate(serviceReference); + } + maybeRegister(); + } + + public void unbindAggregationFilter(@NotNull ServiceReference serviceReference, @NotNull AggregationFilter aggregationFilter) { + synchronized (this) { + aggregationFilters.remove(serviceReference); + removeCandidate(serviceReference); + } + maybeUnregister(); + } + private void maybeRegister() { BundleContext context; @@ -557,6 +586,7 @@ ConfigurationParameters authorizationParams = ConfigurationParameters .of(AccessControlConstants.PARAM_RESTRICTION_PROVIDER, createWhiteboardRestrictionProvider()); + authorizationConfiguration.withAggregationFilter(createAggregationFilter()); return SecurityProviderBuilder.newBuilder().withRootProvider(rootProvider).withTreeProvider(treeProvider) .with(authenticationConfiguration, EMPTY, privilegeConfiguration, EMPTY, userConfiguration, userParams, @@ -621,6 +651,49 @@ }; } + private AggregationFilter createAggregationFilter() { + List filters; + synchronized (aggregationFilters) { + filters = newArrayList(aggregationFilters.values()); + } + switch (filters.size()) { + case 0: return AggregationFilter.DEFAULT; + case 1: return filters.get(0); + default: + return new AggregationFilter() { + @Override + public boolean stop(@NotNull AggregatedPermissionProvider permissionProvider, @NotNull Set principals) { + for (AggregationFilter f : filters) { + if (f.stop(permissionProvider, principals)) { + return true; + } + } + return false; + } + + @Override + public boolean stop(@NotNull JackrabbitAccessControlManager accessControlManager, @NotNull Set principals) { + for (AggregationFilter f : filters) { + if (f.stop(accessControlManager, principals)) { + return true; + } + } + return false; + } + + @Override + public boolean stop(@NotNull AccessControlManager accessControlManager, @Nullable String absPath) { + for (AggregationFilter f : filters) { + if (f.stop(accessControlManager, absPath)) { + return true; + } + } + return false; + } + }; + } + } + private void addCandidate(Map properties) { String pidOrName = getServicePidOrComponentName(properties); Index: oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/AggregationFilter.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/AggregationFilter.java (date 1558687254000) +++ oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/AggregationFilter.java (date 1558687254000) @@ -0,0 +1,78 @@ +/* + * 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.jackrabbit.oak.spi.security.authorization.permission; + +import org.apache.jackrabbit.api.security.JackrabbitAccessControlManager; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.jcr.security.AccessControlManager; +import java.security.Principal; +import java.util.Set; + +public interface AggregationFilter { + + /** + * + * @param permissionProvider + * @param principals + * @return {@code true} if aggregation of permission providers should be stopped after the given {@code permissionProvider} + * created for the given set of {@code principals}. + */ + boolean stop(@NotNull AggregatedPermissionProvider permissionProvider, @NotNull Set principals); + + /** + * + * @param accessControlManager + * @param principals + * @return {@code true} if aggregation of effective policies for the specified principals should be stopped after + * the given {@code accessControlManager}. + * @see AccessControlManager#getEffectivePolicies(String) + */ + boolean stop(@NotNull JackrabbitAccessControlManager accessControlManager, @NotNull Set principals); + + /** + * + * @param accessControlManager + * @param absPath + * @return {@code true} if aggregation of effective policies for the specified effective path should be stopped after + * the given {@code accessControlManager}. + * @see JackrabbitAccessControlManager#getEffectivePolicies(Set) + */ + boolean stop(@NotNull AccessControlManager accessControlManager, @Nullable String absPath); + + /** + * Default implementation of the {@code AggregationFilter} interface that handles all combinations of permission + * providers and principals and never aborts the evaluation. + */ + AggregationFilter DEFAULT = new AggregationFilter() { + @Override + public boolean stop(@NotNull AggregatedPermissionProvider permissionProvider, @NotNull Set principals) { + return false; + } + + @Override + public boolean stop(@NotNull JackrabbitAccessControlManager accessControlManager, @NotNull Set principals) { + return false; + } + + @Override + public boolean stop(@NotNull AccessControlManager accessControlManager, @Nullable String absPath) { + return false; + } + }; +} \ No newline at end of file Index: oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/package-info.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/package-info.java (revision 1859856) +++ oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/package-info.java (date 1557839761000) @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -@Version("4.1.0") +@Version("4.2.0") package org.apache.jackrabbit.oak.spi.security.authorization.permission; import org.osgi.annotation.versioning.Version;