diff --git a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/DynamicRegistrationUiDefinitionController.groovy b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/DynamicRegistrationUiDefinitionController.groovy new file mode 100644 index 000000000..b2a75350b --- /dev/null +++ b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/DynamicRegistrationUiDefinitionController.groovy @@ -0,0 +1,58 @@ +package edu.internet2.tier.shibboleth.admin.ui.controller + +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 edu.internet2.tier.shibboleth.admin.ui.service.JsonSchemaBuilderService +import groovy.util.logging.Slf4j +import io.swagger.v3.oas.annotations.tags.Tag +import io.swagger.v3.oas.annotations.tags.Tags +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.algorithmFilterSchema +import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaLocationLookup.dynamicRegistrationSchema +import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR + +/** + * Controller implementing REST resource responsible for exposing structure definition for dynamic registration user + * interface in terms of JSON schema. + */ +@RestController +@RequestMapping('/api/ui/DynamicRegistration') +@Slf4j +@Tags(value = [@Tag(name = "ui")]) +class DynamicRegistrationUiDefinitionController { + + @Autowired + JsonSchemaResourceLocationRegistry jsonSchemaResourceLocationRegistry + + JsonSchemaResourceLocation jsonSchemaLocation + + @Autowired + ObjectMapper jacksonObjectMapper + + @Autowired + JsonSchemaBuilderService jsonSchemaBuilderService + + @GetMapping + ResponseEntity getUiDefinitionJsonSchema() { + try { + def parsedJson = jacksonObjectMapper.readValue(this.jsonSchemaLocation.url, Map) + return ResponseEntity.ok(parsedJson) + } catch (Exception e) { + log.error(e.getMessage(), e) + return ResponseEntity.status(INTERNAL_SERVER_ERROR).body([jsonParseError : e.getMessage(), sourceUiSchemaDefinitionFile: this.jsonSchemaLocation.url]) + } + } + + @PostConstruct + void init() { + this.jsonSchemaLocation = dynamicRegistrationSchema(this.jsonSchemaResourceLocationRegistry) + } +} \ 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 index fa8f5db18..c310154d7 100644 --- 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 @@ -13,6 +13,7 @@ import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.JsonSchemaLocationBuilder; import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.ALGORITHM_FILTER; import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.DYNAMIC_HTTP_METADATA_RESOLVER; +import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.DYNAMIC_REGISTRATION; import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.ENTITY_ATTRIBUTES_FILTERS; import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.EXTERNAL_METADATA_RESOLVER; import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.FILESYSTEM_METADATA_RESOLVER; @@ -73,6 +74,11 @@ public class JsonSchemaComponentsConfiguration { @Setter private String algorithmFilterUiSchemaLocation = "classpath:algorithm-filter.schema.json"; + //Configured via @ConfigurationProperties (using setter method) with 'shibui.dynamic-registration-ui-schema-location' property and + // default value set here if that property is not explicitly set in application.properties + @Setter + private String dynamicRegistrationUiSchemaLocation = "classpath:dynamic-registration.schema.json"; + @Bean public JsonSchemaResourceLocationRegistry jsonSchemaResourceLocationRegistry(ResourceLoader resourceLoader, ObjectMapper jacksonMapper) { return JsonSchemaResourceLocationRegistry.inMemory() @@ -129,6 +135,12 @@ public JsonSchemaResourceLocationRegistry jsonSchemaResourceLocationRegistry(Res .resourceLoader(resourceLoader) .jacksonMapper(jacksonMapper) .detectMalformedJson(true) + .build()) + .register(DYNAMIC_REGISTRATION, JsonSchemaLocationBuilder.with() + .jsonSchemaLocation(dynamicRegistrationUiSchemaLocation) + .resourceLoader(resourceLoader) + .jacksonMapper(jacksonMapper) + .detectMalformedJson(true) .build()); } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/JsonSchemaLocationLookup.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/JsonSchemaLocationLookup.java index a5e5406ef..dc28fc1ad 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/JsonSchemaLocationLookup.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/JsonSchemaLocationLookup.java @@ -2,6 +2,7 @@ import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.ALGORITHM_FILTER; import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.DYNAMIC_HTTP_METADATA_RESOLVER; +import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.DYNAMIC_REGISTRATION; import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.ENTITY_ATTRIBUTES_FILTERS; import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.EXTERNAL_METADATA_RESOLVER; import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.FILESYSTEM_METADATA_RESOLVER; @@ -132,4 +133,15 @@ public static JsonSchemaResourceLocation algorithmFilterSchema(JsonSchemaResourc return resourceLocationRegistry.lookup(ALGORITHM_FILTER) .orElseThrow(() -> new IllegalStateException("JSON schema resource location for algorithm filter is not registered.")); } + + /** + * Searches algorithm filter JSON schema resource location object in the given location registry. + * + * @param resourceLocationRegistry + * @return algorithm filter JSON schema resource location object + * @throws IllegalStateException if schema is not found in the given registry + */ + public static JsonSchemaResourceLocation dynamicRegistrationSchema(JsonSchemaResourceLocationRegistry resourceLocationRegistry) { + return resourceLocationRegistry.lookup(DYNAMIC_REGISTRATION).orElseThrow(() -> new IllegalStateException("JSON schema resource location for dynamic registration is not registered.")); + } } \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/JsonSchemaResourceLocation.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/JsonSchemaResourceLocation.java index 58b1e2d66..dd3262888 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/JsonSchemaResourceLocation.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/JsonSchemaResourceLocation.java @@ -96,6 +96,7 @@ public enum SchemaType { // common types METADATA_SOURCES_SAML("MetadataSourcesSAML"), METADATA_SOURCES_OIDC("MetadataSourcesOIDC"), + DYNAMIC_REGISTRATION("DynamicRegistration"), // filter types ENTITY_ATTRIBUTES_FILTERS("EntityAttributesFilters"), diff --git a/backend/src/main/resources/dynamic-registration.schema.json b/backend/src/main/resources/dynamic-registration.schema.json new file mode 100644 index 000000000..1a0ca9305 --- /dev/null +++ b/backend/src/main/resources/dynamic-registration.schema.json @@ -0,0 +1,96 @@ +{ + "type": "object", + "required": [ + "redirectUris" + ], + "properties": { + "applicationType": { + "title": "label.application-type", + "description": "tooltip.application-type", + "type": "string" + }, + "approved": { + "title": "label.approved", + "description": "tooltip.approved", + "type": "boolean" + }, + "contacts": { + "title": "label.contacts", + "description": "tooltip.contacts", + "type": "string" + }, + "enabled": { + "title": "label.enabled", + "description": "tooltip.enabled", + "type": "boolean" + }, + "grantType": { + "title": "label.grant-type", + "description": "tooltip.grant-type", + "type": "string", + "widget": "select", + "minLength": 1, + "oneOf": [ + { + "enum": ["authorization_code"], + "description": "value.authorization-code" + }, + { + "enum": ["implicit"], + "description": "value.implicit" + }, + { + "enum": ["refresh_token"], + "description": "value.refresh-token" + } + ] + }, + "jwks": { + "title": "label.jwks", + "description": "tooltip.jwks", + "type": "string" + }, + "logoUri": { + "title": "label.logo-uri", + "description": "tooltip.logo-uri", + "type": "string" + }, + "policyUri": { + "title": "label.policy-uri", + "description": "tooltip.policy-uri", + "type": "string" + }, + "redirectUris": { + "title": "label.redirect-uris", + "description": "tooltip.redirect-uris", + "type": "string", + "minLength": 1, + "maxLength": 255 + }, + "responseTypes": { + "title": "label.response-types", + "description": "tooltip.response-types", + "type": "string" + }, + "scope": { + "title": "label.scope", + "description": "tooltip.scope", + "type": "string" + }, + "subjectType": { + "title": "label.subject-type", + "description": "tooltip.subject-type", + "type": "string" + }, + "tokenEndpointAuthMethod": { + "title": "label.token-endpoint-auth-method", + "description": "tooltip.token-endpoint-auth-method", + "type": "string" + }, + "tosUri": { + "title": "label.tos-uri", + "description": "tooltip.tos-uri", + "type": "string" + } + } +} \ No newline at end of file