All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.captaingoldfish.scim.sdk.server.patch.msazure.MsAzurePatchAttributeRebuilder Maven / Gradle / Ivy

There is a newer version: 1.26.0
Show newest version
// Generated by delombok at Thu Nov 02 20:38:53 CET 2023
package de.captaingoldfish.scim.sdk.server.patch.msazure;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import de.captaingoldfish.scim.sdk.common.constants.AttributeNames;
import de.captaingoldfish.scim.sdk.common.constants.enums.PatchOp;
import de.captaingoldfish.scim.sdk.common.schemas.SchemaAttribute;
import de.captaingoldfish.scim.sdk.common.utils.JsonHelper;
import de.captaingoldfish.scim.sdk.server.schemas.ResourceType;
import de.captaingoldfish.scim.sdk.server.utils.RequestUtils;


/**
 * This class is a workaround handler in order to handle the broken patch requests of Microsoft Azure. Azure
 * sends illegal patch requests that look as follows:
 *
 * 
 * PATCH /scim/Users/2752513
 * {
 *     "schemas": [
 *         "urn:ietf:params:scim:api:messages:2.0:PatchOp"
 *     ],
 *     "Operations": [
 *         {
 *             "op": "replace",
 *             "value": {
 *                 "name.givenName": "captain",
 *                 "name.familyName": "goldfish"
 *             }
 *         }
 *     ]
 * }
 * 
* * the value in the request must not be present. Instead, the request should look like this: * *
 * PATCH /scim/Users/2752513
 * {
 *     "schemas": [
 *         "urn:ietf:params:scim:api:messages:2.0:PatchOp"
 *     ],
 *     "Operations": [
 *         {
 *             "op": "replace",
 *             "value": {
 *                 "name": {
 *                     "givenName": "captain",
 *                     "familyName": "goldfish"
 *                 }
 *             }
 *         }
 *     ]
 * }
 * 
*/ public final class MsAzurePatchAttributeRebuilder { @java.lang.SuppressWarnings("all") private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(MsAzurePatchAttributeRebuilder.class); /** * the patch operation that is currently executed */ private final PatchOp patchOp; /** * the values of the patch operation. This attribute should actually be empty */ private final List values; /** * used to check if an attribute is a complex type or a multivalued complex type */ private final ResourceType resourceType; public List fixValues() { // simply a check to return early from this method if a remove operation is used if (PatchOp.REMOVE.equals(patchOp)) { log.trace("[MS Azure REPLACE workaround] only handling \'REPLACE\' and \'ADD\' requests"); return values; } // nothing must be done patch request can be handled normally since no illegal value operand is present if (values.isEmpty()) { log.trace("[MS Azure REPLACE workaround] workaround not executed for values-list is empty"); return values; } if (values.size() > 1) { log.trace("[MS Azure REPLACE workaround] workaround not executed for values-list with more than one value"); return values; } String value = values.get(0); if (!JsonHelper.isValidJson(value)) { // do nothing anymore this will cause the request to normally abort at the specific validation point log.trace("[MS Azure REPLACE workaround] attribute in \'value\' operand is not valid json: {}", value); return values; } JsonNode jsonNode = JsonHelper.readJsonDocument(value); final boolean isNodeAnObject = Optional.ofNullable(jsonNode).map(JsonNode::isObject).orElse(false); if (!isNodeAnObject) { // do nothing anymore this will cause the request to normally abort at the specific validation point log.trace("[MS Azure REPLACE workaround] attribute in \'value\' operand is not an object: {}", value); return values; } ObjectNode rootObjectNode = (ObjectNode)jsonNode; List resourceObjectNodes = new ArrayList<>(); resourceObjectNodes.add(rootObjectNode); Optional> schemas = JsonHelper.getSimpleAttributeArray(rootObjectNode, AttributeNames.RFC7643.SCHEMAS); if (schemas.isPresent()) { // set extension resource object as resourceObjectNode if found for ( Iterator it = rootObjectNode.fieldNames() ; it.hasNext() ; ) { String fieldName = it.next(); if (schemas.get().contains(fieldName)) { JsonNode childNode = rootObjectNode.get(fieldName); final boolean isChildNodeAnObject = Optional.ofNullable(childNode).map(JsonNode::isObject).orElse(false); if (!isChildNodeAnObject) { // do nothing anymore this will cause the request to normally abort at the specific validation point log.trace("[MS Azure REPLACE workaround] extension attribute in \'value\' operand is not an object: {}", value); return values; } resourceObjectNodes.add((ObjectNode)childNode); } } } boolean workaroundApplied = false; for ( ObjectNode resourceObjectNode : resourceObjectNodes ) { if (fixValuesForResourceObjectNode(resourceObjectNode)) { workaroundApplied = true; } } if (workaroundApplied) { List newValues = Arrays.asList(JsonHelper.toJsonString(rootObjectNode)); return newValues; } else { return values; } } private boolean fixValuesForResourceObjectNode(ObjectNode resourceObjectNode) { boolean workaroundApplied = false; List fieldNames = new ArrayList<>(); resourceObjectNode.fieldNames().forEachRemaining(fieldNames::add); // apply workaround if necessary for ( String originalFieldName : fieldNames ) { if (originalFieldName.lastIndexOf(":") > -1) { // another resourceObjectNode, it is handled in the outer loop continue; } String[] split = originalFieldName.split("\\."); // only one level of nested dot notation supported if (split.length == 2) { JsonNode originalFieldValue = resourceObjectNode.get(originalFieldName); String fieldName = split[0]; String childFieldName = split[1]; SchemaAttribute childSchemaAttribute = RequestUtils.getSchemaAttributeByAttributeName(resourceType, originalFieldName); JsonNode node = resourceObjectNode.get(fieldName); if (node != null && node.isObject()) { ((ObjectNode)node).set(childFieldName, originalFieldValue); } else { if (childSchemaAttribute.getParent().isMultiValued()) { resourceObjectNode.putArray(fieldName).addObject().set(childFieldName, originalFieldValue); } else { resourceObjectNode.putObject(fieldName).set(childFieldName, originalFieldValue); } } resourceObjectNode.remove(originalFieldName); workaroundApplied = true; } } return workaroundApplied; } @java.lang.SuppressWarnings("all") public MsAzurePatchAttributeRebuilder(final PatchOp patchOp, final List values, final ResourceType resourceType) { this.patchOp = patchOp; this.values = values; this.resourceType = resourceType; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy