Index: oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionProviderImpl.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/permission/PermissionProviderImpl.java	(revision 1585917)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionProviderImpl.java	(revision )
@@ -28,9 +28,12 @@
 import org.apache.jackrabbit.oak.api.Tree;
 import org.apache.jackrabbit.oak.core.ImmutableRoot;
 import org.apache.jackrabbit.oak.plugins.tree.ImmutableTree;
+import org.apache.jackrabbit.oak.plugins.tree.TreeLocation;
 import org.apache.jackrabbit.oak.plugins.version.VersionConstants;
 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.ControlFlag;
 import org.apache.jackrabbit.oak.spi.security.authorization.permission.PermissionConstants;
 import org.apache.jackrabbit.oak.spi.security.authorization.permission.PermissionProvider;
 import org.apache.jackrabbit.oak.spi.security.authorization.permission.Permissions;
@@ -38,9 +41,8 @@
 import org.apache.jackrabbit.oak.spi.security.authorization.permission.TreePermission;
 import org.apache.jackrabbit.oak.spi.security.principal.AdminPrincipal;
 import org.apache.jackrabbit.oak.spi.security.principal.SystemPrincipal;
-import org.apache.jackrabbit.oak.plugins.tree.TreeLocation;
 
-public class PermissionProviderImpl implements PermissionProvider, AccessControlConstants, PermissionConstants {
+public class PermissionProviderImpl implements PermissionProvider, AccessControlConstants, PermissionConstants, AggregatedPermissionProvider {
 
     private final Root root;
 
@@ -114,6 +116,32 @@
             isGranted = compiledPermissions.isGranted(oakPath, permissions);
         }
         return isGranted;
+    }
+
+    //---------------------------------------< AggregatedPermissionProvider >---
+    @Override
+    public ControlFlag getFlag() {
+        return acConfig.getParameters().getConfigValue(AggregatedPermissionProvider.PARAM_CONTROL_FLAG, ControlFlag.REQUISITE);
+    }
+
+    @Override
+    public boolean handles(@Nonnull String path, @Nonnull String jcrActions) {
+        return true;
+    }
+
+    @Override
+    public boolean handles(@Nonnull Tree tree) {
+        return true;
+    }
+
+    @Override
+    public boolean handles(@Nonnull Tree tree, long permissions) {
+        return true;
+    }
+
+    @Override
+    public boolean handlesRepositoryPermissions() {
+        return true;
     }
 
     //--------------------------------------------------------------------------
Index: oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/privilege/PrivilegeBitsTest.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/privilege/PrivilegeBitsTest.java	(revision 1585917)
+++ oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/privilege/PrivilegeBitsTest.java	(revision )
@@ -426,6 +426,11 @@
     }
 
     @Test
