diff --git a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataSourcesUiDefinitionController.groovy b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataSourcesUiDefinitionController.groovy index 319951144..1425b5af1 100644 --- a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataSourcesUiDefinitionController.groovy +++ b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataSourcesUiDefinitionController.groovy @@ -2,13 +2,20 @@ package edu.internet2.tier.shibboleth.admin.ui.controller import com.fasterxml.jackson.databind.ObjectMapper import edu.internet2.tier.shibboleth.admin.ui.configuration.CustomPropertiesConfiguration -import edu.internet2.tier.shibboleth.admin.ui.jsonschema.MetadataSourcesJsonSchemaResourceLocation +import edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaLocationLookup +import edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation +import edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocationRegistry +import org.springframework.beans.factory.BeanInitializationException import org.springframework.beans.factory.annotation.Autowired import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController +import javax.annotation.PostConstruct + +import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaLocationLookup.metadataSourcesSchema +import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.ShemaType.METADATA_SOURCES import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR /** @@ -23,7 +30,9 @@ import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR class MetadataSourcesUiDefinitionController { @Autowired - MetadataSourcesJsonSchemaResourceLocation jsonSchemaLocation + JsonSchemaResourceLocationRegistry jsonSchemaResourceLocationRegistry + + JsonSchemaResourceLocation jsonSchemaLocation @Autowired ObjectMapper jacksonObjectMapper @@ -48,6 +57,11 @@ class MetadataSourcesUiDefinitionController { } } + @PostConstruct + void init() { + this.jsonSchemaLocation = metadataSourcesSchema(this.jsonSchemaResourceLocationRegistry); + } + private void addReleaseAttributesToJson(Object json) { json['data'] = customPropertiesConfiguration.getAttributes().collect { [key: it['name'], label: it['displayName']] @@ -63,12 +77,12 @@ class MetadataSourcesUiDefinitionController { property = [$ref: '#/definitions/' + it['name']] } else { property = - [title: it['displayName'], - description: it['helpText'], - type: it['displayType'], - default: it['defaultValue']] + [title : it['displayName'], + description: it['helpText'], + type : it['displayType'], + default : it['defaultValue']] } - properties[(String)it['name']] = property + properties[(String) it['name']] = property } json['properties'] = properties } @@ -77,22 +91,22 @@ class MetadataSourcesUiDefinitionController { customPropertiesConfiguration.getOverrides().stream().filter { it -> it['displayType'] && (it['displayType'] == 'list' || it['displayType'] == 'set') }.each { - def definition = [title: it['displayName'], - description: it['helpText'], - type: 'array', - default: null] + def definition = [title : it['displayName'], + description: it['helpText'], + type : 'array', + default : null] if (it['displayType'] == 'set') { definition['uniqueItems'] = true } else if (it['displayType'] == 'list') { definition['uniqueItems'] = false } - def items = [type: 'string', + def items = [type : 'string', minLength: '1', // TODO: should this be configurable? maxLength: '255'] //TODO: or this? items.widget = [id: 'datalist', data: it['defaultValues']] definition['items'] = items - json[(String)it['name']] = definition + json[(String) it['name']] = definition } } } diff --git a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/jsonschema/RelyingPartyOverridesJsonSchemaValidatingControllerAdvice.groovy b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/jsonschema/RelyingPartyOverridesJsonSchemaValidatingControllerAdvice.groovy index 4bc76ce2e..cf342eef9 100644 --- a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/jsonschema/RelyingPartyOverridesJsonSchemaValidatingControllerAdvice.groovy +++ b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/jsonschema/RelyingPartyOverridesJsonSchemaValidatingControllerAdvice.groovy @@ -2,6 +2,7 @@ package edu.internet2.tier.shibboleth.admin.ui.jsonschema import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.EntityDescriptorRepresentation import mjson.Json +import org.springframework.beans.factory.BeanInitializationException import org.springframework.beans.factory.annotation.Autowired import org.springframework.core.MethodParameter import org.springframework.http.HttpInputMessage @@ -13,8 +14,12 @@ import org.springframework.web.bind.annotation.ExceptionHandler import org.springframework.web.context.request.WebRequest import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdviceAdapter +import javax.annotation.PostConstruct import java.lang.reflect.Type +import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaLocationLookup.metadataSourcesSchema +import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.ShemaType.METADATA_SOURCES + /** * Controller advice implementation for validating relying party overrides payload coming from UI layer * against pre-defined JSON schema. @@ -25,7 +30,9 @@ import java.lang.reflect.Type class RelyingPartyOverridesJsonSchemaValidatingControllerAdvice extends RequestBodyAdviceAdapter { @Autowired - MetadataSourcesJsonSchemaResourceLocation schemaLocation + JsonSchemaResourceLocationRegistry jsonSchemaResourceLocationRegistry + + JsonSchemaResourceLocation jsonSchemaLocation @Override boolean supports(MethodParameter methodParameter, Type targetType, Class extends HttpMessageConverter>> converterType) { @@ -36,7 +43,7 @@ class RelyingPartyOverridesJsonSchemaValidatingControllerAdvice extends RequestB Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class extends HttpMessageConverter>> converterType) { def relyingPartyOverrides = EntityDescriptorRepresentation.cast(body).relyingPartyOverrides def relyingPartyOverridesJson = Json.make([relyingPartyOverrides: relyingPartyOverrides]) - def schema = Json.schema(this.schemaLocation.uri) + def schema = Json.schema(this.jsonSchemaLocation.uri) def validationResult = schema.validate(relyingPartyOverridesJson) if (!validationResult.at('ok')) { throw new JsonSchemaValidationFailedException(validationResult.at('errors').asList()) @@ -48,4 +55,9 @@ class RelyingPartyOverridesJsonSchemaValidatingControllerAdvice extends RequestB final ResponseEntity> handleUserNotFoundException(JsonSchemaValidationFailedException ex, WebRequest request) { new ResponseEntity<>([errors: ex.errors], HttpStatus.BAD_REQUEST) } + + @PostConstruct + void init() { + this.jsonSchemaLocation = metadataSourcesSchema(this.jsonSchemaResourceLocationRegistry); + } } diff --git a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImpl.groovy b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImpl.groovy index 4e6879d0f..4a95484e8 100644 --- a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImpl.groovy +++ b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImpl.groovy @@ -125,15 +125,15 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { resolversPositionOrderContainerService.allMetadataResolversInDefinedOrderOrUnordered.each { edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver mr -> - //TODO: We do not currently marshall the internal incommon chaining resolver (with BaseMetadataResolver type) - if ((mr.type != 'BaseMetadataResolver') && (mr.enabled)) { - constructXmlNodeForResolver(mr, delegate) { - //TODO: enhance - mr.metadataFilters.each { edu.internet2.tier.shibboleth.admin.ui.domain.filters.MetadataFilter filter -> - constructXmlNodeForFilter(filter, delegate) + //TODO: We do not currently marshall the internal incommon chaining resolver (with BaseMetadataResolver type) + if ((mr.type != 'BaseMetadataResolver') && (mr.enabled)) { + constructXmlNodeForResolver(mr, delegate) { + //TODO: enhance + mr.metadataFilters.each { edu.internet2.tier.shibboleth.admin.ui.domain.filters.MetadataFilter filter -> + constructXmlNodeForFilter(filter, delegate) + } } } - } } } return DOMBuilder.newInstance().parseText(writer.toString()) @@ -407,4 +407,4 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { } } -} +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/JsonSchemaComponentsConfiguration.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/JsonSchemaComponentsConfiguration.java new file mode 100644 index 000000000..8d70fa410 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/JsonSchemaComponentsConfiguration.java @@ -0,0 +1,50 @@ +package edu.internet2.tier.shibboleth.admin.ui.configuration; + +import com.fasterxml.jackson.databind.ObjectMapper; +import edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation; +import edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocationRegistry; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ResourceLoader; + +import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.*; +import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.ShemaType.ENTITY_ATTRIBUTES_FILTERS; +import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.ShemaType.METADATA_SOURCES; + +/** + * @author Dmitriy Kopylenko + */ +@Configuration +@ConfigurationProperties("shibui") +public class JsonSchemaComponentsConfiguration { + + //Configured via @ConfigurationProperties (using setter method) with 'shibui.metadata-sources-ui-schema-location' property and default + //value set here if that property is not explicitly set in application.properties + @Setter + private String metadataSourcesUiSchemaLocation = "classpath:metadata-sources-ui-schema.json"; + + //Configured via @ConfigurationProperties (using setter method) with 'shibui.entity-attributes-filters-ui-schema-location' property and + // default value set here if that property is not explicitly set in application.properties + @Setter + private String entityAttributesFiltersUiSchemaLocation = "classpath:entity-attributes-filters-ui-schema.json"; + + @Bean + public JsonSchemaResourceLocationRegistry jsonSchemaResourceLocationRegistry(ResourceLoader resourceLoader, ObjectMapper jacksonMapper) { + return JsonSchemaResourceLocationRegistry.inMemory() + .register(METADATA_SOURCES, JsonSchemaLocationBuilder.with() + .jsonSchemaLocation(metadataSourcesUiSchemaLocation) + .resourceLoader(resourceLoader) + .jacksonMapper(jacksonMapper) + .detectMalformedJson(true) + .build()) + .register(ENTITY_ATTRIBUTES_FILTERS, JsonSchemaLocationBuilder.with() + .jsonSchemaLocation(entityAttributesFiltersUiSchemaLocation) + .resourceLoader(resourceLoader) + .jacksonMapper(jacksonMapper) + .detectMalformedJson(true) + .build()); + + } +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/JsonSchemaValidationComponentsConfiguration.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/JsonSchemaValidationComponentsConfiguration.java deleted file mode 100644 index 48fb33ede..000000000 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/JsonSchemaValidationComponentsConfiguration.java +++ /dev/null @@ -1,30 +0,0 @@ -package edu.internet2.tier.shibboleth.admin.ui.configuration; - -import com.fasterxml.jackson.databind.ObjectMapper; -import edu.internet2.tier.shibboleth.admin.ui.jsonschema.MetadataSourcesJsonSchemaResourceLocation; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.io.ResourceLoader; - -/** - * @author Dmitriy Kopylenko - */ -@Configuration -@ConfigurationProperties("shibui") -public class JsonSchemaValidationComponentsConfiguration { - - //Configured via @ConfigurationProperties (using setter method) with 'shibui.metadata-sources-ui-schema-location' property and default - //value set here if that property is not explicitly set in application.properties - private String metadataSourcesUiSchemaLocation ="classpath:metadata-sources-ui-schema.json"; - - //This setter is used by Boot's @ConfiguratonProperties binding machinery - public void setMetadataSourcesUiSchemaLocation(String metadataSourcesUiSchemaLocation) { - this.metadataSourcesUiSchemaLocation = metadataSourcesUiSchemaLocation; - } - - @Bean - public MetadataSourcesJsonSchemaResourceLocation metadataSourcesJsonSchemaResourceLocation(ResourceLoader resourceLoader, ObjectMapper jacksonMapper) { - return new MetadataSourcesJsonSchemaResourceLocation(metadataSourcesUiSchemaLocation, resourceLoader, jacksonMapper); - } -} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/InMemoryJsonSchemaResourceLocationRegistry.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/InMemoryJsonSchemaResourceLocationRegistry.java new file mode 100644 index 000000000..56d6985bd --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/InMemoryJsonSchemaResourceLocationRegistry.java @@ -0,0 +1,31 @@ +package edu.internet2.tier.shibboleth.admin.ui.jsonschema; + +import java.util.EnumMap; +import java.util.Map; +import java.util.Optional; + +/** + * Default implementation of {@link JsonSchemaResourceLocationRegistry}. + *
+ * This class has package private visibility as creation of it is delegated to public static factory method
+ * on the registry interface itself.
+ *
+ * @author Dmitriy Kopylenko
+ */
+class InMemoryJsonSchemaResourceLocationRegistry implements JsonSchemaResourceLocationRegistry {
+
+ private Map