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

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


The following commit(s) were added to refs/heads/master by this push:
     new 63e0d13  SLING-10281 support new "ensure principal ACL" statements 
(#14)
63e0d13 is described below

commit 63e0d135e7220165f5a761930309c60406474718
Author: Konrad Windszus <[email protected]>
AuthorDate: Mon Jan 23 19:33:06 2023 +0100

    SLING-10281 support new "ensure principal ACL" statements (#14)
    
    make repoinit throw exceptions in case principal acls can not be applied
    fail even earlier when no principal access control list is available
    improve logging in case principal acl is not available
---
 .../apache/sling/jcr/repoinit/impl/AclUtil.java    |  20 +-
 .../apache/sling/jcr/repoinit/impl/AclVisitor.java |  16 +-
 .../sling/jcr/repoinit/impl/DoNothingVisitor.java  |   6 +
 .../sling/jcr/repoinit/PrincipalBasedAclTest.java  | 214 ++++++++++++++++++++-
 4 files changed, 252 insertions(+), 4 deletions(-)

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 68e1466..5f6da64 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
@@ -21,6 +21,7 @@ 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.principal.ItemBasedPrincipal;
 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;
@@ -291,7 +292,7 @@ public class AclUtil {
         }
     }
 
-    public static void setPrincipalAcl(Session session, String principalName, 
Collection<AclLine> lines) throws RepositoryException {
+    public static void setPrincipalAcl(Session session, String principalName, 
Collection<AclLine> lines, boolean isStrict) throws RepositoryException {
         final JackrabbitAccessControlManager acMgr = getJACM(session);
         Principal principal = AccessControlUtils.getPrincipal(session, 
principalName);
         if (principal == null) {
@@ -303,6 +304,14 @@ public class AclUtil {
         }
 
         final PrincipalAccessControlList acl = 
getPrincipalAccessControlList(acMgr, principal, true);
+        if (acl == null && isStrict) {
+            String principalDescription = principal.getName();
+            // try to get path of principal in case it is backed by a JCR 
user/group
+            if (principal instanceof ItemBasedPrincipal) {
+                principalDescription += " (" + ((ItemBasedPrincipal) 
principal).getPath() + ")";
+            }
+            throw new IllegalStateException("No PrincipalAccessControlList 
available for principal '" + principalDescription + "'.");
+        }
         boolean modified = false;
         for (AclLine line : lines) {
             AclLine.Action action = line.getAction();
@@ -407,7 +416,14 @@ public class AclUtil {
     private static boolean isValidPath(@NotNull Session session, @Nullable 
String jcrPath) throws RepositoryException {
         return jcrPath == null || session.nodeExists(jcrPath);
     }
-    
+
+    /**
+     * 
+     * @param acMgr the access control manager
+     * @param principal the principal
+     * @return the first available {@link PrincipalAccessControlList} bound to 
the given principal or {@code null} of <a 
href="https://jackrabbit.apache.org/oak/docs/security/authorization/principalbased.html";>principal-based
 authorization</a> is not enabled for the given principal
+     * @throws RepositoryException
+     */
     @Nullable
     private static JackrabbitAccessControlList getAccessControlList(@NotNull 
AccessControlManager acMgr,
                                                                     @Nullable 
String path, boolean includeApplicable) throws RepositoryException {
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 b69dd24..3aef4ec 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
@@ -29,6 +29,7 @@ import javax.jcr.Session;
 import org.apache.sling.repoinit.parser.operations.AclLine;
 import org.apache.sling.repoinit.parser.operations.DeleteAclPrincipalBased;
 import org.apache.sling.repoinit.parser.operations.DeleteAclPrincipals;
+import org.apache.sling.repoinit.parser.operations.EnsureAclPrincipalBased;
 import org.apache.sling.repoinit.parser.operations.DeleteAclPaths;
 import org.apache.sling.repoinit.parser.operations.RemoveAcePaths;
 import org.apache.sling.repoinit.parser.operations.RemoveAcePrincipalBased;
@@ -101,6 +102,7 @@ class AclVisitor extends DoNothingVisitor {
         }
     }
 
+    
     @Override
     public void visitSetAclPrincipal(SetAclPrincipals s) {
         final List<String> principals = s.getPrincipals();
@@ -127,7 +129,19 @@ class AclVisitor extends DoNothingVisitor {
         for (String principalName : s.getPrincipals()) {
             try {
                 log.info("Adding principal-based access control entry for {}", 
principalName);
-                AclUtil.setPrincipalAcl(session, principalName, s.getLines());
+                AclUtil.setPrincipalAcl(session, principalName, s.getLines(), 
false);
+            } catch (Exception e) {
+                report(e, "Failed to set principal-based ACL (" + 
e.getMessage() + ")");
+            }
+        }
+    }
+
+    @Override
+    public void visitEnsureAclPrincipalBased(EnsureAclPrincipalBased s) {
+        for (String principalName : s.getPrincipals()) {
+            try {
+                log.info("Enforcing principal-based access control entry for 
{}", principalName);
+                AclUtil.setPrincipalAcl(session, principalName, s.getLines(), 
true);
             } catch (Exception e) {
                 report(e, "Failed to set 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 b9a5f39..8f313ec 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
@@ -29,6 +29,7 @@ import 
org.apache.sling.repoinit.parser.operations.DeleteGroup;
 import org.apache.sling.repoinit.parser.operations.DeleteServiceUser;
 import org.apache.sling.repoinit.parser.operations.DeleteUser;
 import org.apache.sling.repoinit.parser.operations.DisableServiceUser;
+import org.apache.sling.repoinit.parser.operations.EnsureAclPrincipalBased;
 import org.apache.sling.repoinit.parser.operations.EnsureNodes;
 import org.apache.sling.repoinit.parser.operations.OperationVisitor;
 import org.apache.sling.repoinit.parser.operations.RegisterNamespace;
@@ -116,6 +117,11 @@ class DoNothingVisitor implements OperationVisitor {
         // no-op
     }
 
+    @Override
+    public void visitEnsureAclPrincipalBased(EnsureAclPrincipalBased 
ensureAclPrincipalBased) {
+        // no-op
+    }
+
     @Override
     public void visitRemoveAcePrincipal(RemoveAcePrincipals s) {
         // no-op
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 c9468c1..8083a35 100644
--- a/src/test/java/org/apache/sling/jcr/repoinit/PrincipalBasedAclTest.java
+++ b/src/test/java/org/apache/sling/jcr/repoinit/PrincipalBasedAclTest.java
@@ -79,6 +79,7 @@ public class PrincipalBasedAclTest {
 
     private static final String PRINCIPAL_BASED_SUBTREE = "principalbased";
     private static final String REMOVE_NOT_SUPPORTED_REGEX = ".*REMOVE[a-zA-Z 
]+not supported.*";
+    private static final String NO_PRINCIPAL_CONTROL_LIST_AVAILABLE = ".*No 
PrincipalAccessControlList available.*";
 
     @Rule
     public final OsgiContext context = new OsgiContext();
@@ -496,6 +497,26 @@ public class PrincipalBasedAclTest {
         }
     }
 
+    @Test(expected = None.class)
+    public void  principalAclNotAvailableStrict() throws Exception {
+        try {
+            // create service user outside of supported tree for 
principal-based access control
+            U.parseAndExecute("create service user otherSystemPrincipal");
+            // principal-based ac-setup must fail as service user is not 
located below supported path
+            String setup = "ensure principal ACL for otherSystemPrincipal \n"
+                            + "allow jcr:read on " + path + "\n"
+                            + "end";
+            try {
+                U.parseAndExecute(setup);
+                fail("Setting a principal ACL outside a supported path must 
not succeed");
+            } catch (RuntimeException e) {
+                assertRegex(NO_PRINCIPAL_CONTROL_LIST_AVAILABLE, 
e.getMessage());
+            }
+        } finally {
+            U.cleanupServiceUser("otherSystemPrincipal");
+        }
+    }
+
     @Test
     public void  principalAclNotAvailableRestrictionMismatch() throws 
Exception {
         JackrabbitAccessControlManager acMgr = 
(JackrabbitAccessControlManager) adminSession.getAccessControlManager();
@@ -520,6 +541,37 @@ public class PrincipalBasedAclTest {
         }
     }
 
+    @Test
+    public void  principalAclNotAvailableRestrictionMismatchStrict() throws 
Exception {
+        JackrabbitAccessControlManager acMgr = 
(JackrabbitAccessControlManager) adminSession.getAccessControlManager();
+        try {
+            // create service user outside of supported tree for 
principal-based access control
+            U.parseAndExecute("create service user otherSystemPrincipal");
+            // setup path-based access control to establish effective 
permission setup
+            String setup = "set ACL for otherSystemPrincipal \n"
+                    + "allow jcr:read on " + path + "\n"
+                    + "end";
+            U.parseAndExecute(setup);
+
+            Principal principal = 
adminSession.getUserManager().getAuthorizable("otherSystemPrincipal").getPrincipal();
+            assertTrue(acMgr.hasPrivileges(path, 
Collections.singleton(principal), 
AccessControlUtils.privilegesFromNames(adminSession, Privilege.JCR_READ)));
+
+            // setting up principal-acl will not succeed (principal not 
located below supported path)
+            // since effective entry doesn't match the restriction -> setup 
must fail
+            setup = "ensure principal ACL for otherSystemPrincipal \n"
+                    + "allow jcr:read on " + path + " 
restriction(rep:glob,*mismatch)\n"
+                    + "end";
+            try {
+                U.parseAndExecute(setup);
+                fail("Setting a principal ACL outside a supported path must 
not succeed");
+            } catch (RuntimeException e) {
+                assertRegex(NO_PRINCIPAL_CONTROL_LIST_AVAILABLE, 
e.getMessage());
+            }
+        } finally {
+            U.cleanupServiceUser("otherSystemPrincipal");
+        }
+    }
+
     @Test
     public void  principalAclNotAvailableEntryPresent() throws Exception {
         JackrabbitAccessControlManager acMgr = 
(JackrabbitAccessControlManager) adminSession.getAccessControlManager();
@@ -550,6 +602,36 @@ public class PrincipalBasedAclTest {
         }
     }
 
+    @Test
+    public void  principalAclNotAvailableEntryPresentStrict() throws Exception 
{
+        JackrabbitAccessControlManager acMgr = 
(JackrabbitAccessControlManager) adminSession.getAccessControlManager();
+        try {
+            // create service user outside of supported tree for 
principal-based access control
+            U.parseAndExecute("create service user otherSystemPrincipal");
+            // setup path-based access control to establish effective 
permission setup
+            String setup = "set ACL for otherSystemPrincipal \n"
+                    + "allow jcr:read on " + path + "\n"
+                    + "end";
+            U.parseAndExecute(setup);
+
+            Principal principal = 
adminSession.getUserManager().getAuthorizable("otherSystemPrincipal").getPrincipal();
+            assertTrue(acMgr.hasPrivileges(path, 
Collections.singleton(principal), 
AccessControlUtils.privilegesFromNames(adminSession, Privilege.JCR_READ)));
+
+            // setting up principal-acl will not succeed (principal not 
located below supported path)
+            setup = "ensure principal ACL for otherSystemPrincipal \n"
+                    + "allow jcr:read on " + path + "\n"
+                    + "end";
+            try {
+                U.parseAndExecute(setup);
+                fail("Setting a principal ACL outside a supported path must 
not succeed");
+            } catch (RuntimeException e) {
+                assertRegex(NO_PRINCIPAL_CONTROL_LIST_AVAILABLE, 
e.getMessage());
+            }
+        } finally {
+            U.cleanupServiceUser("otherSystemPrincipal");
+        }
+    }
+
     @Test
     public void  principalAclNotAvailableEntryWithRestrictionPresent() throws 
Exception {
         JackrabbitAccessControlManager acMgr = 
(JackrabbitAccessControlManager) adminSession.getAccessControlManager();
@@ -578,6 +660,33 @@ public class PrincipalBasedAclTest {
         }
     }
 
+    @Test
+    public void  principalAclNotAvailableEntryWithRestrictionPresentStrict() 
throws Exception {
+        JackrabbitAccessControlManager acMgr = 
(JackrabbitAccessControlManager) adminSession.getAccessControlManager();
+        try {
+            // create service user outside of supported tree for 
principal-based access control
+            U.parseAndExecute("create service user otherSystemPrincipal");
+            // setup path-based access control to establish effective 
permission setup
+            String setup = "set ACL for otherSystemPrincipal \n"
+                    + "allow jcr:read on " + path + " 
restriction(rep:glob,*abc*)\n"
+                    + "end";
+            U.parseAndExecute(setup);
+
+            // setting up principal-acl will not succeed (principal not 
located below supported path)
+            setup = "ensure principal ACL for otherSystemPrincipal \n"
+                    + "allow jcr:read on " + path + " 
restriction(rep:glob,*abc*)\n"
+                    + "end";
+            try {
+                U.parseAndExecute(setup);
+                fail("Setting a principal ACL outside a supported path must 
not succeed");
+            } catch (RuntimeException e) {
+                assertRegex(NO_PRINCIPAL_CONTROL_LIST_AVAILABLE, 
e.getMessage());
+            }
+        } finally {
+            U.cleanupServiceUser("otherSystemPrincipal");
+        }
+    }
+
     @Test
     public void  principalAclNotAvailableRepoLevelPermissions() throws 
Exception {
         JackrabbitAccessControlManager acMgr = 
(JackrabbitAccessControlManager) adminSession.getAccessControlManager();
@@ -606,6 +715,34 @@ public class PrincipalBasedAclTest {
         }
     }
 
+    @Test
+    public void  principalAclNotAvailableRepoLevelPermissionsStrict() throws 
Exception {
+        JackrabbitAccessControlManager acMgr = 
(JackrabbitAccessControlManager) adminSession.getAccessControlManager();
+        try {
+            // create service user outside of supported tree for 
principal-based access control
+            U.parseAndExecute("create service user otherSystemPrincipal");
+            // setup path-based access control to establish effective 
permission setup
+            String setup = "set ACL for otherSystemPrincipal \n"
+                    + "allow jcr:namespaceManagement on :repository\n"
+                    + "end";
+            U.parseAndExecute(setup);
+
+            // setting up principal-acl will not succeed (principal not 
located below supported path)
+            setup = "ensure principal ACL for otherSystemPrincipal \n"
+                    + "allow jcr:namespaceManagement on :repository\n"
+                    + "end";
+            try {
+                U.parseAndExecute(setup);
+                fail("Setting a principal ACL outside a supported path must 
not succeed");
+            } catch (RuntimeException e) {
+                assertRegex(NO_PRINCIPAL_CONTROL_LIST_AVAILABLE, 
e.getMessage());
+            }
+
+        } finally {
+            U.cleanupServiceUser("otherSystemPrincipal");
+        }
+    }
+
     @Test
     public void  principalAclNotAvailableNonExistingNode() throws Exception {
         JackrabbitAccessControlManager acMgr = 
(JackrabbitAccessControlManager) adminSession.getAccessControlManager();
@@ -630,6 +767,34 @@ public class PrincipalBasedAclTest {
         }
     }
 
+    @Test
+    public void  principalAclNotAvailableNonExistingNodeStrict() throws 
Exception {
+        JackrabbitAccessControlManager acMgr = 
(JackrabbitAccessControlManager) adminSession.getAccessControlManager();
+        try {
+            // create service user outside of supported tree for 
principal-based access control
+            U.parseAndExecute("create service user otherSystemPrincipal");
+
+            // setting up principal-acl will not succeed (principal not 
located below supported path)
+            String setup = "ensure principal ACL for otherSystemPrincipal \n"
+                    + "allow jcr:read on /non/existing/path\n"
+                    + "end";
+            try {
+                U.parseAndExecute(setup);
+                fail("Setting a principal ACL outside a supported path must 
not succeed");
+            } catch (RuntimeException e) {
+                assertRegex(NO_PRINCIPAL_CONTROL_LIST_AVAILABLE, 
e.getMessage());
+            }
+
+            Principal principal = getPrincipal("otherSystemPrincipal");
+            for (AccessControlPolicy policy : acMgr.getPolicies(principal)) {
+                assertFalse(policy instanceof PrincipalAccessControlList);
+            }
+
+        } finally {
+            U.cleanupServiceUser("otherSystemPrincipal");
+        }
+    }
+
     @Test
     public void testHomePath() throws Exception {
         Authorizable su = getServiceUser(U.username);
@@ -641,7 +806,7 @@ public class PrincipalBasedAclTest {
         line.setProperty(AclLine.PROP_PRINCIPALS, 
Collections.singletonList(principal.getName()));
         line.setProperty(AclLine.PROP_PRIVILEGES, 
Collections.singletonList(Privilege.JCR_READ));
         line.setProperty(AclLine.PROP_PATHS, 
Collections.singletonList(":home:"+U.username+"#"));
-        AclUtil.setPrincipalAcl(U.adminSession, U.username, 
Collections.singletonList(line));
+        AclUtil.setPrincipalAcl(U.adminSession, U.username, 
Collections.singletonList(line), false);
 
         PrincipalAccessControlList acl = getAcl(principal, U.adminSession);
         assertNotNull(acl);
@@ -783,6 +948,34 @@ public class PrincipalBasedAclTest {
         assertNull(getAcl(getPrincipal("otherSystemPrincipal"), 
U.adminSession));
     }
 
+    @Test
+    public void testRemovePrincipalMismatchStrict() throws Exception {
+        String setup = "ensure principal ACL for " + U.username + "\n"
+                + "allow jcr:write on "+path+"\n"
+                + "end";
+        U.parseAndExecute(setup);
+        U.parseAndExecute("create service user otherSystemPrincipal");
+        assertPolicy(getPrincipal(U.username), U.adminSession, 1);
+
+        setup = "remove principal ACE for otherSystemPrincipal\n"
+                + "allow jcr:write on " + path + "\n"
+                + "end";
+        U.parseAndExecute(setup);
+        // ace must not have been removed
+        assertPolicy(getPrincipal(U.username), U.adminSession, 1);
+        assertNull(getAcl(getPrincipal("otherSystemPrincipal"), 
U.adminSession));
+
+        try {
+            setup = "ensure principal ACL for otherSystemPrincipal\n"
+            + "remove jcr:write on " + path + "\n"
+            + "end";
+            U.parseAndExecute(setup);
+            fail("Expecting REMOVE to fail");
+        } catch(RuntimeException rex) {
+            assertRegex(NO_PRINCIPAL_CONTROL_LIST_AVAILABLE, rex.getMessage());
+        }
+    }
+
     @Test
     public void testRemoveAllNoExistingPolicy() throws Exception {
         String setup = "set principal ACL for " + U.username + "\n"
@@ -902,6 +1095,25 @@ public class PrincipalBasedAclTest {
         U.parseAndExecute(setup);
     }
 
+    @Test(expected = None.class)
+    public void testRemoveAllPrincipalMismatchStrict() throws Exception {
+        String setup = "ensure principal ACL for " + U.username + "\n"
+                + "allow jcr:write on "+path+"\n"
+                + "end";
+        U.parseAndExecute(setup);
+        U.parseAndExecute("create service user otherSystemPrincipal");
+
+        setup = "ensure principal ACL for otherSystemPrincipal\n"
+                + "remove * on " + path + "\n"
+                + "end";
+        try {
+            U.parseAndExecute(setup);
+            fail("Setting a principal ACL outside a supported path must not 
succeed");
+        } catch (RuntimeException e) {
+            assertRegex(NO_PRINCIPAL_CONTROL_LIST_AVAILABLE, e.getMessage());
+        }
+    }
+
     @Test
     public void testRemoveAllPrincipalDuplicated() throws Exception {
         U.parseAndExecute(""

Reply via email to