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 an...@apache.org on 2023/01/19 15:55:08 UTC

[jackrabbit-oak] branch trunk updated: OAK-10073 : Additional tests combining dynamic groups and automembership

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

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


The following commit(s) were added to refs/heads/trunk by this push:
     new 0d116a9cfc OAK-10073 : Additional tests combining dynamic groups and automembership
0d116a9cfc is described below

commit 0d116a9cfc3480d62672ee9316c2b5344bf7c9ab
Author: angela <an...@adobe.com>
AuthorDate: Thu Jan 19 16:54:48 2023 +0100

    OAK-10073 : Additional tests combining dynamic groups and automembership
---
 .../external/impl/AbstractDynamicTest.java         | 126 ++++++++++++++++
 .../external/impl/DynamicGroupsTest.java           |  19 +--
 .../external/impl/DynamicSyncContextTest.java      |  69 +--------
 .../external/impl/DynamicSyncTest.java             | 167 +++++++++++++++++++++
 4 files changed, 297 insertions(+), 84 deletions(-)

diff --git a/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/AbstractDynamicTest.java b/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/AbstractDynamicTest.java
new file mode 100644
index 0000000000..392508b72a
--- /dev/null
+++ b/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/AbstractDynamicTest.java
@@ -0,0 +1,126 @@
+/*
+ * 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.spi.security.authentication.external.impl;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Iterators;
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.UserManager;
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.AbstractExternalAuthTest;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentity;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalUser;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.SyncResult;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.basic.DefaultSyncConfig;
+import org.jetbrains.annotations.NotNull;
+import org.junit.After;
+import org.junit.Before;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.ValueFactory;
+import java.security.Principal;
+import java.util.Iterator;
+import java.util.List;
+
+import static org.junit.Assert.assertSame;
+
+public abstract class AbstractDynamicTest extends AbstractExternalAuthTest {
+    
+    Root r;
+    UserManager userManager;
+    ValueFactory valueFactory;
+
+    DynamicSyncContext syncContext;
+
+    // the external user identity that has been synchronized before dynamic membership is enabled.
+    ExternalUser previouslySyncedUser;
+
+    @Before
+    public void before() throws Exception {
+        super.before();
+        r = getSystemRoot();
+
+        createAutoMembershipGroups();
+        previouslySyncedUser = syncPriorToDynamicMembership();
+
+        userManager = getUserManager(r);
+        valueFactory = getValueFactory(r);
+        syncContext = new DynamicSyncContext(syncConfig, idp, userManager, valueFactory);
+
+        // inject user-configuration as well as sync-handler and sync-hander-mapping to have get dynamic-membership 
+        // providers registered.
+        context.registerInjectActivateService(getUserConfiguration());
+        registerSyncHandler(syncConfigAsMap(), idp.getName());
+    }
+
+    @After
+    public void after() throws Exception {
+        try {
+            syncContext.close();
+            r.refresh();
+        } finally {
+            super.after();
+        }
+    }
+
+    private void createAutoMembershipGroups() throws RepositoryException {
+        DefaultSyncConfig sc = createSyncConfig();
+        UserManager um = getUserManager(r);
+        // create automembership groups
+        for (String id : Iterables.concat(sc.user().getAutoMembership(), sc.group().getAutoMembership())) {
+            um.createGroup(id);
+        }
+    }
+
+    /**
+     * Synchronized a separate user with DefaultSyncContext to test behavior for previously synchronized user/group
+     * with deep membership-nesting => all groups synched
+     */
+    @NotNull
+    abstract ExternalUser syncPriorToDynamicMembership() throws Exception;
+
+    @Override
+    @NotNull
+    protected DefaultSyncConfig createSyncConfig() {
+        DefaultSyncConfig sc = super.createSyncConfig();
+        sc.user().setDynamicMembership(true);
+        return sc;
+    }
+
+    protected void sync(@NotNull ExternalIdentity externalIdentity, @NotNull SyncResult.Status expectedStatus) throws Exception {
+        SyncResult result = syncContext.sync(externalIdentity);
+        assertSame(expectedStatus, result.getStatus());
+        r.commit();
+    }
+
+    @NotNull
+    static List<String> getIds(@NotNull Iterator<? extends Authorizable> authorizables) {
+        return ImmutableList.copyOf(Iterators.transform(authorizables, authorizable -> {
+            try {
+                return authorizable.getID();
+            } catch (RepositoryException repositoryException) {
+                throw new RuntimeException();
+            }
+        }));
+    }
+
+    @NotNull
+    static List<String> getPrincipalNames(@NotNull Iterator<Principal> groupPrincipals) {
+        return ImmutableList.copyOf(Iterators.transform(groupPrincipals, Principal::getName));
+    }
+}
\ No newline at end of file
diff --git a/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DynamicGroupsTest.java b/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DynamicGroupsTest.java
index ba6caa8b3f..28975e3254 100644
--- a/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DynamicGroupsTest.java
+++ b/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DynamicGroupsTest.java
@@ -46,7 +46,6 @@ import javax.jcr.RepositoryException;
 import java.security.Principal;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
