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 2014/07/30 17:38:27 UTC

svn commit: r1614686 - in /jackrabbit/oak/trunk/oak-core/src: main/java/org/apache/jackrabbit/oak/core/ main/java/org/apache/jackrabbit/oak/security/authorization/ main/java/org/apache/jackrabbit/oak/security/authorization/permission/ main/java/org/apa...

Author: angela
Date: Wed Jul 30 15:38:27 2014
New Revision: 1614686

URL: http://svn.apache.org/r1614686
Log:
OAK-1268 : Add support for composite authorization setup (untested, wip)

Added:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/accesscontrol/CompositeAccessControlManager.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/AggregatedPermissionProvider.java   (with props)
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/CompositePermissionProvider.java   (with props)
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/ControlFlag.java   (with props)
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/EmptyPermissionProvider.java
      - copied, changed from r1603387, jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/OpenPermissionProvider.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/CompositeConfigurationTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/CompositePermissionProviderTest.java
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/ImmutableRoot.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/AuthorizationConfigurationImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionProviderImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/CompositeConfiguration.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/CompositeAuthorizationConfiguration.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/Permissions.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/privilege/PrivilegeBits.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/PermissionsTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/privilege/PrivilegeBitsTest.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/ImmutableRoot.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/ImmutableRoot.java?rev=1614686&r1=1614685&r2=1614686&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/ImmutableRoot.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/ImmutableRoot.java Wed Jul 30 15:38:27 2014
@@ -67,6 +67,14 @@ public final class ImmutableRoot impleme
         this.rootTree = rootTree;
     }
 
+    public static ImmutableRoot getInstance(@Nonnull Root root) {
+        if (root instanceof ImmutableRoot) {
+            return (ImmutableRoot) root;
+        } else {
+            return new ImmutableRoot(root);
+        }
+    }
+
     //---------------------------------------------------------------< Root >---
 
     @Nonnull

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/AuthorizationConfigurationImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/AuthorizationConfigurationImpl.java?rev=1614686&r1=1614685&r2=1614686&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/AuthorizationConfigurationImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/AuthorizationConfigurationImpl.java Wed Jul 30 15:38:27 2014
@@ -48,6 +48,7 @@ import org.apache.jackrabbit.oak.spi.com
 import org.apache.jackrabbit.oak.spi.commit.MoveTracker;
 import org.apache.jackrabbit.oak.spi.commit.ValidatorProvider;
 import org.apache.jackrabbit.oak.spi.lifecycle.WorkspaceInitializer;
+import org.apache.jackrabbit.oak.spi.security.CompositeConfiguration;
 import org.apache.jackrabbit.oak.spi.security.ConfigurationBase;
 import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
 import org.apache.jackrabbit.oak.spi.security.Context;
@@ -55,6 +56,8 @@ 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.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.restriction.RestrictionProvider;
@@ -95,7 +98,19 @@ import org.apache.jackrabbit.oak.spi.xml
         @Property(name = PermissionConstants.PARAM_ADMINISTRATIVE_PRINCIPALS,
                 label = "Administrative Principals",
                 description = "Allows to specify principals that should be granted full permissions on the complete repository content.",
-                cardinality = 10)
+                cardinality = 10),
+        @Property(name = CompositeConfiguration.PARAM_RANKING,
+                label = "Ranking",
+                description = "Ranking of this configuration in a setup with multiple authorization configurations.",
+                intValue = 100),
+        @Property(name = AggregatedPermissionProvider.PARAM_CONTROL_FLAG,
+                label = "Control Flag",
+                description = "Control flag defining if the permission provider is SUFFICIENT or REQUIRED.",
+                options = {
+                        @PropertyOption(name = ControlFlag.SUFFICIENT_NAME, value = ControlFlag.SUFFICIENT_NAME),
+                        @PropertyOption(name = ControlFlag.REQUISITE_NAME, value = ControlFlag.REQUISITE_NAME)
+                },
+                value = ControlFlag.REQUISITE_NAME)
 })
 public class AuthorizationConfigurationImpl extends ConfigurationBase implements AuthorizationConfiguration {
     public AuthorizationConfigurationImpl() {

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionProviderImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionProviderImpl.java?rev=1614686&r1=1614685&r2=1614686&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionProviderImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionProviderImpl.java Wed Jul 30 15:38:27 2014
@@ -32,6 +32,8 @@ import org.apache.jackrabbit.oak.plugins
 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;
@@ -40,7 +42,7 @@ import org.apache.jackrabbit.oak.spi.sec
 import org.apache.jackrabbit.oak.spi.security.principal.AdminPrincipal;
 import org.apache.jackrabbit.oak.spi.security.principal.SystemPrincipal;
 
-public class PermissionProviderImpl implements PermissionProvider, AccessControlConstants, PermissionConstants {
+public class PermissionProviderImpl implements PermissionProvider, AccessControlConstants, PermissionConstants, AggregatedPermissionProvider {
 
     private final Root root;
 
@@ -52,6 +54,8 @@ public class PermissionProviderImpl impl
 
     private ImmutableRoot immutableRoot;
 
+    private ControlFlag flag;
+
     public PermissionProviderImpl(@Nonnull Root root, @Nonnull String workspaceName, @Nonnull Set<Principal> principals,
                                   @Nonnull AuthorizationConfiguration acConfig) {
         this.root = root;
@@ -65,6 +69,8 @@ public class PermissionProviderImpl impl
         } else {
             compiledPermissions = CompiledPermissionImpl.create(immutableRoot, workspaceName, principals, acConfig);
         }
+
+        flag = ControlFlag.valueOf(acConfig.getParameters().getConfigValue(AggregatedPermissionProvider.PARAM_CONTROL_FLAG, ControlFlag.REQUISITE_NAME));
     }
 
     @Override
@@ -116,6 +122,37 @@ public class PermissionProviderImpl impl
         return isGranted;
     }
 
+    //---------------------------------------< AggregatedPermissionProvider >---
+    @Override
+    public ControlFlag getFlag() {
+        return flag;
+    }
+
+    @Override
+    public boolean handles(@Nonnull String path, @Nonnull String jcrAction) {
+        return true;
+    }
+
+    @Override
+    public boolean handles(@Nonnull Tree tree) {
+        return true;
+    }
+
+    @Override
+    public boolean handles(@Nonnull Tree tree, long permission) {
+        return true;
+    }
+
+    @Override
+    public boolean handles(@Nonnull TreePermission treePermission, long permission) {
+        return true;
+    }
+
+    @Override
+    public boolean handlesRepositoryPermissions() {
+        return true;
+    }
+
     //--------------------------------------------------------------------------
 
     private boolean isAdmin(Set<Principal> principals) {
@@ -137,11 +174,7 @@ public class PermissionProviderImpl impl
     }
 
     private static ImmutableRoot getImmutableRoot(@Nonnull Root base) {
-        if (base instanceof ImmutableRoot) {
-            return (ImmutableRoot) base;
-        } else {
-            return new ImmutableRoot(base);
-        }
+        return ImmutableRoot.getInstance(base);
     }
 
     private static boolean isVersionStorePath(@Nonnull String oakPath) {

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/CompositeConfiguration.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/CompositeConfiguration.java?rev=1614686&r1=1614685&r2=1614686&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/CompositeConfiguration.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/CompositeConfiguration.java Wed Jul 30 15:38:27 2014
@@ -48,6 +48,19 @@ import org.apache.jackrabbit.oak.plugins
  */
 public abstract class CompositeConfiguration<T extends SecurityConfiguration> implements SecurityConfiguration {
 
+    /**
+     * Parameter used to define the ranking of a given configuration compared to
+     * other registered configuration in this aggregate. If the ranking parameter
+     * is missing a new configuration will be added at the end of the list.
+     */
+    public static final String PARAM_RANKING = "configurationRanking";
+
+    /**
+     * Default ranking value used to insert a new configuration at the end of
+     * the list.
+     */
+    private static final int NO_RANKING = Integer.MIN_VALUE;
+
     private final List<T> configurations = new CopyOnWriteArrayList<T>();
 
     private final String name;
@@ -65,7 +78,21 @@ public abstract class CompositeConfigura
     }
 
     public void addConfiguration(@Nonnull T configuration) {
-        configurations.add(configuration);
+        int ranking = configuration.getParameters().getConfigValue(PARAM_RANKING, NO_RANKING);
+        if (ranking == NO_RANKING || configurations.isEmpty()) {
+            configurations.add(configuration);
+        } else {
+            int i = 0;
+            for (T c : configurations) {
+                int r = c.getParameters().getConfigValue(PARAM_RANKING, NO_RANKING);
+                if (ranking > r) {
+                    break;
+                } else {
+                    i++;
+                }
+            }
+            configurations.add(i, configuration);
+        }
     }
 
     public void removeConfiguration(@Nonnull T configuration) {

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/CompositeAuthorizationConfiguration.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/CompositeAuthorizationConfiguration.java?rev=1614686&r1=1614685&r2=1614686&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/CompositeAuthorizationConfiguration.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/CompositeAuthorizationConfiguration.java Wed Jul 30 15:38:27 2014
@@ -20,26 +20,17 @@ import java.security.Principal;
 import java.util.List;
 import java.util.Set;
 import javax.annotation.Nonnull;
-import javax.jcr.RepositoryException;
-import javax.jcr.security.AccessControlException;
 import javax.jcr.security.AccessControlManager;
-import javax.jcr.security.AccessControlPolicy;
-import javax.jcr.security.AccessControlPolicyIterator;
-import javax.jcr.security.Privilege;
 
 import com.google.common.base.Function;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterators;
 import com.google.common.collect.Lists;
-import org.apache.jackrabbit.api.security.JackrabbitAccessControlManager;
-import org.apache.jackrabbit.api.security.JackrabbitAccessControlPolicy;
-import org.apache.jackrabbit.commons.iterator.AccessControlPolicyIteratorAdapter;
 import org.apache.jackrabbit.oak.api.Root;
 import org.apache.jackrabbit.oak.namepath.NamePathMapper;
 import org.apache.jackrabbit.oak.spi.security.CompositeConfiguration;
 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.accesscontrol.CompositeAccessControlManager;
+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;
@@ -60,13 +51,20 @@ public class CompositeAuthorizationConfi
     @Override
     public AccessControlManager getAccessControlManager(final @Nonnull Root root,
                                                         final @Nonnull NamePathMapper namePathMapper) {
-        List<AccessControlManager> mgrs = Lists.transform(getConfigurations(), new Function<AuthorizationConfiguration, AccessControlManager>() {
-            @Override
-            public AccessControlManager apply(AuthorizationConfiguration authorizationConfiguration) {
-                return authorizationConfiguration.getAccessControlManager(root, namePathMapper);
-            }
-        });
-        return new CompositeAcMgr(root, namePathMapper, getSecurityProvider(), mgrs);
+        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 CompositeAccessControlManager(root, namePathMapper, getSecurityProvider(), mgrs);
+
+        }
     }
 
     @Nonnull
@@ -84,129 +82,22 @@ public class CompositeAuthorizationConfi
 
     @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)");
-    }
-
-    /**
-     * Access control manager that aggregates a list of different access control
-     * manager implementations. Note, that the implementations *must* implement
-     * the {@link org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.PolicyOwner}
-     * interface in order to be able to set and remove individual access control
-     * policies.
-     */
-    private static class CompositeAcMgr extends AbstractAccessControlManager {
-
-        private final List<AccessControlManager> acMgrs;
-
-        private CompositeAcMgr(@Nonnull Root root,
-                               @Nonnull NamePathMapper namePathMapper,
-                               @Nonnull SecurityProvider securityProvider,
-                               @Nonnull List<AccessControlManager> acMgrs) {
-            super(root, namePathMapper, securityProvider);
-            this.acMgrs = acMgrs;
-        }
-
-        //-------------------------------------------< AccessControlManager >---
-        @Override
-        public Privilege[] getSupportedPrivileges(String absPath) throws RepositoryException {
-            ImmutableList.Builder<Privilege> privs = ImmutableList.builder();
-            for (AccessControlManager acMgr : acMgrs) {
-                privs.add(acMgr.getSupportedPrivileges(absPath));
-            }
-            List<Privilege> l = privs.build();
-            return l.toArray(new Privilege[l.size()]);
-        }
-
-        @Override
-        public AccessControlPolicy[] getPolicies(String absPath) throws RepositoryException {
-            ImmutableList.Builder<AccessControlPolicy> policies = ImmutableList.builder();
-            for (AccessControlManager acMgr : acMgrs) {
-                policies.add(acMgr.getPolicies(absPath));
-            }
-            List<AccessControlPolicy> l = policies.build();
-            return l.toArray(new AccessControlPolicy[l.size()]);
-        }
-
-        @Override
-        public AccessControlPolicy[] getEffectivePolicies(String absPath) throws RepositoryException {
-            ImmutableList.Builder<AccessControlPolicy> privs = ImmutableList.builder();
-            for (AccessControlManager acMgr : acMgrs) {
-                privs.add(acMgr.getEffectivePolicies(absPath));
-            }
-            List<AccessControlPolicy> l = privs.build();
-            return l.toArray(new AccessControlPolicy[l.size()]);
-        }
-
-        @Override
-        public AccessControlPolicyIterator getApplicablePolicies(String absPath) throws RepositoryException {
-            List<AccessControlPolicyIterator> l = Lists.newArrayList();
-            for (AccessControlManager acMgr : acMgrs) {
-                if (acMgr instanceof PolicyOwner) {
-                    l.add(acMgr.getApplicablePolicies(absPath));
-                }
-            }
-            return new AccessControlPolicyIteratorAdapter(Iterators.concat(l.toArray(new AccessControlPolicyIterator[l.size()])));
-        }
-
-        @Override
-        public void setPolicy(String absPath, AccessControlPolicy policy) throws RepositoryException {
-            for (AccessControlManager acMgr : acMgrs) {
-                if (acMgr instanceof PolicyOwner && ((PolicyOwner) acMgr).defines(absPath, policy)) {
-                    acMgr.setPolicy(absPath, policy);
-                    return;
-                }
-            }
-            throw new AccessControlException("Cannot set access control policy " + policy + "; no PolicyOwner found.");
-        }
-
-        @Override
-        public void removePolicy(String absPath, AccessControlPolicy policy) throws RepositoryException {
-            for (AccessControlManager acMgr : acMgrs) {
-                if (acMgr instanceof PolicyOwner && ((PolicyOwner) acMgr).defines(absPath, policy)) {
-                    acMgr.removePolicy(absPath, policy);
-                    return;
-                }
-            }
-            throw new AccessControlException("Cannot remove access control policy " + policy + "; no PolicyOwner found.");
-        }
-
-        //---------------------------------< JackrabbitAccessControlManager >---
-        @Override
-        public JackrabbitAccessControlPolicy[] getApplicablePolicies(Principal principal) throws RepositoryException {
-            ImmutableList.Builder<JackrabbitAccessControlPolicy> policies = ImmutableList.builder();
-            for (AccessControlManager acMgr : acMgrs) {
-                if (acMgr instanceof JackrabbitAccessControlManager && acMgr instanceof PolicyOwner) {
-                    policies.add(((JackrabbitAccessControlManager) acMgr).getApplicablePolicies(principal));
-                }
-            }
-            List<JackrabbitAccessControlPolicy> l = policies.build();
-            return l.toArray(new JackrabbitAccessControlPolicy[l.size()]);
-        }
-
-        @Override
-        public JackrabbitAccessControlPolicy[] getPolicies(Principal principal) throws RepositoryException {
-            ImmutableList.Builder<JackrabbitAccessControlPolicy> privs = ImmutableList.builder();
-            for (AccessControlManager acMgr : acMgrs) {
-                if (acMgr instanceof JackrabbitAccessControlManager) {
-                    privs.add(((JackrabbitAccessControlManager) acMgr).getPolicies(principal));
-                }
-            }
-            List<JackrabbitAccessControlPolicy> l = privs.build();
-            return l.toArray(new JackrabbitAccessControlPolicy[l.size()]);
-        }
-
-        @Override
-        public AccessControlPolicy[] getEffectivePolicies(Set<Principal> principals) throws RepositoryException {
-            ImmutableList.Builder<AccessControlPolicy> privs = ImmutableList.builder();
-            for (AccessControlManager acMgr : acMgrs) {
-                if (acMgr instanceof JackrabbitAccessControlManager) {
-                    privs.add(((JackrabbitAccessControlManager) acMgr).getEffectivePolicies(principals));
+    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> aggrPermissionProviders = Lists.newArrayListWithCapacity(configurations.size());
+                for (AuthorizationConfiguration conf : configurations) {
+                    PermissionProvider pProvider = conf.getPermissionProvider(root, workspaceName, principals);
+                    if (pProvider instanceof AggregatedPermissionProvider) {
+                        aggrPermissionProviders.add((AggregatedPermissionProvider) pProvider);
+                    }
                 }
-            }
-            List<AccessControlPolicy> l = privs.build();
-            return l.toArray(new AccessControlPolicy[l.size()]);
+                return new CompositePermissionProvider(root, aggrPermissionProviders);
         }
     }
 }
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/accesscontrol/CompositeAccessControlManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/accesscontrol/CompositeAccessControlManager.java?rev=1614686&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/accesscontrol/CompositeAccessControlManager.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/accesscontrol/CompositeAccessControlManager.java Wed Jul 30 15:38:27 2014
@@ -0,0 +1,159 @@
+/*
+ * 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.accesscontrol;
+
+import java.security.Principal;
+import java.util.List;
+import java.util.Set;
+import javax.annotation.Nonnull;
+import javax.jcr.RepositoryException;
+import javax.jcr.security.AccessControlException;
+import javax.jcr.security.AccessControlManager;
+import javax.jcr.security.AccessControlPolicy;
+import javax.jcr.security.AccessControlPolicyIterator;
+import javax.jcr.security.Privilege;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterators;
+import com.google.common.collect.Lists;
+import org.apache.jackrabbit.api.security.JackrabbitAccessControlManager;
+import org.apache.jackrabbit.api.security.JackrabbitAccessControlPolicy;
+import org.apache.jackrabbit.commons.iterator.AccessControlPolicyIteratorAdapter;
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.namepath.NamePathMapper;
+import org.apache.jackrabbit.oak.spi.security.SecurityProvider;
+
+/**
+ * Access control manager that aggregates a list of different access control
+ * manager implementations. Note, that the implementations *must* implement
+ * the {@link org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.PolicyOwner}
+ * interface in order to be able to set and remove individual access control
+ * policies.
+ */
+public class CompositeAccessControlManager extends AbstractAccessControlManager {
+
+    private final List<AccessControlManager> acMgrs;
+
+    public CompositeAccessControlManager(@Nonnull Root root,
+                                         @Nonnull NamePathMapper namePathMapper,
+                                         @Nonnull SecurityProvider securityProvider,
+                                         @Nonnull List<AccessControlManager> acMgrs) {
+        super(root, namePathMapper, securityProvider);
+        this.acMgrs = acMgrs;
+    }
+
+    //-----------------------------------------------< AccessControlManager >---
+    @Override
+    public Privilege[] getSupportedPrivileges(String absPath) throws RepositoryException {
+        ImmutableList.Builder<Privilege> privs = ImmutableList.builder();
+        for (AccessControlManager acMgr : acMgrs) {
+            privs.add(acMgr.getSupportedPrivileges(absPath));
+        }
+        List<Privilege> l = privs.build();
+        return l.toArray(new Privilege[l.size()]);
+    }
+
+    @Override
+    public AccessControlPolicy[] getPolicies(String absPath) throws RepositoryException {
+        ImmutableList.Builder<AccessControlPolicy> policies = ImmutableList.builder();
+        for (AccessControlManager acMgr : acMgrs) {
+            policies.add(acMgr.getPolicies(absPath));
+        }
+        List<AccessControlPolicy> l = policies.build();
+        return l.toArray(new AccessControlPolicy[l.size()]);
+    }
+
+    @Override
+    public AccessControlPolicy[] getEffectivePolicies(String absPath) throws RepositoryException {
+        ImmutableList.Builder<AccessControlPolicy> privs = ImmutableList.builder();
+        for (AccessControlManager acMgr : acMgrs) {
+            privs.add(acMgr.getEffectivePolicies(absPath));
+        }
+        List<AccessControlPolicy> l = privs.build();
+        return l.toArray(new AccessControlPolicy[l.size()]);
+    }
+
+    @Override
+    public AccessControlPolicyIterator getApplicablePolicies(String absPath) throws RepositoryException {
+        List<AccessControlPolicyIterator> l = Lists.newArrayList();
+        for (AccessControlManager acMgr : acMgrs) {
+            if (acMgr instanceof PolicyOwner) {
+                l.add(acMgr.getApplicablePolicies(absPath));
+            }
+        }
+        return new AccessControlPolicyIteratorAdapter(Iterators.concat(l.toArray(new AccessControlPolicyIterator[l.size()])));
+    }
+
+    @Override
+    public void setPolicy(String absPath, AccessControlPolicy policy) throws RepositoryException {
+        for (AccessControlManager acMgr : acMgrs) {
+            if (acMgr instanceof PolicyOwner && ((PolicyOwner) acMgr).defines(absPath, policy)) {
+                acMgr.setPolicy(absPath, policy);
+                return;
+            }
+        }
+        throw new AccessControlException("Cannot set access control policy " + policy + "; no PolicyOwner found.");
+    }
+
+    @Override
+    public void removePolicy(String absPath, AccessControlPolicy policy) throws RepositoryException {
+        for (AccessControlManager acMgr : acMgrs) {
+            if (acMgr instanceof PolicyOwner && ((PolicyOwner) acMgr).defines(absPath, policy)) {
+                acMgr.removePolicy(absPath, policy);
+                return;
+            }
+        }
+        throw new AccessControlException("Cannot remove access control policy " + policy + "; no PolicyOwner found.");
+    }
+
+    //-------------------------------------< JackrabbitAccessControlManager >---
+    @Override
+    public JackrabbitAccessControlPolicy[] getApplicablePolicies(Principal principal) throws RepositoryException {
+        ImmutableList.Builder<JackrabbitAccessControlPolicy> policies = ImmutableList.builder();
+        for (AccessControlManager acMgr : acMgrs) {
+            if (acMgr instanceof JackrabbitAccessControlManager && acMgr instanceof PolicyOwner) {
+                policies.add(((JackrabbitAccessControlManager) acMgr).getApplicablePolicies(principal));
+            }
+        }
+        List<JackrabbitAccessControlPolicy> l = policies.build();
+        return l.toArray(new JackrabbitAccessControlPolicy[l.size()]);
+    }
+
+    @Override
+    public JackrabbitAccessControlPolicy[] getPolicies(Principal principal) throws RepositoryException {
+        ImmutableList.Builder<JackrabbitAccessControlPolicy> privs = ImmutableList.builder();
+        for (AccessControlManager acMgr : acMgrs) {
+            if (acMgr instanceof JackrabbitAccessControlManager) {
+                privs.add(((JackrabbitAccessControlManager) acMgr).getPolicies(principal));
+            }
+        }
+        List<JackrabbitAccessControlPolicy> l = privs.build();
+        return l.toArray(new JackrabbitAccessControlPolicy[l.size()]);
+    }
+
+    @Override
+    public AccessControlPolicy[] getEffectivePolicies(Set<Principal> principals) throws RepositoryException {
+        ImmutableList.Builder<AccessControlPolicy> privs = ImmutableList.builder();
+        for (AccessControlManager acMgr : acMgrs) {
+            if (acMgr instanceof JackrabbitAccessControlManager) {
+                privs.add(((JackrabbitAccessControlManager) acMgr).getEffectivePolicies(principals));
+            }
+        }
+        List<AccessControlPolicy> l = privs.build();
+        return l.toArray(new AccessControlPolicy[l.size()]);
+    }
+}
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/AggregatedPermissionProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/AggregatedPermissionProvider.java?rev=1614686&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/AggregatedPermissionProvider.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/AggregatedPermissionProvider.java Wed Jul 30 15:38:27 2014
@@ -0,0 +1,47 @@
+/*
+ * 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.
+     */
+    String PARAM_CONTROL_FLAG = "controlFlag";
+
+    @Nonnull
+    ControlFlag getFlag();
+
+    boolean handles(@Nonnull String path, @Nonnull String jcrAction);
+
+    boolean handles(@Nonnull Tree tree);
+
+    boolean handles(@Nonnull Tree tree, long permission);
+
+    boolean handles(@Nonnull TreePermission treePermission, long permission);
+
+    boolean handlesRepositoryPermissions();
+}
\ No newline at end of file

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

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/CompositePermissionProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/CompositePermissionProvider.java?rev=1614686&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/CompositePermissionProvider.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/CompositePermissionProvider.java Wed Jul 30 15:38:27 2014
@@ -0,0 +1,411 @@
+/*
+ * 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.ImmutableMap;
+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;
+
+/**
+ * Permission provider implementation that aggregates a list of different
+ * provider implementations. Note, that the implementations *must* implement
+ * the {@link org.apache.jackrabbit.oak.spi.security.authorization.permission.AggregatedPermissionProvider}
+ * interface.
+ */
+public class CompositePermissionProvider implements PermissionProvider {
+
+    private final Root root;
+    private final List<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 = 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) {
+        if (Permissions.isAggregate(permissions)) {
+            for (final long permission : Permissions.aggregates(permissions)) {
+                Iterable<AggregatedPermissionProvider> providers = Iterables.filter(pps, new Predicate<AggregatedPermissionProvider>() {
+                    @Override
+                    public boolean apply(@Nullable AggregatedPermissionProvider pp) {
+                        return pp != null && pp.handles(parent, permission);
+                    }
+                });
+                if (!grantsPermission(parent, property, permission, providers)) {
+                    return false;
+                }
+            }
+            return true;
+        } else {
+            Iterable<AggregatedPermissionProvider> providers = Iterables.filter(pps, new Predicate<AggregatedPermissionProvider>() {
+                @Override
+                public boolean apply(@Nullable AggregatedPermissionProvider pp) {
+                    return pp != null && pp.handles(parent, permissions);
+                }
+            });
+            return grantsPermission(parent, property, permissions, providers);
+        }
+    }
+
+    @Override
+    public boolean isGranted(final @Nonnull String oakPath, final @Nonnull String jcrActions) {
+        String[] actions = Text.explode(jcrActions, ',', false);
+        switch (actions.length) {
+            case 0: return true;
+            case 1:
+                Iterable<AggregatedPermissionProvider> providers = Iterables.filter(pps, new Predicate<AggregatedPermissionProvider>() {
+                    @Override
+                    public boolean apply(@Nullable AggregatedPermissionProvider pp) {
+                        return pp != null && pp.handles(oakPath, jcrActions);
+                    }
+                });
+                return grantsAction(oakPath, actions[0], providers);
+            default:
+                for (final String action : actions) {
+                    providers = Iterables.filter(pps, new Predicate<AggregatedPermissionProvider>() {
+                        @Override
+                        public boolean apply(@Nullable AggregatedPermissionProvider pp) {
+                            return pp != null && pp.handles(oakPath, action);
+                        }
+                    });
+                    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);
+                }
+            }
+        }
+        if (required != null) {
+            sufficient.add(required);
+        }
+        return sufficient;
+    }
+
+    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> map;
+
+        private Boolean canRead;
+
+        private CompositeTreePermission() {
+            tree = null;
+            parentPermission = null;
+
+            map = ImmutableMap.of();
+        }
+
+        private CompositeTreePermission(final @Nonnull ImmutableTree tree, @Nonnull CompositeTreePermission parentPermission) {
+            this.tree = tree;
+            this.parentPermission = parentPermission;
+
+            map = new LinkedHashMap<AggregatedPermissionProvider, TreePermission>(pps.size());
+            for (AggregatedPermissionProvider provider : pps) {
+                TreePermission tp = provider.getTreePermission(tree, getParentPermission(provider));
+                map.put(provider, tp);
+            }
+        }
+
+        @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 = map.entrySet().iterator();
+                while (it.hasNext()) {
+                    Map.Entry<AggregatedPermissionProvider, TreePermission> entry = it.next();
+                    TreePermission tp = entry.getValue();
+                    if (entry.getKey().handles(tp, Permissions.READ_NODE)) {
+                        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 = map.entrySet().iterator();
+            while (it.hasNext()) {
+                Map.Entry<AggregatedPermissionProvider, TreePermission> entry = it.next();
+                TreePermission tp = entry.getValue();
+                if (entry.getKey().handles(tp, Permissions.READ_PROPERTY)) {
+                    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 = map.entrySet().iterator();
+            while (it.hasNext()) {
+                Map.Entry<AggregatedPermissionProvider, TreePermission> entry = it.next();
+                if (entry.getKey().handles(this, permission)) {
+                    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 = null;
+            if (parentPermission != null) {
+                parent = parentPermission.map.get(provider);
+            }
+            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);
+            }
+        }
+    }
+}

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

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/ControlFlag.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/ControlFlag.java?rev=1614686&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/ControlFlag.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/ControlFlag.java Wed Jul 30 15:38:27 2014
@@ -0,0 +1,59 @@
+/*
+ * 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;
+
+/**
+ * This class is used to define the evaluation behavior of a given aggregated
+ * permission provider. It can be SUFFICIENT or REQUISITE.
+ */
+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");
+
+    public static final String SUFFICIENT_NAME = "SUFFICIENT";
+    public static final String REQUISITE_NAME = "REQUISITE";
+
+    private final String name;
+
+    private ControlFlag(@Nonnull String name) {
+        this.name = name;
+    }
+
+    @Override
+    public String toString() {
+        return name;
+    }
+}

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

