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