Merge pull request #7 from anchela/master

SLING-9091 : Principal-based AC-setup fails for transient service users
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 f09c7de..17e3e9c 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
@@ -47,7 +47,6 @@
 import org.apache.sling.repoinit.parser.operations.AclLine;
 import org.apache.sling.repoinit.parser.operations.RestrictionClause;
 import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -169,7 +168,13 @@
     public static void setPrincipalAcl(Session session, String principalName, Collection<AclLine> lines) throws RepositoryException {
         JackrabbitAccessControlManager acMgr = getJACM(session);
         Principal principal = AccessControlUtils.getPrincipal(session, principalName);
-        checkState(principal != null, "Principal not found: " + principalName);
+        if (principal == null) {
+            // due to transient nature of the repo-init the principal lookup may not succeed if completed through query
+            // -> save transitent changes and retry principal lookup
+            session.save();
+            principal = AccessControlUtils.getPrincipal(session, principalName);
+            checkState(principal != null, "Principal not found: " + principalName);
+        }
 
         PrincipalAccessControlList acl = getPrincipalAccessControlList(acMgr, principal);
         boolean modified = false;
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 aeb33b4..bcb11c0 100644
--- a/src/test/java/org/apache/sling/jcr/repoinit/PrincipalBasedAclTest.java
+++ b/src/test/java/org/apache/sling/jcr/repoinit/PrincipalBasedAclTest.java
@@ -21,6 +21,7 @@
 import org.apache.jackrabbit.api.security.JackrabbitAccessControlManager;
 import org.apache.jackrabbit.api.security.authorization.PrincipalAccessControlList;
 import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.User;
 import org.apache.jackrabbit.api.security.user.UserManager;
 import org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils;
 import org.apache.jackrabbit.oak.commons.PathUtils;
@@ -62,6 +63,7 @@
 import java.security.Principal;
 import java.security.PrivilegedExceptionAction;
 import java.util.Collections;
+import java.util.UUID;
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
@@ -558,6 +560,34 @@
     }
 
     @Test
+    public void  principalAclNotAvailableRepoLevelPermissions() 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)
+            // but there exists an equivalent entry with the same definition -> no exception
+            setup = "set principal ACL for otherSystemPrincipal \n"
+                    + "allow jcr:namespaceManagement on :repository\n"
+                    + "end";
+            U.parseAndExecute(setup);
+
+            Principal principal = adminSession.getUserManager().getAuthorizable("otherSystemPrincipal").getPrincipal();
+            for (AccessControlPolicy policy : acMgr.getPolicies(principal)) {
+                assertFalse(policy instanceof PrincipalAccessControlList);
+            }
+        } finally {
+            U.cleanupServiceUser("otherSystemPrincipal");
+        }
+    }
+
+    @Test
     public void testHomePath() throws Exception {
         UserManager uMgr = ((JackrabbitSession) U.adminSession).getUserManager();
         Authorizable a = uMgr.getAuthorizable(U.username);
@@ -580,6 +610,30 @@
         assertEquals(a.getPath(), entry.getEffectivePath());
     }
 
+    @Test
+    public void testTransientUser() throws Exception {
+        UserManager uMgr = ((JackrabbitSession) U.adminSession).getUserManager();
+        String id = "systemUser_" + UUID.randomUUID().toString();
+        try {
+            User su = uMgr.createSystemUser(id, relPath);
+            String setup = "set principal ACL for "+su.getPrincipal().getName()+" \n"
+                    + "allow jcr:read on " + path + "\n"
+                    + "end";
+            U.parseAndExecute(setup);
+
+            PrincipalAccessControlList acl = getAcl(su.getPrincipal(), AclUtil.getJACM(U.adminSession));
+            assertNotNull(acl);
+            assertEquals(1, acl.size());
+        } finally {
+            U.adminSession.refresh(false);
+            Authorizable a = uMgr.getAuthorizable(id);
+            if (a != null) {
+                a.remove();
+                U.adminSession.save();
+            }
+        }
+    }
+
     @Nullable
     private static PrincipalAccessControlList getAcl(@NotNull Principal principal, @NotNull JackrabbitAccessControlManager jacm) throws RepositoryException {
         for (AccessControlPolicy policy : jacm.getPolicies(principal)) {