Copied: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/EmptyPermissionProvider.java (from r1603387, jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/OpenPermissionProvider.java)
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/EmptyPermissionProvider.java?p2=jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/EmptyPermissionProvider.java&p1=jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/OpenPermissionProvider.java&r1=1603387&r2=1614686&rev=1614686&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/OpenPermissionProvider.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/EmptyPermissionProvider.java Wed Jul 30 15:38:27 2014
@@ -23,16 +23,15 @@ import javax.annotation.Nullable;
 
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Tree;
-import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants;
 
 /**
- * Permission provider implementation that grants full access everywhere.
+ * Permission provider implementation that does not grant any permissions.
  */
-public final class OpenPermissionProvider implements PermissionProvider {
+public final class EmptyPermissionProvider implements PermissionProvider {
 
-    private static final PermissionProvider INSTANCE = new OpenPermissionProvider();
+    private static final PermissionProvider INSTANCE = new EmptyPermissionProvider();
 
-    private OpenPermissionProvider() {
+    private EmptyPermissionProvider() {
     }
 
     public static PermissionProvider getInstance() {
@@ -47,31 +46,31 @@ public final class OpenPermissionProvide
     @Nonnull
     @Override
     public Set<String> getPrivileges(@Nullable Tree tree) {
-        return Collections.singleton(PrivilegeConstants.JCR_ALL);
+        return Collections.emptySet();
     }
 
     @Override
     public boolean hasPrivileges(@Nullable Tree tree, String... privilegeNames) {
-        return true;
+        return false;
     }
 
     @Override
     public RepositoryPermission getRepositoryPermission() {
-        return RepositoryPermission.ALL;
+        return RepositoryPermission.EMPTY;
     }
 
     @Override
     public TreePermission getTreePermission(@Nonnull Tree tree, @Nonnull TreePermission parentPermission) {
-        return TreePermission.ALL;
+        return TreePermission.EMPTY;
     }
 
     @Override
     public boolean isGranted(@Nonnull Tree tree, @Nullable PropertyState property, long permissions) {
-        return true;
+        return false;
     }
 
     @Override
     public boolean isGranted(@Nonnull String oakPath, @Nonnull String jcrActions) {
-        return true;
+        return false;
     }
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/Permissions.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/Permissions.java?rev=1614686&r1=1614685&r2=1614686&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/Permissions.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/Permissions.java Wed Jul 30 15:38:27 2014
@@ -26,13 +26,15 @@ import javax.annotation.Nonnull;
 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 @@ public final class Permissions {
             | 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");
@@ -246,6 +271,23 @@ public final class Permissions {
                 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) {
         return (permissions & permissionsToTest) == permissionsToTest;
     }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/privilege/PrivilegeBits.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/privilege/PrivilegeBits.java?rev=1614686&r1=1614685&r2=1614686&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/privilege/PrivilegeBits.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/privilege/PrivilegeBits.java Wed Jul 30 15:38:27 2014
@@ -425,6 +425,24 @@ public final class PrivilegeBits impleme
         }
     }
 
+    /**
+     * Retains the elements in this {@code PrivilegeBits} that are contained in
+     * the specified other {@code PrivilegeBits}.
+     *
+     * @param other Other privilege bits.
+     * @return This modifiable instance of privilege bits modified such it contains
+     * only privileges that were also contained in the {@code other} instance.
+     */
+    @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);
@@ -753,6 +771,33 @@ public final class PrivilegeBits impleme
             }
         }
 
