You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by an...@apache.org on 2019/06/06 16:01:15 UTC

svn commit: r1860721 - in /jackrabbit/oak/trunk: oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/ oak-core/src/main/java/org/apache/jackrabbit/oak/security/internal/ oak-core/src/test/java/org/apache/jackrabbit/oak/sec...

Author: angela
Date: Thu Jun  6 16:01:14 2019
New Revision: 1860721

URL: http://svn.apache.org/viewvc?rev=1860721&view=rev
Log:
OAK-8155 : CompositePermissionProvider: add possibility to abort evaluation

Added:
    jackrabbit/oak/trunk/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/AggregationFilter.java   (with props)
    jackrabbit/oak/trunk/oak-security-spi/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/AggregationFilterDefaultTest.java   (with props)
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeAccessControlManager.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeAuthorizationConfiguration.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/internal/SecurityProviderRegistration.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeAccessControlManagerTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeAuthorizationConfigurationTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/internal/SecurityProviderRegistrationTest.java
    jackrabbit/oak/trunk/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/package-info.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeAccessControlManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeAccessControlManager.java?rev=1860721&r1=1860720&r2=1860721&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeAccessControlManager.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeAccessControlManager.java Thu Jun  6 16:01:14 2019
@@ -38,6 +38,7 @@ import org.apache.jackrabbit.oak.namepat
 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 @@ import org.jetbrains.annotations.NotNull
 class CompositeAccessControlManager extends AbstractAccessControlManager {
 
     private final List<AccessControlManager> acMgrs;
+    private final AggregationFilter aggregationFilter;
 
     public CompositeAccessControlManager(@NotNull Root root,
                                          @NotNull NamePathMapper namePathMapper,
                                          @NotNull SecurityProvider securityProvider,
-                                         @NotNull List<AccessControlManager> acMgrs) {
+                                         @NotNull List<AccessControlManager> acMgrs,
+                                         @NotNull AggregationFilter aggregationFilter) {
         super(root, namePathMapper, securityProvider);
         this.acMgrs = acMgrs;
+        this.aggregationFilter = aggregationFilter;
     }
 
     //-----------------------------------------------< AccessControlManager >---
@@ -86,6 +90,9 @@ class CompositeAccessControlManager exte
         ImmutableList.Builder<AccessControlPolicy> policies = ImmutableList.builder();
         for (AccessControlManager acMgr : acMgrs) {
             policies.add(acMgr.getEffectivePolicies(absPath));
+            if (aggregationFilter.stop(acMgr, absPath)) {
+                break;
+            }
         }
         List<AccessControlPolicy> l = policies.build();
         return l.toArray(new AccessControlPolicy[0]);
@@ -154,7 +161,11 @@ class CompositeAccessControlManager exte
         ImmutableList.Builder<AccessControlPolicy> 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<AccessControlPolicy> l = policies.build();

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeAuthorizationConfiguration.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeAuthorizationConfiguration.java?rev=1860721&r1=1860720&r2=1860721&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeAuthorizationConfiguration.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeAuthorizationConfiguration.java Thu Jun  6 16:01:14 2019
@@ -30,6 +30,7 @@ import org.apache.jackrabbit.oak.spi.sec
 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 @@ public class CompositeAuthorizationConfi
 
     private CompositionType compositionType = CompositionType.AND;
 
+    private AggregationFilter aggregationFilter = AggregationFilter.DEFAULT;
+
     public CompositeAuthorizationConfiguration() {
         super(AuthorizationConfiguration.NAME);
     }
@@ -124,6 +127,10 @@ public class CompositeAuthorizationConfi
         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 @@ public class CompositeAuthorizationConfi
             case 1: return configurations.get(0).getAccessControlManager(root, namePathMapper);
             default:
                 List<AccessControlManager> 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 @@ public class CompositeAuthorizationConfi
                 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());
                     }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/internal/SecurityProviderRegistration.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/internal/SecurityProviderRegistration.java?rev=1860721&r1=1860720&r2=1860721&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/internal/SecurityProviderRegistration.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/internal/SecurityProviderRegistration.java Thu Jun  6 16:01:14 2019
@@ -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.sec
 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.whi
 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 org.slf4j.LoggerFactory;
 
 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 @@ public class SecurityProviderRegistratio
     private final SortedMap<ServiceReference, AuthorizableActionProvider> authorizableActionProviders = Collections.synchronizedSortedMap(new TreeMap<>());
     private final SortedMap<ServiceReference, RestrictionProvider> restrictionProviders = Collections.synchronizedSortedMap(new TreeMap<>());
     private final SortedMap<ServiceReference, UserAuthenticationFactory> userAuthenticationFactories = Collections.synchronizedSortedMap(new TreeMap<>());
+    private final SortedMap<ServiceReference, AggregationFilter> aggregationFilters = Collections.synchronizedSortedMap(new TreeMap<>());
 
     private RootProvider rootProvider;
     private TreeProvider treeProvider;
@@ -424,6 +433,26 @@ public class SecurityProviderRegistratio
         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 @@ public class SecurityProviderRegistratio
 
         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 @@ public class SecurityProviderRegistratio
         };
     }
 
+    private AggregationFilter createAggregationFilter() {
+        List<AggregationFilter> 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<Principal> principals) {
+                        for (AggregationFilter f : filters) {
+                            if (f.stop(permissionProvider, principals)) {
+                                return true;
+                            }
+                        }
+                        return false;
+                    }
+
+                    @Override
+                    public boolean stop(@NotNull JackrabbitAccessControlManager accessControlManager, @NotNull Set<Principal> 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<String, Object> properties) {
         String pidOrName = getServicePidOrComponentName(properties);
 

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeAccessControlManagerTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeAccessControlManagerTest.java?rev=1860721&r1=1860720&r2=1860721&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeAccessControlManagerTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeAccessControlManagerTest.java Thu Jun  6 16:01:14 2019
@@ -26,12 +26,13 @@ import org.apache.jackrabbit.api.securit
 import org.apache.jackrabbit.commons.iterator.AccessControlPolicyIteratorAdapter;
 import org.apache.jackrabbit.oak.AbstractSecurityTest;
 import org.apache.jackrabbit.oak.api.Tree;
-import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.namepath.NamePathMapper;
 import org.apache.jackrabbit.oak.plugins.tree.TreeUtil;
 import org.apache.jackrabbit.oak.spi.nodetype.NodeTypeConstants;
 import org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.PolicyOwner;
+import org.apache.jackrabbit.oak.spi.security.authorization.permission.AggregationFilter;
 import org.apache.jackrabbit.oak.spi.security.principal.EveryonePrincipal;
+import org.apache.jackrabbit.oak.spi.security.principal.PrincipalImpl;
 import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants;
 import org.jetbrains.annotations.NotNull;
 import org.junit.Test;
@@ -46,6 +47,7 @@ import java.security.Principal;
 import java.util.Collections;
 import java.util.Set;
 
+import static org.apache.jackrabbit.oak.commons.PathUtils.ROOT_PATH;
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -74,7 +76,7 @@ public class CompositeAccessControlManag
 
         acMgr = createComposite(getAccessControlManager(root), new TestAcMgr());
 
-        Tree tree = root.getTree(PathUtils.ROOT_PATH);
+        Tree tree = root.getTree(ROOT_PATH);
         TreeUtil.addChild(tree,"test", NodeTypeConstants.NT_OAK_UNSTRUCTURED);
 
         root.commit();
@@ -93,7 +95,12 @@ public class CompositeAccessControlManag
 
     @NotNull
     private CompositeAccessControlManager createComposite(@NotNull AccessControlManager... acMgrs) {
-        return new CompositeAccessControlManager(root, NamePathMapper.DEFAULT, getSecurityProvider(), ImmutableList.copyOf(acMgrs));
+        return createComposite(AggregationFilter.DEFAULT, acMgrs);
+    }
+
+    @NotNull
+    private CompositeAccessControlManager createComposite(@NotNull AggregationFilter aggregationFilter, @NotNull AccessControlManager... acMgrs) {
+        return new CompositeAccessControlManager(root, NamePathMapper.DEFAULT, getSecurityProvider(), ImmutableList.copyOf(acMgrs), aggregationFilter);
     }
 
     @Test
@@ -127,10 +134,10 @@ public class CompositeAccessControlManag
         AccessControlManager mgr = when(mock(AccessControlManager.class).getApplicablePolicies(anyString())).thenReturn(new AccessControlPolicyIteratorAdapter(ImmutableSet.of(policy))).getMock();
 
         CompositeAccessControlManager composite = createComposite(mgr);
-        AccessControlPolicyIterator it = composite.getApplicablePolicies(PathUtils.ROOT_PATH);
+        AccessControlPolicyIterator it = composite.getApplicablePolicies(ROOT_PATH);
         assertFalse(it.hasNext());
 
-        verify(mgr, never()).getApplicablePolicies(PathUtils.ROOT_PATH);
+        verify(mgr, never()).getApplicablePolicies(ROOT_PATH);
     }
 
     @Test
@@ -218,9 +225,9 @@ public class CompositeAccessControlManag
 
         try {
             CompositeAccessControlManager composite = createComposite(mgr);
-            composite.setPolicy(PathUtils.ROOT_PATH, policy);
+            composite.setPolicy(ROOT_PATH, policy);
         } finally {
-            verify(mgr, never()).setPolicy(PathUtils.ROOT_PATH, policy);
+            verify(mgr, never()).setPolicy(ROOT_PATH, policy);
         }
     }
 
@@ -233,10 +240,10 @@ public class CompositeAccessControlManag
 
         try {
             CompositeAccessControlManager composite = createComposite(mgr);
-            composite.setPolicy(PathUtils.ROOT_PATH, policy);
+            composite.setPolicy(ROOT_PATH, policy);
         } finally {
-            verify(mgr, never()).setPolicy(PathUtils.ROOT_PATH, policy);
-            verify(((PolicyOwner) mgr), times(1)).defines(PathUtils.ROOT_PATH, policy);
+            verify(mgr, never()).setPolicy(ROOT_PATH, policy);
+            verify(((PolicyOwner) mgr), times(1)).defines(ROOT_PATH, policy);
         }
     }
 
@@ -267,9 +274,9 @@ public class CompositeAccessControlManag
 
         try {
             CompositeAccessControlManager composite = createComposite(mgr);
-            composite.removePolicy(PathUtils.ROOT_PATH, policy);
+            composite.removePolicy(ROOT_PATH, policy);
         } finally {
-            verify(mgr, never()).removePolicy(PathUtils.ROOT_PATH, policy);
+            verify(mgr, never()).removePolicy(ROOT_PATH, policy);
         }
     }
 
@@ -282,10 +289,10 @@ public class CompositeAccessControlManag
 
         try {
             CompositeAccessControlManager composite = createComposite(mgr);
-            composite.removePolicy(PathUtils.ROOT_PATH, policy);
+            composite.removePolicy(ROOT_PATH, policy);
         } finally {
-            verify(mgr, never()).removePolicy(PathUtils.ROOT_PATH, policy);
-            verify(((PolicyOwner) mgr), times(1)).defines(PathUtils.ROOT_PATH, policy);
+            verify(mgr, never()).removePolicy(ROOT_PATH, policy);
+            verify(((PolicyOwner) mgr), times(1)).defines(ROOT_PATH, policy);
         }
     }
 
@@ -384,6 +391,78 @@ public class CompositeAccessControlManag
         verify(mgr, times(1)).getEffectivePolicies(principalSet);
     }
 
