From 303c2e38b5cba1913205ea102fa3ff586d84881b Mon Sep 17 00:00:00 2001 From: Maxim Thomas Date: Tue, 19 Dec 2023 14:05:39 +0300 Subject: [PATCH] [#105] Added setGroups action to the user REST endpoint (#691) Added `setGroups` action to the user REST endpoint. Pass group names array in the `groups` request body property see the example below: ```bash curl --location --request POST 'http://openam.example.org:8080/openam/json/realms/root/users/demo?_action=setGroups' \ --header 'Content-Type: application/json' \ --header 'iPlanetDirectoryPro: AQIC5wM2LY4....1MTk4AAJTMQAA*' \ --data-raw '{ "groups": ["managers", "group1"] }' ``` --- .../openam/core/rest/IdentityResourceV2.java | 20 ++++++- .../openam/core/rest/IdentityRestUtils.java | 53 +++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/openam-core-rest/src/main/java/org/forgerock/openam/core/rest/IdentityResourceV2.java b/openam-core-rest/src/main/java/org/forgerock/openam/core/rest/IdentityResourceV2.java index 78b31219af..46132eee1c 100644 --- a/openam-core-rest/src/main/java/org/forgerock/openam/core/rest/IdentityResourceV2.java +++ b/openam-core-rest/src/main/java/org/forgerock/openam/core/rest/IdentityResourceV2.java @@ -12,6 +12,7 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2012-2016 ForgeRock AS. + * Portions copyright 2023 3A Systems LLC */ package org.forgerock.openam.core.rest; @@ -22,7 +23,6 @@ import static org.forgerock.json.JsonValue.object; import static org.forgerock.json.resource.ResourceException.*; import static org.forgerock.json.resource.Responses.newActionResponse; -import static org.forgerock.json.resource.http.HttpUtils.PROTOCOL_VERSION_1; import static org.forgerock.openam.core.rest.IdentityRestUtils.*; import static org.forgerock.openam.core.rest.UserAttributeInfo.*; import static org.forgerock.openam.rest.RestUtils.*; @@ -1013,6 +1013,24 @@ public Promise actionInstance(final Context c debug.warning("Cannot change password! " + resourceId + ":" + re); return re.asPromise(); } + } else if("setGroups".equalsIgnoreCase(action)) { + RealmContext realmContext = context.asContext(RealmContext.class); + final String realm = realmContext.getRealm().asPath(); + + JsonValue value = request.getContent(); + try { + Set groups = new HashSet<>(value.get("groups").asList(String.class)); + IdentityRestUtils.changeMemberships(context, realm, resourceId, groups); + if (debug.messageEnabled()) { + String principalName = PrincipalRestUtils.getPrincipalNameFromServerContext(context); + debug.message("IdentityResource.actionInstance :: ACTION of set groups for " + resourceId + + " in realm " + realm + " performed by " + principalName); + } + return newResultPromise(newActionResponse(json(object()))); + } catch (ResourceException re) { + debug.warning("Cannot set groups! " + resourceId + ":" + re); + return re.asPromise(); + } } else { return new NotSupportedException(action + " not supported for resource instances").asPromise(); } diff --git a/openam-core-rest/src/main/java/org/forgerock/openam/core/rest/IdentityRestUtils.java b/openam-core-rest/src/main/java/org/forgerock/openam/core/rest/IdentityRestUtils.java index 6e16b41777..d75330c302 100644 --- a/openam-core-rest/src/main/java/org/forgerock/openam/core/rest/IdentityRestUtils.java +++ b/openam-core-rest/src/main/java/org/forgerock/openam/core/rest/IdentityRestUtils.java @@ -12,6 +12,7 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2015-2016 ForgeRock AS. + * Portions copyright 2023 3A Systems LLC */ package org.forgerock.openam.core.rest; @@ -28,6 +29,7 @@ import com.sun.identity.idsvcs.IdentityDetails; import com.sun.identity.idsvcs.ListWrapper; import com.sun.identity.shared.debug.Debug; +import org.apache.commons.collections4.CollectionUtils; import org.forgerock.guice.core.InjectorHolder; import org.forgerock.json.JsonValue; import org.forgerock.json.JsonValueException; @@ -53,6 +55,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; public final class IdentityRestUtils { @@ -94,6 +97,56 @@ public static void changePassword(Context serverContext, String realm, String us } } + public static void changeMemberships(Context serverContext, String realm, String username, Set groupNames) + throws ResourceException { + try { + SSOToken token = serverContext.asContext(SSOTokenContext.class).getCallerSSOToken(); + + //check if groups exist + for(String gn : groupNames) { + AMIdentity groupIdentity = new AMIdentity(token, gn, IdType.GROUP, realm, null); + if(!groupIdentity.isExists()) { + throw new SSOException("group " + gn + " does not exist"); + } + } + + AMIdentity userIdentity = new AMIdentity(token, username, IdType.USER, realm, null); + + Set membershipsToAdd = new HashSet<>(groupNames); + Set membershipsToRemove = new HashSet<>(); + Set currentMembershipsIdentity = userIdentity.getMemberships(IdType.GROUP); + final Set currentMemberships = CollectionUtils.isNotEmpty(currentMembershipsIdentity) + ? currentMembershipsIdentity.stream().map(AMIdentity::getName).collect(Collectors.toSet()) + : null; + + if (!CollectionUtils.isEmpty(currentMemberships)) { + membershipsToRemove = currentMemberships.stream().filter(cm -> !groupNames.contains(cm)).collect(Collectors.toSet()); + membershipsToAdd = groupNames.stream().filter(m -> !currentMemberships.contains(m)).collect(Collectors.toSet()); + } + + if (!CollectionUtils.isEmpty(membershipsToRemove)) { + for (String idName : membershipsToRemove) { + AMIdentity groupIdentity = new AMIdentity(token, idName, IdType.GROUP, realm, null); + groupIdentity.removeMember(userIdentity); + } + } + + if (!CollectionUtils.isEmpty(membershipsToAdd)) { + for (String idName : membershipsToAdd) { + AMIdentity groupIdentity = new AMIdentity(token, idName, IdType.GROUP, realm, null); + groupIdentity.addMember(userIdentity); + } + } + + } catch (SSOException ssoe) { + debug.warning("IdentityRestUtils.changeMemberships() :: SSOException occurred while changing " + + "the groups for user: " + username, ssoe); + throw new PermanentException(401, "An error occurred while trying to change the user groups", ssoe); + } catch (IdRepoException ire) { + throw RESOURCE_MAPPING_HANDLER.handleError(ire); + } + } + public static Map> getIdentityServicesAttributes(String realm, String objectType) { Map> identityServicesAttributes = new HashMap<>(); identityServicesAttributes.put("objecttype", Collections.singleton(objectType));