diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/AuthorizationConfigurationImpl.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/AuthorizationConfigurationImpl.java index b5259862f9..09288bde8d 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/AuthorizationConfigurationImpl.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/AuthorizationConfigurationImpl.java @@ -17,19 +17,20 @@ package org.apache.jackrabbit.oak.security.authorization; import java.security.Principal; -import java.util.Collections; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; + import javax.annotation.Nonnull; import javax.jcr.security.AccessControlManager; -import com.google.common.collect.ImmutableList; import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; 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.Reference; import org.apache.felix.scr.annotations.Service; import org.apache.jackrabbit.oak.api.Root; import org.apache.jackrabbit.oak.namepath.NamePathMapper; @@ -39,6 +40,7 @@ import org.apache.jackrabbit.oak.plugins.version.VersionablePathHook; import org.apache.jackrabbit.oak.security.authorization.accesscontrol.AccessControlImporter; import org.apache.jackrabbit.oak.security.authorization.accesscontrol.AccessControlManagerImpl; import org.apache.jackrabbit.oak.security.authorization.accesscontrol.AccessControlValidatorProvider; +import org.apache.jackrabbit.oak.security.authorization.composite.CompositePermissionProvider; import org.apache.jackrabbit.oak.security.authorization.permission.PermissionHook; import org.apache.jackrabbit.oak.security.authorization.permission.PermissionProviderImpl; import org.apache.jackrabbit.oak.security.authorization.permission.PermissionStoreValidatorProvider; @@ -48,6 +50,9 @@ import org.apache.jackrabbit.oak.spi.commit.CommitHook; import org.apache.jackrabbit.oak.spi.commit.MoveTracker; import org.apache.jackrabbit.oak.spi.commit.ValidatorProvider; import org.apache.jackrabbit.oak.spi.lifecycle.WorkspaceInitializer; +import org.apache.jackrabbit.oak.spi.mount.Mount; +import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider; +import org.apache.jackrabbit.oak.spi.mount.Mounts; import org.apache.jackrabbit.oak.spi.security.CompositeConfiguration; import org.apache.jackrabbit.oak.spi.security.ConfigurationBase; import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters; @@ -56,6 +61,7 @@ import org.apache.jackrabbit.oak.spi.security.SecurityConfiguration; import org.apache.jackrabbit.oak.spi.security.SecurityProvider; import org.apache.jackrabbit.oak.spi.security.authorization.AuthorizationConfiguration; import org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.AccessControlConstants; +import org.apache.jackrabbit.oak.spi.security.authorization.permission.AggregatedPermissionProvider; import org.apache.jackrabbit.oak.spi.security.authorization.permission.PermissionConstants; import org.apache.jackrabbit.oak.spi.security.authorization.permission.PermissionProvider; import org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionProvider; @@ -63,6 +69,8 @@ import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants; import org.apache.jackrabbit.oak.spi.xml.ImportBehavior; import org.apache.jackrabbit.oak.spi.xml.ProtectedItemImporter; +import com.google.common.collect.ImmutableList; + /** * Default implementation of the {@code AccessControlConfiguration}. */ @@ -104,6 +112,9 @@ import org.apache.jackrabbit.oak.spi.xml.ProtectedItemImporter; }) public class AuthorizationConfigurationImpl extends ConfigurationBase implements AuthorizationConfiguration { + @Reference + private MountInfoProvider mountInfoProvider; + public AuthorizationConfigurationImpl() { super(); } @@ -116,6 +127,8 @@ public class AuthorizationConfigurationImpl extends ConfigurationBase implements public AuthorizationConfigurationImpl(SecurityProvider securityProvider) { super(securityProvider, securityProvider.getParameters(NAME)); + mountInfoProvider = getParameters().getConfigValue(AccessControlConstants.PARAM_MOUNT_PROVIDER, + Mounts.defaultMountInfoProvider(), MountInfoProvider.class); } //----------------------------------------------< SecurityConfiguration >--- @@ -134,7 +147,7 @@ public class AuthorizationConfigurationImpl extends ConfigurationBase implements @Nonnull @Override public WorkspaceInitializer getWorkspaceInitializer() { - return new AuthorizationInitializer(); + return new AuthorizationInitializer(mountInfoProvider); } @Nonnull @@ -157,7 +170,7 @@ public class AuthorizationConfigurationImpl extends ConfigurationBase implements @Nonnull @Override public List getProtectedItemImporters() { - return Collections.singletonList(new AccessControlImporter()); + return ImmutableList.of(new AccessControlImporter()); } //-----------------------------------------< AccessControlConfiguration >--- @@ -180,8 +193,23 @@ public class AuthorizationConfigurationImpl extends ConfigurationBase implements @Nonnull @Override - public PermissionProvider getPermissionProvider(@Nonnull Root root, @Nonnull String workspaceName, @Nonnull Set principals) { + public PermissionProvider getPermissionProvider(@Nonnull Root root, @Nonnull String workspaceName, + @Nonnull Set principals) { Context ctx = getSecurityProvider().getConfiguration(AuthorizationConfiguration.class).getContext(); - return new PermissionProviderImpl(root, workspaceName, principals, getRestrictionProvider(), getParameters(), ctx); + + if (mountInfoProvider.hasNonDefaultMounts()) { + List agg = new ArrayList<>(); + agg.add(new PermissionProviderImpl(root, workspaceName, principals, getRestrictionProvider(), + getParameters(), ctx)); + for (Mount m : mountInfoProvider.getNonDefaultMounts()) { + // TODO concatenate? + String ws = m.getPathFragmentName() + "-" + workspaceName; + agg.add(new PermissionProviderImpl(root, ws, principals, getRestrictionProvider(), getParameters(), + ctx)); + } + return new CompositePermissionProvider(root, agg, ctx, CompositePermissionProvider.CheckType.OR); + } + return new PermissionProviderImpl(root, workspaceName, principals, getRestrictionProvider(), getParameters(), + ctx); } } diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/AuthorizationInitializer.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/AuthorizationInitializer.java index 993b0dc49b..04e853aa07 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/AuthorizationInitializer.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/AuthorizationInitializer.java @@ -20,9 +20,11 @@ import com.google.common.collect.ImmutableList; import org.apache.jackrabbit.JcrConstants; import org.apache.jackrabbit.oak.api.Type; import org.apache.jackrabbit.oak.plugins.index.IndexUtils; -import org.apache.jackrabbit.oak.spi.security.authorization.permission.PermissionConstants; import org.apache.jackrabbit.oak.spi.lifecycle.WorkspaceInitializer; +import org.apache.jackrabbit.oak.spi.mount.Mount; +import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider; import org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.AccessControlConstants; +import org.apache.jackrabbit.oak.spi.security.authorization.permission.PermissionConstants; import org.apache.jackrabbit.oak.spi.state.NodeBuilder; import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE; @@ -40,6 +42,12 @@ import static org.apache.jackrabbit.JcrConstants.JCR_SYSTEM; */ class AuthorizationInitializer implements WorkspaceInitializer, AccessControlConstants, PermissionConstants { + private final MountInfoProvider mountInfoProvider; + + public AuthorizationInitializer(MountInfoProvider mountInfoProvider) { + this.mountInfoProvider = mountInfoProvider; + } + @Override public void initialize(NodeBuilder builder, String workspaceName) { // property index for rep:principalName stored in ACEs @@ -62,6 +70,13 @@ class AuthorizationInitializer implements WorkspaceInitializer, AccessControlCon if (!permissionStore.hasChildNode(workspaceName)) { permissionStore.child(workspaceName).setProperty(JcrConstants.JCR_PRIMARYTYPE, NT_REP_PERMISSION_STORE, Type.NAME); } + for (Mount m : mountInfoProvider.getNonDefaultMounts()) { + // TODO concatenate? + String ws = m.getPathFragmentName() + "-" + workspaceName; + if (!permissionStore.hasChildNode(ws)) { + permissionStore.child(ws).setProperty(JcrConstants.JCR_PRIMARYTYPE, NT_REP_PERMISSION_STORE, Type.NAME); + } + } } } 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..9675f2281d 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 @@ -45,11 +45,27 @@ import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeBitsProvider; * {@link org.apache.jackrabbit.oak.spi.security.authorization.permission.AggregatedPermissionProvider} * interface. */ -class CompositePermissionProvider implements PermissionProvider { +public class CompositePermissionProvider implements PermissionProvider { + + public static enum CheckType { + + /** + * 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 final Root root; private final AggregatedPermissionProvider[] pps; private final Context ctx; + private final CheckType checkType; private final RepositoryPermission repositoryPermission; @@ -57,12 +73,19 @@ class CompositePermissionProvider implements PermissionProvider { private PrivilegeBitsProvider privilegeBitsProvider; private TreeTypeProvider typeProvider; - CompositePermissionProvider(@Nonnull Root root, @Nonnull List pps, @Nonnull Context acContext) { + public CompositePermissionProvider(@Nonnull Root root, @Nonnull List pps, + @Nonnull Context acContext) { + this(root, pps, acContext, CheckType.AND); + } + + public CompositePermissionProvider(@Nonnull Root root, @Nonnull List pps, + @Nonnull Context acContext, CheckType checkType) { this.root = root; this.pps = pps.toArray(new AggregatedPermissionProvider[pps.size()]); this.ctx = acContext; + this.checkType = checkType; - repositoryPermission = new CompositeRepositoryPermission(); + repositoryPermission = new CompositeRepositoryPermission(this.pps, this.checkType); immutableRoot = RootFactory.createReadOnlyRoot(root); privilegeBitsProvider = new PrivilegeBitsProvider(immutableRoot); typeProvider = new TreeTypeProvider(ctx); @@ -144,7 +167,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, checkType); } else if (parentPermission instanceof CompositeTreePermission) { return CompositeTreePermission.create(immutableTree, ((CompositeTreePermission) parentPermission)); } else { @@ -163,9 +186,10 @@ class CompositePermissionProvider implements PermissionProvider { long supportedPermissions = aggregatedPermissionProvider.supportedPermissions(immParent, property, permissions); if (doEvaluate(supportedPermissions)) { isGranted = aggregatedPermissionProvider.isGranted(immParent, property, supportedPermissions); + // TODO only if isGranted true? coveredPermissions |= supportedPermissions; - if (!isGranted) { + if (!isGranted && checkType == CheckType.AND) { break; } } @@ -193,9 +217,10 @@ class CompositePermissionProvider implements PermissionProvider { long supportedPermissions = aggregatedPermissionProvider.supportedPermissions(location, permissions); if (doEvaluate(supportedPermissions)) { isGranted = aggregatedPermissionProvider.isGranted(location, supportedPermissions); + // TODO only if isGranted true? coveredPermissions |= supportedPermissions; - if (!isGranted) { + if (!isGranted && checkType == CheckType.AND) { break; } } @@ -218,7 +243,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 CheckType checkType; + + public CompositeRepositoryPermission(AggregatedPermissionProvider[] pps, CheckType checkType) { + this.pps = pps; + this.checkType = checkType; + } @Override public boolean isGranted(long repositoryPermissions) { @@ -229,8 +263,9 @@ class CompositePermissionProvider implements PermissionProvider { long supportedPermissions = aggregatedPermissionProvider.supportedPermissions((Tree) null, null, repositoryPermissions); if (doEvaluate(supportedPermissions)) { isGranted = aggregatedPermissionProvider.getRepositoryPermission().isGranted(supportedPermissions); + // TODO only if isGranted true? coveredPermissions |= supportedPermissions; - if (!isGranted) { + if (!isGranted && checkType == CheckType.AND) { break; } } 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..229898fcab 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 @@ -24,6 +24,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.CompositePermissionProvider.CheckType; 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 +38,7 @@ final class CompositeTreePermission implements TreePermission { private final ImmutableTree tree; private final TreeType type; + private final CheckType checkType; private final TreeTypeProvider typeProvider; private final AggregatedPermissionProvider[] providers; @@ -46,7 +48,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, CheckType checkType) { this.tree = tree; this.type = type; @@ -54,9 +58,11 @@ final class CompositeTreePermission implements TreePermission { this.providers = providers; this.treePermissions = treePermissions; this.childSize = providers.length - cnt; + this.checkType = checkType; } - static TreePermission create(@Nonnull ImmutableTree rootTree, @Nonnull TreeTypeProvider typeProvider, @Nonnull AggregatedPermissionProvider[] providers) { + static TreePermission create(@Nonnull ImmutableTree rootTree, @Nonnull TreeTypeProvider typeProvider, + @Nonnull AggregatedPermissionProvider[] providers, CheckType checkType) { switch (providers.length) { case 0 : return TreePermission.EMPTY; case 1 : return providers[0].getTreePermission(rootTree, TreeType.DEFAULT, TreePermission.EMPTY); @@ -70,7 +76,7 @@ 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, checkType); } } @@ -115,7 +121,7 @@ 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.checkType); } } @@ -191,9 +197,12 @@ final class CompositeTreePermission implements TreePermission { isGranted = (property == null) ? tp.isGranted(supported) : tp.isGranted(supported, property); coveredPermissions |= supported; - if (!isGranted) { + if (!isGranted && checkType == CheckType.AND) { return false; } + if (isGranted && checkType == CheckType.OR) { + return true; + } } } return isGranted && coveredPermissions == permissions; @@ -209,7 +218,10 @@ 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) { + if (!readable && checkType == CheckType.AND) { + break; + } + if (readable && checkType == CheckType.OR) { break; } } diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/accesscontrol/AccessControlConstants.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/accesscontrol/AccessControlConstants.java index 59b6a22381..08779194c9 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/accesscontrol/AccessControlConstants.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/accesscontrol/AccessControlConstants.java @@ -82,4 +82,6 @@ public interface AccessControlConstants { Collection AC_NODETYPE_NAMES = ImmutableSet.of(NT_REP_POLICY, NT_REP_ACL, NT_REP_ACE, NT_REP_DENY_ACE, NT_REP_GRANT_ACE, NT_REP_RESTRICTIONS); String PARAM_RESTRICTION_PROVIDER = "restrictionProvider"; + + String PARAM_MOUNT_PROVIDER = "mountInfoProvider"; } diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/MultiplexingSupportTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/MultiplexingSupportTest.java new file mode 100644 index 0000000000..aaf116ac83 --- /dev/null +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/MultiplexingSupportTest.java @@ -0,0 +1,159 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jackrabbit.oak.security.authorization.permission; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.security.Principal; +import java.util.Collections; + +import javax.jcr.security.AccessControlManager; +import javax.jcr.security.Privilege; + +import org.apache.jackrabbit.JcrConstants; +import org.apache.jackrabbit.api.security.JackrabbitAccessControlList; +import org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils; +import org.apache.jackrabbit.oak.AbstractSecurityTest; +import org.apache.jackrabbit.oak.api.ContentSession; +import org.apache.jackrabbit.oak.api.Root; +import org.apache.jackrabbit.oak.api.Tree; +import org.apache.jackrabbit.oak.plugins.tree.TreeUtil; +import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider; +import org.apache.jackrabbit.oak.spi.mount.Mounts; +import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters; +import org.apache.jackrabbit.oak.spi.security.authorization.AuthorizationConfiguration; +import org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.AccessControlConstants; +import org.apache.jackrabbit.oak.spi.security.authorization.permission.PermissionConstants; +import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; + +public class MultiplexingSupportTest extends AbstractSecurityTest + implements AccessControlConstants, PrivilegeConstants, PermissionConstants { + + private MountInfoProvider mountInfoProvider; + private String testPath = "/testPath"; + + @Override + @Before + public void before() throws Exception { + mountInfoProvider = Mounts.newBuilder().mount("testMount", testPath + "/a").build(); + + super.before(); + Tree rootNode = root.getTree("/"); + Tree testNode = TreeUtil.addChild(rootNode, "testPath", JcrConstants.NT_UNSTRUCTURED); + TreeUtil.addChild(testNode, "childNode", JcrConstants.NT_UNSTRUCTURED); + root.commit(); + } + + @Override + @After + public void after() throws Exception { + try { + root.refresh(); + Tree test = root.getTree(testPath); + if (test.exists()) { + test.remove(); + } + root.commit(); + } finally { + super.after(); + } + } + + @Override + protected ConfigurationParameters getSecurityConfigParameters() { + ConfigurationParameters authConfig = ConfigurationParameters.of(Collections.singletonMap( + AccessControlConstants.PARAM_MOUNT_PROVIDER, Preconditions.checkNotNull(mountInfoProvider))); + return ConfigurationParameters.of(ImmutableMap.of(AuthorizationConfiguration.NAME, authConfig)); + } + + @Test + public void multiplexingPermissionStore() throws Exception { + String path_a = testPath + "/a"; + String path_c = testPath + "/c"; + String path_d = testPath + "/d"; + + Principal p = getTestUser().getPrincipal(); + Tree testNode = root.getTree(testPath); + TreeUtil.addChild(testNode, "a", JcrConstants.NT_UNSTRUCTURED); + TreeUtil.addChild(testNode, "c", JcrConstants.NT_UNSTRUCTURED); + TreeUtil.addChild(testNode, "d", JcrConstants.NT_UNSTRUCTURED); + + setPrivs(path_a, privilegesFromNames(JCR_READ, REP_WRITE), p); + setPrivs(path_c, privilegesFromNames(JCR_READ, REP_WRITE), p); + + root.commit(); + + assertTrue(root.getTree(path_a + "/rep:policy").exists()); + assertTrue(root.getTree(path_c + "/rep:policy").exists()); + + // TODO too impl specific + // Tree principalRoot = getPrincipalRoot(mountInfoProvider, path_a, + // p.getName()); + // assertEquals(2, cntEntries(principalRoot)); + // principalRoot = getPrincipalRoot(mountInfoProvider, path_c, + // p.getName()); + // assertEquals(2, cntEntries(principalRoot)); + + ContentSession testSession = createTestSession(); + + try { + Root r = testSession.getLatestRoot(); + + Tree t0 = r.getTree(path_a); + assertTrue(t0.exists()); + assertTrue(r.getTree(path_c).exists()); + assertFalse(r.getTree(path_d).exists()); + } finally { + testSession.close(); + } + } + + private void setPrivs(String path, Privilege[] privileges, Principal principal) throws Exception { + AccessControlManager acMgr = getAccessControlManager(root); + JackrabbitAccessControlList acl = AccessControlUtils.getAccessControlList(acMgr, path); + acl.addAccessControlEntry(principal, privileges); + acMgr.setPolicy(path, acl); + } + +// private Tree getPrincipalRoot(MountInfoProvider mip, String path, String principalName) { +// Mount m = mip.getMountByPath(path); +// Tree permStore = root.getTree(PERMISSIONS_STORE_PATH); +// String ws = adminSession.getWorkspaceName(); +// if (m.isDefault()) { +// return permStore.getChild(ws).getChild(principalName); +// } else { +// // TODO concatenation +// return permStore.getChild(m.getPathFragmentName() + "-" + ws).getChild(principalName); +// } +// } + + protected long cntEntries(Tree parent) { + long cnt = parent.getChildrenCount(Long.MAX_VALUE); + for (Tree child : parent.getChildren()) { + cnt += cntEntries(child); + } + return cnt; + } + +}