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 st...@apache.org on 2017/06/27 14:11:33 UTC

svn commit: r1800063 [1/2] - in /jackrabbit/oak/trunk: oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/ oak-core/src/main/java/org/apache/jackrabbit/oak/security/internal/ oak-core/src/test/java/org/apache/jackrabbit/o...

Author: stillalex
Date: Tue Jun 27 14:11:33 2017
New Revision: 1800063

URL: http://svn.apache.org/viewvc?rev=1800063&view=rev
Log:
OAK-6356 Allow CompositePermissionProvider to OR entries


Added:
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderCustomMixTest.java   (with props)
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeAuthorizationConfiguration.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositePermissionProvider.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeTreePermission.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/internal/SecurityProviderRegistration.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/AbstractCompositeProviderTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderAllTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderCoverageTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderEmptyTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderNoScopeTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeTreePermissionTest.java
    jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authorization/composite.md
    jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/introduction.md

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeAuthorizationConfiguration.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeAuthorizationConfiguration.java?rev=1800063&r1=1800062&r2=1800063&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeAuthorizationConfiguration.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeAuthorizationConfiguration.java Tue Jun 27 14:11:33 2017
@@ -21,6 +21,7 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 import javax.jcr.security.AccessControlManager;
 
 import com.google.common.base.Function;
@@ -77,6 +78,40 @@ public class CompositeAuthorizationConfi
 
     private static final Logger log = LoggerFactory.getLogger(CompositeAuthorizationConfiguration.class);
 
+    public enum CompositionType {
+
+        /**
+         * Break as soon as any one of the aggregated permission providers
+         * denies a privilege (default setup)
+         */
+        AND,
+
+        /**
+         * Check all aggregated permission providers for one that could provide
+         * a privilege (multiplexing setup)
+         */
+        OR;
+
+        /**
+         * Returns the corresponding composition type.
+         * @param type
+         *            String representation of the composition type, or
+         *            {@code null}
+         * @return corresponding composition type, or {@code AND} if the
+         *         provided type is {@code null}
+         */
+        public static CompositionType fromString(@Nullable String type) {
+            String or = OR.name();
+            if (or.equals(type) || or.toLowerCase().equals(type)) {
+                return OR;
+            } else {
+                return AND;
+            }
+        }
+    }
+
+    private CompositionType compositionType = CompositionType.AND;
+
     public CompositeAuthorizationConfiguration() {
         super(AuthorizationConfiguration.NAME);
     }
@@ -85,6 +120,10 @@ public class CompositeAuthorizationConfi
         super(AuthorizationConfiguration.NAME, securityProvider);
     }
 
