diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeAuthorizationConfiguration.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeAuthorizationConfiguration.java index 14b380f67c..fceb37a324 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeAuthorizationConfiguration.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeAuthorizationConfiguration.java @@ -77,6 +77,23 @@ public class CompositeAuthorizationConfiguration extends CompositeConfiguration< private static final Logger log = LoggerFactory.getLogger(CompositeAuthorizationConfiguration.class); + public static 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 + } + + private CompositionType compositionType = CompositionType.AND; + public CompositeAuthorizationConfiguration() { super(AuthorizationConfiguration.NAME); } @@ -85,6 +102,15 @@ public class CompositeAuthorizationConfiguration extends CompositeConfiguration< super(AuthorizationConfiguration.NAME, securityProvider); } + public void withCompositionType(String ct) { + String or = CompositionType.OR.name(); + if (or.equals(ct) || or.toLowerCase().equals(ct)) { + this.compositionType = CompositionType.OR; + } else { + this.compositionType = CompositionType.AND; + } + } + @Nonnull @Override public AccessControlManager getAccessControlManager(@Nonnull final Root root, @@ -134,7 +160,7 @@ public class CompositeAuthorizationConfiguration extends CompositeConfiguration< case 0: throw new IllegalStateException(); case 1: return configurations.get(0).getPermissionProvider(root, workspaceName, principals); default: - List aggrPermissionProviders = new ArrayList(configurations.size()); + List aggrPermissionProviders = new ArrayList<>(configurations.size()); for (AuthorizationConfiguration conf : configurations) { PermissionProvider pProvider = conf.getPermissionProvider(root, workspaceName, principals); if (pProvider instanceof AggregatedPermissionProvider) { @@ -152,7 +178,7 @@ public class CompositeAuthorizationConfiguration extends CompositeConfiguration< pp = aggrPermissionProviders.get(0); break; default : - pp = new CompositePermissionProvider(root, aggrPermissionProviders, getContext()); + pp = new CompositePermissionProvider(root, aggrPermissionProviders, getContext(), compositionType); } return pp; } diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositePermissionProvider.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositePermissionProvider.java index 1452a2bdb8..4d2db26288 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositePermissionProvider.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositePermissionProvider.java @@ -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.tree.RootFactory; 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 implements PermissionProvider { 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 implements PermissionProvider { private PrivilegeBitsProvider privilegeBitsProvider; private TreeTypeProvider typeProvider; - CompositePermissionProvider(@Nonnull Root root, @Nonnull List pps, @Nonnull Context acContext) { + CompositePermissionProvider(@Nonnull Root root, @Nonnull List pps, + @Nonnull Context acContext, 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 implements PermissionProvider { PrivilegeBits supported = aggregatedPermissionProvider.supportedPrivileges(immutableTree, privilegeBits); if (doEvaluate(supported)) { Set supportedNames = privilegeBitsProvider.getPrivilegeNames(supported); - hasPrivileges = aggregatedPermissionProvider.hasPrivileges(immutableTree, supportedNames.toArray(new String[supportedNames.size()])); - coveredPrivs.add(supported); - - if (!hasPrivileges) { - break; + if (compositionType == AND) { + hasPrivileges = aggregatedPermissionProvider.hasPrivileges(immutableTree, + supportedNames.toArray(new String[supportedNames.size()])); + if (!hasPrivileges) { + return false; + } + coveredPrivs.add(supported); + + } 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 implements PermissionProvider { 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 implements PermissionProvider { 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 implements PermissionProvider { 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,16 @@ class CompositePermissionProvider implements PermissionProvider { /** * {@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(AggregatedPermissionProvider[] pps, CompositionType compositionType) { + this.pps = pps; + this.compositionType = compositionType; + } @Override public boolean isGranted(long repositoryPermissions) { @@ -228,10 +272,20 @@ class CompositePermissionProvider implements PermissionProvider { 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; + } + } } } } diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeTreePermission.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeTreePermission.java index 1423b21b8a..b078321f5a 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeTreePermission.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeTreePermission.java @@ -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.Tree; 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 implements TreePermission { 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 implements TreePermission { 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, CompositionType compositionType) { this.tree = tree; this.type = type; @@ -54,9 +61,11 @@ final class CompositeTreePermission implements TreePermission { 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, 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 implements TreePermission { } 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 implements TreePermission { 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 implements TreePermission { 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 implements TreePermission { 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 implements TreePermission { 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; } } } diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/security/internal/SecurityProviderRegistration.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/security/internal/SecurityProviderRegistration.java index 867299b885..624c20638a 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/security/internal/SecurityProviderRegistration.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/security/internal/SecurityProviderRegistration.java @@ -28,6 +28,7 @@ import org.apache.felix.scr.annotations.Deactivate; 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.Lists.newCopyOnWriteArrayList; "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.", + value = "AND", + options = { + @PropertyOption(name = "AND", value = "AND"), + @PropertyOption(name = "OR", value = "OR") + } ) }) @References({ @@ -184,6 +196,7 @@ public class SecurityProviderRegistration { this.context = context; } + this.authorizationConfiguration.withCompositionType(getAuthorizationCompositionType(configuration)); maybeRegister(); } @@ -199,6 +212,7 @@ public class SecurityProviderRegistration { preconditions.addPrecondition(pid); } } + this.authorizationConfiguration.withCompositionType(getAuthorizationCompositionType(configuration)); maybeUnregister(); maybeRegister(); @@ -572,12 +586,15 @@ public class SecurityProviderRegistration { preconditions.removeCandidate(pid); } - private String getServicePid(Map properties) { + private static String getServicePid(Map properties) { return PropertiesUtil.toString(properties.get(Constants.SERVICE_PID), null); } - private String[] getRequiredServicePids(Map configuration) { + private static String[] getRequiredServicePids(Map configuration) { return PropertiesUtil.toStringArray(configuration.get("requiredServicePids"), new String[]{}); } + private static String getAuthorizationCompositionType(Map properties) { + return PropertiesUtil.toString(properties.get("authorizationCompositionType"), null); + } } diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/AbstractCompositeProviderTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/AbstractCompositeProviderTest.java index 6f60b01114..ea675048a4 100644 --- a/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/AbstractCompositeProviderTest.java +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/AbstractCompositeProviderTest.java @@ -42,6 +42,7 @@ import org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants; 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 AbstractCompositeProviderTest extends AbstractSecurityTest } 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 AbstractCompositeProviderTest extends AbstractSecurityTest } } - CompositePermissionProvider createPermissionProvider(Principal... principals) { return createPermissionProvider(ImmutableSet.copyOf(principals)); } @@ -254,13 +254,25 @@ public abstract class AbstractCompositeProviderTest extends AbstractSecurityTest CompositePermissionProvider createPermissionProvider(Set 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 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 AbstractCompositeProviderTest extends AbstractSecurityTest 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 AbstractCompositeProviderTest extends AbstractSecurityTest @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 AbstractCompositeProviderTest extends AbstractSecurityTest 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 AbstractCompositeProviderTest extends AbstractSecurityTest 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 AbstractCompositeProviderTest extends AbstractSecurityTest 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 AbstractCompositeProviderTest extends AbstractSecurityTest 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 AbstractCompositeProviderTest extends AbstractSecurityTest 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 AbstractCompositeProviderTest extends AbstractSecurityTest } @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 AbstractCompositeProviderTest extends AbstractSecurityTest } @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 AbstractCompositeProviderTest extends AbstractSecurityTest } @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 AbstractCompositeProviderTest extends AbstractSecurityTest } @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 childNames = ImmutableList.of("test", "a", "b", "c", "nonexisting"); @@ -471,9 +585,26 @@ public abstract class AbstractCompositeProviderTest extends AbstractSecurityTest } @Test + public void testTreePermissionGetChildOR() throws Exception { + List 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 @@ -485,4 +616,14 @@ public abstract class AbstractCompositeProviderTest extends AbstractSecurityTest 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)); + } } \ No newline at end of file diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderAllTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderAllTest.java index 7b79762966..d0a0f66e45 100644 --- a/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderAllTest.java +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderAllTest.java @@ -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.assertFalse; 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.assertTrue; * * The expected result is only the subset of permissions granted by the default * provider. The test user must never have full access anywhere. + *

+ * 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 extends AbstractCompositeProviderTest { 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 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 extends AbstractCompositeProviderTest { public void testGetPrivilegesOnRepo() throws Exception { Set privilegeNames = cpp.getPrivileges(null); assertEquals(ImmutableSet.of(JCR_NAMESPACE_MANAGEMENT, JCR_NODE_TYPE_DEFINITION_MANAGEMENT), privilegeNames); + Set privilegeNamesO = cppO.getPrivileges(null); + assertEquals(ImmutableSet.of(JCR_NAMESPACE_MANAGEMENT, JCR_NODE_TYPE_DEFINITION_MANAGEMENT), privilegeNamesO); } @@ -93,18 +249,17 @@ public class CompositeProviderAllTest extends AbstractCompositeProviderTest { for (String p : defPrivileges.keySet()) { Set 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 extends AbstractCompositeProviderTest { 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 extends AbstractCompositeProviderTest { 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 extends AbstractCompositeProviderTest { 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 extends AbstractCompositeProviderTest { 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 extends AbstractCompositeProviderTest { 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 extends AbstractCompositeProviderTest { } @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 extends AbstractCompositeProviderTest { } @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 readMap = ImmutableMap.builder(). put(ROOT_PATH, false). @@ -210,6 +402,29 @@ public class CompositeProviderAllTest extends AbstractCompositeProviderTest { } @Test + public void testTreePermissionCanReadOR() throws Exception { + Map readMap = ImmutableMap.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 readMap = ImmutableMap.builder(). put(ROOT_PATH, false). @@ -233,6 +448,30 @@ public class CompositeProviderAllTest extends AbstractCompositeProviderTest { } } + @Test + public void testTreePermissionCanReadPropertyOR() throws Exception { + Map readMap = ImmutableMap.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; + } + } + /** * Custom permission provider that supports all permissions and grants * full access for everyone. diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderCoverageTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderCoverageTest.java index 020eef9890..d0dfc48f18 100644 --- a/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderCoverageTest.java +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderCoverageTest.java @@ -64,12 +64,14 @@ import static org.junit.Assert.assertTrue; 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 CompositeProviderCoverageTest extends AbstractCompositeProviderTest } } + @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 childNames = ImmutableList.of("test", "a", "b", "c", "nonexisting"); @@ -110,16 +126,34 @@ public class CompositeProviderCoverageTest extends AbstractCompositeProviderTest } } + @Override + @Test + public void testTreePermissionGetChildOR() throws Exception { + List 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 CompositeProviderCoverageTest extends AbstractCompositeProviderTest 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 CompositeProviderCoverageTest extends AbstractCompositeProviderTest 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 CompositeProviderCoverageTest extends AbstractCompositeProviderTest 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 CompositeProviderCoverageTest extends AbstractCompositeProviderTest 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 CompositeProviderCoverageTest extends AbstractCompositeProviderTest 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 CompositeProviderCoverageTest extends AbstractCompositeProviderTest 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 CompositeProviderCoverageTest extends AbstractCompositeProviderTest } @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 CompositeProviderCoverageTest extends AbstractCompositeProviderTest } @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 CompositeProviderCoverageTest extends AbstractCompositeProviderTest } @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; @@ -253,6 +365,19 @@ public class CompositeProviderCoverageTest extends AbstractCompositeProviderTest } } + @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; + } + } + private final class LimitCoverageProvider extends AbstractAggrProvider { LimitCoverageProvider(Root root) { diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderCustomMixTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderCustomMixTest.java new file mode 100644 index 0000000000..3b6cfb6ffb --- /dev/null +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderCustomMixTest.java @@ -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 supp1 = ImmutableSet.of(JCR_READ, JCR_NAMESPACE_MANAGEMENT); + Set supp2 = ImmutableSet.of(JCR_READ, JCR_WRITE); + Set all = Sets.union(supp1, supp2); + + // tests all possible 256 shuffles + for (CompositionType type : CompositionType.values()) { + for (Set granted1 : Sets.powerSet(supp1)) { + for (Set granted2 : Sets.powerSet(supp2)) { + for (Set 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 supp1 = ImmutableSet.of(JCR_READ, JCR_NODE_TYPE_MANAGEMENT); + Set supp2 = ImmutableSet.of(JCR_READ, JCR_WRITE); + Set all = Sets.union(supp1, supp2); + + Map 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 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 granted1 : Sets.powerSet(supp1)) { + for (Set granted2 : Sets.powerSet(supp2)) { + for (Set 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 supp1 = ImmutableSet.of(JCR_READ, JCR_NODE_TYPE_MANAGEMENT); + Set supp2 = ImmutableSet.of(JCR_READ, JCR_WRITE); + Set all = Sets.union(supp1, supp2); + + Map 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 granted1 : Sets.powerSet(supp1)) { + for (Set granted2 : Sets.powerSet(supp2)) { + for (Set 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 supp1 = ImmutableSet.of(JCR_READ, JCR_NODE_TYPE_MANAGEMENT); + Set supp2 = ImmutableSet.of(JCR_READ, JCR_WRITE); + Set all = Sets.union(supp1, supp2); + + Map 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 granted1 : Sets.powerSet(supp1)) { + for (Set granted2 : Sets.powerSet(supp2)) { + for (Set 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 items, Map grantMap) { + long perm = Permissions.NO_PERMISSION; + for (String i : items) { + perm |= grantMap.get(i); + } + return perm; + } + + private static String mapToActions(Set items, Map 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 check, Set supported1, Set granted1, Set supported2, + Set 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 f1 = Sets.intersection(supported1, check); + boolean hasf1 = granted1.containsAll(f1); + Set f2 = Sets.intersection(supported2, check); + boolean hasf2 = granted2.containsAll(f2); + return hasf1 && hasf2; + } + } + + private CompositePermissionProvider buildCpp(Set supported1, Set granted1, Set supported2, + Set granted2, CompositionType type, Map grantMap) { + AggregatedPermissionProvider a1 = new CustomProvider(root, supported1, granted1, grantMap); + AggregatedPermissionProvider a2 = new CustomProvider(root, supported2, granted2, grantMap); + + AuthorizationConfiguration config = getConfig(AuthorizationConfiguration.class); + List 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 supported; + private final Set granted; + private final Map grantMap; + + private CustomProvider(@Nonnull Root root, Set supported, Set granted, + Map grantMap) { + this.pbp = new PrivilegeBitsProvider(root); + + this.supported = supported; + this.granted = granted; + this.grantMap = grantMap; + } + + private static PrivilegeBits toBits(Set 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 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 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 granted; + private final Map grantMap; + + public CustomTreePermission(Set granted, Map 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; + } + + } +} diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderEmptyTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderEmptyTest.java index 399507782c..a1c13f8518 100644 --- a/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderEmptyTest.java +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderEmptyTest.java @@ -40,7 +40,7 @@ import static org.junit.Assert.assertFalse; 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 always grants full access to an administrative session. * - custom provider that prevents all access but supports all permissions and @@ -51,15 +51,28 @@ import static org.junit.Assert.assertTrue; * * The expected outcome is that despite the default provider granting full access, * the combination effectively prevents any access. + *

+ * Test the effect of the 'OR' combination of + * + * - default permission provider, which always grants full access to an administrative session. + * - custom provider that prevents all access but supports all permissions and + * thus is always called during composite evaluation. + * + * The tests are executed with the set of principals associated with the admin session, + * which in the default permission provider is granted full access. + * + * The expected outcome is that the combination effectively allows any access. */ public class CompositeProviderEmptyTest extends AbstractCompositeProviderTest { private CompositePermissionProvider cpp; + private CompositePermissionProvider cppO; @Override public void before() throws Exception { super.before(); cpp = createPermissionProvider(root.getContentSession().getAuthInfo().getPrincipals()); + cppO = createPermissionProviderOR(root.getContentSession().getAuthInfo().getPrincipals()); } @Override @@ -71,12 +84,14 @@ public class CompositeProviderEmptyTest extends AbstractCompositeProviderTest { public void testGetPrivileges() throws Exception { for (String p : NODE_PATHS) { assertTrue(cpp.getPrivileges(readOnlyRoot.getTree(p)).isEmpty()); + assertTrue(cppO.getPrivileges(readOnlyRoot.getTree(p)).isEmpty()); } } @Test public void testGetPrivilegesOnRepo() throws Exception { assertTrue(cpp.getPrivileges(null).isEmpty()); + assertTrue(cppO.getPrivileges(null).isEmpty()); } @Test @@ -92,10 +107,10 @@ public class CompositeProviderEmptyTest extends AbstractCompositeProviderTest { @Test public void testHasPrivilegesOnRepo() throws Exception { - assertFalse(cpp.hasPrivileges(null, JCR_NAMESPACE_MANAGEMENT)); + assertFalse(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 : NODE_PATHS) { @@ -104,6 +119,9 @@ public class CompositeProviderEmptyTest extends AbstractCompositeProviderTest { assertFalse(cpp.isGranted(tree, null, Permissions.READ_NODE)); assertFalse(cpp.isGranted(tree, null, Permissions.READ_NODE | Permissions.MODIFY_CHILD_NODE_COLLECTION)); assertFalse(cpp.isGranted(tree, null, Permissions.READ_ACCESS_CONTROL | Permissions.MODIFY_ACCESS_CONTROL)); + assertTrue(cppO.isGranted(tree, null, Permissions.READ_NODE)); + assertTrue(cppO.isGranted(tree, null, Permissions.READ_NODE | Permissions.MODIFY_CHILD_NODE_COLLECTION)); + assertTrue(cppO.isGranted(tree, null, Permissions.READ_ACCESS_CONTROL | Permissions.MODIFY_ACCESS_CONTROL)); } } @@ -117,6 +135,11 @@ public class CompositeProviderEmptyTest extends AbstractCompositeProviderTest { assertFalse(cpp.isGranted(tree, PROPERTY_STATE, Permissions.ADD_PROPERTY)); assertFalse(cpp.isGranted(tree, PROPERTY_STATE, Permissions.REMOVE_PROPERTY)); assertFalse(cpp.isGranted(tree, PROPERTY_STATE, Permissions.READ_ACCESS_CONTROL | Permissions.MODIFY_ACCESS_CONTROL)); + assertTrue(cppO.isGranted(tree, PROPERTY_STATE, Permissions.READ_PROPERTY)); + assertTrue(cppO.isGranted(tree, PROPERTY_STATE, Permissions.MODIFY_PROPERTY)); + assertTrue(cppO.isGranted(tree, PROPERTY_STATE, Permissions.ADD_PROPERTY)); + assertTrue(cppO.isGranted(tree, PROPERTY_STATE, Permissions.REMOVE_PROPERTY)); + assertTrue(cppO.isGranted(tree, PROPERTY_STATE, Permissions.READ_ACCESS_CONTROL | Permissions.MODIFY_ACCESS_CONTROL)); } } @@ -128,10 +151,14 @@ public class CompositeProviderEmptyTest extends AbstractCompositeProviderTest { 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))); assertFalse(cpp.isGranted(nonExisting, JackrabbitSession.ACTION_ADD_PROPERTY)); assertFalse(cpp.isGranted(nonExisting, Session.ACTION_ADD_NODE)); + assertTrue(cppO.isGranted(nodePath, Session.ACTION_REMOVE)); + assertTrue(cppO.isGranted(propPath, JackrabbitSession.ACTION_MODIFY_PROPERTY)); + assertTrue(cppO.isGranted(nodePath, getActionString(JackrabbitSession.ACTION_MODIFY_ACCESS_CONTROL, JackrabbitSession.ACTION_READ_ACCESS_CONTROL))); + assertTrue(cppO.isGranted(nonExisting, JackrabbitSession.ACTION_ADD_PROPERTY)); + assertTrue(cppO.isGranted(nonExisting, Session.ACTION_ADD_NODE)); } } @@ -140,20 +167,31 @@ public class CompositeProviderEmptyTest extends AbstractCompositeProviderTest { RepositoryPermission rp = cpp.getRepositoryPermission(); assertFalse(rp.isGranted(Permissions.NAMESPACE_MANAGEMENT)); assertFalse(rp.isGranted(Permissions.NODE_TYPE_DEFINITION_MANAGEMENT)); + RepositoryPermission rpO = cppO.getRepositoryPermission(); + assertTrue(rpO.isGranted(Permissions.NAMESPACE_MANAGEMENT)); + assertTrue(rpO.isGranted(Permissions.NODE_TYPE_DEFINITION_MANAGEMENT)); } @Test public void testTreePermissionIsGranted() throws Exception { TreePermission parentPermission = TreePermission.EMPTY; - for (String path : TP_PATHS) { TreePermission tp = cpp.getTreePermission(readOnlyRoot.getTree(path), parentPermission); - assertFalse(tp.isGranted(Permissions.READ_NODE)); assertFalse(tp.isGranted(Permissions.REMOVE_NODE)); assertFalse(tp.isGranted(Permissions.ALL)); + parentPermission = tp; + } + } - + @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)); + assertTrue(tp.isGranted(Permissions.REMOVE_NODE)); + assertTrue(tp.isGranted(Permissions.ALL)); parentPermission = tp; } } @@ -161,13 +199,21 @@ public class CompositeProviderEmptyTest extends AbstractCompositeProviderTest { @Test public void testTreePermissionIsGrantedProperty() throws Exception { TreePermission parentPermission = TreePermission.EMPTY; - for (String path : TP_PATHS) { TreePermission tp = cpp.getTreePermission(readOnlyRoot.getTree(path), parentPermission); - assertFalse(tp.isGranted(Permissions.READ_PROPERTY, PROPERTY_STATE)); assertFalse(tp.isGranted(Permissions.REMOVE_PROPERTY, PROPERTY_STATE)); + parentPermission = tp; + } + } + @Test + public void testTreePermissionIsGrantedPropertyOR() throws Exception { + TreePermission parentPermission = TreePermission.EMPTY; + for (String path : TP_PATHS) { + TreePermission tp = cppO.getTreePermission(readOnlyRoot.getTree(path), parentPermission); + assertTrue(tp.isGranted(Permissions.READ_PROPERTY, PROPERTY_STATE)); + assertTrue(tp.isGranted(Permissions.REMOVE_PROPERTY, PROPERTY_STATE)); parentPermission = tp; } } @@ -175,12 +221,21 @@ public class CompositeProviderEmptyTest extends AbstractCompositeProviderTest { @Test public void testTreePermissionCanRead() throws Exception { TreePermission parentPermission = TreePermission.EMPTY; - for (String path : TP_PATHS) { Tree t = readOnlyRoot.getTree(path); TreePermission tp = cpp.getTreePermission(t, parentPermission); assertFalse(tp.canRead()); + parentPermission = tp; + } + } + @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; } } @@ -188,12 +243,21 @@ public class CompositeProviderEmptyTest extends AbstractCompositeProviderTest { @Test public void testTreePermissionCanReadProperty() throws Exception { TreePermission parentPermission = TreePermission.EMPTY; - for (String path : TP_PATHS) { Tree t = readOnlyRoot.getTree(path); TreePermission tp = cpp.getTreePermission(t, parentPermission); 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); + assertTrue(tp.canRead(PROPERTY_STATE)); parentPermission = tp; } } @@ -201,11 +265,11 @@ public class CompositeProviderEmptyTest extends AbstractCompositeProviderTest { /** * {@code AggregatedPermissionProvider} that doesn't grant any access. */ - private static final class EmptyAggregatedProvider extends AbstractAggrProvider { + static class EmptyAggregatedProvider extends AbstractAggrProvider { private static final PermissionProvider BASE = EmptyPermissionProvider.getInstance(); - private EmptyAggregatedProvider(@Nonnull Root root) { + public EmptyAggregatedProvider(@Nonnull Root root) { super(root); } diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderNoScopeTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderNoScopeTest.java index 4ddb079cc9..7f503e5efd 100644 --- a/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderNoScopeTest.java +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderNoScopeTest.java @@ -93,6 +93,20 @@ public class CompositeProviderNoScopeTest extends AbstractCompositeProviderTest @Override @Test public void testGetTreePermissionInstance() throws Exception { + PermissionProvider pp = createPermissionProviderOR(); + TreePermission parentPermission = TreePermission.EMPTY; + + for (String path : TP_PATHS) { + Tree t = readOnlyRoot.getTree(path); + TreePermission tp = pp.getTreePermission(t, parentPermission); + assertCompositeTreePermission(t.isRoot(), tp); + parentPermission = tp; + } + } + + @Override + @Test + public void testGetTreePermissionInstanceOR() throws Exception { PermissionProvider pp = createPermissionProvider(); TreePermission parentPermission = TreePermission.EMPTY; @@ -121,6 +135,23 @@ public class CompositeProviderNoScopeTest extends AbstractCompositeProviderTest } } + @Override + @Test + public void testTreePermissionGetChildOR() throws Exception { + List 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); + assertCompositeTreePermission(tp); + + for (String cName : childNames) { + ns = ns.getChildNode(cName); + tp = tp.getChildPermission(cName, ns); + assertCompositeTreePermission(false, tp); + } + } + @Test public void testGetPrivileges() throws Exception { for (String p : defPrivileges.keySet()) { diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeTreePermissionTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeTreePermissionTest.java index e5e6eebec1..39e5cd321e 100644 --- a/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeTreePermissionTest.java +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeTreePermissionTest.java @@ -23,14 +23,16 @@ import javax.annotation.Nonnull; import org.apache.jackrabbit.oak.AbstractSecurityTest; import org.apache.jackrabbit.oak.api.Root; +import org.apache.jackrabbit.oak.api.Tree; import org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants; 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.Context; import org.apache.jackrabbit.oak.spi.security.authorization.permission.AggregatedPermissionProvider; import org.apache.jackrabbit.oak.spi.security.authorization.permission.TreePermission; import org.apache.jackrabbit.oak.spi.state.NodeState; -import org.apache.jackrabbit.oak.util.NodeUtil; import org.junit.Test; import static org.junit.Assert.assertEquals; @@ -50,8 +52,8 @@ public class CompositeTreePermissionTest extends AbstractSecurityTest { public void before() throws Exception { super.before(); - NodeUtil rootNode = new NodeUtil(root.getTree("/")); - rootNode.addChild("test", NodeTypeConstants.NT_OAK_UNSTRUCTURED); + Tree rootNode = root.getTree("/"); + TreeUtil.addChild(rootNode, "test", NodeTypeConstants.NT_OAK_UNSTRUCTURED); root.commit(); readOnlyRoot = RootFactory.createReadOnlyRoot(root); @@ -72,7 +74,8 @@ public class CompositeTreePermissionTest extends AbstractSecurityTest { } private TreePermission createRootTreePermission(AggregatedPermissionProvider... providers) { - return new CompositePermissionProvider(readOnlyRoot, Arrays.asList(providers), Context.DEFAULT).getTreePermission(rootTree, TreePermission.EMPTY); + return new CompositePermissionProvider(readOnlyRoot, Arrays.asList(providers), Context.DEFAULT, CompositionType.AND) + .getTreePermission(rootTree, TreePermission.EMPTY); } private static void assertCompositeTreePermission(boolean expected, @Nonnull TreePermission tp) {