@@ -318,7 +317,7 @@ public class DynamicGroupsTest extends DynamicSyncContextTest {
         assertTrue(r.getTree(a.getPath()).hasProperty(REP_EXTERNAL_PRINCIPAL_NAMES));
         
         // verify membership
-        List<String> groupIds = toIds(a.memberOf());
+        List<String> groupIds = getIds(a.memberOf());
         if (membershipNestingDepth == 0) {
             assertFalse(groupIds.contains("localGroup"));
             assertFalse(local.isMember(a));
@@ -329,7 +328,7 @@ public class DynamicGroupsTest extends DynamicSyncContextTest {
             
             for (String id : new String[] {groupId, groupId2}) {
                 Authorizable extGroup = um.getAuthorizable(id);
-                assertTrue(toIds(extGroup.declaredMemberOf()).contains("localGroup"));
+                assertTrue(getIds(extGroup.declaredMemberOf()).contains("localGroup"));
                 assertTrue(local.isMember(extGroup));
             }
         }
@@ -349,18 +348,4 @@ public class DynamicGroupsTest extends DynamicSyncContextTest {
             assertTrue(principalNames.contains(local.getPrincipal().getName()));
         }
     }
-    
-    private @NotNull List<String> toIds(@NotNull Iterator<Group> groups) {
-        return ImmutableList.copyOf(Iterators.transform(groups, group -> {
-            try {
-                return group.getID();
-            } catch (RepositoryException repositoryException) {
-                throw new RuntimeException();
-            }
-        }));
-    }
-    
-    private @NotNull List<String> getPrincipalNames(@NotNull Iterator<Principal> groupPrincipals) {
-        return ImmutableList.copyOf(Iterators.transform(groupPrincipals, Principal::getName));
-    }
 }
\ No newline at end of file
diff --git a/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DynamicSyncContextTest.java b/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DynamicSyncContextTest.java
index 9a5920af97..dcd69d6ee9 100644
--- a/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DynamicSyncContextTest.java
+++ b/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DynamicSyncContextTest.java
@@ -26,11 +26,9 @@ import org.apache.jackrabbit.api.security.user.Group;
 import org.apache.jackrabbit.api.security.user.User;
 import org.apache.jackrabbit.api.security.user.UserManager;
 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.plugins.tree.TreeUtil;
-import org.apache.jackrabbit.oak.spi.security.authentication.external.AbstractExternalAuthTest;
 import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalGroup;
 import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentity;
 import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentityException;
@@ -46,13 +44,10 @@ import org.apache.jackrabbit.oak.spi.security.authentication.external.basic.Defa
 import org.apache.jackrabbit.oak.spi.security.principal.PrincipalImpl;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
-import org.junit.After;
-import org.junit.Before;
 import org.junit.Test;
 
 import javax.jcr.RepositoryException;
 import javax.jcr.Value;
-import javax.jcr.ValueFactory;
 import java.util.HashSet;
 import java.util.Objects;
 import java.util.Set;