+        private void retain(Data other) {
+            if (isSimple()) {
+                bits[0] &= other.longValue();
+            } else {
+                long[] lvs = longValues();
+                long[] bLvs = other.longValues();
+
+                long[] res = (lvs.length <= bLvs.length) ? new long[lvs.length] : new long[bLvs.length];
+                int compactSize = -1;
+                for (int i = 0; i < res.length; i++) {
+                    res[i] = (lvs[i] & bLvs[i]);
+                    if (res[i] == 0) {
+                        if (compactSize == -1) {
+                            compactSize = i+1;
+                        }
+                    } else {
+                        compactSize = -1;
+                    }
+                }
+                if (compactSize != -1 && res.length > compactSize) {
+                    bits = Arrays.copyOfRange(res, 0, compactSize);
+                } else {
+                    bits = res;
+                }
+            }
+        }
+
         private static long[] diff(long[] a, long[] b) {
             int index = -1;
             long[] res = new long[((a.length > b.length) ? a.length : b.length)];

Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/CompositeConfigurationTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/CompositeConfigurationTest.java?rev=1614686&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/CompositeConfigurationTest.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/CompositeConfigurationTest.java Wed Jul 30 15:38:27 2014
@@ -0,0 +1,26 @@
+/*
+ * 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;
+
+/**
+ * CompositeConfigurationTest... TODO
+ */
+public class CompositeConfigurationTest extends AbstractCompositeConfigurationTest {
+
+    // TODO
+
+}
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/CompositePermissionProviderTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/CompositePermissionProviderTest.java?rev=1614686&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/CompositePermissionProviderTest.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/CompositePermissionProviderTest.java Wed Jul 30 15:38:27 2014
@@ -0,0 +1,24 @@
+/*
+ * 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.oak.AbstractSecurityTest;
+
+public class CompositePermissionProviderTest extends AbstractSecurityTest {
+
+    // TODO
+}
\ No newline at end of file

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/PermissionsTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/PermissionsTest.java?rev=1614686&r1=1614685&r2=1614686&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/PermissionsTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/PermissionsTest.java Wed Jul 30 15:38:27 2014
@@ -73,4 +73,9 @@ public class PermissionsTest extends Abs
         }
     }
 
+    @Test
+    public void testAggregates() {
+        // TODO
+    }
+
 }
\ No newline at end of file

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/privilege/PrivilegeBitsTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/privilege/PrivilegeBitsTest.java?rev=1614686&r1=1614685&r2=1614686&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/privilege/PrivilegeBitsTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/privilege/PrivilegeBitsTest.java Wed Jul 30 15:38:27 2014
@@ -16,9 +16,12 @@
  */
 package org.apache.jackrabbit.oak.spi.security.privilege;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
+import java.util.Random;
 
 import org.apache.jackrabbit.oak.AbstractSecurityTest;
 import org.apache.jackrabbit.oak.api.PropertyState;
@@ -426,6 +429,100 @@ public class PrivilegeBitsTest extends A
     }
 
     @Test
