
de.captaingoldfish.scim.sdk.server.schemas.validation.AbstractSchemaValidator Maven / Gradle / Ivy
// Generated by delombok at Thu Nov 02 20:38:53 CET 2023
package de.captaingoldfish.scim.sdk.server.schemas.validation;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.apache.commons.lang3.exception.ExceptionUtils;
import com.fasterxml.jackson.databind.JsonNode;
import de.captaingoldfish.scim.sdk.common.constants.AttributeNames;
import de.captaingoldfish.scim.sdk.common.constants.HttpStatus;
import de.captaingoldfish.scim.sdk.common.exceptions.DocumentValidationException;
import de.captaingoldfish.scim.sdk.common.resources.ResourceNode;
import de.captaingoldfish.scim.sdk.common.resources.ServiceProvider;
import de.captaingoldfish.scim.sdk.common.resources.base.ScimArrayNode;
import de.captaingoldfish.scim.sdk.common.resources.base.ScimObjectNode;
import de.captaingoldfish.scim.sdk.common.resources.base.ScimTextNode;
import de.captaingoldfish.scim.sdk.common.schemas.Schema;
import de.captaingoldfish.scim.sdk.common.schemas.SchemaAttribute;
import de.captaingoldfish.scim.sdk.common.utils.AttributeExtractor;
import de.captaingoldfish.scim.sdk.common.utils.CaseInsensitiveAttributeExtractor;
import de.captaingoldfish.scim.sdk.common.utils.CaseSensitiveAttributeExtractor;
import de.captaingoldfish.scim.sdk.common.utils.JsonHelper;
import de.captaingoldfish.scim.sdk.server.schemas.exceptions.AttributeValidationException;
/**
* @author Pascal Knueppel
* @since 23.04.2021
*/
public abstract class AbstractSchemaValidator
{
@java.lang.SuppressWarnings("all")
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AbstractSchemaValidator.class);
/**
* the service provider configuration in order to check if case-insensitive attribute extraction is enabled or
* not
*/
private final ServiceProvider serviceProvider;
protected final Class resourceNodeType;
/**
* the concrete attribute validation that is differs by the context in which the attribute is validated
*
* @param schemaAttribute the definition of the attribute that must be validated
* @param attribute the attribute to validate
* @return the validated attribute
*/
protected abstract Optional validateAttribute(SchemaAttribute schemaAttribute, JsonNode attribute);
/**
* the http status code to use in the {@link DocumentValidationException} if the validation fails. Should be
* 400 (bad request) for requests and 500 (internal server error) for responses
*/
protected abstract int getHttpStatusCode();
/**
* checks the given document against the schema definition of the {@link #resourceType}
*
* @param resource the document that should be validated
* @return the validated resource
*/
public ScimObjectNode validateDocument(Schema schema, JsonNode resource)
{
try
{
checkDocumentAndMetaSchemaRelationship(schema, resource);
ScimObjectNode scimObjectNode = JsonHelper.getNewInstance(resourceNodeType);
return validateDocument(scimObjectNode, schema, resource);
}
catch (AttributeValidationException ex)
{
Throwable cause = ExceptionUtils.getRootCause(ex);
String errorMessage = Optional.ofNullable(cause).map(Throwable::getMessage).orElse(ex.getMessage());
throw new DocumentValidationException(errorMessage, ex, getHttpStatusCode(), null);
}
}
/**
* this method will verify that the meta schema is the correct schema to validate the document. This is done
* by comparing the "id"-attribute of the metaSchema with the "schemas"-attribute of the document
*
* @param resourceSchema the resources schema that should be used to validate the document
* @param document the document that should be validated
*/
protected JsonNode checkDocumentAndMetaSchemaRelationship(Schema resourceSchema, JsonNode document)
{
final String resourceSchemaId = resourceSchema.getNonNullId();
Supplier noSchemasMessage = () -> String.format("Document does not have a \'%s\'-attribute",
AttributeNames.RFC7643.SCHEMAS);
List documentSchemas = JsonHelper.getSimpleAttributeArray(document, AttributeNames.RFC7643.SCHEMAS)
.orElseThrow(() -> new DocumentValidationException(noSchemasMessage.get(),
HttpStatus.BAD_REQUEST,
null));
log.trace("Resource schema with id {} does apply to document with schemas \'{}\'",
resourceSchemaId,
documentSchemas);
ScimArrayNode schemasNode = new ScimArrayNode(null);
schemasNode.addAll(documentSchemas.stream().map(s -> new ScimTextNode(null, s)).collect(Collectors.toList()));
return schemasNode;
}
/**
* this method will validates either a resource document or an extension document that is part of the resource
* document. Extensions are handled as individual schemas.
*
* @param validatedResource The object into which the validated attributes will be added. In case of main
* document validation this object will be of type {@link ResourceNode} and in case of extension
* validation of type {@link ScimObjectNode}
* @param resourceSchema the definition of the document that is either the main schema of the
* {@link #resourceType} of an extension that is present within the current document
* @param resource the document that should be validated
* @return the validated document with its scim attribute representations
*/
protected ScimObjectNode validateDocument(ScimObjectNode validatedResource, Schema resourceSchema, JsonNode resource)
{
AttributeExtractor attributeExtractor = getAttributeExtractor(resource);
for ( SchemaAttribute schemaAttribute : resourceSchema.getAttributes() )
{
log.trace("Validating attribute \'{}\'", schemaAttribute.getScimNodeName());
final String attributeName = schemaAttribute.getName();
JsonNode attribute = attributeExtractor.getAttribute(schemaAttribute).orElse(null);
Optional validatedAttributeOptional = validateAttribute(schemaAttribute, attribute);
validatedAttributeOptional.ifPresent(validatedAttribute -> {
validatedResource.set(attributeName, validatedAttribute);
});
}
return validatedResource;
}
/**
* retrieves the attribute extractor that should be used based on the service providers configuration
*
* @param resource the resource that acts as the attribute extractors base document
* @return the attribute extractor to use. Default is the case-sensitive attribute extractor
*/
public AttributeExtractor getAttributeExtractor(JsonNode resource)
{
final boolean caseInsensitiveValidation = Optional.ofNullable(serviceProvider)
.map(ServiceProvider::isCaseInsensitiveValidation)
.orElse(false);
if (caseInsensitiveValidation)
{
return new CaseInsensitiveAttributeExtractor(resource);
}
else
{
return new CaseSensitiveAttributeExtractor(resource);
}
}
@java.lang.SuppressWarnings("all")
public AbstractSchemaValidator(final ServiceProvider serviceProvider, final Class resourceNodeType)
{
this.serviceProvider = serviceProvider;
this.resourceNodeType = resourceNodeType;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy