You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by tr...@apache.org on 2015/09/04 19:12:50 UTC

svn commit: r1701296 - in /jackrabbit/oak/branches/1.0/oak-core/src: main/java/org/apache/jackrabbit/oak/spi/security/authorization/restriction/ test/java/org/apache/jackrabbit/oak/security/authorization/restriction/

Author: tripod
Date: Fri Sep  4 17:12:50 2015
New Revision: 1701296

URL: http://svn.apache.org/r1701296
Log:
OAK-3324 hasPermission does not reflect actual behavior with restrictions

- adding more tests


Conflicts:
	oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/restriction/PermissionTest.java

Added:
    jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/restriction/CustomRestrictionProviderTest.java
    jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/restriction/PermissionTest.java
Modified:
    jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/restriction/RestrictionPattern.java

Modified: jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/restriction/RestrictionPattern.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/restriction/RestrictionPattern.java?rev=1701296&r1=1701295&r2=1701296&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/restriction/RestrictionPattern.java (original)
+++ jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/restriction/RestrictionPattern.java Fri Sep  4 17:12:50 2015
@@ -79,5 +79,10 @@ public interface RestrictionPattern {
         public boolean matches() {
             return true;
         }
+
+        @Override
+        public String toString() {
+            return "RestrictionPattern.EMPTY";
+        }
     };
 }