@@ -82,63 +77,17 @@ import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-public class DynamicSyncContextTest extends AbstractExternalAuthTest {
+public class DynamicSyncContextTest extends AbstractDynamicTest {
 
     static final String PREVIOUS_SYNCED_ID = "third";
     static final long PREVIOUS_NESTING_DEPTH = Long.MAX_VALUE;
     static final String GROUP_ID = "aaa";
-    
-    Root r;
-    UserManager userManager;
-    ValueFactory valueFactory;
-
-    DynamicSyncContext syncContext;
-
-    // the external user identity that has been synchronized before dynamic membership is enabled.
-    ExternalUser previouslySyncedUser;
-
-    @Before
-    public void before() throws Exception {
-        super.before();
-        r = getSystemRoot();
-        
-        createAutoMembershipGroups();
-        previouslySyncedUser = syncPriorToDynamicMembership();
-        
-        userManager = getUserManager(r);
-        valueFactory = getValueFactory(r);
-        syncContext = new DynamicSyncContext(syncConfig, idp, userManager, valueFactory);
-        
-        // inject user-configuration as well as sync-handler and sync-hander-mapping to have get dynamic-membership 
-        // providers registered.
-        context.registerInjectActivateService(getUserConfiguration());
-        registerSyncHandler(syncConfigAsMap(), idp.getName());
-    }
-
-    @After
-    public void after() throws Exception {
-        try {
-            syncContext.close();
-            r.refresh();
-        } finally {
-            super.after();
-        }
-    }
-
-    private void createAutoMembershipGroups() throws RepositoryException {
-        DefaultSyncConfig sc = createSyncConfig();
-        UserManager um = getUserManager(r);
-        // create automembership groups
-        for (String id : Iterables.concat(sc.user().getAutoMembership(), sc.group().getAutoMembership())) {
-            um.createGroup(id);
-        }
-    }
 
     /**
      * Synchronized a separate user with DefaultSyncContext to test behavior for previously synchronized user/group
      * with deep membership-nesting => all groups synched
      */
-    private ExternalUser syncPriorToDynamicMembership() throws Exception {
+    @NotNull ExternalUser syncPriorToDynamicMembership() throws Exception {
         DefaultSyncConfig priorSyncConfig = createSyncConfig();
         priorSyncConfig.user().setMembershipNestingDepth(PREVIOUS_NESTING_DEPTH);
         
@@ -161,20 +110,6 @@ public class DynamicSyncContextTest extends AbstractExternalAuthTest {
         return previouslySyncedUser;
     }
 
-    @Override
-    @NotNull
-    protected DefaultSyncConfig createSyncConfig() {
-        DefaultSyncConfig sc = super.createSyncConfig();
-        sc.user().setDynamicMembership(true);
-        return sc;
-    }
-
-    protected void sync(@NotNull ExternalIdentity externalIdentity, @NotNull SyncResult.Status expectedStatus) throws Exception {
-        SyncResult result = syncContext.sync(externalIdentity);
-        assertSame(expectedStatus, result.getStatus());
-        r.commit();
-    }
-
     protected void assertDynamicMembership(@NotNull ExternalIdentity externalIdentity, long depth) throws Exception {
         Authorizable a = userManager.getAuthorizable(externalIdentity.getId());
         assertNotNull(a);
diff --git a/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DynamicSyncTest.java b/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DynamicSyncTest.java
new file mode 100644
index 0000000000..0f55531f0e
--- /dev/null
+++ b/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DynamicSyncTest.java
@@ -0,0 +1,167 @@
+/*
+ * 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.spi.security.authentication.external.impl;
+
+import com.google.common.collect.ImmutableSet;
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalUser;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.SyncResult;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.basic.DefaultSyncConfig;
+import org.apache.jackrabbit.oak.spi.security.principal.EveryonePrincipal;
+import org.apache.jackrabbit.oak.spi.security.user.UserConfiguration;
+import org.apache.jackrabbit.oak.spi.xml.ImportBehavior;
+import org.apache.jackrabbit.oak.spi.xml.ProtectedItemImporter;
+import org.jetbrains.annotations.NotNull;
+import org.junit.Test;
+
+import javax.jcr.RepositoryException;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+
+/**
+ * Test combining dynamic sync, automembership and manual member relationship between external group and local group
+ */
+public class DynamicSyncTest extends AbstractDynamicTest {
+    
+    private static final String BASE_ID = "base";
+    private static final String BASE2_ID = "base2";
+    private static final String AUTO_GROUPS = "autoForGroups";
+    private static final String AUTO_USERS = "autoForUsers";
+
+    private Group autoForGroups;
+    private Group autoForUsers;
+    private Group base;
+
+    @Override
+    public void before() throws Exception {
+        super.before();
+
+        autoForGroups = userManager.getAuthorizable(AUTO_GROUPS, Group.class);
+        autoForUsers = userManager.getAuthorizable(AUTO_USERS, Group.class);
+
+        base = userManager.createGroup(BASE_ID);
+        // add automembership groups
+        assertTrue(base.addMembers(AUTO_GROUPS, AUTO_USERS).isEmpty());
+        // add external groups as members breaching IDP boundary (Not recommended!)
+        assertTrue(base.addMembers("a", "b").isEmpty());
+        
+        userManager.createGroup(EveryonePrincipal.getInstance());
+
+        Group base2 = userManager.createGroup(BASE2_ID);
+        base2.addMember(autoForUsers);
+        
+        r.commit();
+    }
+
+    @Override
+    @NotNull ExternalUser syncPriorToDynamicMembership() {
+        // method not needed
+        return mock(ExternalUser.class);
+    }
+
+    @Override
+    protected ConfigurationParameters getSecurityConfigParameters() {
+        return ConfigurationParameters.of(UserConfiguration.NAME,
+                ConfigurationParameters.of(
+                        ProtectedItemImporter.PARAM_IMPORT_BEHAVIOR, ImportBehavior.NAME_BESTEFFORT)
+        );
+    }
+
+    @Override
+    protected @NotNull DefaultSyncConfig createSyncConfig() {
+        DefaultSyncConfig config = super.createSyncConfig();
+        config.group()
+                .setDynamicGroups(true)
+                .setAutoMembership(AUTO_GROUPS);
+        config.user()
+                .setEnforceDynamicMembership(true)
+                .setMembershipNestingDepth(2)
+                .setAutoMembership(AUTO_USERS);
+        return config;
+    }
+
+    @Test
+    public void testSyncedUser() throws Exception {
+        ExternalUser externalUser = idp.getUser(USER_ID);
+        sync(externalUser, SyncResult.Status.ADD);
+
+        Authorizable user = userManager.getAuthorizable(USER_ID);
+        assertNotNull(user);
+
+        // assert membership
+        Set<String> expDeclaredGroupIds = ImmutableSet.of("a", "b", "c", "aa", "aaa", AUTO_GROUPS, AUTO_USERS, EveryonePrincipal.NAME);
+        assertExpectedIds(expDeclaredGroupIds, user.declaredMemberOf());
+
+        Set<String> expGroupIds = ImmutableSet.of(BASE_ID, BASE2_ID, "a", "b", "c", "aa", "aaa", AUTO_GROUPS, AUTO_USERS, EveryonePrincipal.NAME);
+        assertExpectedIds(expGroupIds, user.memberOf());
+
+        // assert groups
+        user.declaredMemberOf().forEachRemaining(group -> assertIsMember(group, true, user));
+        user.memberOf().forEachRemaining(group -> assertIsMember(group, false, user));
+
+        // assert principals
+        List<String> principalNames = getPrincipalNames(getPrincipalManager(r).getGroupMembership(user.getPrincipal()));
+        assertEquals(10, principalNames.size());
+    }
+
+    @Test
+    public void testSyncedGroup() throws Exception {
+        ExternalUser externalUser = idp.getUser(USER_ID);
+        sync(externalUser, SyncResult.Status.ADD);
+
+        // verify group 'a'
+        Group aGroup = userManager.getAuthorizable("a", Group.class);
+        assertNotNull(aGroup);
+        
+        assertExpectedIds(Collections.singleton(USER_ID), aGroup.getDeclaredMembers(), aGroup.getMembers());
+        
+        Set<String> expectedIds = ImmutableSet.of(AUTO_GROUPS, BASE_ID, EveryonePrincipal.NAME);
+        assertExpectedIds(expectedIds, aGroup.declaredMemberOf(), aGroup.memberOf());
+    }
+
+    private static void assertIsMember(@NotNull Group group, boolean declared, @NotNull Authorizable... members) {
+        try {
+            for (Authorizable member : members) {
+                if (declared) {
+                    assertTrue(group.isDeclaredMember(member));
+                } else {
+                    assertTrue(group.isMember(member));
+                }
+            }
+        } catch (RepositoryException e) {
+            fail(e.getMessage());
+        }
+    }
+    
+    private static void assertExpectedIds(@NotNull Set<String> expectedIds, @NotNull Iterator<? extends Authorizable>... iterators) {
+        for (Iterator<? extends Authorizable> it : iterators) {
+            List<String> ids = getIds(it);
+            assertEquals(expectedIds.size(), ids.size());
+            assertTrue(ids.containsAll(expectedIds));
+        }
+    }
+}
\ No newline at end of file