+    public void withCompositionType(@Nullable String ct) {
+        this.compositionType = CompositionType.fromString(ct);
+    }
+
     @Nonnull
     @Override
     public AccessControlManager getAccessControlManager(@Nonnull final Root root,
@@ -134,7 +173,7 @@ public class CompositeAuthorizationConfi
             case 0: throw new IllegalStateException();
             case 1: return configurations.get(0).getPermissionProvider(root, workspaceName, principals);
             default:
-                List<AggregatedPermissionProvider> aggrPermissionProviders = new ArrayList(configurations.size());
+                List<AggregatedPermissionProvider> aggrPermissionProviders = new ArrayList<>(configurations.size());
                 for (AuthorizationConfiguration conf : configurations) {
                     PermissionProvider pProvider = conf.getPermissionProvider(root, workspaceName, principals);
                     if (pProvider instanceof AggregatedPermissionProvider) {
@@ -152,7 +191,7 @@ public class CompositeAuthorizationConfi
                         pp = aggrPermissionProviders.get(0);
                         break;
                     default :
-                        pp = new CompositePermissionProvider(root, aggrPermissionProviders, getContext());
+                        pp = new CompositePermissionProvider(root, aggrPermissionProviders, getContext(), compositionType);
                 }
                 return pp;
         }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositePermissionProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositePermissionProvider.java?rev=1800063&r1=1800062&r2=1800063&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositePermissionProvider.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositePermissionProvider.java Tue Jun 27 14:11:33 2017
@@ -16,8 +16,11 @@
  */
 package org.apache.jackrabbit.oak.security.authorization.composite;
 
+import static org.apache.jackrabbit.oak.security.authorization.composite.CompositeAuthorizationConfiguration.CompositionType.AND;
+
 import java.util.List;
 import java.util.Set;
+
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
@@ -28,6 +31,7 @@ import org.apache.jackrabbit.oak.plugins
 import org.apache.jackrabbit.oak.plugins.tree.TreeLocation;
 import org.apache.jackrabbit.oak.plugins.tree.TreeTypeProvider;
 import org.apache.jackrabbit.oak.plugins.tree.impl.ImmutableTree;
+import org.apache.jackrabbit.oak.security.authorization.composite.CompositeAuthorizationConfiguration.CompositionType;
 import org.apache.jackrabbit.oak.security.authorization.permission.PermissionUtil;
 import org.apache.jackrabbit.oak.spi.security.Context;
 import org.apache.jackrabbit.oak.spi.security.authorization.permission.AggregatedPermissionProvider;
@@ -50,6 +54,7 @@ class CompositePermissionProvider implem
     private final Root root;
     private final AggregatedPermissionProvider[] pps;
     private final Context ctx;
+    private final CompositionType compositionType;
 
     private final RepositoryPermission repositoryPermission;
 
@@ -57,12 +62,14 @@ class CompositePermissionProvider implem
     private PrivilegeBitsProvider privilegeBitsProvider;
     private TreeTypeProvider typeProvider;
 
-    CompositePermissionProvider(@Nonnull Root root, @Nonnull List<AggregatedPermissionProvider> pps, @Nonnull Context acContext) {
+    CompositePermissionProvider(@Nonnull Root root, @Nonnull List<AggregatedPermissionProvider> pps,
+            @Nonnull Context acContext, @Nonnull CompositionType compositionType) {
         this.root = root;
         this.pps = pps.toArray(new AggregatedPermissionProvider[pps.size()]);
         this.ctx = acContext;
+        this.compositionType = compositionType;
 
-        repositoryPermission = new CompositeRepositoryPermission();
+        repositoryPermission = new CompositeRepositoryPermission(this.pps, this.compositionType);
         immutableRoot = RootFactory.createReadOnlyRoot(root);
         privilegeBitsProvider = new PrivilegeBitsProvider(immutableRoot);
         typeProvider = new TreeTypeProvider(ctx);
@@ -122,11 +129,24 @@ class CompositePermissionProvider implem
             PrivilegeBits supported = aggregatedPermissionProvider.supportedPrivileges(immutableTree, privilegeBits);
             if (doEvaluate(supported)) {
                 Set<String> supportedNames = privilegeBitsProvider.getPrivilegeNames(supported);
-                hasPrivileges = aggregatedPermissionProvider.hasPrivileges(immutableTree, supportedNames.toArray(new String[supportedNames.size()]));
-                coveredPrivs.add(supported);
+                if (compositionType == AND) {
+                    hasPrivileges = aggregatedPermissionProvider.hasPrivileges(immutableTree,
+                            supportedNames.toArray(new String[supportedNames.size()]));
+                    if (!hasPrivileges) {
+                        return false;
+                    }
+                    coveredPrivs.add(supported);
 
-                if (!hasPrivileges) {
-                    break;
+                } else {
+                    // evaluate one by one so we can aggregate fragments of
+                    // supported privileges
+                    for (String p : supportedNames) {
+                        if (aggregatedPermissionProvider.hasPrivileges(immutableTree, p)) {
+                            PrivilegeBits granted = privilegeBitsProvider.getBits(p);
+                            coveredPrivs.add(granted);
+                            hasPrivileges = true;
+                        }
+                    }
                 }
             }
         }
@@ -144,7 +164,7 @@ class CompositePermissionProvider implem
     public TreePermission getTreePermission(@Nonnull Tree tree, @Nonnull TreePermission parentPermission) {
         ImmutableTree immutableTree = (ImmutableTree) PermissionUtil.getImmutableTree(tree, immutableRoot);
         if (tree.isRoot()) {
-            return CompositeTreePermission.create(immutableTree, typeProvider, pps);
+            return CompositeTreePermission.create(immutableTree, typeProvider, pps, compositionType);
         } else if (parentPermission instanceof CompositeTreePermission) {
             return CompositeTreePermission.create(immutableTree, ((CompositeTreePermission) parentPermission));
         } else {
@@ -158,15 +178,22 @@ class CompositePermissionProvider implem
 
         boolean isGranted = false;
         long coveredPermissions = Permissions.NO_PERMISSION;
-
         for (AggregatedPermissionProvider aggregatedPermissionProvider : pps) {
             long supportedPermissions = aggregatedPermissionProvider.supportedPermissions(immParent, property, permissions);
             if (doEvaluate(supportedPermissions)) {
-                isGranted = aggregatedPermissionProvider.isGranted(immParent, property, supportedPermissions);
-                coveredPermissions |= supportedPermissions;
-
-                if (!isGranted) {
-                    break;
+                if (compositionType == AND) {
+                    isGranted = aggregatedPermissionProvider.isGranted(immParent, property, supportedPermissions);
+                    if (!isGranted) {
+                        return false;
+                    }
+                    coveredPermissions |= supportedPermissions;
+                } else {
+                    for (long p : Permissions.aggregates(permissions)) {
+                        if (aggregatedPermissionProvider.isGranted(immParent, property, p)) {
+                            coveredPermissions |= p;
+                            isGranted = true;
+                        }
+                    }
                 }
             }
         }
@@ -192,11 +219,19 @@ class CompositePermissionProvider implem
             for (AggregatedPermissionProvider aggregatedPermissionProvider : pps) {
                 long supportedPermissions = aggregatedPermissionProvider.supportedPermissions(location, permissions);
                 if (doEvaluate(supportedPermissions)) {
-                    isGranted = aggregatedPermissionProvider.isGranted(location, supportedPermissions);
-                    coveredPermissions |= supportedPermissions;
-
-                    if (!isGranted) {
-                        break;
+                    if (compositionType == AND) {
+                        isGranted = aggregatedPermissionProvider.isGranted(location, supportedPermissions);
+                        if (!isGranted) {
+                            return false;
+                        }
+                        coveredPermissions |= supportedPermissions;
+                    } else {
+                        for (long p : Permissions.aggregates(permissions)) {
+                            if (aggregatedPermissionProvider.isGranted(location, p)) {
+                                coveredPermissions |= p;
+                                isGranted = true;
+                            }
+                        }
                     }
                 }
             }
@@ -218,7 +253,17 @@ class CompositePermissionProvider implem
     /**
      * {@code RepositoryPermission} implementation that wraps multiple implementations.
      */
-    private final class CompositeRepositoryPermission implements RepositoryPermission {
+    private final static class CompositeRepositoryPermission implements RepositoryPermission {
+
+        private final AggregatedPermissionProvider[] pps;
+
+        private final CompositionType compositionType;
+
+        public CompositeRepositoryPermission(@Nonnull AggregatedPermissionProvider[] pps,
+                @Nonnull CompositionType compositionType) {
+            this.pps = pps;
+            this.compositionType = compositionType;
+        }
 
         @Override
         public boolean isGranted(long repositoryPermissions) {
@@ -228,10 +273,20 @@ class CompositePermissionProvider implem
             for (AggregatedPermissionProvider aggregatedPermissionProvider : pps) {
                 long supportedPermissions = aggregatedPermissionProvider.supportedPermissions((Tree) null, null, repositoryPermissions);
                 if (doEvaluate(supportedPermissions)) {
-                    isGranted = aggregatedPermissionProvider.getRepositoryPermission().isGranted(supportedPermissions);
-                    coveredPermissions |= supportedPermissions;
-                    if (!isGranted) {
-                        break;
+                    RepositoryPermission rp = aggregatedPermissionProvider.getRepositoryPermission();
+                    if (compositionType == AND) {
+                        isGranted = rp.isGranted(supportedPermissions);
+                        if (!isGranted) {
+                            return false;
+                        }
+                        coveredPermissions |= supportedPermissions;
+                    } else {
+                        for (long p : Permissions.aggregates(repositoryPermissions)) {
+                            if (rp.isGranted(p)) {
+                                coveredPermissions |= p;
+                                isGranted = true;
+                            }
+                        }
                     }
                 }
             }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeTreePermission.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeTreePermission.java?rev=1800063&r1=1800062&r2=1800063&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeTreePermission.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeTreePermission.java Tue Jun 27 14:11:33 2017
@@ -16,6 +16,9 @@
  */
 package org.apache.jackrabbit.oak.security.authorization.composite;
 
+import static org.apache.jackrabbit.oak.security.authorization.composite.CompositeAuthorizationConfiguration.CompositionType.AND;
+import static org.apache.jackrabbit.oak.security.authorization.composite.CompositeAuthorizationConfiguration.CompositionType.OR;
+
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
@@ -24,6 +27,7 @@ import org.apache.jackrabbit.oak.api.Tre
 import org.apache.jackrabbit.oak.plugins.tree.TreeType;
 import org.apache.jackrabbit.oak.plugins.tree.TreeTypeProvider;
 import org.apache.jackrabbit.oak.plugins.tree.impl.ImmutableTree;
+import org.apache.jackrabbit.oak.security.authorization.composite.CompositeAuthorizationConfiguration.CompositionType;
 import org.apache.jackrabbit.oak.spi.security.authorization.permission.AggregatedPermissionProvider;
 import org.apache.jackrabbit.oak.spi.security.authorization.permission.Permissions;
 import org.apache.jackrabbit.oak.spi.security.authorization.permission.TreePermission;
@@ -37,6 +41,7 @@ final class CompositeTreePermission impl
 
     private final ImmutableTree tree;
     private final TreeType type;
+    private final CompositionType compositionType;
 
     private final TreeTypeProvider typeProvider;
     private final AggregatedPermissionProvider[] providers;
@@ -46,7 +51,9 @@ final class CompositeTreePermission impl
     private Boolean canRead;
     private Boolean canReadProperties;
 
-    private CompositeTreePermission(@Nonnull ImmutableTree tree, @Nonnull TreeType type, @Nonnull TreeTypeProvider typeProvider, @Nonnull AggregatedPermissionProvider[] providers, @Nonnull TreePermission[] treePermissions, int cnt) {
+    private CompositeTreePermission(@Nonnull ImmutableTree tree, @Nonnull TreeType type,
+            @Nonnull TreeTypeProvider typeProvider, @Nonnull AggregatedPermissionProvider[] providers,
+            @Nonnull TreePermission[] treePermissions, int cnt, @Nonnull CompositionType compositionType) {
         this.tree = tree;
         this.type = type;
 
@@ -54,9 +61,11 @@ final class CompositeTreePermission impl
         this.providers = providers;
         this.treePermissions = treePermissions;
         this.childSize = providers.length - cnt;
+        this.compositionType = compositionType;
     }
 
-    static TreePermission create(@Nonnull ImmutableTree rootTree, @Nonnull TreeTypeProvider typeProvider, @Nonnull AggregatedPermissionProvider[] providers) {
+    static TreePermission create(@Nonnull ImmutableTree rootTree, @Nonnull TreeTypeProvider typeProvider,
+            @Nonnull AggregatedPermissionProvider[] providers, @Nonnull CompositionType compositionType) {
         switch (providers.length) {
             case 0 : return TreePermission.EMPTY;
             case 1 : return providers[0].getTreePermission(rootTree, TreeType.DEFAULT, TreePermission.EMPTY);
@@ -70,7 +79,8 @@ final class CompositeTreePermission impl
                     }
                     treePermissions[i] = tp;
                 }
-                return new CompositeTreePermission(rootTree, TreeType.DEFAULT, typeProvider, providers, treePermissions, cnt);
+            return new CompositeTreePermission(rootTree, TreeType.DEFAULT, typeProvider, providers, treePermissions,
+                    cnt, compositionType);
         }
     }
 
@@ -115,7 +125,8 @@ final class CompositeTreePermission impl
                         j++;
                     }
                 }
-                return new CompositeTreePermission(tree, type, parentPermission.typeProvider, pvds, tps, cnt);
+            return new CompositeTreePermission(tree, type, parentPermission.typeProvider, pvds, tps, cnt,
+                    parentPermission.compositionType);
         }
     }
 
@@ -158,7 +169,10 @@ final class CompositeTreePermission impl
                 long supported = providers[i].supportedPermissions(tp, null, Permissions.READ_PROPERTY);
                 if (doEvaluate(supported)) {
                     readable = tp.canReadProperties();
-                    if (!readable) {
+                    if (!readable && compositionType == AND) {
+                        break;
+                    }
+                    if (readable && compositionType == OR) {
                         break;
                     }
                 }
@@ -188,11 +202,20 @@ final class CompositeTreePermission impl
             TreePermission tp = treePermissions[i];
             long supported = providers[i].supportedPermissions(tp, property, permissions);
             if (doEvaluate(supported)) {
-                isGranted = (property == null) ? tp.isGranted(supported) : tp.isGranted(supported, property);
-                coveredPermissions |= supported;
-
-                if (!isGranted) {
-                    return false;
+                if (compositionType == AND) {
+                    isGranted = (property == null) ? tp.isGranted(supported) : tp.isGranted(supported, property);
+                    if (!isGranted) {
+                        return false;
+                    }
+                    coveredPermissions |= supported;
+                } else {
+                    for (long p : Permissions.aggregates(permissions)) {
+                        boolean aGrant = (property == null) ? tp.isGranted(p) : tp.isGranted(p, property);
+                        if (aGrant) {
+                            coveredPermissions |= p;
+                            isGranted = true;
+                        }
+                    }
                 }
             }
         }
@@ -209,8 +232,11 @@ final class CompositeTreePermission impl
             long supported = providers[i].supportedPermissions(tp, property, (property == null) ? Permissions.READ_NODE : Permissions.READ_PROPERTY);
             if (doEvaluate(supported)) {
                 readable = (property == null) ? tp.canRead() : tp.canRead(property);
-                if (!readable) {
-                    break;
+                if (!readable && compositionType == AND) {
+                    return false;
+                }
+                if (readable && compositionType == OR) {
+                    return true;
                 }
             }
         }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/internal/SecurityProviderRegistration.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/internal/SecurityProviderRegistration.java?rev=1800063&r1=1800062&r2=1800063&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/internal/SecurityProviderRegistration.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/internal/SecurityProviderRegistration.java Tue Jun 27 14:11:33 2017
@@ -28,6 +28,7 @@ import org.apache.felix.scr.annotations.
 import org.apache.felix.scr.annotations.Modified;
 import org.apache.felix.scr.annotations.Properties;
 import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.PropertyOption;
 import org.apache.felix.scr.annotations.PropertyUnbounded;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
@@ -94,6 +95,17 @@ import static com.google.common.collect.
                         "org.apache.jackrabbit.oak.security.user.UserAuthenticationFactoryImpl"
                 },
                 unbounded = PropertyUnbounded.ARRAY
+        ),
+        @Property(
+                name = "authorizationCompositionType",
+                label = "Authorization Composition Type",
+                description = "The Composite Authorization model uses this flag to determine what type of logic "
+                        + "to apply to the existing providers (default value is AND).",
+                value = "AND",
+                options = {
+                        @PropertyOption(name = "AND", value = "AND"),
+                        @PropertyOption(name = "OR", value = "OR")
+                }
         )
 })
 @References({
@@ -184,6 +196,7 @@ public class SecurityProviderRegistratio
 
             this.context = context;
         }
+        this.authorizationConfiguration.withCompositionType(getAuthorizationCompositionType(configuration));
 
         maybeRegister();
     }
@@ -199,6 +212,7 @@ public class SecurityProviderRegistratio
                 preconditions.addPrecondition(pid);
             }
         }
+        this.authorizationConfiguration.withCompositionType(getAuthorizationCompositionType(configuration));
 
         maybeUnregister();
         maybeRegister();
@@ -572,12 +586,16 @@ public class SecurityProviderRegistratio
         preconditions.removeCandidate(pid);
     }
 
-    private String getServicePid(Map<String, Object> properties) {
+    private static String getServicePid(Map<String, Object> properties) {
         return PropertiesUtil.toString(properties.get(Constants.SERVICE_PID), null);
     }
 
-    private String[] getRequiredServicePids(Map<String, Object> configuration) {
+    private static String[] getRequiredServicePids(Map<String, Object> configuration) {
         return PropertiesUtil.toStringArray(configuration.get("requiredServicePids"), new String[]{});
     }
 
+    @Nonnull
+    private static String getAuthorizationCompositionType(Map<String, Object> properties) {
+        return PropertiesUtil.toString(properties.get("authorizationCompositionType"), "AND");
+    }
 }

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/AbstractCompositeProviderTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/AbstractCompositeProviderTest.java?rev=1800063&r1=1800062&r2=1800063&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/AbstractCompositeProviderTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/AbstractCompositeProviderTest.java Tue Jun 27 14:11:33 2017
@@ -42,6 +42,7 @@ import org.apache.jackrabbit.oak.plugins
 import org.apache.jackrabbit.oak.plugins.tree.RootFactory;
 import org.apache.jackrabbit.oak.plugins.tree.TreeUtil;
 import org.apache.jackrabbit.oak.plugins.tree.impl.ImmutableTree;
+import org.apache.jackrabbit.oak.security.authorization.composite.CompositeAuthorizationConfiguration.CompositionType;
 import org.apache.jackrabbit.oak.spi.security.authorization.AuthorizationConfiguration;
 import org.apache.jackrabbit.oak.spi.security.authorization.permission.AggregatedPermissionProvider;
 import org.apache.jackrabbit.oak.spi.security.authorization.permission.PermissionProvider;
@@ -220,7 +221,7 @@ public abstract class AbstractCompositeP
     }
 
     static void assertCompositeTreePermission(@Nonnull TreePermission tp) {
-        assertTrue(tp instanceof CompositeTreePermission);
+        assertTrue(tp.getClass()+ "", tp instanceof CompositeTreePermission);
     }
 
     static void assertCompositeTreePermission(boolean expected, @Nonnull TreePermission tp) {
@@ -246,7 +247,6 @@ public abstract class AbstractCompositeP
         }
     }
 