Added: jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/restriction/CustomRestrictionProviderTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/restriction/CustomRestrictionProviderTest.java?rev=1701296&view=auto
==============================================================================
--- jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/restriction/CustomRestrictionProviderTest.java (added)
+++ jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/restriction/CustomRestrictionProviderTest.java Fri Sep  4 17:12:50 2015
@@ -0,0 +1,344 @@
+/*
+ * 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.restriction;
+
+import java.security.Principal;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.jcr.Value;
+import javax.jcr.security.AccessControlManager;
+
+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.CommitFailedException;
+import org.apache.jackrabbit.oak.api.ContentSession;
+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.api.Type;
+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.PermissionProvider;
+import org.apache.jackrabbit.oak.spi.security.authorization.permission.Permissions;
+import org.apache.jackrabbit.oak.spi.security.authorization.restriction.AbstractRestrictionProvider;
+import org.apache.jackrabbit.oak.spi.security.authorization.restriction.CompositeRestrictionProvider;
+import org.apache.jackrabbit.oak.spi.security.authorization.restriction.Restriction;
+import org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionDefinition;
+import org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionDefinitionImpl;
+import org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionPattern;
+import org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionProvider;
+import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants;
+import org.apache.jackrabbit.oak.util.NodeUtil;
+import org.apache.jackrabbit.value.StringValue;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableMap;
+
+import static org.apache.jackrabbit.JcrConstants.NT_UNSTRUCTURED;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+public class CustomRestrictionProviderTest extends AbstractSecurityTest {
+
+    private static final String TEST_ROOT_PATH = "/testRoot";
+    private static final String TEST_A_PATH = "/testRoot/a";
+    private static final String TEST_B_PATH = "/testRoot/a/b";
+    private static final String TEST_C_PATH = "/testRoot/a/b/c";
+    private static final String TEST_D_PATH = "/testRoot/a/b/c/d";
+    private static final String TEST_E_PATH = "/testRoot/a/b/c/d/e";
+    private static final String PROP_NAME_PROTECT_ME = "protect-me";
+
+    private Principal testPrincipal;
+
+    @Override
+    protected ConfigurationParameters getSecurityConfigParameters() {
+        RestrictionProvider rProvider = CompositeRestrictionProvider.newInstance(new PropertyRestrictionProvider(), new RestrictionProviderImpl());
+        Map<String, RestrictionProvider> authorizMap = ImmutableMap.of(AccessControlConstants.PARAM_RESTRICTION_PROVIDER, rProvider);
+        return ConfigurationParameters.of(ImmutableMap.of(AuthorizationConfiguration.NAME, ConfigurationParameters.of(authorizMap)));
+    }
+
+    @Before
+    @Override
+    public void before() throws Exception {
+        super.before();
+
+        NodeUtil rootNode = new NodeUtil(root.getTree("/"));
+        NodeUtil testRootNode = rootNode.addChild("testRoot", NT_UNSTRUCTURED);
+        NodeUtil a = testRootNode.addChild("a", NT_UNSTRUCTURED);
+        NodeUtil b = a.addChild("b", NT_UNSTRUCTURED);
+        NodeUtil c = b.addChild("c", NT_UNSTRUCTURED);
+        c.setBoolean(PROP_NAME_PROTECT_ME, true);
+        NodeUtil d = c.addChild("d", NT_UNSTRUCTURED);
+        d.addChild("e", NT_UNSTRUCTURED);
+        root.commit();
+
+        testPrincipal = getTestUser().getPrincipal();
+    }
+
+    @After
+    @Override
+    public void after() throws Exception {
+        try {
+            // revert uncommitted changes
+            root.refresh();
+
+            // remove all test content
+            root.getTree(TEST_ROOT_PATH).remove();
+            root.commit();
+        } finally {
+            super.after();
+        }
+    }
+
+    private void addEntry(String path, boolean grant, String restriction, String... privilegeNames) throws Exception {
+        AccessControlManager acMgr = getAccessControlManager(root);
+        JackrabbitAccessControlList acl = AccessControlUtils.getAccessControlList(acMgr, path);
+        if (restriction.length() > 0) {
+            Map<String, Value> rs = new HashMap<String, Value>();
+            rs.put(PropertyRestrictionProvider.RESTRICTION_NAME, new StringValue(restriction));
+            acl.addEntry(testPrincipal, AccessControlUtils.privilegesFromNames(acMgr, privilegeNames), grant, rs);
+        } else {
+            acl.addEntry(testPrincipal, AccessControlUtils.privilegesFromNames(acMgr, privilegeNames), grant);
+        }
+        acMgr.setPolicy(path, acl);
+        root.commit();
+    }
+
+    private void assertIsGranted(PermissionProvider pp, Root root, boolean allow, String path, long permissions) {
+        assertEquals("user should " + (allow ? "" : "not ") + "have " + permissions + " on " + path,
+                allow, pp.isGranted(root.getTree(path), null, permissions));
+    }
+
+    private PermissionProvider getPermissionProvider(ContentSession session) {
+        return getSecurityProvider()
+                .getConfiguration(AuthorizationConfiguration.class)
+                .getPermissionProvider(root, session.getWorkspaceName(), session.getAuthInfo().getPrincipals());
+    }
+
+    /**
+     * Tests the custom restriction provider that checks on the existence of a property.
+     * @throws Exception
+     */
+    @Test
+    @Ignore("OAK-3324")
+    public void testProtectByRestriction() throws Exception {
+        // create permissions
+        // allow rep:write      /testroot
+        // deny  jcr:removeNode /testroot/a  hasProperty = protect-me
+
+        addEntry(TEST_ROOT_PATH, true, "", PrivilegeConstants.JCR_READ, PrivilegeConstants.REP_WRITE);
+        addEntry(TEST_A_PATH, false, PROP_NAME_PROTECT_ME, PrivilegeConstants.JCR_REMOVE_NODE);
+
+        ContentSession testSession = createTestSession();
+        try {
+            Root testRoot = testSession.getLatestRoot();
+            PermissionProvider pp = getPermissionProvider(testSession);
+            assertIsGranted(pp, testRoot, true, TEST_A_PATH, Permissions.REMOVE_NODE);
+            assertIsGranted(pp, testRoot, true, TEST_B_PATH, Permissions.REMOVE_NODE);
+            assertIsGranted(pp, testRoot, false, TEST_C_PATH, Permissions.REMOVE_NODE);
+            assertIsGranted(pp, testRoot, true , TEST_D_PATH, Permissions.REMOVE_NODE);
+            assertIsGranted(pp, testRoot, true, TEST_E_PATH, Permissions.REMOVE_NODE);
+
+            // should be able to remove /a/b/c/d
+            testRoot.getTree(TEST_D_PATH).remove();
+            testRoot.commit();
+
+            try {
+                testRoot.getTree(TEST_C_PATH).remove();
+                testRoot.commit();
+                fail("should not be able to delete " + TEST_C_PATH);
+            } catch (CommitFailedException e) {
+                // all ok
+            }
+        } finally {
+            testSession.close();
+        }
+    }
+
+    /**
+     * Tests the custom restriction provider that checks on the existence of a property.
+     * @throws Exception
+     */
+    @Test
+    public void testProtectPropertiesByRestriction() throws Exception {
+        // create permissions
+        // allow rep:write          /testroot
+        // deny  jcr:modifyProperties /testroot/a  hasProperty = protect-me
+
+        addEntry(TEST_ROOT_PATH, true, "", PrivilegeConstants.JCR_READ, PrivilegeConstants.REP_WRITE);
+        addEntry(TEST_A_PATH, false, PROP_NAME_PROTECT_ME, PrivilegeConstants.JCR_MODIFY_PROPERTIES);
+
+        ContentSession testSession = createTestSession();
+        try {
+            Root testRoot = testSession.getLatestRoot();
+
+            PermissionProvider pp = getPermissionProvider(testSession);
+            assertIsGranted(pp, testRoot, true , TEST_A_PATH, Permissions.MODIFY_PROPERTY);
+            assertIsGranted(pp, testRoot, true, TEST_B_PATH, Permissions.MODIFY_PROPERTY);
+            assertIsGranted(pp, testRoot, false, TEST_C_PATH, Permissions.MODIFY_PROPERTY);
+            assertIsGranted(pp, testRoot, true, TEST_D_PATH, Permissions.MODIFY_PROPERTY);
+            assertIsGranted(pp, testRoot, true, TEST_E_PATH, Permissions.MODIFY_PROPERTY);
+
+        } finally {
+            testSession.close();
+        }
+    }
+
+    /**
+     * Tests the custom restriction provider that checks on the existence of a property.
+     * @throws Exception
+     */
+    @Test
+    @Ignore("OAK-3324")
+    public void testUnProtectByRestriction() throws Exception {
+        // create permissions
+        // allow rep:write      /testroot
+        // deny  jcr:removeNode /testroot
+        // allow jcr:removeNode /testroot/a  hasProperty = !protect-me
+
+        addEntry(TEST_ROOT_PATH, true, "", PrivilegeConstants.JCR_READ, PrivilegeConstants.REP_WRITE);
+        addEntry(TEST_ROOT_PATH, false, "", PrivilegeConstants.JCR_REMOVE_NODE);
+        addEntry(TEST_A_PATH, true, "!" + PROP_NAME_PROTECT_ME, PrivilegeConstants.JCR_REMOVE_NODE);
+
+        ContentSession testSession = createTestSession();
+        try {
+            Root testRoot = testSession.getLatestRoot();
+            PermissionProvider pp = getPermissionProvider(testSession);
+            assertIsGranted(pp, testRoot, true, TEST_A_PATH, Permissions.REMOVE_NODE);
+            assertIsGranted(pp, testRoot, true, TEST_B_PATH, Permissions.REMOVE_NODE);
+            assertIsGranted(pp, testRoot, false, TEST_C_PATH, Permissions.REMOVE_NODE);
+            assertIsGranted(pp, testRoot, true, TEST_D_PATH, Permissions.REMOVE_NODE);
+            assertIsGranted(pp, testRoot, true, TEST_E_PATH, Permissions.REMOVE_NODE);
+
+        } finally {
+            testSession.close();
+        }
+    }
+
+    public static class PropertyRestrictionProvider extends AbstractRestrictionProvider {
+
+        public static final String RESTRICTION_NAME = "hasProperty";
+
+        public PropertyRestrictionProvider() {
+            super(supportedRestrictions());
+        }
+
+        private static Map<String, RestrictionDefinition> supportedRestrictions() {
+            RestrictionDefinition dates = new RestrictionDefinitionImpl(RESTRICTION_NAME, Type.STRING, false);
+            return Collections.singletonMap(dates.getName(), dates);
+        }
+
+        //------------------------------------------------< RestrictionProvider >---
+
+        @Override
+        public RestrictionPattern getPattern(String oakPath, Tree tree) {
+            if (oakPath != null) {
+                PropertyState property = tree.getProperty(RESTRICTION_NAME);
+                if (property != null) {
+                    return HasPropertyPattern.create(property);
+                }
+            }
+            return RestrictionPattern.EMPTY;
+        }
+
+        @Nonnull
+        @Override
+        public RestrictionPattern getPattern(@Nullable String oakPath, @Nonnull Set<Restriction> restrictions) {
+            if (oakPath != null) {
+                for (Restriction r : restrictions) {
+                    String name = r.getDefinition().getName();
+                    if (RESTRICTION_NAME.equals(name)) {
+                        return HasPropertyPattern.create(r.getProperty());
+                    }
+                }
+            }
+            return RestrictionPattern.EMPTY;
+        }
+    }
+
+    public static class HasPropertyPattern implements RestrictionPattern {
+
+        private final String propertyName;
+
+        private final boolean negate;
+
+        private HasPropertyPattern(@Nonnull String propertyName) {
+            if (propertyName.startsWith("!")) {
+                this.propertyName = propertyName.substring(1);
+                negate = true;
+            } else {
+                this.propertyName = propertyName;
+                negate = false;
+            }
+        }
+
+        static RestrictionPattern create(PropertyState stringProperty) {
+            if (stringProperty.count() == 1) {
+                return new HasPropertyPattern(stringProperty.getValue(Type.STRING));
+            } else {
+                return RestrictionPattern.EMPTY;
+            }
+        }
+
+        @Override
+        public boolean matches(@Nonnull Tree tree, @Nullable PropertyState property) {
+            boolean match = false;
+
+            // configured property name found on underlying jcr:content node has precedence
+            if (tree.hasChild(JcrConstants.JCR_CONTENT)) {
+                match = tree.getChild(JcrConstants.JCR_CONTENT).hasProperty(propertyName);
+            }
+            if (!match) {
+                match = tree.hasProperty(propertyName);
+            }
+
+            return negate ? !match : match;
+        }
+
+        @Override
+        public boolean matches(@Nonnull String path) {
+            return matches();
+        }
+
+        @Override
+        public boolean matches() {
+            return false;
+        }
+
+        @Override
+        public String toString() {
+            final StringBuilder sb = new StringBuilder("HasPropertyPattern{");
+            sb.append("propertyName='").append(propertyName).append('\'');
+            sb.append(", negate=").append(negate);
+            sb.append('}');
+            return sb.toString();
+        }
+    }
+}
+
+