+    @Test
+    public void testAggregationFilterByPath() throws Exception {
+        String matchingPath = TEST_PATH;
+
+        JackrabbitAccessControlManager acMgr1 = mock(JackrabbitAccessControlManager.class, withSettings().extraInterfaces(PolicyOwner.class));
+        JackrabbitAccessControlManager acMgr2 = mock(JackrabbitAccessControlManager.class, withSettings().extraInterfaces(PolicyOwner.class));
+        for (JackrabbitAccessControlManager acMgr : new JackrabbitAccessControlManager[] {acMgr1, acMgr2}) {
+            when(acMgr.getApplicablePolicies(anyString())).thenReturn(AccessControlPolicyIteratorAdapter.EMPTY);
+            when(acMgr.getPolicies(anyString())).thenReturn(new AccessControlPolicy[0]);
+            when(acMgr.getEffectivePolicies(anyString())).thenReturn(new AccessControlPolicy[0]);
+        }
+
+        AggregationFilter aggregationFilter = mock(AggregationFilter.class);
+        when(aggregationFilter.stop(acMgr1, matchingPath)).thenReturn(true);
+
+        CompositeAccessControlManager composite = createComposite(aggregationFilter, acMgr1, acMgr2);
+        composite.getApplicablePolicies(TEST_PATH); // must not call AggregationFilter
+        composite.getPolicies(TEST_PATH);           // must not call AggregationFilter
+        composite.getEffectivePolicies(TEST_PATH);  // must call AggregationFilter
+
+        verify(aggregationFilter, times(1)).stop(acMgr1, TEST_PATH);
+        verify(aggregationFilter, never()).stop(acMgr2, TEST_PATH);
+
+        verify(acMgr1, times(1)).getEffectivePolicies(TEST_PATH);
+        verify(acMgr2, never()).getEffectivePolicies(TEST_PATH);
+
+        composite.getEffectivePolicies(ROOT_PATH);  // must call AggregationFilter but not trigger stop
+
+        verify(aggregationFilter, times(1)).stop(acMgr1, ROOT_PATH);
+        verify(aggregationFilter, times(1)).stop(acMgr2, ROOT_PATH);
+
+        verify(acMgr1, times(1)).getEffectivePolicies(ROOT_PATH);
+        verify(acMgr2, times(1)).getEffectivePolicies(ROOT_PATH);
+    }
+
+    @Test
+    public void testAggregationFilterByPrincipals() throws Exception {
+        Principal principal = new PrincipalImpl("matchingPrincipal");
+        Set<Principal> matchingSet = ImmutableSet.of(principal);
+        Set<Principal> notMatching = ImmutableSet.of(EveryonePrincipal.getInstance());
+
+        JackrabbitAccessControlManager acMgr1 = mock(JackrabbitAccessControlManager.class, withSettings().extraInterfaces(PolicyOwner.class));
+        JackrabbitAccessControlManager acMgr2 = mock(JackrabbitAccessControlManager.class, withSettings().extraInterfaces(PolicyOwner.class));
+        for (JackrabbitAccessControlManager acMgr : new JackrabbitAccessControlManager[] {acMgr1, acMgr2}) {
+            when(acMgr.getApplicablePolicies(any(Principal.class))).thenReturn(new JackrabbitAccessControlPolicy[0]);
+            when(acMgr.getPolicies(any(Principal.class))).thenReturn(new JackrabbitAccessControlPolicy[0]);
+            when(acMgr.getEffectivePolicies(any(Set.class))).thenReturn(new JackrabbitAccessControlPolicy[0]);
+        }
+
+        AggregationFilter aggregationFilter = mock(AggregationFilter.class);
+        when(aggregationFilter.stop(acMgr1, matchingSet)).thenReturn(true);
+
+        CompositeAccessControlManager composite = createComposite(aggregationFilter, acMgr1, acMgr2);
+        composite.getApplicablePolicies(principal);  // must not call AggregationFilter
+        composite.getPolicies(principal);            // must not call AggregationFilter
+        composite.getEffectivePolicies(matchingSet); // must call AggregationFilter
+
+        verify(aggregationFilter, times(1)).stop(acMgr1, matchingSet);
+        verify(aggregationFilter, never()).stop(acMgr2, matchingSet);
+
+        verify(acMgr1, times(1)).getEffectivePolicies(matchingSet);
+        verify(acMgr2, never()).getEffectivePolicies(matchingSet);
+
+        composite.getEffectivePolicies(notMatching);  // must call AggregationFilter but not trigger stop
+
+        verify(aggregationFilter, times(1)).stop(acMgr1, notMatching);
+        verify(aggregationFilter, times(1)).stop(acMgr2, notMatching);
+
+        verify(acMgr1, times(1)).getEffectivePolicies(notMatching);
+        verify(acMgr2, times(1)).getEffectivePolicies(notMatching);
+    }
+
     private final static class TestAcMgr implements AccessControlManager, PolicyOwner {
 
         private boolean hasPolicy = false;

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeAuthorizationConfigurationTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeAuthorizationConfigurationTest.java?rev=1860721&r1=1860720&r2=1860721&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeAuthorizationConfigurationTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeAuthorizationConfigurationTest.java Thu Jun  6 16:01:14 2019
@@ -16,31 +16,42 @@
  */
 package org.apache.jackrabbit.oak.security.authorization.composite;
 
-import java.security.Principal;
-import java.util.Collections;
-
-import javax.jcr.RepositoryException;
-import javax.jcr.security.AccessControlManager;
-
+import com.google.common.collect.ImmutableSet;
 import org.apache.jackrabbit.oak.AbstractSecurityTest;
+import org.apache.jackrabbit.oak.api.Root;
 import org.apache.jackrabbit.oak.namepath.NamePathMapper;
 import org.apache.jackrabbit.oak.security.authorization.AuthorizationConfigurationImpl;
 import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
+import org.apache.jackrabbit.oak.spi.security.Context;
 import org.apache.jackrabbit.oak.spi.security.authorization.AuthorizationConfiguration;
 import org.apache.jackrabbit.oak.spi.security.authorization.OpenAuthorizationConfiguration;
+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;
 import org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionProvider;
+import org.apache.jackrabbit.oak.spi.security.principal.EveryonePrincipal;
 import org.jetbrains.annotations.NotNull;
 import org.junit.Test;
-import org.mockito.Mockito;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.security.AccessControlManager;
+import java.security.Principal;
+import java.util.Collections;
+import java.util.Set;
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotSame;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.withSettings;
 
 public class CompositeAuthorizationConfigurationTest extends AbstractSecurityTest {
 
@@ -146,8 +157,8 @@ public class CompositeAuthorizationConfi
     public void testMultipleRestrictionProvider() {
         // 2 authorization configuration with different RestrictionProvider
         AuthorizationConfiguration ac = createAuthorizationConfigurationImpl();
-        AuthorizationConfiguration ac2 = Mockito.mock(AuthorizationConfiguration.class);
-        when(ac2.getRestrictionProvider()).thenReturn(Mockito.mock(RestrictionProvider.class));
+        AuthorizationConfiguration ac2 = mock(AuthorizationConfiguration.class);
+        when(ac2.getRestrictionProvider()).thenReturn(mock(RestrictionProvider.class));
         when(ac2.getParameters()).thenReturn(ConfigurationParameters.EMPTY);
 
         CompositeAuthorizationConfiguration cc = getCompositeConfiguration(ac, ac2);
@@ -160,7 +171,7 @@ public class CompositeAuthorizationConfi
     public void testRedundantRestrictionProvider() {
         // 2 authorization configuration sharing the same RestrictionProvider
         AuthorizationConfiguration ac = createAuthorizationConfigurationImpl();
-        AuthorizationConfiguration ac2 = Mockito.mock(AuthorizationConfiguration.class);
+        AuthorizationConfiguration ac2 = mock(AuthorizationConfiguration.class);
         when(ac2.getRestrictionProvider()).thenReturn(ac.getRestrictionProvider());
         when(ac2.getParameters()).thenReturn(ConfigurationParameters.EMPTY);
 
@@ -204,4 +215,47 @@ public class CompositeAuthorizationConfi
         assertFalse(rp instanceof CompositeRestrictionProvider);
         assertSame(RestrictionProvider.EMPTY, rp);
     }
+
+    @Test
+    public void testDefaultEvaluationFilter() {
+        PermissionProvider pp = mock(PermissionProvider.class, withSettings().extraInterfaces(AggregatedPermissionProvider.class));
+        AuthorizationConfiguration ac1 = mock(AuthorizationConfiguration.class);
+        AuthorizationConfiguration ac2 = mock(AuthorizationConfiguration.class);
+        for (AuthorizationConfiguration ac : new AuthorizationConfiguration[] {ac1, ac2}) {
+            when(ac.getPermissionProvider(any(Root.class), anyString(), any(Set.class))).thenReturn(pp);
+            when(ac.getParameters()).thenReturn(ConfigurationParameters.EMPTY);
+            when(ac.getContext()).thenReturn(Context.DEFAULT);
+        }
+        CompositeAuthorizationConfiguration cc = getCompositeConfiguration(ac1, ac2);
+        PermissionProvider permissionProvider = cc.getPermissionProvider(root, adminSession.getWorkspaceName(), ImmutableSet.of(EveryonePrincipal.getInstance()));
+        permissionProvider.refresh();
+
+        verify(pp, times(2)).refresh();
+    }
+
+    @Test
+    public void testAbortingEvaluationFilter() {
+        Set<Principal> principalSet = ImmutableSet.of(EveryonePrincipal.getInstance());
+        AggregatedPermissionProvider pp = mock(AggregatedPermissionProvider.class);
+        AggregationFilter filter = when(mock(AggregationFilter.class).stop(pp, principalSet)).thenReturn(true).getMock();
+
+        AuthorizationConfiguration ac1 = mock(AuthorizationConfiguration.class);
+        AuthorizationConfiguration ac2 = mock(AuthorizationConfiguration.class);
+        for (AuthorizationConfiguration ac : new AuthorizationConfiguration[] {ac1, ac2}) {
+            when(ac.getPermissionProvider(any(Root.class), anyString(), any(Set.class))).thenReturn(pp);
+            when(ac.getParameters()).thenReturn(ConfigurationParameters.EMPTY);
+            when(ac.getContext()).thenReturn(Context.DEFAULT);
+        }
+        CompositeAuthorizationConfiguration cc = getCompositeConfiguration(ac1, ac2);
+        cc.withAggregationFilter(filter);
+
+        PermissionProvider permissionProvider = cc.getPermissionProvider(root, adminSession.getWorkspaceName(), principalSet);
+        permissionProvider.refresh();
+
+        Set<Principal> nonMatchingSet = adminSession.getAuthInfo().getPrincipals();
+        permissionProvider = cc.getPermissionProvider(root, adminSession.getWorkspaceName(), nonMatchingSet);
+        permissionProvider.refresh();
+
+        verify(pp, times(3)).refresh();
+    }
 }

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/internal/SecurityProviderRegistrationTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/internal/SecurityProviderRegistrationTest.java?rev=1860721&r1=1860720&r2=1860721&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/internal/SecurityProviderRegistrationTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/internal/SecurityProviderRegistrationTest.java Thu Jun  6 16:01:14 2019
@@ -19,9 +19,14 @@ package org.apache.jackrabbit.oak.securi
 import com.google.common.base.Predicates;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
+import org.apache.jackrabbit.api.security.JackrabbitAccessControlManager;
 import org.apache.jackrabbit.oak.AbstractSecurityTest;
+import org.apache.jackrabbit.oak.api.Root;
 import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.namepath.NamePathMapper;
 import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
 import org.apache.jackrabbit.oak.plugins.tree.RootProvider;
 import org.apache.jackrabbit.oak.plugins.tree.TreeLocation;
@@ -48,8 +53,12 @@ import org.apache.jackrabbit.oak.spi.sec
 import org.apache.jackrabbit.oak.spi.security.authentication.token.CompositeTokenConfiguration;
 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.permission.AggregatedPermissionProvider;
+import org.apache.jackrabbit.oak.spi.security.authorization.permission.AggregationFilter;
+import org.apache.jackrabbit.oak.spi.security.authorization.permission.PermissionProvider;
 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.EveryonePrincipal;
 import org.apache.jackrabbit.oak.spi.security.principal.PrincipalConfiguration;
 import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConfiguration;
 import org.apache.jackrabbit.oak.spi.security.user.AuthorizableNodeName;
@@ -66,12 +75,14 @@ import org.osgi.framework.Constants;
 import org.osgi.framework.ServiceReference;
 import org.osgi.framework.ServiceRegistration;
 
+import javax.jcr.security.AccessControlPolicy;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Field;
 import java.util.Collection;
 import java.util.Hashtable;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.SortedMap;
 
 import static org.apache.jackrabbit.oak.spi.security.RegistrationConstants.OAK_SECURITY_NAME;
@@ -82,12 +93,15 @@ import static org.junit.Assert.assertNul
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 import static org.mockito.Mockito.withSettings;
 import static org.osgi.framework.Constants.SERVICE_PID;
+import static org.osgi.framework.Constants.SERVICE_RANKING;
 
 public class SecurityProviderRegistrationTest extends AbstractSecurityTest {
 
@@ -850,4 +864,181 @@ public class SecurityProviderRegistratio
         registration.unbindTreeProvider(tp);
         assertNull(f.get(registration));
     }
+
+    @Test
+    public void testBindAggregationFilter() throws Exception {
+        registration.activate(context.bundleContext(), configWithRequiredServiceIds("filterId", "a1", "a2"));
+
+        AggregationFilter filter = mock(AggregationFilter.class, withSettings().defaultAnswer(invocationOnMock -> Boolean.TRUE));
+        ServiceRegistration sr = context.bundleContext().registerService(AggregationFilter.class.getName(), filter, new Hashtable(ImmutableMap.of(SERVICE_PID, "filterId")));
+        registration.bindAggregationFilter(sr.getReference(), filter);
+
+        AggregatedPermissionProvider pp = mock(AggregatedPermissionProvider.class);
+        JackrabbitAccessControlManager acMgr = mock(JackrabbitAccessControlManager.class);
+        AccessControlPolicy policy = mock(AccessControlPolicy.class);
+        when(acMgr.getEffectivePolicies(anyString())).thenReturn(new AccessControlPolicy[] {policy});
+        when(acMgr.getEffectivePolicies(any(Set.class))).thenReturn(new AccessControlPolicy[] {policy});
+
+        AuthorizationConfiguration ac1 = mock(AuthorizationConfiguration.class);
+        AuthorizationConfiguration ac2 = mock(AuthorizationConfiguration.class);
+        for (AuthorizationConfiguration ac : new AuthorizationConfiguration[]{ac1, ac2}) {
+            when(ac.getPermissionProvider(any(Root.class), anyString(), any(Set.class))).thenReturn(pp);
+            when(ac.getAccessControlManager(any(Root.class), any(NamePathMapper.class))).thenReturn(acMgr);
+            when(ac.getParameters()).thenReturn(ConfigurationParameters.EMPTY);
+            when(ac.getContext()).thenReturn(Context.DEFAULT);
+        }
+
+        registration.bindAuthorizationConfiguration(ac1, new Hashtable(ImmutableMap.of(SERVICE_PID, "a1")));
+        registration.bindAuthorizationConfiguration(ac2, new Hashtable(ImmutableMap.of(SERVICE_PID, "a2")));
+
+        SecurityProvider service = context.getService(SecurityProvider.class);
+
+        AuthorizationConfiguration ac = service.getConfiguration(AuthorizationConfiguration.class);
+        assertTrue(ac instanceof CompositeAuthorizationConfiguration);
+
+        PermissionProvider permissionProvider = ac.getPermissionProvider(root, adminSession.getWorkspaceName(), ImmutableSet.of());
+        assertSame(pp, permissionProvider);
+        verify(filter, times(1)).stop(pp, ImmutableSet.of());
+
+        JackrabbitAccessControlManager am = (JackrabbitAccessControlManager) ac.getAccessControlManager(root, getNamePathMapper());
+
+        assertEquals(1, am.getEffectivePolicies(PathUtils.ROOT_PATH).length);
+        verify(filter, times(1)).stop(acMgr, PathUtils.ROOT_PATH);
+
+        assertEquals(1, am.getEffectivePolicies(ImmutableSet.of(EveryonePrincipal.getInstance())).length);
+        verify(filter, times(1)).stop(acMgr, ImmutableSet.of(EveryonePrincipal.getInstance()));
+    }
+
+    @Test
+    public void testUnbindAggregationFilter() {
+        registration.activate(context.bundleContext(), configWithRequiredServiceIds("a1", "a2", "f1"));
+
+        AggregationFilter filter = mock(AggregationFilter.class, withSettings().defaultAnswer(invocationOnMock -> Boolean.TRUE));
+        ServiceRegistration sr = context.bundleContext().registerService(AggregationFilter.class.getName(), filter, new Hashtable(ImmutableMap.of(SERVICE_PID, "f1")));
+        registration.bindAggregationFilter(sr.getReference(), filter);
+
+        AggregatedPermissionProvider pp = mock(AggregatedPermissionProvider.class);
+        AuthorizationConfiguration ac1 = mock(AuthorizationConfiguration.class);
+        AuthorizationConfiguration ac2 = mock(AuthorizationConfiguration.class);
+        for (AuthorizationConfiguration ac : new AuthorizationConfiguration[]{ac1, ac2}) {
+            when(ac.getPermissionProvider(any(Root.class), anyString(), any(Set.class))).thenReturn(pp);
+            when(ac.getParameters()).thenReturn(ConfigurationParameters.EMPTY);
+            when(ac.getContext()).thenReturn(Context.DEFAULT);
+        }
+
+        registration.bindAuthorizationConfiguration(ac1, new Hashtable(ImmutableMap.of(SERVICE_PID, "a1")));
+        registration.bindAuthorizationConfiguration(ac2, new Hashtable(ImmutableMap.of(SERVICE_PID, "a2")));
+
+        AuthorizationConfiguration ac = context.getService(SecurityProvider.class).getConfiguration(AuthorizationConfiguration.class);
+        assertTrue(ac instanceof CompositeAuthorizationConfiguration);
+
+        PermissionProvider permissionProvider = ac.getPermissionProvider(root, adminSession.getWorkspaceName(), ImmutableSet.of());
+        assertSame(pp, permissionProvider);
+        verify(filter, times(1)).stop(pp, ImmutableSet.of());
+
+        registration.unbindAggregationFilter(sr.getReference(), filter);
+        assertNull(context.getService(SecurityProvider.class));
+
+        registration.modified(configWithRequiredServiceIds("a1", "a2"));
+
+        context.getService(SecurityProvider.class).getConfiguration(AuthorizationConfiguration.class).getPermissionProvider(root, adminSession.getWorkspaceName(), ImmutableSet.of());
+        // since unbind was called on filter -> no additional calls
+        verify(filter, times(1)).stop(pp, ImmutableSet.of());
+    }
+
+    @Test
+    public void testMultipleEvaluationFilterFalse() throws Exception {
+        registration.activate(context.bundleContext(), configWithRequiredServiceIds("f1", "f2", "ac1", "ac2"));
+
+        AggregationFilter filter1 = mock(AggregationFilter.class, withSettings().defaultAnswer(invocationOnMock -> Boolean.FALSE));
+        ServiceRegistration sr1 = context.bundleContext().registerService(AggregationFilter.class.getName(), filter1, new Hashtable(ImmutableMap.of(SERVICE_PID, "f1", SERVICE_RANKING, 100)));
+        registration.bindAggregationFilter(sr1.getReference(), filter1);
+
+        AggregationFilter filter2 = mock(AggregationFilter.class, withSettings().defaultAnswer(invocationOnMock -> Boolean.FALSE));
+        ServiceRegistration sr2 = context.bundleContext().registerService(AggregationFilter.class.getName(), filter2, new Hashtable(ImmutableMap.of(SERVICE_PID, "f2", SERVICE_RANKING, 200)));
+        registration.bindAggregationFilter(sr2.getReference(), filter2);
+
+        AggregatedPermissionProvider pp = mock(AggregatedPermissionProvider.class);
+        JackrabbitAccessControlManager acMgr = mock(JackrabbitAccessControlManager.class);
+        AccessControlPolicy policy = mock(AccessControlPolicy.class);
+        when(acMgr.getEffectivePolicies(anyString())).thenReturn(new AccessControlPolicy[] {policy});
+        when(acMgr.getEffectivePolicies(any(Set.class))).thenReturn(new AccessControlPolicy[] {policy});
+
+        AuthorizationConfiguration ac1 = mock(AuthorizationConfiguration.class);
+        AuthorizationConfiguration ac2 = mock(AuthorizationConfiguration.class);
+        for (AuthorizationConfiguration ac : new AuthorizationConfiguration[]{ac1, ac2}) {
+            when(ac.getPermissionProvider(any(Root.class), anyString(), any(Set.class))).thenReturn(pp);
+            when(ac.getAccessControlManager(any(Root.class), any(NamePathMapper.class))).thenReturn(acMgr);
+            when(ac.getParameters()).thenReturn(ConfigurationParameters.EMPTY);
+            when(ac.getContext()).thenReturn(Context.DEFAULT);
+        }
+
+        registration.bindAuthorizationConfiguration(ac1, new Hashtable(ImmutableMap.of(SERVICE_PID, "ac1")));
+        registration.bindAuthorizationConfiguration(ac2, new Hashtable(ImmutableMap.of(SERVICE_PID, "ac2")));
+
+        AuthorizationConfiguration config = context.getService(SecurityProvider.class).getConfiguration(AuthorizationConfiguration.class);
+        PermissionProvider permissionProvider = config.getPermissionProvider(root, adminSession.getWorkspaceName(), ImmutableSet.of());
+
+        verify(filter1, times(2)).stop(pp, ImmutableSet.of());
+        verify(filter2, times(2)).stop(pp, ImmutableSet.of());
+
+
+        JackrabbitAccessControlManager am = (JackrabbitAccessControlManager) config.getAccessControlManager(root, getNamePathMapper());
+
+        assertEquals(2, am.getEffectivePolicies(PathUtils.ROOT_PATH).length);
+        verify(filter1, times(2)).stop(acMgr, PathUtils.ROOT_PATH);
+        verify(filter2, times(2)).stop(acMgr, PathUtils.ROOT_PATH);
+
+        assertEquals(2, am.getEffectivePolicies(ImmutableSet.of(EveryonePrincipal.getInstance())).length);
+        verify(filter1, times(2)).stop(acMgr, ImmutableSet.of(EveryonePrincipal.getInstance()));
+        verify(filter2, times(2)).stop(acMgr, ImmutableSet.of(EveryonePrincipal.getInstance()));
+    }
+
+    @Test
+    public void testMultipleEvaluationFilterTrue() throws Exception {
+        registration.activate(context.bundleContext(), configWithRequiredServiceIds("f1", "f2", "ac1", "ac2"));
+
+        AggregationFilter filter1 = mock(AggregationFilter.class, withSettings().defaultAnswer(invocationOnMock -> Boolean.TRUE));
+        ServiceRegistration sr1 = context.bundleContext().registerService(AggregationFilter.class.getName(), filter1, new Hashtable(ImmutableMap.of(SERVICE_PID, "f1", SERVICE_RANKING, 200)));
+        registration.bindAggregationFilter(sr1.getReference(), filter1);
+
+        AggregationFilter filter2 = mock(AggregationFilter.class, withSettings().defaultAnswer(invocationOnMock -> Boolean.TRUE));
+        ServiceRegistration sr2 = context.bundleContext().registerService(AggregationFilter.class.getName(), filter2, new Hashtable(ImmutableMap.of(SERVICE_PID, "f2", SERVICE_RANKING, 100)));
+        registration.bindAggregationFilter(sr2.getReference(), filter2);
+
+        AggregatedPermissionProvider pp = mock(AggregatedPermissionProvider.class);
+        JackrabbitAccessControlManager acMgr = mock(JackrabbitAccessControlManager.class);
+        AccessControlPolicy policy = mock(AccessControlPolicy.class);
+        when(acMgr.getEffectivePolicies(anyString())).thenReturn(new AccessControlPolicy[] {policy});
+        when(acMgr.getEffectivePolicies(any(Set.class))).thenReturn(new AccessControlPolicy[] {policy});
+
+        AuthorizationConfiguration ac1 = mock(AuthorizationConfiguration.class);
+        AuthorizationConfiguration ac2 = mock(AuthorizationConfiguration.class);
+        for (AuthorizationConfiguration ac : new AuthorizationConfiguration[]{ac1, ac2}) {
+            when(ac.getPermissionProvider(any(Root.class), anyString(), any(Set.class))).thenReturn(pp);
+            when(ac.getAccessControlManager(any(Root.class), any(NamePathMapper.class))).thenReturn(acMgr);
+            when(ac.getParameters()).thenReturn(ConfigurationParameters.EMPTY);
+            when(ac.getContext()).thenReturn(Context.DEFAULT);
+        }
+
+        registration.bindAuthorizationConfiguration(ac1, new Hashtable(ImmutableMap.of(SERVICE_PID, "ac1")));
+        registration.bindAuthorizationConfiguration(ac2, new Hashtable(ImmutableMap.of(SERVICE_PID, "ac2")));
+
+        AuthorizationConfiguration config = context.getService(SecurityProvider.class).getConfiguration(AuthorizationConfiguration.class);
+        PermissionProvider permissionProvider = config.getPermissionProvider(root, adminSession.getWorkspaceName(), ImmutableSet.of());
+
+        verify(filter1, never()).stop(pp, ImmutableSet.of());
+        verify(filter2, times(1)).stop(pp, ImmutableSet.of());
+
+        JackrabbitAccessControlManager am = (JackrabbitAccessControlManager) config.getAccessControlManager(root, getNamePathMapper());
+
+        assertEquals(1, am.getEffectivePolicies(PathUtils.ROOT_PATH).length);
+        verify(filter1, never()).stop(acMgr, PathUtils.ROOT_PATH);
+        verify(filter2, times(1)).stop(acMgr, PathUtils.ROOT_PATH);
+
+        assertEquals(1, am.getEffectivePolicies(ImmutableSet.of(EveryonePrincipal.getInstance())).length);
+        verify(filter1, never()).stop(acMgr, ImmutableSet.of(EveryonePrincipal.getInstance()));
+        verify(filter2, times(1)).stop(acMgr, ImmutableSet.of(EveryonePrincipal.getInstance()));
+
+    }
 }

Added: jackrabbit/oak/trunk/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/AggregationFilter.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/AggregationFilter.java?rev=1860721&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/AggregationFilter.java (added)
+++ jackrabbit/oak/trunk/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/AggregationFilter.java Thu Jun  6 16:01:14 2019
@@ -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<Principal> 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<Principal> 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<Principal> principals) {
+            return false;
+        }
+
+        @Override
+        public boolean stop(@NotNull JackrabbitAccessControlManager accessControlManager, @NotNull Set<Principal> principals) {
+            return false;
+        }
+
+        @Override
+        public boolean stop(@NotNull AccessControlManager accessControlManager, @Nullable String absPath) {
+            return false;
+        }
+    };
+}
\ No newline at end of file