+    public void testRetainUnmodifiable() {
+        PrivilegeBits unmodifiable = READ_NODES_PRIVILEGE_BITS;
+        try {
+            unmodifiable.retain(PrivilegeBits.getInstance());
+            fail();
+        } catch (UnsupportedOperationException e) {
+            // success
+        }
+    }
+
+    @Test
+    public void testRetainSimple() {
+        PrivilegeBits pb = PrivilegeBits.getInstance(READ_NODES_PRIVILEGE_BITS);
+        assertEquals(pb, pb.retain(pb));
+        assertEquals(pb, pb.retain(READ_NODES_PRIVILEGE_BITS));
+
+        pb = PrivilegeBits.getInstance(READ_NODES_PRIVILEGE_BITS);
+        pb.retain(PrivilegeBits.getInstance());
+        assertTrue(pb.isEmpty());
+
+        pb = PrivilegeBits.getInstance(READ_NODES_PRIVILEGE_BITS);
+        pb.retain(PrivilegeBits.EMPTY);
+        assertTrue(pb.isEmpty());
+
+        PrivilegeBits write = PrivilegeBits.BUILT_IN.get(PrivilegeBits.REP_WRITE);
+        pb = PrivilegeBits.getInstance().add(write);
+        assertEquals(pb, pb.retain(pb));
+        assertEquals(pb, pb.retain(write));
+
+        pb.retain(READ_NODES_PRIVILEGE_BITS);
+        assertTrue(pb.isEmpty());
+
+        pb.add(READ_NODES_PRIVILEGE_BITS).add(write);
+        pb.retain(write);
+        assertEquivalent(write, pb);
+        assertFalse(pb.includes(READ_NODES_PRIVILEGE_BITS));
+
+        PrivilegeBits lock = PrivilegeBits.BUILT_IN.get(PrivilegeBits.JCR_LOCK_MANAGEMENT);
+        PrivilegeBits lw = PrivilegeBits.getInstance(write, lock);
+
+        pb.add(READ_NODES_PRIVILEGE_BITS).add(write).add(lock);
+        pb.retain(lw);
+        assertEquivalent(lw, pb);
+        assertFalse(pb.includes(READ_NODES_PRIVILEGE_BITS));
+    }
+
+    @Test
+    public void testRetain() {
+        PrivilegeBits pb = READ_NODES_PRIVILEGE_BITS;
+        List<PrivilegeBits> pbs = new ArrayList<PrivilegeBits>();
+        pbs.add(pb);
+        Random random = new Random();
+
+        for (int i = 0; i < 100; i++) {
+            PrivilegeBits nxt = pb.nextBits();
+
+            PrivilegeBits mod = PrivilegeBits.getInstance(nxt, pb);
+            mod.retain(nxt);
+            assertEquivalent(nxt, mod);
+
+            mod = PrivilegeBits.getInstance(nxt);
+            mod.retain(pb);
+            assertTrue(nxt.toString(), mod.isEmpty());
+
+            mod = PrivilegeBits.getInstance(nxt);
+            mod.retain(READ_NODES_PRIVILEGE_BITS);
+            assertTrue(nxt.toString(), mod.isEmpty());
+
+            mod = PrivilegeBits.getInstance(nxt, READ_NODES_PRIVILEGE_BITS);
+            mod.retain(nxt);
+            assertEquivalent(nxt, mod);
+
+            mod = PrivilegeBits.getInstance(nxt, pb, READ_NODES_PRIVILEGE_BITS);
+            mod.retain(READ_NODES_PRIVILEGE_BITS);
+            assertEquivalent(READ_NODES_PRIVILEGE_BITS, mod);
+
+            mod = PrivilegeBits.getInstance(nxt);
+            PrivilegeBits other = PrivilegeBits.getInstance();
+            for (int j = 0; j < pbs.size()/2; j++) {
+                other.add(pbs.get(random.nextInt(pbs.size()-1)));
+            }
+            mod.add(other);
+            mod.retain(other);
+            assertEquivalent(other, mod);
+
+            other.retain(nxt);
+            assertTrue(other.isEmpty());
+
+            pbs.add(nxt);
+            pb = nxt;
+        }
+    }
+
+    @Test
     public void testGetInstance() {
         PrivilegeBits pb = PrivilegeBits.getInstance();
         assertEquivalent(PrivilegeBits.EMPTY, pb);