-
     CompositePermissionProvider createPermissionProvider(Principal... principals) {
         return createPermissionProvider(ImmutableSet.copyOf(principals));
     }
@@ -254,13 +254,25 @@ public abstract class AbstractCompositeP
     CompositePermissionProvider createPermissionProvider(Set<Principal> principals) {
         String workspaceName = root.getContentSession().getWorkspaceName();
         AuthorizationConfiguration config = getConfig(AuthorizationConfiguration.class);
-        return new CompositePermissionProvider(root, getAggregatedProviders(workspaceName, config, principals), config.getContext());
+        return new CompositePermissionProvider(root, getAggregatedProviders(workspaceName, config, principals),
+                config.getContext(), CompositionType.AND);
+    }
+
+    CompositePermissionProvider createPermissionProviderOR(Principal... principals) {
+        return createPermissionProviderOR(ImmutableSet.copyOf(principals));
+    }
+
+    CompositePermissionProvider createPermissionProviderOR(Set<Principal> principals) {
+        String workspaceName = root.getContentSession().getWorkspaceName();
+        AuthorizationConfiguration config = getConfig(AuthorizationConfiguration.class);
+        return new CompositePermissionProvider(root, getAggregatedProviders(workspaceName, config, principals),
+                config.getContext(), CompositionType.OR);
     }
 
     @Test
     public void testRefresh() throws Exception {
-        CompositePermissionProvider pp = createPermissionProvider();
-        pp.refresh();
+         createPermissionProvider().refresh();
+         createPermissionProviderOR().refresh();
     }
 
     @Test
@@ -268,7 +280,15 @@ public abstract class AbstractCompositeP
         PermissionProvider pp = createPermissionProvider();
         for (String p : NODE_PATHS) {
             Tree tree = readOnlyRoot.getTree(p);
+            assertFalse(p, pp.hasPrivileges(tree, JCR_ALL));
+        }
+    }
 
+    @Test
+    public void testHasPrivilegesJcrAllOR() throws Exception {
+        PermissionProvider pp = createPermissionProviderOR();
+        for (String p : NODE_PATHS) {
+            Tree tree = readOnlyRoot.getTree(p);
             assertFalse(p, pp.hasPrivileges(tree, JCR_ALL));
         }
     }
@@ -276,10 +296,11 @@ public abstract class AbstractCompositeP
     @Test
     public void testHasPrivilegesNone() throws Exception {
         PermissionProvider pp = createPermissionProvider();
+        PermissionProvider ppo = createPermissionProviderOR();
         for (String p : NODE_PATHS) {
             Tree tree = readOnlyRoot.getTree(p);
-
             assertTrue(p, pp.hasPrivileges(tree));
+            assertTrue(p, ppo.hasPrivileges(tree));
         }
     }
 
@@ -287,17 +308,22 @@ public abstract class AbstractCompositeP
     public void testHasPrivilegesOnRepoJcrAll() throws Exception {
         PermissionProvider pp = createPermissionProvider();
         assertFalse(pp.hasPrivileges(null, JCR_ALL));
+        PermissionProvider ppo = createPermissionProviderOR();
+        assertFalse(ppo.hasPrivileges(null, JCR_ALL));
     }
 
     @Test
     public void testHasPrivilegesOnRepoNone() throws Exception {
         PermissionProvider pp = createPermissionProvider();
         assertTrue(pp.hasPrivileges(null));
+        PermissionProvider ppo = createPermissionProviderOR();
+        assertTrue(ppo.hasPrivileges(null));
     }
 
     @Test
     public void testIsGrantedAll() throws Exception {
         PermissionProvider pp = createPermissionProvider();
+        PermissionProvider ppo = createPermissionProviderOR();
 
         for (String p : NODE_PATHS) {
             Tree tree = readOnlyRoot.getTree(p);
@@ -305,12 +331,15 @@ public abstract class AbstractCompositeP
 
             assertFalse(p, pp.isGranted(tree, null, Permissions.ALL));
             assertFalse(PathUtils.concat(p, JcrConstants.JCR_PRIMARYTYPE), pp.isGranted(tree, ps, Permissions.ALL));
+            assertFalse(p, ppo.isGranted(tree, null, Permissions.ALL));
+            assertFalse(PathUtils.concat(p, JcrConstants.JCR_PRIMARYTYPE), ppo.isGranted(tree, ps, Permissions.ALL));
         }
     }
 
     @Test
     public void testIsGrantedNone() throws Exception {
         PermissionProvider pp = createPermissionProvider();
+        PermissionProvider ppo = createPermissionProviderOR();
 
         for (String p : NODE_PATHS) {
             Tree tree = readOnlyRoot.getTree(p);
@@ -318,12 +347,15 @@ public abstract class AbstractCompositeP
 
             assertFalse(p, pp.isGranted(tree, null, Permissions.NO_PERMISSION));
             assertFalse(PathUtils.concat(p, JcrConstants.JCR_PRIMARYTYPE), pp.isGranted(tree, ps, Permissions.NO_PERMISSION));
+            assertFalse(p, ppo.isGranted(tree, null, Permissions.NO_PERMISSION));
+            assertFalse(PathUtils.concat(p, JcrConstants.JCR_PRIMARYTYPE), ppo.isGranted(tree, ps, Permissions.NO_PERMISSION));
         }
     }
 
     @Test
     public void testIsNotGranted() throws Exception {
         PermissionProvider pp = createPermissionProvider();
+        PermissionProvider ppo = createPermissionProviderOR();
 
         for (String p : NODE_PATHS) {
             Tree tree = readOnlyRoot.getTree(p);
@@ -331,39 +363,49 @@ public abstract class AbstractCompositeP
 
             assertFalse(p, pp.isGranted(tree, null, Permissions.MODIFY_ACCESS_CONTROL));
             assertFalse(PathUtils.concat(p, JcrConstants.JCR_PRIMARYTYPE), pp.isGranted(tree, ps, Permissions.MODIFY_ACCESS_CONTROL));
+            assertFalse(p, ppo.isGranted(tree, null, Permissions.MODIFY_ACCESS_CONTROL));
+            assertFalse(PathUtils.concat(p, JcrConstants.JCR_PRIMARYTYPE), ppo.isGranted(tree, ps, Permissions.MODIFY_ACCESS_CONTROL));
         }
     }
 
     @Test
     public void testIsGrantedActionNone() throws Exception {
         PermissionProvider pp = createPermissionProvider();
+        PermissionProvider ppo = createPermissionProviderOR();
         String actions = "";
 
         for (String nodePath : NODE_PATHS) {
             assertFalse(nodePath, pp.isGranted(nodePath, actions));
+            assertFalse(nodePath, ppo.isGranted(nodePath, actions));
 
             String propPath = PathUtils.concat(nodePath, JcrConstants.JCR_PRIMARYTYPE);
             assertFalse(propPath, pp.isGranted(propPath, actions));
+            assertFalse(propPath, ppo.isGranted(propPath, actions));
 
             String nonExPath = PathUtils.concat(nodePath, "nonExisting");
             assertFalse(nonExPath, pp.isGranted(nonExPath, actions));
+            assertFalse(nonExPath, ppo.isGranted(nonExPath, actions));
         }
     }
 
     @Test
     public void testIsNotGrantedAction() throws Exception {
         PermissionProvider pp = createPermissionProvider();
+        PermissionProvider ppo = createPermissionProviderOR();
         String[] actions = new String[]{JackrabbitSession.ACTION_LOCKING, JackrabbitSession.ACTION_MODIFY_ACCESS_CONTROL};
 
         for (String nodePath : NODE_PATHS) {
             String actionStr = getActionString(actions);
             assertFalse(nodePath, pp.isGranted(nodePath, actionStr));
+            assertFalse(nodePath, ppo.isGranted(nodePath, actionStr));
 
             String propPath = PathUtils.concat(nodePath, JcrConstants.JCR_PRIMARYTYPE);
             assertFalse(propPath, pp.isGranted(propPath, actionStr));
+            assertFalse(propPath, ppo.isGranted(propPath, actionStr));
 
             String nonExPath = PathUtils.concat(nodePath, "nonExisting");
             assertFalse(nonExPath, pp.isGranted(nonExPath, actionStr));
+            assertFalse(nonExPath, ppo.isGranted(nonExPath, actionStr));
         }
     }
 
@@ -371,12 +413,16 @@ public abstract class AbstractCompositeP
     public void testGetTreePermissionAllParent() throws Exception {
         TreePermission tp = createPermissionProvider().getTreePermission(readOnlyRoot.getTree(TEST_PATH), TreePermission.ALL);
         assertSame(TreePermission.ALL, tp);
+        TreePermission tpo = createPermissionProviderOR().getTreePermission(readOnlyRoot.getTree(TEST_PATH), TreePermission.ALL);
+        assertSame(TreePermission.ALL, tpo);
     }
 
     @Test
     public void testGetTreePermissionEmptyParent() throws Exception {
         TreePermission tp = createPermissionProvider().getTreePermission(readOnlyRoot.getTree(TEST_PATH), TreePermission.EMPTY);
         assertSame(TreePermission.EMPTY, tp);
+        TreePermission tpo = createPermissionProviderOR().getTreePermission(readOnlyRoot.getTree(TEST_PATH), TreePermission.EMPTY);
+        assertSame(TreePermission.EMPTY, tpo);
     }
 
     @Test