Propchange: jackrabbit/oak/trunk/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/AggregationFilter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: jackrabbit/oak/trunk/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/package-info.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/package-info.java?rev=1860721&r1=1860720&r2=1860721&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/package-info.java (original)
+++ jackrabbit/oak/trunk/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/package-info.java Thu Jun  6 16:01:14 2019
@@ -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;

Added: jackrabbit/oak/trunk/oak-security-spi/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/AggregationFilterDefaultTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-security-spi/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/AggregationFilterDefaultTest.java?rev=1860721&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-security-spi/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/AggregationFilterDefaultTest.java (added)
+++ jackrabbit/oak/trunk/oak-security-spi/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/AggregationFilterDefaultTest.java Thu Jun  6 16:01:14 2019
@@ -0,0 +1,46 @@
+/*
+ * 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.apache.jackrabbit.oak.commons.PathUtils;
+import org.junit.Test;
+
+import javax.jcr.security.AccessControlManager;
+import java.util.Collections;
+
+import static org.junit.Assert.assertFalse;
+import static org.mockito.Mockito.mock;
+
+public class AggregationFilterDefaultTest {
+
+    @Test
+    public void testStopPermissionProvider() {
+        assertFalse(AggregationFilter.DEFAULT.stop(mock(AggregatedPermissionProvider.class), Collections.EMPTY_SET));
+    }
+
+    @Test
+    public void testStopEffectivePoliciesByPath() {
+        assertFalse(AggregationFilter.DEFAULT.stop(mock(AccessControlManager.class), null));
+        assertFalse(AggregationFilter.DEFAULT.stop(mock(AccessControlManager.class), PathUtils.ROOT_PATH));
+    }
+
+    @Test
+    public void testStopEffectivePoliciesByPrincipals() {
+        assertFalse(AggregationFilter.DEFAULT.stop(mock(JackrabbitAccessControlManager.class), Collections.EMPTY_SET));
+    }
+}
\ No newline at end of file

Propchange: jackrabbit/oak/trunk/oak-security-spi/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/AggregationFilterDefaultTest.java
------------------------------------------------------------------------------
    svn:eol-style = native