You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by an...@apache.org on 2021/05/05 12:56:50 UTC

[sling-org-apache-sling-jcr-repoinit] 01/01: SLING-10299 : Allow for removal of access control policies (not just individual entries)

This is an automated email from the ASF dual-hosted git repository.

angela pushed a commit to branch SLING-10299
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-repoinit.git

commit c1ccb1a2c4c23771c6b2099d38ce6fc6d85287c1
Author: angela <an...@adobe.com>
AuthorDate: Wed May 5 14:55:36 2021 +0200

    SLING-10299 : Allow for removal of access control policies (not just individual entries)
---
 pom.xml                                            |   2 +-
 .../apache/sling/jcr/repoinit/impl/AclUtil.java    |  82 +++++++++-
 .../apache/sling/jcr/repoinit/impl/AclVisitor.java |  37 +++++
 .../sling/jcr/repoinit/impl/DoNothingVisitor.java  |  15 ++
 .../sling/jcr/repoinit/PrincipalBasedAclTest.java  | 120 +++++++++++---
 .../apache/sling/jcr/repoinit/RemoveAclTest.java   | 174 +++++++++++++++++++++
 6 files changed, 404 insertions(+), 26 deletions(-)

diff --git a/pom.xml b/pom.xml
index 6c5e80d..3733564 100644
--- a/pom.xml
+++ b/pom.xml
@@ -219,7 +219,7 @@
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.repoinit.parser</artifactId>
-            <version>1.6.7-SNAPSHOT</version>
+            <version>1.6.9-SNAPSHOT</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
diff --git a/src/main/java/org/apache/sling/jcr/repoinit/impl/AclUtil.java b/src/main/java/org/apache/sling/jcr/repoinit/impl/AclUtil.java
index cde2e34..7264163 100644
--- a/src/main/java/org/apache/sling/jcr/repoinit/impl/AclUtil.java
+++ b/src/main/java/org/apache/sling/jcr/repoinit/impl/AclUtil.java
@@ -40,9 +40,11 @@ import javax.jcr.security.Privilege;
 import org.apache.jackrabbit.api.security.JackrabbitAccessControlEntry;
 import org.apache.jackrabbit.api.security.JackrabbitAccessControlList;
 import org.apache.jackrabbit.api.security.JackrabbitAccessControlManager;
+import org.apache.jackrabbit.api.security.JackrabbitAccessControlPolicy;
 import org.apache.jackrabbit.api.security.authorization.PrincipalAccessControlList;
 import org.apache.jackrabbit.api.security.user.Authorizable;
 import org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils;
+import org.apache.jackrabbit.oak.spi.security.principal.PrincipalImpl;
 import org.apache.jackrabbit.util.Text;
 import org.apache.sling.repoinit.parser.operations.AclLine;
 import org.apache.sling.repoinit.parser.operations.RestrictionClause;
@@ -166,6 +168,51 @@ public class AclUtil {
         setAcl(session, principals, (String)null, privileges, isAllow, restrictionClauses);
     }
 
