Skip to content

Commit

Permalink
handle scim me andpoint for migrating users
Browse files Browse the repository at this point in the history
update scim me endpoint
  • Loading branch information
amanda-ariyaratne committed Feb 5, 2025
1 parent 1598c3f commit 55e6d4d
Show file tree
Hide file tree
Showing 5 changed files with 231 additions and 174 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package org.wso2.charon3.core.protocol.endpoints;

import org.apache.commons.lang.StringUtils;
import org.wso2.charon3.core.encoder.JSONDecoder;
import org.wso2.charon3.core.encoder.JSONEncoder;
import org.wso2.charon3.core.exceptions.BadRequestException;
Expand All @@ -38,10 +39,14 @@
import org.wso2.charon3.core.utils.ResourceManagerUtil;
import org.wso2.charon3.core.utils.codeutils.PatchOperation;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.wso2.charon3.core.schema.SCIMConstants.OperationalConstants.COLON;
import static org.wso2.charon3.core.utils.PatchOperationUtil.determineScimAttributes;

/**
* A client MAY use a URL of the form "<base-uri>/Me" as a uri alias for
* the User or other resource associated with the currently
Expand Down Expand Up @@ -323,6 +328,8 @@ public SCIMResponse updateWithPATCH(String existingId, String scimObjectString,

User newUser = null;

Map<String, String> syncedAttributes = userManager.getSyncedUserAttributes();
List<String> deletedSyncedAttributes = new ArrayList<>();
for (PatchOperation operation : opList) {

if (operation.getOperation().equals(SCIMConstants.OperationalConstants.ADD)) {
Expand All @@ -338,13 +345,29 @@ public SCIMResponse updateWithPATCH(String existingId, String scimObjectString,

}
} else if (operation.getOperation().equals(SCIMConstants.OperationalConstants.REMOVE)) {
if (newUser == null) {
newUser = (User) PatchOperationUtil.doPatchRemove(operation, oldUser, copyOfOldUser, schema);
copyOfOldUser = (User) CopyUtil.deepCopy(newUser);

} else {
newUser = (User) PatchOperationUtil.doPatchRemove(operation, newUser, copyOfOldUser, schema);
copyOfOldUser = (User) CopyUtil.deepCopy(newUser);
try {
if (newUser == null) {
newUser = (User) PatchOperationUtil.doPatchRemove(operation, oldUser, copyOfOldUser,
schema);
copyOfOldUser = (User) CopyUtil.deepCopy(newUser);

} else {
newUser = (User) PatchOperationUtil.doPatchRemove(operation, newUser, copyOfOldUser,
schema);
copyOfOldUser = (User) CopyUtil.deepCopy(newUser);
}
} catch (BadRequestException e) {
/*
* This condition is for the migrated users who have enterprise user attributes and system
* schema attributes both mapped to a single local claim. In such cases, if both the scim
* attributes are specified to be removed in the patch request, for the second attribute, there
* will be an error thrown because it is already removed when processing the first attribute.
*/
if (!ResponseCodeConstants.INVALID_PATH.equals(e.getScimType()) ||
determineScimAttributes(operation).stream()
.noneMatch(deletedSyncedAttributes::contains)) {
throw e;
}
}
} else if (operation.getOperation().equals(SCIMConstants.OperationalConstants.REPLACE)) {
if (newUser == null) {
Expand All @@ -360,6 +383,47 @@ public SCIMResponse updateWithPATCH(String existingId, String scimObjectString,
} else {
throw new BadRequestException("Unknown operation.", ResponseCodeConstants.INVALID_SYNTAX);
}

/*
* The following logic is for the migrated users who have enterprise user attributes and system schema
* attributes both mapped to a single local claim. In such cases, if any operation is only sent for
* one scim attributes, there will be conflicts for the final value because the other scim attribute's
* value is not changed. Therefore, we are removing the other scim attribute from the user object.
*/
if (syncedAttributes.isEmpty()) {
continue;
}
List<String> scimAttributes = determineScimAttributes(operation);
for (String scimAttribute : scimAttributes) {
String syncedAttribute = syncedAttributes.get(scimAttribute);

if (syncedAttribute == null) {
continue;
}

int lastColonIndex = syncedAttribute.lastIndexOf(COLON);
String baseAttributeName = (lastColonIndex != -1)
? syncedAttribute.substring(0, lastColonIndex) : StringUtils.EMPTY;
String subAttributeName = (lastColonIndex != -1)
? syncedAttribute.substring(lastColonIndex + 1) : syncedAttribute;
String[] subAttributes = subAttributeName.split("\\.");

switch (subAttributes.length) {
case 1:
newUser.deleteSubAttribute(baseAttributeName, subAttributes[0]);
deletedSyncedAttributes.add(syncedAttribute);
break;
case 2:
newUser.deleteSubSubAttribute(baseAttributeName, subAttributes[0], subAttributes[1]);
deletedSyncedAttributes.add(syncedAttribute);
break;
default:
break;
}

copyOfOldUser = (User) CopyUtil.deepCopy(newUser);
syncedAttributes.remove(syncedAttribute);
}
}

//get the URIs of required attributes which must be given a value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@


import org.apache.commons.lang.StringUtils;
import org.json.JSONArray;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wso2.charon3.core.attributes.Attribute;
Expand Down Expand Up @@ -52,16 +50,13 @@

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import static org.wso2.charon3.core.schema.SCIMConstants.OperationalConstants.COLON;
import static org.wso2.charon3.core.schema.SCIMConstants.OperationalConstants.DOT_SEPARATOR;
import static org.wso2.charon3.core.schema.SCIMConstants.OperationalConstants.OPEN_SQUARE_BRACKET;
import static org.wso2.charon3.core.utils.PatchOperationUtil.determineScimAttributes;

/**
* REST API exposed by Charon-Core to perform operations on UserResource.
Expand Down Expand Up @@ -855,71 +850,4 @@ private SCIMResourceTypeSchema getSchema(UserManager userManager) throws BadRequ
}
return schema;
}

private static List<String> determineScimAttributes(PatchOperation operation) {

if (operation == null) {
return Collections.emptyList();
}
List<String> attributes = new ArrayList<>();
String path = operation.getPath();
if (path != null) {
int bracketIndex = path.indexOf(OPEN_SQUARE_BRACKET);
path = (bracketIndex != -1) ? path.substring(0, bracketIndex) : path;
}

Object values = operation.getValues();
if (values instanceof JSONObject) {
extractScimAttributes((JSONObject) values, path, attributes);
} else if (values instanceof JSONArray) {
extractScimAttributes((JSONArray) values, path, attributes);
} else if (values != null) {
attributes.add(path);
}

return attributes.isEmpty() && path != null ? Collections.singletonList(path) : attributes;
}

private static void extractScimAttributes(JSONObject jsonObject, String basePath, List<String> attributes) {

for (Iterator<String> it = jsonObject.keys(); it.hasNext(); ) {
String key = it.next();
Object value = jsonObject.get(key);
String separator = DOT_SEPARATOR;
if (defaultScimSchemas().contains(basePath)) {
separator = COLON;
}
String newPath = (basePath != null) ? basePath + separator + key : key;

if (value instanceof JSONObject) {
extractScimAttributes((JSONObject) value, newPath, attributes);
} else if (value instanceof JSONArray) {
extractScimAttributes((JSONArray) value, newPath, attributes);
} else {
attributes.add(newPath);
}
}
}

private static void extractScimAttributes(JSONArray jsonArray, String basePath, List<String> attributes) {

for (int i = 0; i < jsonArray.length(); i++) {
Object value = jsonArray.get(i);
if (value instanceof JSONObject) {
extractScimAttributes((JSONObject) value, basePath, attributes);
} else if (!(value instanceof JSONArray)) {
attributes.add(basePath);
}
}
}

private static List<String> defaultScimSchemas() {

return Arrays.asList(
SCIMConstants.CORE_SCHEMA_URI,
SCIMConstants.USER_CORE_SCHEMA_URI,
SCIMConstants.ENTERPRISE_USER_SCHEMA_URI,
SCIMConstants.SYSTEM_USER_SCHEMA_URI
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,15 @@

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import static org.wso2.charon3.core.schema.SCIMConstants.OperationalConstants.COLON;
import static org.wso2.charon3.core.schema.SCIMConstants.OperationalConstants.DOT_SEPARATOR;
import static org.wso2.charon3.core.schema.SCIMConstants.OperationalConstants.OPEN_SQUARE_BRACKET;

/**
* This provides the methods on the PATCH operation of any resource type.
*/
Expand Down Expand Up @@ -3613,4 +3618,71 @@ private static AbstractSCIMObject doPatchReplaceOnResource(AbstractSCIMObject ol
throw new CharonException("Error in performing the replace operation", e);
}
}