+    public void testRetain() {
+        // TODO
+    }
+
+    @Test
     public void testGetInstance() {
         PrivilegeBits pb = PrivilegeBits.getInstance();
         assertEquivalent(PrivilegeBits.EMPTY, pb);
\ No newline at end of file
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/accesscontrol/AbstractAccessControlManager.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/accesscontrol/AbstractAccessControlManager.java	(revision 1585917)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/accesscontrol/AbstractAccessControlManager.java	(revision )
@@ -203,8 +203,8 @@
 
     @Nonnull
     private Privilege[] getPrivileges(@Nullable String absPath,
-                                        @Nonnull PermissionProvider provider,
-                                        long permissions) throws RepositoryException {
+                                      @Nonnull PermissionProvider provider,
+                                      long permissions) throws RepositoryException {
         Tree tree;
         if (absPath == null) {
             tree = null;
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/ControlFlag.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/ControlFlag.java	(revision )
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/ControlFlag.java	(revision )
@@ -0,0 +1,55 @@
+/*
+ * 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 javax.annotation.Nonnull;
+
+/**
+ *
+ */
+public enum ControlFlag {
+
+    /**
+     * The {@code AggregatedPermissionProvider} is not required to return {@code true}
+     * upon permission evaluation. If it does grant the permissions in question,
+     * control is immediately returned to the caller and the evaluation does
+     * not proceed down the list of {@code PermissionProvider}s. If it returns
+     * {@code false}, the evaluation continues down the list of {@code PermissionProvider}s.
+     */
+    SUFFICIENT("SUFFICIENT"),
+
+    /**
+     * The {@code AggregatedPermissionProvider} is required to return {@code true}
+     * upon permission evaluation. If it grants access the evaluation continues
+     * down the list of {@code PermissionProvider}s. However, if it returns
+     * {@code false} indicating that permissions are not granted, the evaluation
+     * is immediately stopped at this point control is returned to the caller
+     * without proceeding down the list of the {@code PermissionProvider}s.
+     */
+    REQUISITE("REQUISITE");
+
+    private final String name;
+
+    private ControlFlag(@Nonnull String name) {
+        this.name = name;
+    }
+
+    @Override
+    public String toString() {
+        return name;
+    }
+}
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/CompositeAuthorizationConfiguration.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/CompositeAuthorizationConfiguration.java	(revision 1585917)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/CompositeAuthorizationConfiguration.java	(revision )
@@ -20,6 +20,7 @@
 import java.util.List;
 import java.util.Set;
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 import javax.jcr.RepositoryException;
 import javax.jcr.security.AccessControlException;
 import javax.jcr.security.AccessControlManager;
@@ -28,7 +29,9 @@
 import javax.jcr.security.Privilege;
 
 import com.google.common.base.Function;
+import com.google.common.base.Predicates;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
 import com.google.common.collect.Iterators;
 import com.google.common.collect.Lists;
 import org.apache.jackrabbit.api.security.JackrabbitAccessControlManager;
@@ -40,9 +43,13 @@
 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.AggregatedPermissionProvider;
+import org.apache.jackrabbit.oak.spi.security.authorization.permission.CompositePermissionProvider;
 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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * {@link CompositeAuthorizationConfiguration} that combines different
@@ -52,6 +59,8 @@
  */
 public class CompositeAuthorizationConfiguration extends CompositeConfiguration<AuthorizationConfiguration> implements AuthorizationConfiguration {
 
+    private static final Logger log = LoggerFactory.getLogger(CompositeAuthorizationConfiguration.class);
+
     public CompositeAuthorizationConfiguration(@Nonnull SecurityProvider securityProvider) {
         super(AuthorizationConfiguration.NAME, securityProvider);
     }
@@ -60,14 +69,21 @@
     @Override
     public AccessControlManager getAccessControlManager(final @Nonnull Root root,
                                                         final @Nonnull NamePathMapper namePathMapper) {
-        List<AccessControlManager> mgrs = Lists.transform(getConfigurations(), new Function<AuthorizationConfiguration, AccessControlManager>() {
+        List<AuthorizationConfiguration> configurations = getConfigurations();
+        switch (configurations.size()) {
+            case 0: throw new IllegalArgumentException();
+            case 1: return configurations.get(0).getAccessControlManager(root, namePathMapper);
+            default:
+                List<AccessControlManager> mgrs = Lists.transform(configurations, new Function<AuthorizationConfiguration, AccessControlManager>() {
-            @Override
-            public AccessControlManager apply(AuthorizationConfiguration authorizationConfiguration) {
-                return authorizationConfiguration.getAccessControlManager(root, namePathMapper);
-            }
-        });
-        return new CompositeAcMgr(root, namePathMapper, getSecurityProvider(), mgrs);
+                    @Override
+                    public AccessControlManager apply(AuthorizationConfiguration authorizationConfiguration) {
+                        return authorizationConfiguration.getAccessControlManager(root, namePathMapper);
+                    }
+                });
+                return new CompositeAcMgr(root, namePathMapper, getSecurityProvider(), mgrs);
+
-    }
+        }
+    }
 
     @Nonnull
     @Override
@@ -84,11 +100,36 @@
 
     @Nonnull
     @Override
-    public PermissionProvider getPermissionProvider(@Nonnull Root root, @Nonnull String workspaceName, @Nonnull Set<Principal> principals) {
-        // TODO OAK-1268
-        throw new UnsupportedOperationException("not yet implemented (OAK-1268)");
+    public PermissionProvider getPermissionProvider(final @Nonnull Root root,
+                                                    final @Nonnull String workspaceName,
+                                                    final @Nonnull Set<Principal> principals) {
+        List<AuthorizationConfiguration> configurations = getConfigurations();
+        switch (configurations.size()) {
+            case 0: throw new IllegalArgumentException();
+            case 1: return configurations.get(0).getPermissionProvider(root, workspaceName, principals);
+            default:
+                List<AggregatedPermissionProvider> pps = Lists.transform(configurations, new Function<AuthorizationConfiguration, AggregatedPermissionProvider>() {
+                    @Override
+                    @Nullable
+                    public AggregatedPermissionProvider apply(@Nullable AuthorizationConfiguration authorizationConfiguration) {
+                        if (authorizationConfiguration == null) {
+                            return null;
-    }
+                        }
 
+                        PermissionProvider pp = authorizationConfiguration.getPermissionProvider(root, workspaceName, principals);
+                        if (pp instanceof AggregatedPermissionProvider) {
+                            return (AggregatedPermissionProvider) pp;
+                        } else {
+                            log.debug("Permission provider for aggregation must implement AggregatedPermissionProvider -> Ignoring " + pp.toString());
+                            return null;
+                        }
+                    }
+                });
+                return new CompositePermissionProvider(root, ImmutableList.copyOf(Iterables.filter(pps, Predicates.notNull())));
+        }
+    }
+
+    //------------------------------------------------------------< private >---
     /**
      * Access control manager that aggregates a list of different access control
      * manager implementations. Note, that the implementations *must* implement
\ No newline at end of file
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/AggregatedPermissionProvider.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/AggregatedPermissionProvider.java	(revision )
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/AggregatedPermissionProvider.java	(revision )
@@ -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 javax.annotation.Nonnull;
+
+import org.apache.jackrabbit.oak.api.Tree;
+
+/**
+ * Extension of the {@link PermissionProvider} interface that allows it to be
+ * used in combination with other provider implementations.
+ */
+public interface AggregatedPermissionProvider extends PermissionProvider {
+
+    /**
+     * Name of the configuration option that specifies the {@link org.apache.jackrabbit.oak.spi.security.authorization.permission.ControlFlag}
+     * of this provider instance.
+     */
+    public static final String PARAM_CONTROL_FLAG = "ControlFlag";
+
+    @Nonnull
+    ControlFlag getFlag();
+
+    boolean handles(@Nonnull String path, @Nonnull String jcrActions);
+
+    boolean handles(@Nonnull Tree tree);
+
+    boolean handles(@Nonnull Tree tree, long permissions);
+
+    boolean handlesRepositoryPermissions();
+
+}
\ No newline at end of file
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/CompositePermissionProvider.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/CompositePermissionProvider.java	(revision )
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/CompositePermissionProvider.java	(revision )
@@ -0,0 +1,378 @@
+/*
+ * 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 java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.core.ImmutableRoot;
+import org.apache.jackrabbit.oak.plugins.tree.ImmutableTree;
+import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeBits;
+import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeBitsProvider;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.util.Text;
+
+/**
+ * CompositePermissionProvider... TODO
+ */
+public class CompositePermissionProvider implements PermissionProvider {
+
+    private final Root root;
+    private final Iterable<AggregatedPermissionProvider> pps;
+    private final CompositeRepositoryPermission repositoryPermission;
+
+    private ImmutableRoot immutableRoot;
+    private PrivilegeBitsProvider pbp;
+
+    public CompositePermissionProvider(@Nonnull Root root, @Nonnull List<AggregatedPermissionProvider> pps) {
+        this.root = root;
+        this.pps = Iterables.unmodifiableIterable(pps);
+        repositoryPermission = new CompositeRepositoryPermission();
+
+        immutableRoot = (root instanceof ImmutableRoot) ? (ImmutableRoot) root : new ImmutableRoot(root);
+        pbp = new PrivilegeBitsProvider(immutableRoot);
+    }
+
+    @Override
+    public void refresh() {
+        immutableRoot = (root instanceof ImmutableRoot) ? (ImmutableRoot) root : new ImmutableRoot(root);
+        pbp = new PrivilegeBitsProvider(immutableRoot);
+
+        for (PermissionProvider pp : pps) {
+            pp.refresh();
+        }
+    }
+
+    @Nonnull
+    @Override
+    public Set<String> getPrivileges(@Nullable Tree tree) {
+        return pbp.getPrivilegeNames(getPrivilegeBits(tree));
+    }
+
+    @Override
+    public boolean hasPrivileges(@Nullable Tree tree, @Nonnull String... privilegeNames) {
+        return getPrivilegeBits(tree).includes(pbp.getBits(privilegeNames));
+    }
+
+    @Override
+    public RepositoryPermission getRepositoryPermission() {
+        return repositoryPermission;
+    }
+
+    @Override
+    public TreePermission getTreePermission(@Nonnull Tree tree, @Nonnull TreePermission parentPermission) {
+        ImmutableTree immTree = (tree instanceof ImmutableTree) ? (ImmutableTree) tree : immutableRoot.getTree(tree.getPath());
+        if (tree.isRoot()) {
+            return new CompositeTreePermission(immTree, new CompositeTreePermission());
+        } else {
+            if (!(parentPermission instanceof CompositeTreePermission)) {
+                throw new IllegalArgumentException("Illegal parent permission instance. Expected CompositeTreePermission.");
+            }
+            return new CompositeTreePermission(immTree, (CompositeTreePermission) parentPermission);
+        }
+    }
+
+    @Override
+    public boolean isGranted(final @Nonnull Tree parent, @Nullable PropertyState property, final long permissions) {
+        Iterable<AggregatedPermissionProvider> providers = Iterables.filter(pps, new Predicate<AggregatedPermissionProvider>() {
+            @Override
+            public boolean apply(@Nullable AggregatedPermissionProvider pp) {
+                return pp != null && pp.handles(parent, permissions);
+            }
+        });
+        if (Permissions.isAggregate(permissions)) {
+            for (long permission : Permissions.aggregates(permissions)) {
+                if (!grantsPermission(parent, property, permission, providers)) {
+                    return false;
+                }
+            }
+            return true;
+        } else {
+            return grantsPermission(parent, property, permissions, providers);
+        }
+    }
+
+    @Override
+    public boolean isGranted(final @Nonnull String oakPath, final @Nonnull String jcrActions) {
+        Iterable<AggregatedPermissionProvider> providers = Iterables.filter(pps, new Predicate<AggregatedPermissionProvider>() {
+            @Override
+            public boolean apply(@Nullable AggregatedPermissionProvider pp) {
+                return pp != null && pp.handles(oakPath, jcrActions);
+            }
+        });
+        String[] actions = Text.explode(jcrActions, ',', false);
+        switch (actions.length) {
+            case 0: return true;
+            case 1: return grantsAction(oakPath, actions[0], providers);
+            default:
+                for (String action : actions) {
+                    if (!grantsAction(oakPath, action, providers)) {
+                        return false;
+                    }
+                }
+                return true;
+        }
+    }
+
+    //--------------------------------------------------------------------------
+    private PrivilegeBits getPrivilegeBits(@Nullable final Tree tree) {
+        PrivilegeBits sufficient = PrivilegeBits.getInstance();
+        PrivilegeBits required = null;
+
+        Iterable<AggregatedPermissionProvider> providers = Iterables.filter(pps, new Predicate<AggregatedPermissionProvider>() {
+            @Override
+            public boolean apply(@Nullable AggregatedPermissionProvider pp) {
+                return pp != null && ((tree == null) ? pp.handlesRepositoryPermissions() : pp.handles(tree));
+            }
+        });
+        for (AggregatedPermissionProvider pp : providers) {
+            PrivilegeBits privs = pbp.getBits(pp.getPrivileges(tree));
+            ControlFlag flag = pp.getFlag();
+            if (ControlFlag.SUFFICIENT == flag) {
+                sufficient.add(privs);
+                if (required != null) {
+                    sufficient.retain(required);
+                }
+            } else if (ControlFlag.REQUISITE == flag) {
+                if (required == null) {
+                    required = PrivilegeBits.getInstance();
+                    required.add(privs);
+                } else {
+                    required.retain(privs);
+                }
+            }
+        }
+        return sufficient.add(required);
+    }
+
+    private static boolean grantsPermission(@Nonnull final Tree parent,
+                                            @Nullable PropertyState property,
+                                            final long permission,
+                                            @Nonnull Iterable<AggregatedPermissionProvider> providers) {
+        Iterator<AggregatedPermissionProvider> it = providers.iterator();
+        while (it.hasNext()) {
+            AggregatedPermissionProvider pp = it.next();
+            boolean isGranted = pp.isGranted(parent, property, permission);
+            if (!it.hasNext() || evalComplete(isGranted, pp.getFlag())) {
+                return isGranted;
+            }
+        }
+        return false;
+    }
+
+    private static boolean grantsAction(@Nonnull final String oakPath,
+                                        @Nonnull final String action,
+                                        @Nonnull Iterable<AggregatedPermissionProvider> providers) {
+        Iterator<AggregatedPermissionProvider> it = providers.iterator();
+        while (it.hasNext()) {
+            AggregatedPermissionProvider pp = it.next();
+            boolean isGranted = pp.isGranted(oakPath, action);
+            if (!it.hasNext() || evalComplete(isGranted, pp.getFlag())) {
+                return isGranted;
+            }
+        }
+        return false;
+    }
+
+    private static boolean grantsRepoPermission(long permission, @Nonnull Iterable<AggregatedPermissionProvider> providers) {
+        Iterator<AggregatedPermissionProvider> it = providers.iterator();
+        while (it.hasNext()) {
+            AggregatedPermissionProvider pp = it.next();
+            boolean isGranted = pp.getRepositoryPermission().isGranted(permission);
+            if (!it.hasNext() || evalComplete(isGranted, pp.getFlag())) {
+                return isGranted;
+            }
+
+        }
+        return false;
+    }
+
+    private static boolean evalComplete(boolean isGranted, ControlFlag flag) {
+        switch (flag) {
+            case SUFFICIENT:
+                if (isGranted) {
+                    return true;
+                }
+                break;
+            case REQUISITE:
+                if (!isGranted) {
+                    return true;
+                }
+                break;
+            default:
+                throw new IllegalArgumentException("Unsupported PermissionProvider Control Flag " + flag);
+
+        }
+        return false;
+    }
+
+    //--------------------------------------------------------------------------
+
+    private class CompositeTreePermission implements TreePermission {
+
+        private final ImmutableTree tree;
+        private final CompositeTreePermission parentPermission;
+
+        private final Map<AggregatedPermissionProvider, TreePermission> delegates = new LinkedHashMap<AggregatedPermissionProvider, TreePermission>();
+
+        private Boolean canRead;
+
+        private CompositeTreePermission() {
+            tree = null;
+            parentPermission = null;
+        }
+
+        private CompositeTreePermission(final @Nonnull ImmutableTree tree, @Nonnull CompositeTreePermission parentPermission) {
+            this.tree = tree;
+            this.parentPermission = parentPermission;
+
+            for (AggregatedPermissionProvider provider : pps) {
+                if (provider.handles(tree, Permissions.READ | Permissions.READ_ACCESS_CONTROL)) {
+                    delegates.put(provider, provider.getTreePermission(tree, getParentPermission(provider)));
+                }
+            }
+        }
+
+        @Override
+        public TreePermission getChildPermission(String childName, NodeState childState) {
+            ImmutableTree childTree = new ImmutableTree(tree, childName, childState);
+            return new CompositeTreePermission(childTree, this);
+        }
+
+        @Override
+        public boolean canRead() {
+            if (canRead == null) {
+                canRead = false;
+                Iterator<Map.Entry<AggregatedPermissionProvider, TreePermission>> it = delegates.entrySet().iterator();
+                while (it.hasNext()) {
+                    Map.Entry<AggregatedPermissionProvider, TreePermission> entry = it.next();
+                    boolean isGranted = entry.getValue().canRead();
+                    if (!it.hasNext() || evalComplete(isGranted, entry.getKey().getFlag())) {
+                        this.canRead = isGranted;
+                        break;
+                    }
+                }
+            }
+            return canRead;
+        }
+
+        @Override
+        public boolean canRead(@Nonnull PropertyState property) {
+            Iterator<Map.Entry<AggregatedPermissionProvider, TreePermission>> it = delegates.entrySet().iterator();
+            while (it.hasNext()) {
+                Map.Entry<AggregatedPermissionProvider, TreePermission> entry = it.next();
+                boolean isGranted = entry.getValue().canRead(property);
+                if (!it.hasNext() || evalComplete(isGranted, entry.getKey().getFlag())) {
+                    return isGranted;
+                }
+            }
+            return false;
+        }
+
+        @Override
+        public boolean canReadAll() {
+            return false;
+        }
+
+        @Override
+        public boolean canReadProperties() {
+            return false;
+        }
+
+        @Override
+        public boolean isGranted(long permissions) {
+            if (Permissions.isAggregate(permissions)) {
+                for (long permission : Permissions.aggregates(permissions)) {
+                    if (!grantsPermission(permission, null)) {
+                        return false;
+                    }
+                }
+                return true;
+            } else {
+                return grantsPermission(permissions, null);
+            }
+        }
+
+        @Override
+        public boolean isGranted(long permissions, @Nonnull PropertyState property) {
+            if (Permissions.isAggregate(permissions)) {
+                for (long permission : Permissions.aggregates(permissions)) {
+                    if (!grantsPermission(permission, property)) {
+                        return false;
+                    }
+                }
+                return true;
+            } else {
+                return grantsPermission(permissions, property);
+            }
+        }
+
+        private boolean grantsPermission(long permission, @Nullable PropertyState property) {
+            Iterator<Map.Entry<AggregatedPermissionProvider, TreePermission>> it = delegates.entrySet().iterator();
+            while (it.hasNext()) {
+                Map.Entry<AggregatedPermissionProvider, TreePermission> entry = it.next();
+                TreePermission tp = entry.getValue();
+                boolean isGranted = (property == null) ? tp.isGranted(permission) : tp.isGranted(permission, property);
+                if (!it.hasNext() || evalComplete(isGranted, entry.getKey().getFlag())) {
+                    return isGranted;
+                }
+            }
+            return false;
+        }
+
+        @Nonnull
+        private TreePermission getParentPermission(AggregatedPermissionProvider provider) {
+            TreePermission parent = (parentPermission != null) ? parentPermission.delegates.get(provider) : null;
+            return (parent == null) ? TreePermission.EMPTY : parent;
+        }
+
+
+    }
+
+    private class CompositeRepositoryPermission implements RepositoryPermission {
+
+        @Override
+        public boolean isGranted(long repositoryPermissions) {
+            Iterable<AggregatedPermissionProvider> providers = Iterables.filter(pps, new Predicate<AggregatedPermissionProvider>() {
+                @Override
+                public boolean apply(@Nullable AggregatedPermissionProvider provider) {
+                    return provider != null && provider.handlesRepositoryPermissions();
+                }
+            });
+            if (Permissions.isAggregate(repositoryPermissions)) {
+                for (long permission : Permissions.aggregates(repositoryPermissions)) {
+                    if (!grantsRepoPermission(permission, providers)) {
+                        return false;
+                    }
+                }
+                return true;
+            } else {
+                return grantsRepoPermission(repositoryPermissions, providers);
+            }
+        }
+    }
+}
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/privilege/PrivilegeBits.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/privilege/PrivilegeBits.java	(revision 1585917)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/privilege/PrivilegeBits.java	(revision )
@@ -426,6 +426,16 @@
     }
 
     @Nonnull
+    public PrivilegeBits retain(@Nonnull PrivilegeBits other) {
+        if (d instanceof ModifiableData) {
+            ((ModifiableData) d).retain(other.d);
+            return this;
+        }  else {
+            throw new UnsupportedOperationException("immutable privilege bits");
+        }
+    }
+
+    @Nonnull
     public PropertyState asPropertyState(String name) {
         return PropertyStates.createProperty(name, Longs.asList(d.longValues()), Type.LONGS);
     }
@@ -750,6 +760,18 @@
             }
             for (int i = 0; i < b.length; i++) {
                 bits[i] |= b[i];
+            }
+        }
+
+        private void retain(Data other) {
+            if (isSimple()) {
+                bits[0] &= other.longValue();
+            } else {
+                long[] lvs = longValues();
+                long[] bLvs = other.longValues();
+                for (int i = 0; i < lvs.length; i++) {
+                    lvs[i] &= bLvs[i];
+                }
             }
         }
 
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/Permissions.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/Permissions.java	(revision 1585917)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/Permissions.java	(revision )
@@ -26,13 +26,15 @@
 import javax.annotation.Nullable;
 import javax.jcr.Session;
 