@@ -398,6 +444,24 @@ public abstract class AbstractCompositeP
     }
 
     @Test
+    public void testTreePermissionIsGrantedAllOR() throws Exception {
+        PermissionProvider pp = createPermissionProviderOR();
+        TreePermission parentPermission = TreePermission.EMPTY;
+
+        PropertyState ps = PropertyStates.createProperty("propName", "val");
+
+        for (String path : TP_PATHS) {
+            Tree t = readOnlyRoot.getTree(path);
+            TreePermission tp = pp.getTreePermission(t, parentPermission);
+
+            assertFalse(tp.isGranted(Permissions.ALL));
+            assertFalse(tp.isGranted(Permissions.ALL, ps));
+
+            parentPermission = tp;
+        }
+    }
+
+    @Test
     public void testTreePermissionIsNotGranted() throws Exception {
         PermissionProvider pp = createPermissionProvider();
         TreePermission parentPermission = TreePermission.EMPTY;
@@ -418,15 +482,40 @@ public abstract class AbstractCompositeP
     }
 
     @Test
+    public void testTreePermissionIsNotGrantedOR() throws Exception {
+        PermissionProvider pp = createPermissionProviderOR();
+        TreePermission parentPermission = TreePermission.EMPTY;
+
+        PropertyState ps = PropertyStates.createProperty("propName", "val");
+
+        for (String path : TP_PATHS) {
+            Tree t = readOnlyRoot.getTree(path);
+            TreePermission tp = pp.getTreePermission(t, parentPermission);
+
+            assertFalse(tp.isGranted(Permissions.NO_PERMISSION));
+            assertFalse(tp.isGranted(Permissions.MODIFY_ACCESS_CONTROL));
+            assertFalse(tp.isGranted(Permissions.NO_PERMISSION, ps));
+            assertFalse(tp.isGranted(Permissions.MODIFY_ACCESS_CONTROL, ps));
+
+            parentPermission = tp;
+        }
+    }
+
+    @Test
     public void testTreePermissionCanReadAll() throws Exception {
         PermissionProvider pp = createPermissionProvider();
         TreePermission parentPermission = TreePermission.EMPTY;
+        PermissionProvider ppO = createPermissionProviderOR();
+        TreePermission parentPermissionO = TreePermission.EMPTY;
 
         for (String path : TP_PATHS) {
             TreePermission tp = pp.getTreePermission(readOnlyRoot.getTree(path), parentPermission);
             assertFalse(tp.canReadAll());
-
             parentPermission = tp;
+
+            TreePermission tpO = ppO.getTreePermission(readOnlyRoot.getTree(path), parentPermissionO);
+            assertFalse(tpO.canReadAll());
+            parentPermissionO = tpO;
         }
     }
 
@@ -444,6 +533,19 @@ public abstract class AbstractCompositeP
     }
 
     @Test
+    public void testTreePermissionCanReadPropertiesOR() throws Exception {
+        PermissionProvider pp = createPermissionProviderOR();
+        TreePermission parentPermission = TreePermission.EMPTY;
+
+        for (String path : TP_PATHS) {
+            TreePermission tp = pp.getTreePermission(readOnlyRoot.getTree(path), parentPermission);
+            assertFalse(tp.canReadProperties());
+
+            parentPermission = tp;
+        }
+    }
+
+    @Test
     public void testGetTreePermissionInstance() throws Exception {
         PermissionProvider pp = createPermissionProvider();
         TreePermission parentPermission = TreePermission.EMPTY;
@@ -456,6 +558,18 @@ public abstract class AbstractCompositeP
     }
 
     @Test
+    public void testGetTreePermissionInstanceOR() throws Exception {
+        PermissionProvider pp = createPermissionProviderOR();
+        TreePermission parentPermission = TreePermission.EMPTY;
+
+        for (String path : TP_PATHS) {
+            TreePermission tp = pp.getTreePermission(readOnlyRoot.getTree(path), parentPermission);
+            assertCompositeTreePermission(tp);
+            parentPermission = tp;
+        }
+    }
+
+    @Test
     public void testTreePermissionGetChild() throws Exception {
         List<String> childNames = ImmutableList.of("test", "a", "b", "c", "nonexisting");
 
@@ -471,9 +585,26 @@ public abstract class AbstractCompositeP
     }
 
     @Test
+    public void testTreePermissionGetChildOR() throws Exception {
+        List<String> childNames = ImmutableList.of("test", "a", "b", "c", "nonexisting");
+
+        Tree rootTree = readOnlyRoot.getTree(ROOT_PATH);
+        NodeState ns = ((ImmutableTree) rootTree).getNodeState();
+        TreePermission tp = createPermissionProviderOR().getTreePermission(rootTree, TreePermission.EMPTY);
+
+        for (String cName : childNames) {
+            ns = ns.getChildNode(cName);
+            tp = tp.getChildPermission(cName, ns);
+            assertCompositeTreePermission(tp);
+        }
+    }
+
+    @Test
     public void testGetRepositoryPermissionInstance() throws Exception {
         RepositoryPermission rp = createPermissionProvider().getRepositoryPermission();
         assertTrue(rp.getClass().getName().endsWith("CompositeRepositoryPermission"));
+        RepositoryPermission rpO = createPermissionProviderOR().getRepositoryPermission();
+        assertTrue(rpO.getClass().getName().endsWith("CompositeRepositoryPermission"));
     }
 
     @Test
@@ -482,6 +613,16 @@ public abstract class AbstractCompositeP
         assertFalse(rp.isGranted(Permissions.PRIVILEGE_MANAGEMENT));
         assertFalse(rp.isGranted(Permissions.NAMESPACE_MANAGEMENT|Permissions.PRIVILEGE_MANAGEMENT));
         assertFalse(rp.isGranted(Permissions.WORKSPACE_MANAGEMENT));
+        assertFalse(rp.isGranted(Permissions.ALL));
+        assertFalse(rp.isGranted(Permissions.NO_PERMISSION));
+    }
+
+    @Test
+    public void testRepositoryPermissionIsNotGrantedOR() throws Exception {
+        RepositoryPermission rp = createPermissionProviderOR().getRepositoryPermission();
+        assertFalse(rp.isGranted(Permissions.PRIVILEGE_MANAGEMENT));
+        assertFalse(rp.isGranted(Permissions.NAMESPACE_MANAGEMENT|Permissions.PRIVILEGE_MANAGEMENT));
+        assertFalse(rp.isGranted(Permissions.WORKSPACE_MANAGEMENT));
         assertFalse(rp.isGranted(Permissions.ALL));
         assertFalse(rp.isGranted(Permissions.NO_PERMISSION));
     }

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderAllTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderAllTest.java?rev=1800063&r1=1800062&r2=1800063&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderAllTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderAllTest.java Tue Jun 27 14:11:33 2017
@@ -24,11 +24,14 @@ import javax.jcr.Session;
 
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
+
+import org.apache.jackrabbit.JcrConstants;
 import org.apache.jackrabbit.api.JackrabbitSession;
 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.commons.PathUtils;
+import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
 import org.apache.jackrabbit.oak.plugins.tree.TreeLocation;
 import org.apache.jackrabbit.oak.spi.security.authorization.permission.AggregatedPermissionProvider;
 import org.apache.jackrabbit.oak.spi.security.authorization.permission.OpenPermissionProvider;
@@ -44,7 +47,7 @@ import static org.junit.Assert.assertFal
 import static org.junit.Assert.assertTrue;
 
 /**
- * Test the effect of the combination of
+ * Test the effect of the 'AND' combination of
  *
  * - default permission provider (which a limited permission setup)
  * - custom provider that always grants full access and supports all permissions.
@@ -53,17 +56,28 @@ import static org.junit.Assert.assertTru
  *
  * The expected result is only the subset of permissions granted by the default
  * provider. The test user must never have full access anywhere.
+ * <p>
+ * Test the effect of the 'OR'ed combination of
+ *
+ * - default permission provider (which a limited permission setup)
+ * - custom provider that always grants full access and supports all permissions.
+ *
+ * for the {@link #getTestUser()}.
+ *
+ * The expected result is the test user will have full access anywhere.
+
  */
 public class CompositeProviderAllTest extends AbstractCompositeProviderTest {
 
     private CompositePermissionProvider cpp;
-
+    private CompositePermissionProvider cppO;
 
     @Override
     public void before() throws Exception {
         super.before();
 
         cpp = createPermissionProvider(getTestUser().getPrincipal(), EveryonePrincipal.getInstance());
+        cppO = createPermissionProviderOR(getTestUser().getPrincipal(), EveryonePrincipal.getInstance());
     }
 
     @Override
@@ -71,13 +85,153 @@ public class CompositeProviderAllTest ex
         return new OpenAggregateProvider(root);
     }
 