+    /**
+     * Remove resource-based access control setup for the principal with the given name.
+     * 
+     * @param session
+     * @param principalName
+     * @throws RepositoryException
+     */
+    public static void removeAcl(@NotNull Session session, @NotNull final String principalName) throws RepositoryException {
+        Principal principal = AccessControlUtils.getPrincipal(session, principalName);
+        if (principal == null) {
+            LOG.info("Principal {} does not exist.", principalName);
+            // using PrincipalImpl will prevent 'removePolicy' from failing with AccessControlException
+            // in case import-behavior is configured to be ABORT.
+            principal = new PrincipalImpl(principalName);
+        }
+        
+        JackrabbitAccessControlManager acMgr = getJACM(session);
+        for (JackrabbitAccessControlPolicy policy : acMgr.getPolicies(principal)) {
+            // make sure not to remove the principal-based access control list but instead only drop
+            // resource-based access control content for the given principal
+            if (policy instanceof JackrabbitAccessControlList && !(policy instanceof PrincipalAccessControlList)) {
+                acMgr.removePolicy(policy.getPath(), policy);
+            }
+        }
+    }
+
+    /**
+     * Remove resource-based access control setup defined for the specified paths.
+     * 
+     * @param session
+     * @param paths
+     * @throws RepositoryException
+     */
+    public static void removeAcl(@NotNull Session session, @NotNull List<String> paths) throws RepositoryException {
+        for (String jcrPath : getJcrPaths(session, paths)) {
+            LOG.info("Removing access control policy on {}", jcrPath);
+            JackrabbitAccessControlList acl = AccessControlUtils.getAccessControlList(session, jcrPath);
+            if (acl == null) {
+                LOG.info("No ACL to remove at path {}", jcrPath);
+            } else {
+                session.getAccessControlManager().removePolicy(jcrPath, acl);
+            }
+        }
+    }
+    
     public static void removeEntries(@NotNull Session session, @NotNull List<String> principals, @NotNull List<String> paths) throws RepositoryException {
         Set<String> principalNames = new HashSet<>(principals);
         for (String jcrPath : getJcrPaths(session, paths)) {
@@ -202,7 +249,7 @@ public class AclUtil {
             checkState(principal != null, "Principal not found: " + principalName);
         }
 
-        final PrincipalAccessControlList acl = getPrincipalAccessControlList(acMgr, principal);
+        final PrincipalAccessControlList acl = getPrincipalAccessControlList(acMgr, principal, true);
         boolean modified = false;
         for (AclLine line : lines) {
             AclLine.Action action = line.getAction();
@@ -244,16 +291,41 @@ public class AclUtil {
         }
     }
 
-    private static PrincipalAccessControlList getPrincipalAccessControlList(JackrabbitAccessControlManager acMgr, Principal principal) throws RepositoryException {
+    /**
+     * Remove principal-based access control setup for the principal with the given name. 
+     *
+     * @param session
+     * @param principalName
+     * @throws RepositoryException
+     */
+    public static void removePrincipalAcl(@NotNull Session session, @NotNull String principalName) throws RepositoryException {
+        Principal principal = AccessControlUtils.getPrincipal(session, principalName);
+        if (principal == null) {
+            LOG.info("Cannot remove principal-based ACL. Principal {} does not exist.", principalName);
+            return;
+        }
+
+        JackrabbitAccessControlManager acMgr = getJACM(session);
+        PrincipalAccessControlList acl = getPrincipalAccessControlList(acMgr, principal, false);
+        if (acl == null) {
+            LOG.info("Cannot remove principal-based ACL for principal {}. No such policy exists.", principalName);
+        } else {
+            acMgr.removePolicy(acl.getPath(), acl);
+        }
+    }
+
+    @Nullable
+    private static PrincipalAccessControlList getPrincipalAccessControlList(@NotNull JackrabbitAccessControlManager acMgr, 
+                                                                            @NotNull Principal principal, boolean includeApplicable) throws RepositoryException {
         PrincipalAccessControlList acl = null;
-        for (AccessControlPolicy policy : acMgr.getPolicies(principal)) {
+        for (JackrabbitAccessControlPolicy policy : acMgr.getPolicies(principal)) {
             if (policy instanceof PrincipalAccessControlList) {
                 acl = (PrincipalAccessControlList) policy;
                 break;
             }
         }
-        if (acl == null) {
-            for (AccessControlPolicy policy : acMgr.getApplicablePolicies(principal)) {
+        if (acl == null && includeApplicable) {
+            for (JackrabbitAccessControlPolicy policy : acMgr.getApplicablePolicies(principal)) {
                 if (policy instanceof PrincipalAccessControlList) {
                     acl = (PrincipalAccessControlList) policy;
                     break;
diff --git a/src/main/java/org/apache/sling/jcr/repoinit/impl/AclVisitor.java b/src/main/java/org/apache/sling/jcr/repoinit/impl/AclVisitor.java
index 965bd80..a071a91 100644
--- a/src/main/java/org/apache/sling/jcr/repoinit/impl/AclVisitor.java
+++ b/src/main/java/org/apache/sling/jcr/repoinit/impl/AclVisitor.java
@@ -24,11 +24,15 @@ import java.util.Collections;
 import java.util.List;
 
 import javax.jcr.Node;
+import javax.jcr.RepositoryException;
 import javax.jcr.Session;
 
 import org.apache.sling.repoinit.parser.operations.AclLine;
 import org.apache.sling.repoinit.parser.operations.CreatePath;
 import org.apache.sling.repoinit.parser.operations.PathSegmentDefinition;
+import org.apache.sling.repoinit.parser.operations.RemoveAclPaths;
+import org.apache.sling.repoinit.parser.operations.RemoveAclPrincipalBased;
+import org.apache.sling.repoinit.parser.operations.RemoveAclPrincipals;
 import org.apache.sling.repoinit.parser.operations.RestrictionClause;
 import org.apache.sling.repoinit.parser.operations.SetAclPaths;
 import org.apache.sling.repoinit.parser.operations.SetAclPrincipalBased;
@@ -154,4 +158,37 @@ class AclVisitor extends DoNothingVisitor {
             throw new RuntimeException("Session.save failed: "+ e, e);
         }
     }
+    
+    @Override
+    public void visitRemoveAclPrincipals(RemoveAclPrincipals s) {
+        for (String principalName : s.getPrincipals()) {
+            try {
+                log.info("Removing access control policy for {}", principalName);
+                AclUtil.removeAcl(session, principalName);
+            } catch (RepositoryException e) {
+                throw new RuntimeException("Failed to remove ACL ("+e.getMessage()+")");
+            }
+        }
+    }
+
+    @Override
+    public void visitRemoveAclPaths(RemoveAclPaths s) {
+        try {
+            AclUtil.removeAcl(session, s.getPaths());
+        } catch (RepositoryException e) {
+            throw new RuntimeException("Failed to remove ACL ("+e.getMessage()+")");
+        }
+    }
+
+    @Override
+    public void visitRemoveAclPrincipalBased(RemoveAclPrincipalBased s) {
+        for (String principalName : s.getPrincipals()) {
+            try {
+                log.info("Removing principal-based access control policy for {}", principalName);
+                AclUtil.removePrincipalAcl(session, principalName);
+            } catch (RepositoryException e) {
+                throw new RuntimeException("Failed to remove principal-based ACL ("+e.getMessage()+")");
+            }
+        }
+    }
 }
diff --git a/src/main/java/org/apache/sling/jcr/repoinit/impl/DoNothingVisitor.java b/src/main/java/org/apache/sling/jcr/repoinit/impl/DoNothingVisitor.java
index c2ae6ed..424e978 100644
--- a/src/main/java/org/apache/sling/jcr/repoinit/impl/DoNothingVisitor.java
+++ b/src/main/java/org/apache/sling/jcr/repoinit/impl/DoNothingVisitor.java
@@ -31,6 +31,9 @@ import org.apache.sling.repoinit.parser.operations.OperationVisitor;
 import org.apache.sling.repoinit.parser.operations.RegisterNamespace;
 import org.apache.sling.repoinit.parser.operations.RegisterNodetypes;
 import org.apache.sling.repoinit.parser.operations.RegisterPrivilege;
+import org.apache.sling.repoinit.parser.operations.RemoveAclPaths;
+import org.apache.sling.repoinit.parser.operations.RemoveAclPrincipalBased;
+import org.apache.sling.repoinit.parser.operations.RemoveAclPrincipals;
 import org.apache.sling.repoinit.parser.operations.RemoveGroupMembers;
 import org.apache.sling.repoinit.parser.operations.SetAclPaths;
 import org.apache.sling.repoinit.parser.operations.SetAclPrincipalBased;
@@ -97,6 +100,18 @@ class DoNothingVisitor implements OperationVisitor {
     }
 
     @Override
+    public void visitRemoveAclPrincipals(RemoveAclPrincipals s) {
+    }
+
+    @Override
+    public void visitRemoveAclPaths(RemoveAclPaths s) {
+    }
+
+    @Override
+    public void visitRemoveAclPrincipalBased(RemoveAclPrincipalBased s) {
+    }
+
+    @Override
     public void visitCreatePath(CreatePath cp) {
     }
 
diff --git a/src/test/java/org/apache/sling/jcr/repoinit/PrincipalBasedAclTest.java b/src/test/java/org/apache/sling/jcr/repoinit/PrincipalBasedAclTest.java
index b0a9a84..bc95b9f 100644
--- a/src/test/java/org/apache/sling/jcr/repoinit/PrincipalBasedAclTest.java
+++ b/src/test/java/org/apache/sling/jcr/repoinit/PrincipalBasedAclTest.java
@@ -83,6 +83,7 @@ public class PrincipalBasedAclTest {
 
     private Repository repository;
     private JackrabbitSession adminSession;
+    private JackrabbitAccessControlManager acMgr;
 
     private TestUtil U;
 
@@ -99,6 +100,7 @@ public class PrincipalBasedAclTest {
 
         String uid = sp.getParameters(UserConfiguration.NAME).getConfigValue(UserConstants.PARAM_ADMIN_ID, UserConstants.DEFAULT_ADMIN_ID);
         adminSession = (JackrabbitSession) repository.login(new SimpleCredentials(uid, uid.toCharArray()), null);
+        acMgr = AclUtil.getJACM(adminSession);
         U = new TestUtil(adminSession);
 
         Node tmp = adminSession.getRootNode().addNode("tmp_" + U.id);
@@ -180,6 +182,20 @@ public class PrincipalBasedAclTest {
         assertTrue("Expecting '" +  shouldMatch + "'' to match " + regex, shouldMatch.matches(regex));
     }
 
+    private Authorizable getServiceUser(@NotNull String uid) throws RepositoryException {
+        UserManager uMgr = adminSession.getUserManager();
+        Authorizable a = uMgr.getAuthorizable(uid);
+        if (a != null) {
+            return a;
+        } else {
+            throw new RepositoryException("Expected service user " + uid + " to exist.");
+        }
+    }
+    
+    private Principal getPrincipal(@NotNull String serviceUserId) throws RepositoryException {
+        return getServiceUser(serviceUserId).getPrincipal();
+    }
+
     @Test
     public void readGranted() throws Exception {
         String setup =
@@ -595,7 +611,7 @@ public class PrincipalBasedAclTest {
                     + "end";
             U.parseAndExecute(setup);
 
-            Principal principal = adminSession.getUserManager().getAuthorizable("otherSystemPrincipal").getPrincipal();
+            Principal principal = getPrincipal("otherSystemPrincipal");
             for (AccessControlPolicy policy : acMgr.getPolicies(principal)) {
                 assertFalse(policy instanceof PrincipalAccessControlList);
             }
@@ -606,7 +622,7 @@ public class PrincipalBasedAclTest {
 
     @Test
     public void testHomePath() throws Exception {
-        Authorizable su = getServiceUser(U.adminSession, U.username);
+        Authorizable su = getServiceUser(U.username);
         Principal principal = su.getPrincipal();
 
         assertNull(getAcl(principal, U.adminSession));
@@ -728,7 +744,7 @@ public class PrincipalBasedAclTest {
                 + "end";
         U.parseAndExecute(setup);
 
-        assertNull(getAcl(getServiceUser(U.adminSession, U.username).getPrincipal(), U.adminSession));
+        assertNull(getAcl(getPrincipal(U.username), U.adminSession));
     }
 
     @Test(expected = RuntimeException.class)
@@ -752,7 +768,7 @@ public class PrincipalBasedAclTest {
                 + "end";
         U.parseAndExecute(setup);
 
-        PrincipalAccessControlList acl = getAcl(getServiceUser(U.adminSession, U.username).getPrincipal(), U.adminSession);
+        PrincipalAccessControlList acl = getAcl(getPrincipal(U.username), U.adminSession);
         assertNotNull(acl);
         assertTrue(acl.isEmpty());
     }
@@ -765,7 +781,7 @@ public class PrincipalBasedAclTest {
                 + "end";
         U.parseAndExecute(setup);
 
-        PrincipalAccessControlList acl = getAcl(getServiceUser(U.adminSession, U.username).getPrincipal(), U.adminSession);
+        PrincipalAccessControlList acl = getAcl(getPrincipal(U.username), U.adminSession);
         assertNotNull(acl);
         assertEquals(2, acl.size());
 
@@ -774,7 +790,7 @@ public class PrincipalBasedAclTest {
                 + "end";
         U.parseAndExecute(setup);
 
-        acl = getAcl(getServiceUser(U.adminSession, U.username).getPrincipal(), U.adminSession);
+        acl = getAcl(getPrincipal(U.username), U.adminSession);
         assertNotNull(acl);
         assertEquals(1, acl.size());
     }
@@ -787,7 +803,7 @@ public class PrincipalBasedAclTest {
                 + "end";
         U.parseAndExecute(setup);
 
-        PrincipalAccessControlList acl = getAcl(getServiceUser(U.adminSession, U.username).getPrincipal(), U.adminSession);
+        PrincipalAccessControlList acl = getAcl(getPrincipal(U.username), U.adminSession);
         assertNotNull(acl);
         assertEquals(2, acl.size());
 
@@ -796,7 +812,7 @@ public class PrincipalBasedAclTest {
                 + "end";
         U.parseAndExecute(setup);
 
-        acl = getAcl(getServiceUser(U.adminSession, U.username).getPrincipal(), U.adminSession);
+        acl = getAcl(getPrincipal(U.username), U.adminSession);
         assertNotNull(acl);
         assertEquals(1, acl.size());
     }
@@ -809,7 +825,7 @@ public class PrincipalBasedAclTest {
                 + "end";
         U.parseAndExecute(setup);
 
-        PrincipalAccessControlList acl = getAcl(getServiceUser(U.adminSession, U.username).getPrincipal(), U.adminSession);
+        PrincipalAccessControlList acl = getAcl(getPrincipal(U.username), U.adminSession);
         assertNotNull(acl);
         assertEquals(2, acl.size());
 
@@ -818,7 +834,7 @@ public class PrincipalBasedAclTest {
                 + "end";
         U.parseAndExecute(setup);
 
-        acl = getAcl(getServiceUser(U.adminSession, U.username).getPrincipal(), U.adminSession);
+        acl = getAcl(getPrincipal(U.username), U.adminSession);
         assertNotNull(acl);
         assertTrue(acl.isEmpty());
     }
@@ -836,7 +852,7 @@ public class PrincipalBasedAclTest {
                 + "end";
         U.parseAndExecute(setup);
 
-        PrincipalAccessControlList acl = getAcl(getServiceUser(U.adminSession, U.username).getPrincipal(), U.adminSession);
+        PrincipalAccessControlList acl = getAcl(getPrincipal(U.username), U.adminSession);
         assertNotNull(acl);
         assertEquals(2, acl.size());
     }
@@ -870,20 +886,84 @@ public class PrincipalBasedAclTest {
         );
 
         {
-            PrincipalAccessControlList acl = getAcl(getServiceUser(U.adminSession, U.username).getPrincipal(), U.adminSession);
+            PrincipalAccessControlList acl = getAcl(getPrincipal(U.username), U.adminSession);
             assertNotNull(acl);
             assertEquals(0, acl.size());
         }
-}
+    }
 
-    private static Authorizable getServiceUser(@NotNull Session session, @NotNull String uid) throws RepositoryException {
-        UserManager uMgr = ((JackrabbitSession) session).getUserManager();
-        Authorizable a = uMgr.getAuthorizable(uid);
-        if (a != null) {
-            return a;
-        } else {
-            throw new RepositoryException("Expected service user " + uid + " to exist.");
+    @Test
+    public void testAddAndRemoveAcl() throws Exception {
+        U.parseAndExecute(""
+                + "set principal ACL for " + U.username + "\n"
+                + "allow jcr:write on "+path+"\n"
+                + "end\n"
+                + "remove principal ACL for " + U.username + "\n"
+        );
+
+        assertNull(getAcl(getPrincipal(U.username), U.adminSession));
+    }
+
+    @Test
+    public void testRemoveAcl() throws Exception {
+        PrincipalAccessControlList acl = getApplicableAcl(getPrincipal(U.username), U.adminSession);
+        acl.addEntry("/content", AccessControlUtils.privilegesFromNames(U.adminSession, Privilege.JCR_READ));
+        U.adminSession.getAccessControlManager().setPolicy(acl.getPath(), acl);
+        U.adminSession.save();
+
+        assertNotNull(getAcl(getPrincipal(U.username), U.adminSession));
+        U.parseAndExecute("remove principal ACL for " + U.username + "\n");
+        assertNull(getAcl(getPrincipal(U.username), U.adminSession));
+    }
+
+    @Test
+    public void testRemoveNonExistingAcl() throws Exception {
+        assertNull(getAcl(getPrincipal(U.username), U.adminSession));
+        // removing non-existing policy must not fail
+        U.parseAndExecute("remove principal ACL for " + U.username + "\n");
+        assertNull(getAcl(getPrincipal(U.username), U.adminSession));
+    }
+
+    @Test
+    public void testRemoveAclNonExistingPrincipal() throws Exception {
+        assertNull(getAcl(getPrincipal(U.username), U.adminSession));
+        // removing policy for non-existing principal must not fail
+        U.parseAndExecute("remove principal ACL for non-existing-service-user\n");
+        assertNull(getAcl(getPrincipal(U.username), U.adminSession));
+    }
+
+    @Test
+    public void testRemoveResourceBasedAclByPrincipal() throws Exception {
+        U.parseAndExecute(""
+                + "create path (nt:unstructured) /var\n"
+                + "set ACL for " + U.username + "\n"
+                + "allow jcr:read on /var\n"
+                + "end\n"
+                + "set principal ACL for " + U.username + "\n"
+                + "allow jcr:read on /var\n"
+                + "end\n");
+
+        assertEquals(1, acMgr.getPolicies("/var").length);
+
+        U.parseAndExecute("remove ACL for "+U.username+"\n");
+        // resource-based acl at /var must be removed as it only contains a single entry for U.userName
+        assertEquals(0, acMgr.getPolicies("/var").length);
+
+        // removing resource-based ac-setup by principal must not delete any principal-based ac setup.
+        Principal p = getPrincipal(U.username);
+        AccessControlPolicy[] policies = acMgr.getPolicies(p);
+        assertEquals(1, policies.length);
+        assertTrue(policies[0] instanceof PrincipalAccessControlList);
+    }
+
+    @Nullable
+    private static PrincipalAccessControlList getApplicableAcl(@NotNull Principal principal, @NotNull Session session) throws RepositoryException {
+        for (AccessControlPolicy policy : AclUtil.getJACM(session).getApplicablePolicies(principal)) {
+            if (policy instanceof PrincipalAccessControlList) {
+                return (PrincipalAccessControlList) policy;
+            }
         }
+        return null;
     }
 
     @Nullable
diff --git a/src/test/java/org/apache/sling/jcr/repoinit/RemoveAclTest.java b/src/test/java/org/apache/sling/jcr/repoinit/RemoveAclTest.java
new file mode 100644
index 0000000..aaa9fad
--- /dev/null
+++ b/src/test/java/org/apache/sling/jcr/repoinit/RemoveAclTest.java
@@ -0,0 +1,174 @@
+/*
+ * 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.sling.jcr.repoinit;
+
+import com.google.common.collect.Lists;
+import org.apache.jackrabbit.api.JackrabbitRepository;
+import org.apache.jackrabbit.api.security.JackrabbitAccessControlList;
+import org.apache.jackrabbit.api.security.JackrabbitAccessControlManager;
+import org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils;
+import org.apache.jackrabbit.oak.jcr.Jcr;
+import org.apache.jackrabbit.oak.security.internal.SecurityProviderBuilder;
+import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
+import org.apache.jackrabbit.oak.spi.security.SecurityProvider;
+import org.apache.jackrabbit.oak.spi.security.authorization.AuthorizationConfiguration;
+import org.apache.jackrabbit.oak.spi.security.principal.PrincipalImpl;
+import org.apache.jackrabbit.oak.spi.security.user.UserConfiguration;
+import org.apache.jackrabbit.oak.spi.security.user.UserConstants;
+import org.apache.jackrabbit.oak.spi.xml.ImportBehavior;
+import org.apache.jackrabbit.oak.spi.xml.ProtectedItemImporter;
+import org.apache.sling.jcr.repoinit.impl.AclUtil;
+import org.apache.sling.jcr.repoinit.impl.TestUtil;
+import org.apache.sling.repoinit.parser.RepoInitParsingException;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+import javax.jcr.security.AccessControlPolicy;
+import java.security.Principal;
+import java.util.Collection;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+@RunWith(Parameterized.class)
+public class RemoveAclTest {
+    
+    @Parameterized.Parameters(name = "ImportBehavior={0}")
+    public static Collection<Object[]> parameters() {
+        return Lists.newArrayList(
+                new Object[] {ImportBehavior.NAME_IGNORE},
+                new Object[] {ImportBehavior.NAME_BESTEFFORT},
+                new Object[] {ImportBehavior.NAME_ABORT}
+        );
+    }
+
+    private final String importBehaviorName;
+    
+    private Repository repository;
+    private Session adminSession;
+    private JackrabbitAccessControlManager acMgr;
+    
+    private TestUtil U;
+    private String userA;
+
+    public RemoveAclTest(String name) {
+        this.importBehaviorName = name;
+    }
+    
+    @Before
+    public void setup() throws RepositoryException, RepoInitParsingException {
+        SecurityProvider sp = createSecurityProvider();
+        repository = new Jcr().with(sp).createRepository();
+
+        String uid = sp.getParameters(UserConfiguration.NAME).getConfigValue(UserConstants.PARAM_ADMIN_ID, UserConstants.DEFAULT_ADMIN_ID);
+        adminSession = repository.login(new SimpleCredentials(uid, uid.toCharArray()), null);
+        acMgr = AclUtil.getJACM(adminSession);
+        U = new TestUtil(adminSession);
+        
+        userA = "userA_" + U.id;
+
+        U.parseAndExecute("create service user " + U.username);
+        U.parseAndExecute("create service user " + userA);
+        U.parseAndExecute(""
+                + "create path (nt:unstructured) /content\n"
+                + "create path (nt:unstructured) /var\n"
+                + "set ACL for " + U.username + "\n"
+                + "allow jcr:read on /content, /var, home("+userA+")\n"
+                + "allow jcr:namespaceManagement on :repository\n"
+                + "end\n"
+                + "set ACL for " + userA + "\n"
+                + "allow jcr:read on /content, /var\n"
+                + "end\n");
+    }
+
+    private SecurityProvider createSecurityProvider() { 
+        return SecurityProviderBuilder.newBuilder().with(ConfigurationParameters.of(AuthorizationConfiguration.NAME, ConfigurationParameters.of(ProtectedItemImporter.PARAM_IMPORT_BEHAVIOR, importBehaviorName))).build();
+    }
+
+    @After
+    public void after() throws Exception {
+        try {
+            adminSession.removeItem("/content");
+            adminSession.removeItem("/var");
+            adminSession.save();
+            U.cleanupUser();
+            U.cleanupServiceUser(userA);
+        } finally {
+            adminSession.logout();
+            if (repository instanceof JackrabbitRepository) {
+                ((JackrabbitRepository) repository).shutdown();
+            }
+        }
+    }
+
+    @Test
+    public void testRemoveAcl() throws Exception {
+        U.parseAndExecute("remove ACL on /content, :repository\n");
+
+        assertArrayEquals(new AccessControlPolicy[0], acMgr.getPolicies("/content"));
+        assertArrayEquals(new AccessControlPolicy[0], acMgr.getPolicies((String) null));
+    }
+
+    @Test(expected = RuntimeException.class)
+    public void testRemoveAclNonExistingPath() throws Exception {
+        U.parseAndExecute("remove ACL on /nonExisting\n");
+    }
+
+    @Test
+    public void testRemoveAclByPrincipal() throws Exception {
+        U.parseAndExecute("remove ACL for "+userA+"\n");
+
+        // removing resource-based ac setup by principal must leave entries for other principals intact
+        for (String path : new String[] {"/content", "/var"}) {
+            JackrabbitAccessControlList acl = AccessControlUtils.getAccessControlList(adminSession, path);
+            assertNotNull(acl);
+            assertEquals(1, acl.size()); // entry for U.username must not have been removed
+            assertEquals(U.username, acl.getAccessControlEntries()[0].getPrincipal().getName());
+        }
+    }
+    
+    @Test
+    public void testRemoveAclByNonExistingPrincipal() throws Exception {
+        Principal p = new PrincipalImpl("non-existing-service-user");
+        assertEquals(0,  acMgr.getPolicies(p).length);
+        
+        // execution for non-existing principal must not fail
+        U.parseAndExecute("remove ACL for non-existing-service-user\n");
+        
+        assertEquals(0,  acMgr.getPolicies(p).length);
+    }
+
+    @Test
+    public void testRemoveUserAndAcl() throws Exception {
+        Principal p = new PrincipalImpl(userA);
+        assertEquals(1, acMgr.getPolicies(p).length);
+        
+        U.parseAndExecute("delete service user "+userA+"\n");
+        assertEquals(1, acMgr.getPolicies(p).length);
+
+        U.parseAndExecute("remove ACL for "+userA+"\n");
+        assertEquals(0, acMgr.getPolicies(p).length);
+    }
+}
\ No newline at end of file