+import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
 import org.apache.jackrabbit.oak.plugins.name.NamespaceConstants;
 import org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants;
+import org.apache.jackrabbit.oak.plugins.tree.TreeLocation;
 import org.apache.jackrabbit.oak.plugins.version.VersionConstants;
 import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants;
-import org.apache.jackrabbit.oak.plugins.tree.TreeLocation;
 import org.apache.jackrabbit.util.Text;
 
 /**
@@ -132,6 +134,29 @@
             | INDEX_DEFINITION_MANAGEMENT
     );
 
+    private static final Set<Long> NON_AGGREGATES = ImmutableSet.of(
+            READ_NODE,
+            READ_PROPERTY,
+            ADD_PROPERTY,
+            MODIFY_PROPERTY,
+            REMOVE_PROPERTY,
+            ADD_NODE,
+            REMOVE_NODE,
+            MODIFY_CHILD_NODE_COLLECTION,
+            READ_ACCESS_CONTROL,
+            MODIFY_ACCESS_CONTROL,
+            NODE_TYPE_MANAGEMENT,
+            VERSION_MANAGEMENT,
+            LOCK_MANAGEMENT,
+            LIFECYCLE_MANAGEMENT,
+            RETENTION_MANAGEMENT,
+            NODE_TYPE_DEFINITION_MANAGEMENT,
+            NAMESPACE_MANAGEMENT,
+            WORKSPACE_MANAGEMENT,
+            PRIVILEGE_MANAGEMENT,
+            USER_MANAGEMENT,
+            INDEX_DEFINITION_MANAGEMENT);
+
     public static final Map<Long, String> PERMISSION_NAMES = new LinkedHashMap<Long, String>();
     static {
         PERMISSION_NAMES.put(ALL, "ALL");
@@ -244,6 +269,23 @@
                 permission == NODE_TYPE_DEFINITION_MANAGEMENT ||
                 permission == PRIVILEGE_MANAGEMENT ||
                 permission == WORKSPACE_MANAGEMENT;
+    }
+
+    public static boolean isAggregate(long permission) {
+        return !NON_AGGREGATES.contains(permission);
+    }
+
+    public static Iterable<Long> aggregates(final long permissions) {
+        if (ALL == permissions) {
+            return NON_AGGREGATES;
+        } else {
+            return Iterables.filter(NON_AGGREGATES, new Predicate<Long>() {
+                @Override
+                public boolean apply(@Nullable Long permission) {
+                    return permission != null && includes(permissions, permission);
+                }
+            });
+        }
     }
 
     public static boolean includes(long permissions, long permissionsToTest) {
\ No newline at end of file