+    @Override
+    @Test
+    public void testHasPrivilegesJcrAllOR() throws Exception {
+        PermissionProvider pp = createPermissionProviderOR();
+        for (String p : NODE_PATHS) {
+            Tree tree = readOnlyRoot.getTree(p);
+            assertTrue(p, pp.hasPrivileges(tree, JCR_ALL));
+        }
+    }
+
+    @Override
+    @Test
+    public void testIsGrantedAll() throws Exception {
+        PermissionProvider pp = createPermissionProvider();
+        PermissionProvider ppo = createPermissionProviderOR();
+
+        for (String p : NODE_PATHS) {
+            Tree tree = readOnlyRoot.getTree(p);
+            PropertyState ps = tree.getProperty(JcrConstants.JCR_PRIMARYTYPE);
+
+            assertFalse(p, pp.isGranted(tree, null, Permissions.ALL));
+            assertFalse(PathUtils.concat(p, JcrConstants.JCR_PRIMARYTYPE), pp.isGranted(tree, ps, Permissions.ALL));
+            assertTrue(p, ppo.isGranted(tree, null, Permissions.ALL));
+            assertTrue(PathUtils.concat(p, JcrConstants.JCR_PRIMARYTYPE), ppo.isGranted(tree, ps, Permissions.ALL));
+        }
+    }
+
+    @Override
+    @Test
+    public void testHasPrivilegesOnRepoJcrAll() throws Exception {
+        PermissionProvider pp = createPermissionProvider();
+        assertFalse(pp.hasPrivileges(null, JCR_ALL));
+        PermissionProvider ppo = createPermissionProviderOR();
+        assertTrue(ppo.hasPrivileges(null, JCR_ALL));
+    }
+
+    @Override
+    @Test
+    public void testIsNotGranted() throws Exception {
+        PermissionProvider pp = createPermissionProvider();
+        PermissionProvider ppo = createPermissionProviderOR();
+
+        for (String p : NODE_PATHS) {
+            Tree tree = readOnlyRoot.getTree(p);
+            PropertyState ps = tree.getProperty(JcrConstants.JCR_PRIMARYTYPE);
+
+            assertFalse(p, pp.isGranted(tree, null, Permissions.MODIFY_ACCESS_CONTROL));
+            assertFalse(PathUtils.concat(p, JcrConstants.JCR_PRIMARYTYPE), pp.isGranted(tree, ps, Permissions.MODIFY_ACCESS_CONTROL));
+            assertTrue(p, ppo.isGranted(tree, null, Permissions.MODIFY_ACCESS_CONTROL));
+            assertTrue(PathUtils.concat(p, JcrConstants.JCR_PRIMARYTYPE), ppo.isGranted(tree, ps, Permissions.MODIFY_ACCESS_CONTROL));
+        }
+    }
+
+    @Override
+    @Test
+    public void testIsNotGrantedAction() throws Exception {
+        PermissionProvider pp = createPermissionProvider();
+        PermissionProvider ppo = createPermissionProviderOR();
+        String[] actions = new String[]{JackrabbitSession.ACTION_LOCKING, JackrabbitSession.ACTION_MODIFY_ACCESS_CONTROL};
+
+        for (String nodePath : NODE_PATHS) {
+            String actionStr = getActionString(actions);
+            assertFalse(nodePath, pp.isGranted(nodePath, actionStr));
+            assertTrue(nodePath, ppo.isGranted(nodePath, actionStr));
+
+            String propPath = PathUtils.concat(nodePath, JcrConstants.JCR_PRIMARYTYPE);
+            assertFalse(propPath, pp.isGranted(propPath, actionStr));
+            assertTrue(propPath, ppo.isGranted(propPath, actionStr));
+
+            String nonExPath = PathUtils.concat(nodePath, "nonExisting");
+            assertFalse(nonExPath, pp.isGranted(nonExPath, actionStr));
+            assertTrue(nonExPath, ppo.isGranted(nonExPath, actionStr));
+        }
+    }
+
+    @Override
+    @Test
+    public void testTreePermissionIsGrantedAllOR() throws Exception {
+        PermissionProvider pp = createPermissionProviderOR();
+        TreePermission parentPermission = TreePermission.EMPTY;
+
+        PropertyState ps = PropertyStates.createProperty("propName", "val");
+
+        for (String path : TP_PATHS) {
+            Tree t = readOnlyRoot.getTree(path);
+            TreePermission tp = pp.getTreePermission(t, parentPermission);
+
+            assertTrue(tp.isGranted(Permissions.ALL));
+            assertTrue(tp.isGranted(Permissions.ALL, ps));
+
+            parentPermission = tp;
+        }
+    }
+
+    @Override
+    @Test
+    public void testTreePermissionIsNotGrantedOR() throws Exception {
+        PermissionProvider pp = createPermissionProviderOR();
+        TreePermission parentPermission = TreePermission.EMPTY;
+
+        PropertyState ps = PropertyStates.createProperty("propName", "val");
+
+        for (String path : TP_PATHS) {
+            Tree t = readOnlyRoot.getTree(path);
+            TreePermission tp = pp.getTreePermission(t, parentPermission);
+
+            assertFalse(tp.isGranted(Permissions.NO_PERMISSION));
+            assertTrue(tp.isGranted(Permissions.MODIFY_ACCESS_CONTROL));
+            assertFalse(tp.isGranted(Permissions.NO_PERMISSION, ps));
+            assertTrue(tp.isGranted(Permissions.MODIFY_ACCESS_CONTROL, ps));
+
+            parentPermission = tp;
+        }
+    }
+
+    @Override
+    @Test
+    public void testTreePermissionCanReadPropertiesOR() throws Exception {
+        PermissionProvider pp = createPermissionProviderOR();
+        TreePermission parentPermission = TreePermission.EMPTY;
+
+        for (String path : TP_PATHS) {
+            TreePermission tp = pp.getTreePermission(readOnlyRoot.getTree(path), parentPermission);
+            assertTrue(tp.canReadProperties());
+
+            parentPermission = tp;
+        }
+    }
+
+    @Override
+    @Test
+    public void testRepositoryPermissionIsNotGrantedOR() throws Exception {
+        RepositoryPermission rp = createPermissionProviderOR().getRepositoryPermission();
+        assertTrue(rp.isGranted(Permissions.PRIVILEGE_MANAGEMENT));
+        assertTrue(rp.isGranted(Permissions.NAMESPACE_MANAGEMENT | Permissions.PRIVILEGE_MANAGEMENT));
+        assertTrue(rp.isGranted(Permissions.WORKSPACE_MANAGEMENT));
+        assertTrue(rp.isGranted(Permissions.ALL));
+        assertFalse(rp.isGranted(Permissions.NO_PERMISSION));
+    }
+
     @Test
     public void testGetPrivileges() throws Exception {
         for (String p : defPrivileges.keySet()) {
             Set<String> expected = defPrivileges.get(p);
             Tree tree = root.getTree(p);
-
             assertEquals(p, expected, cpp.getPrivileges(tree));
+            assertEquals(p, expected, cppO.getPrivileges(tree));
         }
     }
 
@@ -85,6 +239,8 @@ public class CompositeProviderAllTest ex
     public void testGetPrivilegesOnRepo() throws Exception {
         Set<String> privilegeNames = cpp.getPrivileges(null);
         assertEquals(ImmutableSet.of(JCR_NAMESPACE_MANAGEMENT, JCR_NODE_TYPE_DEFINITION_MANAGEMENT), privilegeNames);
+        Set<String> privilegeNamesO = cppO.getPrivileges(null);
+        assertEquals(ImmutableSet.of(JCR_NAMESPACE_MANAGEMENT, JCR_NODE_TYPE_DEFINITION_MANAGEMENT), privilegeNamesO);
     }
 
 
@@ -93,18 +249,17 @@ public class CompositeProviderAllTest ex
         for (String p : defPrivileges.keySet()) {
             Set<String> expected = defPrivileges.get(p);
             Tree tree = root.getTree(p);
-
             assertTrue(p, cpp.hasPrivileges(tree, expected.toArray(new String[expected.size()])));
+            assertTrue(p, cppO.hasPrivileges(tree, expected.toArray(new String[expected.size()])));
         }
     }
 
-
     @Test
     public void testHasPrivilegesOnRepo() throws Exception {
         assertTrue(cpp.hasPrivileges(null, JCR_NAMESPACE_MANAGEMENT, JCR_NODE_TYPE_DEFINITION_MANAGEMENT));
+        assertTrue(cppO.hasPrivileges(null, JCR_NAMESPACE_MANAGEMENT, JCR_NODE_TYPE_DEFINITION_MANAGEMENT));
     }
 
-
     @Test
     public void testIsGranted() throws Exception {
         for (String p : defPermissions.keySet()) {
@@ -112,6 +267,7 @@ public class CompositeProviderAllTest ex
             Tree tree = readOnlyRoot.getTree(p);
 
             assertTrue(p, cpp.isGranted(tree, null, expected));
+            assertTrue(p, cppO.isGranted(tree, null, expected));
         }
     }
 
@@ -122,6 +278,7 @@ public class CompositeProviderAllTest ex
             Tree tree = readOnlyRoot.getTree(p);
 
             assertTrue(p, cpp.isGranted(tree, PROPERTY_STATE, expected));
+            assertTrue(p, cppO.isGranted(tree, PROPERTY_STATE, expected));
         }
     }
 
@@ -130,6 +287,7 @@ public class CompositeProviderAllTest ex
         for (String p : defActionsGranted.keySet()) {
             String actionStr = getActionString(defActionsGranted.get(p));
             assertTrue(p + " : " + actionStr, cpp.isGranted(p, actionStr));
+            assertTrue(p + " : " + actionStr, cppO.isGranted(p, actionStr));
         }
     }
 
@@ -147,6 +305,7 @@ public class CompositeProviderAllTest ex
 
         for (String p : noAccess.keySet()) {
             assertFalse(p, cpp.isGranted(p, getActionString(noAccess.get(p))));
+            assertTrue(p, cppO.isGranted(p, getActionString(noAccess.get(p))));
         }
     }
 
@@ -156,6 +315,11 @@ public class CompositeProviderAllTest ex
         assertTrue(rp.isGranted(Permissions.NAMESPACE_MANAGEMENT));
         assertTrue(rp.isGranted(Permissions.NODE_TYPE_DEFINITION_MANAGEMENT));
         assertTrue(rp.isGranted(Permissions.NAMESPACE_MANAGEMENT | Permissions.NODE_TYPE_DEFINITION_MANAGEMENT));
+
+        RepositoryPermission rpO = cpp.getRepositoryPermission();
+        assertTrue(rpO.isGranted(Permissions.NAMESPACE_MANAGEMENT));
+        assertTrue(rpO.isGranted(Permissions.NODE_TYPE_DEFINITION_MANAGEMENT));
+        assertTrue(rpO.isGranted(Permissions.NAMESPACE_MANAGEMENT | Permissions.NODE_TYPE_DEFINITION_MANAGEMENT));
     }
 
     @Test
@@ -173,6 +337,20 @@ public class CompositeProviderAllTest ex
     }
 
     @Test
+    public void testTreePermissionIsGrantedOR() throws Exception {
+        TreePermission parentPermission = TreePermission.EMPTY;
+
+        for (String path : TP_PATHS) {
+            TreePermission tp = cppO.getTreePermission(root.getTree(path), parentPermission);
+            Long toTest = (defPermissions.containsKey(path)) ? defPermissions.get(path) : defPermissions.get(PathUtils.getAncestorPath(path, 1));
+            if (toTest != null) {
+                assertTrue(tp.isGranted(toTest));
+            }
+            parentPermission = tp;
+        }
+    }
+
+    @Test
     public void testTreePermissionIsGrantedProperty() throws Exception {
         TreePermission parentPermission = TreePermission.EMPTY;
 
@@ -187,6 +365,20 @@ public class CompositeProviderAllTest ex
     }
 
     @Test