Added: jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/restriction/PermissionTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/restriction/PermissionTest.java?rev=1701296&view=auto
==============================================================================
--- jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/restriction/PermissionTest.java (added)
+++ jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/restriction/PermissionTest.java Fri Sep  4 17:12:50 2015
@@ -0,0 +1,239 @@
+/*
+ * 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.restriction;
+
+import java.security.Principal;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.jcr.Value;
+import javax.jcr.security.AccessControlManager;
+
+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.CommitFailedException;
+import org.apache.jackrabbit.oak.api.ContentSession;
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.spi.security.authorization.AuthorizationConfiguration;
+import org.apache.jackrabbit.oak.spi.security.authorization.permission.PermissionProvider;
+import org.apache.jackrabbit.oak.spi.security.authorization.permission.Permissions;
+import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants;
+import org.apache.jackrabbit.oak.util.NodeUtil;
+import org.apache.jackrabbit.value.StringValue;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import static org.apache.jackrabbit.JcrConstants.NT_UNSTRUCTURED;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+public class PermissionTest extends AbstractSecurityTest {
+
+    private static final String TEST_ROOT_PATH = "/testRoot";
+    private static final String TEST_A_PATH = "/testRoot/a";
+    private static final String TEST_B_PATH = "/testRoot/a/b";
+    private static final String TEST_C_PATH = "/testRoot/a/b/c";
+    private static final String TEST_D_PATH = "/testRoot/a/b/c/d";
+
+    private Principal testPrincipal;
+
+    @Before
+    @Override
+    public void before() throws Exception {
+        super.before();
+
+        NodeUtil rootNode = new NodeUtil(root.getTree("/"));
+        NodeUtil testRootNode = rootNode.addChild("testRoot", NT_UNSTRUCTURED);
+        NodeUtil a = testRootNode.addChild("a", NT_UNSTRUCTURED);
+        NodeUtil b = a.addChild("b", NT_UNSTRUCTURED);
+        NodeUtil c = b.addChild("c", NT_UNSTRUCTURED);
+        c.addChild("d", NT_UNSTRUCTURED);
+        root.commit();
+
+        testPrincipal = getTestUser().getPrincipal();
+    }
+
+    @After
+    @Override
+    public void after() throws Exception {
+        try {
+            // revert uncommitted changes
+            root.refresh();
+
+            // remove all test content
+            root.getTree(TEST_ROOT_PATH).remove();
+            root.commit();
+        } finally {
+            super.after();
+        }
+    }
+
+    private void addEntry(String path, boolean grant, String restriction, String... privilegeNames) throws Exception {
+        AccessControlManager acMgr = getAccessControlManager(root);
+        JackrabbitAccessControlList acl = AccessControlUtils.getAccessControlList(acMgr, path);
+        if (restriction.length() > 0) {
+            Map<String, Value> rs = new HashMap<String, Value>();
+            rs.put("rep:glob", new StringValue(restriction));
+            acl.addEntry(testPrincipal, AccessControlUtils.privilegesFromNames(acMgr, privilegeNames), grant, rs);
+        } else {
+            acl.addEntry(testPrincipal, AccessControlUtils.privilegesFromNames(acMgr, privilegeNames), grant);
+        }
+        acMgr.setPolicy(path, acl);
+        root.commit();
+    }
+
+    private void assertIsGranted(PermissionProvider pp, Root root, boolean allow, String path, long permissions) {
+        assertEquals("user should " + (allow ? "" : "not ") + "have " + permissions + " on " + path,
+                allow, pp.isGranted(root.getTree(path), null, permissions));
+    }
+
+    private PermissionProvider getPermissionProvider(ContentSession session) {
+        return getSecurityProvider()
+                .getConfiguration(AuthorizationConfiguration.class)
+                .getPermissionProvider(root, session.getWorkspaceName(), session.getAuthInfo().getPrincipals());
+    }
+
+
+    @Test
+    public void testHasPermission() throws Exception {
+        // create permissions
+        // allow rep:write      /testroot
+        // allow jcr:removeNode /testroot/a/b
+        // deny  jcr:removeNode /testroot/a/b/c
+
+        addEntry(TEST_ROOT_PATH, true, "", PrivilegeConstants.JCR_READ, PrivilegeConstants.REP_WRITE);
+        addEntry(TEST_B_PATH, true, "", PrivilegeConstants.JCR_REMOVE_NODE);
+        addEntry(TEST_C_PATH, false, "", PrivilegeConstants.JCR_REMOVE_NODE);
+
+        ContentSession testSession = createTestSession();
+        try {
+            Root testRoot = testSession.getLatestRoot();
+            PermissionProvider pp = getPermissionProvider(testSession);
+
+            assertIsGranted(pp, testRoot, true, TEST_A_PATH, Permissions.REMOVE_NODE);
+            assertIsGranted(pp, testRoot, true, TEST_B_PATH, Permissions.REMOVE_NODE);
+            assertIsGranted(pp, testRoot, false, TEST_C_PATH, Permissions.REMOVE_NODE);
+
+            try {
+                testRoot.getTree(TEST_C_PATH).remove();
+                testRoot.commit();
+                fail("removing node on /a/b/c should fail");
+            } catch (CommitFailedException e) {
+                // all ok
+            }
+        } finally {
+            testSession.close();
+        }
+    }
+
+    /**
+     * Tests if the restrictions are properly inherited.
+     * the restriction enable/disable the ACE where it is defined.
+     * since the 'allow' on /a/b is after the 'deny' on a/b/c, the allow wins.
+     *
+     * The test currently fails on evaluation of /a/b/c/d. Probably because the evaluation
+     * of /a/b/c yields a deny, which terminates the iteration.
+     */
+    @Test
+    @Ignore("OAK-3324")
+    public void testHasPermissionWithRestrictions() throws Exception {
+        // create permissions
+        // allow rep:write      /testroot
+        // deny  jcr:removeNode /testroot/a  glob=*/c
+        // allow jcr:removeNode /testroot/a  glob=*/b
+
+        addEntry(TEST_ROOT_PATH, true, "", PrivilegeConstants.JCR_READ, PrivilegeConstants.REP_WRITE);
+        addEntry(TEST_A_PATH, false, "*/c", PrivilegeConstants.JCR_REMOVE_NODE);
+        addEntry(TEST_A_PATH, true, "*/b", PrivilegeConstants.JCR_REMOVE_NODE);
+
+        ContentSession testSession = createTestSession();
+        try {
+            Root testRoot = testSession.getLatestRoot();
+            PermissionProvider pp = getPermissionProvider(testSession);
+
+            assertIsGranted(pp, testRoot, true, TEST_A_PATH, Permissions.REMOVE_NODE);
+            assertIsGranted(pp, testRoot, true, TEST_B_PATH, Permissions.REMOVE_NODE);
+            assertIsGranted(pp, testRoot, false, TEST_C_PATH, Permissions.REMOVE_NODE);
+            assertIsGranted(pp, testRoot, true, TEST_D_PATH, Permissions.REMOVE_NODE);
+
+            // should be able to remove /a/b/c/d
+            testRoot.getTree(TEST_D_PATH).remove();
+            testRoot.commit();
+
+            // should be able to remove /a/b/c
+            try {
+                testRoot.getTree(TEST_C_PATH).remove();
+                testRoot.commit();
+                fail("user should not be able to remove c");
+            } catch (CommitFailedException e) {
+                // ok
+            }
+
+        } finally {
+            testSession.close();
+        }
+    }
+
+    /**
+     * Tests if the restrictions are properly inherited.
+     * the restriction enable/disable the ACE where it is defined.
+     * since the 'deny' on /a/b is after the 'allow' on a/b/c, the deny wins.
+     */
+    @Test
+    @Ignore("OAK-3324")
+    public void testHasPermissionWithRestrictions2() throws Exception {
+        // create permissions
+        // allow rep:write      /testroot
+        // allow jcr:removeNode /testroot/a  glob=*/b
+        // deny  jcr:removeNode /testroot/a  glob=*/c
+
+        addEntry(TEST_ROOT_PATH, true, "", PrivilegeConstants.JCR_READ, PrivilegeConstants.REP_WRITE);
+        addEntry(TEST_A_PATH, true, "*/b", PrivilegeConstants.JCR_REMOVE_NODE);
+        addEntry(TEST_A_PATH, false, "*/c", PrivilegeConstants.JCR_REMOVE_NODE);
+
+        ContentSession testSession = createTestSession();
+        try {
+            Root testRoot = testSession.getLatestRoot();
+
+            PermissionProvider pp = getPermissionProvider(testSession);
+
+            assertIsGranted(pp, testRoot, true, TEST_A_PATH, Permissions.REMOVE_NODE);
+            assertIsGranted(pp, testRoot, true, TEST_B_PATH, Permissions.REMOVE_NODE);
+            assertIsGranted(pp, testRoot, false, TEST_C_PATH, Permissions.REMOVE_NODE);
+            assertIsGranted(pp, testRoot, true, TEST_D_PATH, Permissions.REMOVE_NODE);
+
+            testRoot.getTree(TEST_D_PATH).remove();
+            testRoot.commit();
+
+            try {
+                // should not be able to remove /a/b/c
+                testRoot.getTree(TEST_C_PATH).remove();
+                testRoot.commit();
+                fail("should not be able to delete " + TEST_C_PATH);
+            } catch (CommitFailedException e) {
+                // ok
+                testRoot.refresh();
+            }
+
+        } finally {
+            testSession.close();
+        }
+    }
+}
\ No newline at end of file