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

angela pushed a commit to branch OAK-10318
in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git


The following commit(s) were added to refs/heads/OAK-10318 by this push:
     new 0ea2af524a OAK-10318 : Improve 
AutoMembershipPrincipals#isInheritedMember (add trace to cycle-warning as 
suggested by joergH, fix typo, change order of checks)
0ea2af524a is described below

commit 0ea2af524a168efd502a8ad220377ac6ba670bbe
Author: angela <[email protected]>
AuthorDate: Mon Jul 24 12:17:27 2023 +0200

    OAK-10318 : Improve AutoMembershipPrincipals#isInheritedMember (add trace 
to cycle-warning as suggested by joergH, fix typo, change order of checks)
---
 .../impl/principal/AutoMembershipPrincipals.java   | 25 ++++----
 .../impl/principal/AutoMembershipCycleTest.java    | 70 +++++++++++++++++++++-
 2 files changed, 80 insertions(+), 15 deletions(-)

diff --git 
a/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/AutoMembershipPrincipals.java
 
b/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/AutoMembershipPrincipals.java
index f5ceb84d16..eb26a19b1e 100644
--- 
a/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/AutoMembershipPrincipals.java
+++ 
b/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/AutoMembershipPrincipals.java
@@ -147,7 +147,7 @@ final class AutoMembershipPrincipals {
             Authorizable gr = userManager.getAuthorizable(automembershipId);
             if (gr == null || !gr.isGroup()) {
                 log.warn("Configured automembership id '{}' is not a valid 
group", automembershipId);
-            } else if (hasInheritedMembership(gr.declaredMemberOf(), groupId, 
automembershipId, processed)) {
+            } else if (hasInheritedMembership(gr.declaredMemberOf(), groupId, 
automembershipId, processed, "> ")) {
                 return true;
             }
         }
@@ -155,28 +155,29 @@ final class AutoMembershipPrincipals {
     }
 
     private static boolean hasInheritedMembership(@NotNull Iterator<Group> 
declared, @NotNull String groupId, 
-                                                  @NotNull String memberId, 
@NotNull Set<String> processed) throws RepositoryException {
+                                                  @NotNull String memberId, 
@NotNull Set<String> processed, 
+                                                  @NotNull String trace) 
throws RepositoryException {
         List<Group> groups = new ArrayList<>();
         while (declared.hasNext()) {
             Group gr = declared.next();
             String grId = gr.getID();
-            if (memberId.equals(grId)) {
-                log.error("Cyclic group membership detected for group id {}", 
memberId);
-            }
-            if (!processed.add(grId)) {
-                // group has already been processed before (shared membership 
e.g. for the 'everyone' group)
-                return false;
-            }
             if (groupId.equals(grId)) {
                 // the specified groupId defines inherited membership of a 
configured auto-membership group
                 return true;
             }
-            // remember group for traversing up the membership hierarchy
-            groups.add(gr);
+            if (memberId.equals(grId)) {
+                log.error("Cyclic group membership detected for group id {} 
via {}{}", memberId, trace, grId);
+                continue;
+            }
+            if (processed.add(grId)) {
+                // remember group for traversing up the membership hierarchy 
if it has not already been 
+                // processed before (shared membership e.g. for the 'everyone' 
group)
+                groups.add(gr);
+            }
         }
         // recursively call to search for inherited membership
         for (Group group : groups) {
-            if (hasInheritedMembership(group.declaredMemberOf(), groupId, 
memberId, processed)) {
+            if (hasInheritedMembership(group.declaredMemberOf(), groupId, 
memberId, processed, String.format("%s %s > ", trace, group.getID()))) {
                 return true;
             }
         }
diff --git 
a/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/AutoMembershipCycleTest.java
 
b/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/AutoMembershipCycleTest.java
index c8664a3add..049ef74970 100644
--- 
a/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/AutoMembershipCycleTest.java
+++ 
b/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/AutoMembershipCycleTest.java
@@ -40,7 +40,6 @@ import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoInteractions;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -142,6 +141,48 @@ public class AutoMembershipCycleTest extends 
AbstractAutoMembershipTest {
         verifyNoMoreInteractions(gr, umMock);
     }
 
+    @Test
+    public void testIsInheritedMemberNestedCircularIncludingAutoMembership() 
throws Exception {
+        // create cycle
+        assertTrue(amGr1.addMembers(gr.getID()).isEmpty());
+        assertTrue(gr.addMembers(gr2.getID()).isEmpty());
+        assertTrue(gr2.addMembers(amGr1.getID()).isEmpty());
+        root.commit();
+        clearInvocations(gr, gr2, amGr1);
+
+        // declared automembership in amGr1 (cycle)
+        assertTrue(amp.isInheritedMember(IDP_VALID_AM, gr, authorizable));
+        verifyNoInteractions(authorizable);
+        verify(gr).getID();
+        verify(amGr1).declaredMemberOf();
+        verify(umMock).getAuthorizable(AUTOMEMBERSHIP_GROUP_ID_1);
+        verifyNoMoreInteractions(gr);
+        clearInvocations(authorizable, amGr1, gr, gr2, umMock);
+
+        // declared automembership in amGr1 (cycle)
+        assertTrue(amp.isInheritedMember(IDP_VALID_AM, gr, amGr1));
+        verify(gr).getID();
+        verify(amGr1).declaredMemberOf();
+        verify(umMock).getAuthorizable(AUTOMEMBERSHIP_GROUP_ID_1);
+        verifyNoMoreInteractions(gr);
+        clearInvocations(authorizable, amGr1, gr, gr2, umMock);
+
+        // declared automembership in amGr1 (cycle)
+        assertTrue(amp.isInheritedMember(IDP_VALID_AM, gr2, authorizable));
+        verify(gr2).getID();
+        verify(amGr1).declaredMemberOf();
+        verify(umMock).getAuthorizable(AUTOMEMBERSHIP_GROUP_ID_1);
+        verifyNoMoreInteractions(gr, gr2, umMock);
+        clearInvocations(authorizable, amGr1, gr, gr2, umMock);
+
+        // declared automembership in amGr1 (cycle)
+        assertTrue(amp.isInheritedMember(IDP_VALID_AM, gr2, amGr1));
+        verify(gr2).getID();
+        verify(amGr1).declaredMemberOf();
+        verify(umMock).getAuthorizable(AUTOMEMBERSHIP_GROUP_ID_1);
+        verifyNoMoreInteractions(gr, gr2, umMock);
+    }
+
     @Test
     public void testIsInheritedMemberCircularWithoutAutoMembership() throws 
Exception {
         // create cycle
@@ -152,7 +193,7 @@ public class AutoMembershipCycleTest extends 
AbstractAutoMembershipTest {
         
         assertFalse(amp.isInheritedMember(IDP_VALID_AM, gr, gr2));
         
-        verify(gr, times(1)).getID();
+        verify(gr).getID();
         verifyNoMoreInteractions(gr, gr2);
     }
     
@@ -171,9 +212,32 @@ public class AutoMembershipCycleTest extends 
AbstractAutoMembershipTest {
         gr.setProperty(DefaultSyncContext.REP_EXTERNAL_ID, 
vf.createValue(gr.getID() + ';' + idp.getName()));
         amGr1.setProperty(DefaultSyncContext.REP_EXTERNAL_ID, 
vf.createValue(amGr1.getID() + ';' + idp.getName()));
         root.commit();
-        clearInvocations(gr, amGr1);
 
         assertTrue(amp.isInheritedMember(IDP_VALID_AM, gr, authorizable));
         assertTrue(amp.isInheritedMember(IDP_VALID_AM, gr, amGr1));
     }
+
+    @Test
+    public void testIsInheritedMemberNestedCircularIncludingExternalGroup() 
throws Exception {
+        
externalPrincipalConfiguration.setParameters(ConfigurationParameters.of(
+                PARAM_PROTECT_EXTERNAL_IDS, false));
+
+        // create cycle
+        assertTrue(amGr1.addMembers(gr2.getID()).isEmpty());
+        assertTrue(gr2.addMembers(gr.getID()).isEmpty());
+        assertTrue(gr.addMembers(amGr1.getID()).isEmpty());
+        root.commit();
+
+        // mark groups as external identities
+        ValueFactory vf = getValueFactory(root);
+        gr.setProperty(DefaultSyncContext.REP_EXTERNAL_ID, 
vf.createValue(gr.getID() + ';' + idp.getName()));
+        amGr1.setProperty(DefaultSyncContext.REP_EXTERNAL_ID, 
vf.createValue(amGr1.getID() + ';' + idp.getName()));
+        root.commit();
+
+        assertTrue(amp.isInheritedMember(IDP_VALID_AM, gr, authorizable));
+        assertTrue(amp.isInheritedMember(IDP_VALID_AM, gr, amGr1));
+
+        assertTrue(amp.isInheritedMember(IDP_VALID_AM, gr2, authorizable));
+        assertTrue(amp.isInheritedMember(IDP_VALID_AM, gr2, amGr1));
+    }
 }
\ No newline at end of file

Reply via email to