+    public void testTreePermissionIsGrantedPropertyOR() throws Exception {
+        TreePermission parentPermission = TreePermission.EMPTY;
+
+        for (String path : TP_PATHS) {
+            TreePermission tp = cppO.getTreePermission(readOnlyRoot.getTree(path), parentPermission);
+            Long toTest = (defPermissions.containsKey(path)) ? defPermissions.get(path) : defPermissions.get(PathUtils.getAncestorPath(path, 1));
+            if (toTest != null) {
+                assertTrue(tp.isGranted(toTest, PROPERTY_STATE));
+            }
+            parentPermission = tp;
+        }
+    }
+
+    @Test
     public void testTreePermissionCanRead() throws Exception {
         Map<String, Boolean> readMap = ImmutableMap.<String, Boolean>builder().
                 put(ROOT_PATH, false).
@@ -210,6 +402,29 @@ public class CompositeProviderAllTest ex
     }
 
     @Test
+    public void testTreePermissionCanReadOR() throws Exception {
+        Map<String, Boolean> readMap = ImmutableMap.<String, Boolean>builder().
+                put(ROOT_PATH, true).
+                put(TEST_PATH, true).
+                put(TEST_A_PATH, true).
+                put(TEST_A_B_PATH, true).
+                put(TEST_A_B_C_PATH, true).
+                put(TEST_A_B_C_PATH + "/nonexisting", true).
+                build();
+
+        TreePermission parentPermission = TreePermission.EMPTY;
+        for (String nodePath : readMap.keySet()) {
+            Tree tree = readOnlyRoot.getTree(nodePath);
+            TreePermission tp = cppO.getTreePermission(tree, parentPermission);
+
+            boolean expectedResult = readMap.get(nodePath);
+            assertEquals(nodePath, expectedResult, tp.canRead());
+
+            parentPermission = tp;
+        }
+    }
+
+    @Test
     public void testTreePermissionCanReadProperty() throws Exception {
         Map<String, Boolean> readMap = ImmutableMap.<String, Boolean>builder().
                 put(ROOT_PATH, false).
@@ -228,6 +443,30 @@ public class CompositeProviderAllTest ex
 
             boolean expectedResult = readMap.get(nodePath);
             assertEquals(nodePath, expectedResult, tp.canRead(PROPERTY_STATE));
+
+            parentPermission = tp;
+        }
+    }
+
+    @Test
+    public void testTreePermissionCanReadPropertyOR() throws Exception {
+        Map<String, Boolean> readMap = ImmutableMap.<String, Boolean>builder().
+                put(ROOT_PATH, true).
+                put(TEST_PATH, true).
+                put(TEST_A_PATH, true).
+                put(TEST_A_B_PATH, true).
+                put(TEST_A_B_C_PATH, true).
+                put(TEST_A_B_C_PATH + "/nonexisting", true).
+                build();
+
+        TreePermission parentPermission = TreePermission.EMPTY;
+        for (String nodePath : readMap.keySet()) {
+            Tree tree = readOnlyRoot.getTree(nodePath);
+
+            TreePermission tp = cppO.getTreePermission(tree, parentPermission);
+
+            boolean expectedResult = readMap.get(nodePath);
+            assertEquals(nodePath, expectedResult, tp.canRead(PROPERTY_STATE));
 
             parentPermission = tp;
         }

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderCoverageTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderCoverageTest.java?rev=1800063&r1=1800062&r2=1800063&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderCoverageTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderCoverageTest.java Tue Jun 27 14:11:33 2017
@@ -64,12 +64,14 @@ import static org.junit.Assert.assertTru
 public class CompositeProviderCoverageTest extends AbstractCompositeProviderTest {
 
     private CompositePermissionProvider cpp;
+    private CompositePermissionProvider cppO;
 
     @Override
     public void before() throws Exception {
         super.before();
 
         cpp = createPermissionProvider();
+        cppO = createPermissionProviderOR();
     }
 
     @Override
@@ -95,6 +97,20 @@ public class CompositeProviderCoverageTe
         }
     }
 
+    @Override
+    @Test
+    public void testGetTreePermissionInstanceOR() throws Exception {
+        PermissionProvider pp = createPermissionProviderOR(EveryonePrincipal.getInstance());
+        TreePermission parentPermission = TreePermission.EMPTY;
+
+        for (String path : TP_PATHS) {
+            TreePermission tp = pp.getTreePermission(readOnlyRoot.getTree(path), parentPermission);
+            assertTrue(tp instanceof LimitedTreePermission);
+            parentPermission = tp;
+        }
+    }
+
+    @Override
     @Test
     public void testTreePermissionGetChild() throws Exception {
         List<String> childNames = ImmutableList.of("test", "a", "b", "c", "nonexisting");
@@ -110,16 +126,34 @@ public class CompositeProviderCoverageTe
         }
     }
 
+    @Override
+    @Test
+    public void testTreePermissionGetChildOR() throws Exception {
+        List<String> childNames = ImmutableList.of("test", "a", "b", "c", "nonexisting");
+
+        Tree rootTree = readOnlyRoot.getTree(ROOT_PATH);
+        NodeState ns = ((ImmutableTree) rootTree).getNodeState();
+        TreePermission tp = createPermissionProviderOR().getTreePermission(rootTree, TreePermission.EMPTY);
+
+        for (String cName : childNames) {
+            ns = ns.getChildNode(cName);
+            tp = tp.getChildPermission(cName, ns);
+            assertTrue(tp instanceof LimitedTreePermission);
+        }
+    }
+
     @Test
     public void testGetPrivileges() throws Exception {
         for (String p : NODE_PATHS) {
             assertEquals(ImmutableSet.of(REP_READ_NODES), cpp.getPrivileges(readOnlyRoot.getTree(p)));
+            assertEquals(ImmutableSet.of(REP_READ_NODES), cppO.getPrivileges(readOnlyRoot.getTree(p)));
         }
     }
 
     @Test
     public void testGetPrivilegesOnRepo() throws Exception {
         assertEquals(ImmutableSet.of(JCR_NAMESPACE_MANAGEMENT), cpp.getPrivileges(null));
+        assertEquals(ImmutableSet.of(JCR_NAMESPACE_MANAGEMENT), cppO.getPrivileges(null));
     }
 
     @Test
@@ -131,6 +165,11 @@ public class CompositeProviderCoverageTe
             assertFalse(cpp.hasPrivileges(tree, JCR_READ));
             assertFalse(cpp.hasPrivileges(tree, JCR_WRITE));
             assertFalse(cpp.hasPrivileges(tree, JCR_ALL));
+
+            assertTrue(cppO.hasPrivileges(tree, REP_READ_NODES));
+            assertFalse(cppO.hasPrivileges(tree, JCR_READ));
+            assertFalse(cppO.hasPrivileges(tree, JCR_WRITE));
+            assertFalse(cppO.hasPrivileges(tree, JCR_ALL));
         }
     }
 
@@ -139,6 +178,10 @@ public class CompositeProviderCoverageTe
         assertTrue(cpp.hasPrivileges(null, JCR_NAMESPACE_MANAGEMENT));
         assertFalse(cpp.hasPrivileges(null, JCR_NODE_TYPE_DEFINITION_MANAGEMENT));
         assertFalse(cpp.hasPrivileges(null, JCR_ALL));
+
+        assertTrue(cppO.hasPrivileges(null, JCR_NAMESPACE_MANAGEMENT));
+        assertFalse(cppO.hasPrivileges(null, JCR_NODE_TYPE_DEFINITION_MANAGEMENT));
+        assertFalse(cppO.hasPrivileges(null, JCR_ALL));
     }
 
 
@@ -151,6 +194,11 @@ public class CompositeProviderCoverageTe
             assertFalse(cpp.isGranted(tree, null, Permissions.LOCK_MANAGEMENT));
             assertFalse(cpp.isGranted(tree, null, Permissions.ALL));
             assertFalse(cpp.isGranted(tree, null, Permissions.READ_NODE | Permissions.LOCK_MANAGEMENT));
+
+            assertTrue(cppO.isGranted(tree, null, Permissions.READ_NODE));
+            assertFalse(cppO.isGranted(tree, null, Permissions.LOCK_MANAGEMENT));
+            assertFalse(cppO.isGranted(tree, null, Permissions.ALL));
+            assertFalse(cppO.isGranted(tree, null, Permissions.READ_NODE | Permissions.LOCK_MANAGEMENT));
         }
     }
 
@@ -164,6 +212,12 @@ public class CompositeProviderCoverageTe
             assertFalse(cpp.isGranted(tree, PROPERTY_STATE, Permissions.LOCK_MANAGEMENT));
             assertFalse(cpp.isGranted(tree, PROPERTY_STATE, Permissions.ALL));
             assertFalse(cpp.isGranted(tree, PROPERTY_STATE, Permissions.READ_NODE | Permissions.LOCK_MANAGEMENT));
+
+            assertTrue(cppO.isGranted(tree, PROPERTY_STATE, Permissions.READ_NODE));
+            assertFalse(cppO.isGranted(tree, PROPERTY_STATE, Permissions.READ_PROPERTY));
+            assertFalse(cppO.isGranted(tree, PROPERTY_STATE, Permissions.LOCK_MANAGEMENT));
+            assertFalse(cppO.isGranted(tree, PROPERTY_STATE, Permissions.ALL));
+            assertFalse(cppO.isGranted(tree, PROPERTY_STATE, Permissions.READ_NODE | Permissions.LOCK_MANAGEMENT));
         }
     }
 
@@ -174,16 +228,24 @@ public class CompositeProviderCoverageTe
 
             assertTrue(cpp.isGranted(nodePath, Session.ACTION_READ));
             assertFalse(cpp.isGranted(propPath, Session.ACTION_READ));
-
             assertFalse(cpp.isGranted(nodePath, Session.ACTION_REMOVE));
             assertFalse(cpp.isGranted(propPath, JackrabbitSession.ACTION_MODIFY_PROPERTY));
-
             assertFalse(cpp.isGranted(nodePath, getActionString(JackrabbitSession.ACTION_MODIFY_ACCESS_CONTROL, JackrabbitSession.ACTION_READ_ACCESS_CONTROL)));
 