public static List<String> determineScimAttributes(PatchOperation operation) {

if (operation == null) {
return Collections.emptyList();
}
List<String> attributes = new ArrayList<>();
String path = operation.getPath();
if (path != null) {
int bracketIndex = path.indexOf(OPEN_SQUARE_BRACKET);
path = (bracketIndex != -1) ? path.substring(0, bracketIndex) : path;
}

Object values = operation.getValues();
if (values instanceof JSONObject) {
extractScimAttributes((JSONObject) values, path, attributes);
} else if (values instanceof JSONArray) {
extractScimAttributes((JSONArray) values, path, attributes);
} else if (values != null) {
attributes.add(path);
}

return attributes.isEmpty() && path != null ? Collections.singletonList(path) : attributes;
}

private static void extractScimAttributes(JSONObject jsonObject, String basePath, List<String> attributes) {

for (Iterator<String> it = jsonObject.keys(); it.hasNext(); ) {
String key = it.next();
Object value = jsonObject.get(key);
String separator = DOT_SEPARATOR;
if (defaultScimSchemas().contains(basePath)) {
separator = COLON;
}
String newPath = (basePath != null) ? basePath + separator + key : key;

if (value instanceof JSONObject) {
extractScimAttributes((JSONObject) value, newPath, attributes);
} else if (value instanceof JSONArray) {
extractScimAttributes((JSONArray) value, newPath, attributes);
} else {
attributes.add(newPath);
}
}
}

private static void extractScimAttributes(JSONArray jsonArray, String basePath, List<String> attributes) {

for (int i = 0; i < jsonArray.length(); i++) {
Object value = jsonArray.get(i);
if (value instanceof JSONObject) {
extractScimAttributes((JSONObject) value, basePath, attributes);
} else if (!(value instanceof JSONArray)) {
attributes.add(basePath);
}
}
}

private static List<String> defaultScimSchemas() {

return Arrays.asList(
SCIMConstants.CORE_SCHEMA_URI,
SCIMConstants.USER_CORE_SCHEMA_URI,
SCIMConstants.ENTERPRISE_USER_SCHEMA_URI,
SCIMConstants.SYSTEM_USER_SCHEMA_URI
);
}
}
Loading

0 comments on commit 55e6d4d

Please sign in to comment.