+            assertTrue(cppO.isGranted(nodePath, Session.ACTION_READ));
+            assertFalse(cppO.isGranted(propPath, Session.ACTION_READ));
+            assertFalse(cppO.isGranted(nodePath, Session.ACTION_REMOVE));
+            assertFalse(cppO.isGranted(propPath, JackrabbitSession.ACTION_MODIFY_PROPERTY));
+            assertFalse(cppO.isGranted(nodePath, getActionString(JackrabbitSession.ACTION_MODIFY_ACCESS_CONTROL, JackrabbitSession.ACTION_READ_ACCESS_CONTROL)));
+
             String nonExisting = PathUtils.concat(nodePath, "nonExisting");
             assertFalse(cpp.isGranted(nonExisting, Session.ACTION_READ));
             assertFalse(cpp.isGranted(nonExisting, JackrabbitSession.ACTION_ADD_PROPERTY));
             assertFalse(cpp.isGranted(nonExisting, Session.ACTION_ADD_NODE));
+
+            assertFalse(cppO.isGranted(nonExisting, Session.ACTION_READ));
+            assertFalse(cppO.isGranted(nonExisting, JackrabbitSession.ACTION_ADD_PROPERTY));
+            assertFalse(cppO.isGranted(nonExisting, Session.ACTION_ADD_NODE));
         }
     }
 
@@ -193,6 +255,11 @@ public class CompositeProviderCoverageTe
         assertTrue(rp.isGranted(Permissions.NAMESPACE_MANAGEMENT));
         assertFalse(rp.isGranted(Permissions.NODE_TYPE_DEFINITION_MANAGEMENT));
         assertFalse(rp.isGranted(Permissions.ALL));
+
+        RepositoryPermission rpO = cppO.getRepositoryPermission();
+        assertTrue(rpO.isGranted(Permissions.NAMESPACE_MANAGEMENT));
+        assertFalse(rpO.isGranted(Permissions.NODE_TYPE_DEFINITION_MANAGEMENT));
+        assertFalse(rpO.isGranted(Permissions.ALL));
     }
 
     @Test
@@ -211,6 +278,21 @@ public class CompositeProviderCoverageTe
     }
 
     @Test
+    public void testTreePermissionIsGrantedOR() throws Exception {
+        TreePermission parentPermission = TreePermission.EMPTY;
+        for (String path : TP_PATHS) {
+            TreePermission tp = cppO.getTreePermission(readOnlyRoot.getTree(path), parentPermission);
+
+            assertTrue(tp.isGranted(Permissions.READ_NODE));
+            assertFalse(tp.isGranted(Permissions.REMOVE_NODE));
+            assertFalse(tp.isGranted(Permissions.READ));
+            assertFalse(tp.isGranted(Permissions.ALL));
+
+            parentPermission = tp;
+        }
+    }
+
+    @Test
     public void testTreePermissionIsGrantedProperty() throws Exception {
         TreePermission parentPermission = TreePermission.EMPTY;
 
@@ -227,6 +309,22 @@ public class CompositeProviderCoverageTe
     }
 
     @Test
+    public void testTreePermissionIsGrantedPropertyOR() throws Exception {
+        TreePermission parentPermission = TreePermission.EMPTY;
+
+        for (String path : TP_PATHS) {
+            TreePermission tp = cppO.getTreePermission(readOnlyRoot.getTree(path), parentPermission);
+
+            assertFalse(tp.isGranted(Permissions.READ_PROPERTY, PROPERTY_STATE));
+            assertFalse(tp.isGranted(Permissions.REMOVE_PROPERTY, PROPERTY_STATE));
+            assertFalse(tp.isGranted(Permissions.READ, PROPERTY_STATE));
+            assertFalse(tp.isGranted(Permissions.ALL, PROPERTY_STATE));
+
+            parentPermission = tp;
+        }
+    }
+
+    @Test
     public void testTreePermissionCanRead() throws Exception {
         TreePermission parentPermission = TreePermission.EMPTY;
 
@@ -241,6 +339,20 @@ public class CompositeProviderCoverageTe
     }
 
     @Test
+    public void testTreePermissionCanReadOR() throws Exception {
+        TreePermission parentPermission = TreePermission.EMPTY;
+
+        for (String path : TP_PATHS) {
+            Tree t = readOnlyRoot.getTree(path);
+            TreePermission tp = cppO.getTreePermission(t, parentPermission);
+
+            assertTrue(tp.canRead());
+
+            parentPermission = tp;
+        }
+    }
+
+    @Test
     public void testTreePermissionCanReadProperty() throws Exception {
         TreePermission parentPermission = TreePermission.EMPTY;
 
@@ -250,6 +362,19 @@ public class CompositeProviderCoverageTe
             assertFalse(tp.canRead(PROPERTY_STATE));
 
             parentPermission = tp;
+        }
+    }
+
+    @Test
+    public void testTreePermissionCanReadPropertyOR() throws Exception {
+        TreePermission parentPermission = TreePermission.EMPTY;
+
+        for (String path : TP_PATHS) {
+            Tree t = readOnlyRoot.getTree(path);
+            TreePermission tp = cppO.getTreePermission(t, parentPermission);
+            assertFalse(tp.canRead(PROPERTY_STATE));
+
+            parentPermission = tp;
         }
     }
 

Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderCustomMixTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderCustomMixTest.java?rev=1800063&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderCustomMixTest.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderCustomMixTest.java Tue Jun 27 14:11:33 2017
@@ -0,0 +1,407 @@
+/*
+ * 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.security.authorization.composite;
+
+import static org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants.JCR_NAMESPACE_MANAGEMENT;
+import static org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants.JCR_NODE_TYPE_MANAGEMENT;
+import static org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants.JCR_READ;
+import static org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants.JCR_WRITE;
+import static org.junit.Assert.assertEquals;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.apache.jackrabbit.api.JackrabbitSession;
+import org.apache.jackrabbit.oak.AbstractSecurityTest;
+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.plugins.tree.TreeLocation;
+import org.apache.jackrabbit.oak.plugins.tree.TreeType;
+import org.apache.jackrabbit.oak.security.authorization.composite.CompositeAuthorizationConfiguration.CompositionType;
+import org.apache.jackrabbit.oak.spi.security.authorization.AuthorizationConfiguration;
+import org.apache.jackrabbit.oak.spi.security.authorization.permission.AggregatedPermissionProvider;
+import org.apache.jackrabbit.oak.spi.security.authorization.permission.Permissions;
+import org.apache.jackrabbit.oak.spi.security.authorization.permission.RepositoryPermission;
+import org.apache.jackrabbit.oak.spi.security.authorization.permission.TreePermission;
+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.junit.Assert;
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+public class CompositeProviderCustomMixTest extends AbstractSecurityTest {
+
+    @Test
+    public void hasPrivilegesTest() throws Exception {
+        Set<String> supp1 = ImmutableSet.of(JCR_READ, JCR_NAMESPACE_MANAGEMENT);
+        Set<String> supp2 = ImmutableSet.of(JCR_READ, JCR_WRITE);
+        Set<String> all = Sets.union(supp1, supp2);
+
+        // tests all possible 256 shuffles
+        for (CompositionType type : CompositionType.values()) {
+            for (Set<String> granted1 : Sets.powerSet(supp1)) {
+                for (Set<String> granted2 : Sets.powerSet(supp2)) {
+                    for (Set<String> ps : Sets.powerSet(all)) {
+                        CompositePermissionProvider cpp = buildCpp(supp1, granted1, supp2, granted2, type, null);
+
+                        boolean expected = expected(ps, supp1, granted1, supp2, granted2, type, true);
+                        boolean result = cpp.hasPrivileges(null, ps.toArray(new String[] {}));
+
+                        String err = "Checking " + ps + " in {supported: " + supp1 + ", granted: " + granted1 + "} "
+                                + type + " {supported: " + supp2 + ", granted: " + granted2 + "}";
+                        assertEquals(err, expected, result);
+                    }
+                }
+            }
+        }
+    }
+
+    @Test
+    public void isGrantedTest() throws Exception {
+        Set<String> supp1 = ImmutableSet.of(JCR_READ, JCR_NODE_TYPE_MANAGEMENT);
+        Set<String> supp2 = ImmutableSet.of(JCR_READ, JCR_WRITE);
+        Set<String> all = Sets.union(supp1, supp2);
+
+        Map<String, Long> grantMap = Maps.newHashMap();
+        grantMap.put(JCR_READ, Permissions.READ);
+        grantMap.put(JCR_NODE_TYPE_MANAGEMENT, Permissions.NODE_TYPE_MANAGEMENT);
+        grantMap.put(JCR_WRITE, Permissions.WRITE);
+
+        Map<String, String> actionMap = Maps.newHashMap();
+        actionMap.put(JCR_READ, JackrabbitSession.ACTION_READ);
+        actionMap.put(JCR_NODE_TYPE_MANAGEMENT, JackrabbitSession.ACTION_NODE_TYPE_MANAGEMENT);
+        actionMap.put(JCR_WRITE, JackrabbitSession.ACTION_ADD_NODE);
+
+        // tests all possible 256 shuffles
+        for (CompositionType type : CompositionType.values()) {
+            for (Set<String> granted1 : Sets.powerSet(supp1)) {
+                for (Set<String> granted2 : Sets.powerSet(supp2)) {
+                    for (Set<String> ps : Sets.powerSet(all)) {
+                        CompositePermissionProvider cpp = buildCpp(supp1, granted1, supp2, granted2, type, grantMap);
+                        boolean expected = expected(ps, supp1, granted1, supp2, granted2, type, false);
+
+                        boolean result1 = cpp.isGranted(null, null, mapToPermissions(ps, grantMap));
+                        String err1 = "[isGranted1] Checking " + ps + " in {supported: " + supp1 + ", granted: "
+                                + granted1 + "} " + type + " {supported: " + supp2 + ", granted: " + granted2 + "}";
+                        assertEquals(err1, expected, result1);
+
+                        // check existing path
+                        boolean result2 = cpp.isGranted("/", mapToActions(ps, actionMap));
+                        String err2 = "[isGranted2] Checking " + ps + " in {supported: " + supp1 + ", granted: "
+                                + granted1 + "} " + type + " {supported: " + supp2 + ", granted: " + granted2 + "}";
+                        assertEquals(err2, expected, result2);
+
+                        // check non existing path
+                        boolean result3 = cpp.isGranted("/doesnotexist", mapToActions(ps, actionMap));
+                        String err3 = "[isGranted3] Checking " + ps + " in {supported: " + supp1 + ", granted: "
+                                + granted1 + "} " + type + " {supported: " + supp2 + ", granted: " + granted2 + "}";
+                        assertEquals(err3, expected, result3);
+                    }
+                }
+            }
+        }
+    }
+
+    @Test
+    public void getRepositoryPermissionTest() throws Exception {
+        Set<String> supp1 = ImmutableSet.of(JCR_READ, JCR_NODE_TYPE_MANAGEMENT);
+        Set<String> supp2 = ImmutableSet.of(JCR_READ, JCR_WRITE);
+        Set<String> all = Sets.union(supp1, supp2);
+
+        Map<String, Long> grantMap = Maps.newHashMap();
+        grantMap.put(JCR_READ, Permissions.READ);
+        grantMap.put(JCR_NODE_TYPE_MANAGEMENT, Permissions.NODE_TYPE_MANAGEMENT);
+        grantMap.put(JCR_WRITE, Permissions.WRITE);
+
+        // tests all possible 256 shuffles
+        for (CompositionType type : CompositionType.values()) {
+            for (Set<String> granted1 : Sets.powerSet(supp1)) {
+                for (Set<String> granted2 : Sets.powerSet(supp2)) {
+                    for (Set<String> ps : Sets.powerSet(all)) {
+                        CompositePermissionProvider cpp = buildCpp(supp1, granted1, supp2, granted2, type, grantMap);
+
+                        boolean expected = expected(ps, supp1, granted1, supp2, granted2, type, false);
+                        boolean result = cpp.getRepositoryPermission().isGranted(mapToPermissions(ps, grantMap));
+
+                        String err = "Checking " + ps + " in {supported: " + supp1 + ", granted: " + granted1 + "} "
+                                + type + " {supported: " + supp2 + ", granted: " + granted2 + "}";
+                        assertEquals(err, expected, result);
+                    }
+                }
+            }
+        }
+    }
+
+    @Test
+    public void getTreePermissionTest() throws Exception {
+        Set<String> supp1 = ImmutableSet.of(JCR_READ, JCR_NODE_TYPE_MANAGEMENT);
+        Set<String> supp2 = ImmutableSet.of(JCR_READ, JCR_WRITE);
+        Set<String> all = Sets.union(supp1, supp2);
+
+        Map<String, Long> grantMap = Maps.newHashMap();
+        grantMap.put(JCR_READ, Permissions.READ);
+        grantMap.put(JCR_NODE_TYPE_MANAGEMENT, Permissions.NODE_TYPE_MANAGEMENT);
+        grantMap.put(JCR_WRITE, Permissions.WRITE);
+
+        // tests all possible 256 shuffles
+        for (CompositionType type : CompositionType.values()) {
+            for (Set<String> granted1 : Sets.powerSet(supp1)) {
+                for (Set<String> granted2 : Sets.powerSet(supp2)) {
+                    for (Set<String> ps : Sets.powerSet(all)) {
+                        CompositePermissionProvider cpp = buildCpp(supp1, granted1, supp2, granted2, type, grantMap);
+
+                        boolean expected = expected(ps, supp1, granted1, supp2, granted2, type, false);
+                        boolean result = cpp.getTreePermission(root.getTree("/"), TreePermission.EMPTY)
+                                .isGranted(mapToPermissions(ps, grantMap));
+
+                        String err = "Checking " + ps + " in {supported: " + supp1 + ", granted: " + granted1 + "} "
+                                + type + " {supported: " + supp2 + ", granted: " + granted2 + "}";
+                        assertEquals(err, expected, result);
+                    }
+                }
+            }
+        }
+    }
+
+    private static long mapToPermissions(Set<String> items, Map<String, Long> grantMap) {
+        long perm = Permissions.NO_PERMISSION;
+        for (String i : items) {
+            perm |= grantMap.get(i);
+        }
+        return perm;
+    }
+
+    private static String mapToActions(Set<String> items, Map<String, String> actionMap) {
+        if (items.isEmpty()) {
+            return "";
+        }
+        String actions = "";
+        for (String i : items) {
+            actions += actionMap.get(i) + ",";
+        }
+        return actions.substring(0, actions.length() - 1);
+    }
+
+    private boolean expected(Set<String> check, Set<String> supported1, Set<String> granted1, Set<String> supported2,
+            Set<String> granted2, CompositionType type, boolean emptyIsTrue) {
+        // Special case handled differently in the composite permissions vs.
+        // actions
+        if (check.isEmpty()) {
+            return emptyIsTrue;
+        }
+
+        if (type == CompositionType.OR) {
+            return Sets.difference(Sets.difference(check, granted1), granted2).isEmpty();
+        } else {
+            Set<String> f1 = Sets.intersection(supported1, check);
+            boolean hasf1 = granted1.containsAll(f1);
+            Set<String> f2 = Sets.intersection(supported2, check);
+            boolean hasf2 = granted2.containsAll(f2);
+            return hasf1 && hasf2;
+        }
+    }
+
+    private CompositePermissionProvider buildCpp(Set<String> supported1, Set<String> granted1, Set<String> supported2,
+            Set<String> granted2, CompositionType type, Map<String, Long> grantMap) {
+        AggregatedPermissionProvider a1 = new CustomProvider(root, supported1, granted1, grantMap);
+        AggregatedPermissionProvider a2 = new CustomProvider(root, supported2, granted2, grantMap);
+
+        AuthorizationConfiguration config = getConfig(AuthorizationConfiguration.class);
+        List<AggregatedPermissionProvider> composite = ImmutableList.of(a1, a2);
+        return new CompositePermissionProvider(root, composite, config.getContext(), type);
+    }
+
+    private static class CustomProvider implements AggregatedPermissionProvider {
+
+        private final PrivilegeBitsProvider pbp;
+
+        private final Set<String> supported;
+        private final Set<String> granted;
+        private final Map<String, Long> grantMap;
+
+        private CustomProvider(@Nonnull Root root, Set<String> supported, Set<String> granted,
+                Map<String, Long> grantMap) {
+            this.pbp = new PrivilegeBitsProvider(root);
+
+            this.supported = supported;
+            this.granted = granted;
+            this.grantMap = grantMap;
+        }
+
+        private static PrivilegeBits toBits(Set<String> supported, PrivilegeBitsProvider pbp) {
+            PrivilegeBits suppBits = PrivilegeBits.getInstance();
+            for (String s : supported) {
+                suppBits.add(pbp.getBits(s));
+            }
+            return suppBits;
+        }
+
+        @Nonnull
+        @Override
+        public PrivilegeBits supportedPrivileges(@Nullable Tree tree, @Nullable PrivilegeBits privilegeBits) {
+            return toBits(supported, pbp).retain(privilegeBits);
+        }
+
+        @Override
+        public boolean hasPrivileges(Tree tree, String... privilegeNames) {
+            Set<String> in = Sets.newHashSet(privilegeNames);
+            return granted.containsAll(in);
+        }
+
+        private long supportedPermissions(long permissions) {
+            long allperms = mapToPermissions(supported, grantMap);
+            long delta = Permissions.diff(permissions, allperms);
+            return Permissions.diff(permissions, delta);
+        }
+
+        @Override
+        public long supportedPermissions(@Nullable Tree tree, @Nullable PropertyState property, long permissions) {
+            return supportedPermissions(permissions);
+        }
+
+        @Override
+        public long supportedPermissions(TreeLocation location, long permissions) {
+            return supportedPermissions(permissions);
+        }
+
+        @Override
+        public long supportedPermissions(TreePermission treePermission, PropertyState property, long permissions) {
+            return supportedPermissions(permissions);
+        }
+
+        @Override
+        public boolean isGranted(@Nonnull Tree tree, @Nullable PropertyState property, long permissions) {
+            long myperms = mapToPermissions(granted, grantMap);
+            return Permissions.includes(myperms, permissions);
+        }
+
+        @Override
+        public boolean isGranted(TreeLocation location, long permissions) {
+            long myperms = mapToPermissions(granted, grantMap);
+            return Permissions.includes(myperms, permissions);
+        }
+
+        @Override
+        public RepositoryPermission getRepositoryPermission() {
+            return new RepositoryPermission() {
+
+                @Override
+                public boolean isGranted(long repositoryPermissions) {
+                    long myperms = mapToPermissions(granted, grantMap);
+                    return Permissions.includes(myperms, repositoryPermissions);
+                }
+            };
+        }
+
+        @Override
+        public TreePermission getTreePermission(Tree tree, TreeType type, TreePermission parentPermission) {
+            return new CustomTreePermission(granted, grantMap);
+        }
+
+        @Override
+        public void refresh() {
+            Assert.fail("method should not be called");
+        }
+
+        @Override
+        public Set<String> getPrivileges(Tree tree) {
+            Assert.fail("method should not be called");
+            return null;
+        }
+
+        @Override
+        public TreePermission getTreePermission(Tree tree, TreePermission parentPermission) {
+            Assert.fail("method should not be called");
+            return null;
+        }
+
+        @Override
+        public boolean isGranted(String oakPath, String jcrActions) {
+            Assert.fail("method should not be called");
+            return false;
+        }
+
+        @Override
+        public String toString() {
+            return "CustomProvider [supported=" + supported + ", granted=" + granted + "]";
+        }
+    }
+
+    private static class CustomTreePermission implements TreePermission {
+
+        private final Set<String> granted;
+        private final Map<String, Long> grantMap;
+
+        public CustomTreePermission(Set<String> granted, Map<String, Long> grantMap) {
+            this.granted = granted;
+            this.grantMap = grantMap;
+        }
+
+        @Override
+        public TreePermission getChildPermission(String childName, NodeState childState) {
+            Assert.fail("method should not be called");
+            return null;
+        }
+
+        @Override
+        public boolean canRead() {
+            Assert.fail("method should not be called");
+            return false;
+        }
+
+        @Override
+        public boolean canRead(PropertyState property) {
+            Assert.fail("method should not be called");
+            return false;
+        }
+
+        @Override
+        public boolean canReadAll() {
+            Assert.fail("method should not be called");
+            return false;
+        }
+
+        @Override
+        public boolean canReadProperties() {
+            Assert.fail("method should not be called");
+            return false;
+        }
+
+        @Override
+        public boolean isGranted(long permissions) {
+            long myperms = mapToPermissions(granted, grantMap);
+            return Permissions.includes(myperms, permissions);
+        }
+
+        @Override
+        public boolean isGranted(long permissions, PropertyState property) {
+            Assert.fail("method should not be called");
+            return false;
+        }
+
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderCustomMixTest.java
------------------------------------------------------------------------------
    svn:eol-style = native