From 853c1bd51af5955695b3cbbdb258df946e4530ef Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Thu, 4 Oct 2018 16:28:17 -0400 Subject: [PATCH 1/7] WIP1 --- ...tadataSourcesUiDefinitionController.groovy | 36 ++ .../src/main/resources/application.properties | 3 + .../resources/metadata-sources-ui-schema.json | 439 ++++++++++++++++++ 3 files changed, 478 insertions(+) create mode 100644 backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataSourcesUiDefinitionController.groovy create mode 100644 backend/src/main/resources/metadata-sources-ui-schema.json 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 new file mode 100644 index 000000000..481d95ac5 --- /dev/null +++ b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataSourcesUiDefinitionController.groovy @@ -0,0 +1,36 @@ +package edu.internet2.tier.shibboleth.admin.ui.controller + +import groovy.json.JsonSlurper +import org.springframework.boot.context.properties.ConfigurationProperties +import org.springframework.core.io.ResourceLoader +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RestController + +/** + * Controller implementing REST resource responsible for exposing structure definition for metadata sources user + * interface in terms of JSON schema. + * + * @author Dmitriy Kopylenko + */ +@RestController('/api/ui/metadataSources') +@ConfigurationProperties('shibui') +class MetadataSourcesUiDefinitionController { + + //Configured via @ConfigurationProperties with 'shibui.metadata-sources-ui-schema-location' property and default + //value set here if that property is not set + String metadataSourcesUiSchemaLocation = 'classpath:metadata-sources-ui-schema.json' + + URL jsonSchemaUrl + + MetadataSourcesUiDefinitionController(ResourceLoader resourceLoader) { + jsonSchemaUrl = resourceLoader.getResource(metadataSourcesUiSchemaLocation).getURL() + } + + @GetMapping + ResponseEntity getUiDefinitionJsonSchema() { + //JsonSlurper is not threadsafe, but cheap to init. New instance per-thread is the canonical usage + def json = new JsonSlurper().parse(this.jsonSchemaUrl) + ResponseEntity.ok(json) + } +} diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index 634d32c5b..4d5baf934 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -49,6 +49,9 @@ spring.profiles.active=default #shibui.default-password= +shibui.metadata-sources-ui-schema-location=classpath:metadata-sources-ui-schema-location.json + #Actuator endpoints (info) # Un-comment to get full git details exposed like author, abbreviated SHA-1, commit message #management.info.git.mode=full + diff --git a/backend/src/main/resources/metadata-sources-ui-schema.json b/backend/src/main/resources/metadata-sources-ui-schema.json new file mode 100644 index 000000000..92211e558 --- /dev/null +++ b/backend/src/main/resources/metadata-sources-ui-schema.json @@ -0,0 +1,439 @@ +{ + "entityId": { + "title": "label.entity-id", + "description": "tooltip.entity-id", + "type": "string" + }, + "serviceProviderName": { + "title": "label.service-provider-name", + "description": "tooltip.service-provider-name", + "type": "string" + }, + "serviceEnabled": { + "title": "label.enable-this-service-upon-saving", + "description": "tooltip.enable-this-service-upon-saving", + "type": "boolean" + }, + "organization": { + "type": "object", + "properties": { + "name": { + "title": "label.organization-name", + "description": "tooltip.organization-name", + "type": "string" + }, + "displayName": { + "title": "label.organization-display-name", + "description": "tooltip.organization-display-name", + "type": "string" + }, + "url": { + "title": "label.organization-display-name", + "description": "tooltip.organization-display-name", + "type": "string" + } + }, + "dependencies": { + "name": [ + "displayName", + "url" + ], + "displayName": [ + "name", + "url" + ], + "url": [ + "name", + "displayName" + ] + } + }, + "contacts": { + "title": "label.contact-information", + "description": "tooltip.contact-information", + "type": "array", + "items": { + "$ref": "#/definitions/Contact" + } + }, + "mdui": { + "type": "object", + "properties": { + "displayName": { + "title": "label.display-name", + "description": "tooltip.mdui-display-name", + "type": "string" + }, + "informationUrl": { + "title": "label.information-url", + "description": "tooltip.mdui-information-url", + "type": "string" + }, + "privacyStatementUrl": { + "title": "label.privacy-statement-url", + "description": "tooltip.mdui-privacy-statement-url", + "type": "string" + }, + "description": { + "title": "label.description", + "description": "tooltip.mdui-description", + "type": "string" + }, + "logoUrl": { + "title": "label.logo-url", + "description": "tooltip.mdui-logo-url", + "type": "string" + }, + "logoHeight": { + "title": "label.logo-height", + "description": "tooltip.mdui-logo-height", + "min": 0, + "type": "integer" + }, + "logoWidth": { + "title": "label.logo-width", + "description": "tooltip.mdui-logo-width", + "min": 0, + "type": "integer" + } + } + }, + "securityInfo": { + "type": "object", + "properties": { + "x509CertificateAvailable": { + "title": "label.is-there-a-x509-certificate", + "description": "tooltip.is-there-a-x509-certificate", + "type": "boolean", + "default": false + }, + "authenticationRequestsSigned": { + "title": "label.authentication-requests-signed", + "description": "tooltip.authentication-requests-signed", + "type": "boolean", + "default": false + }, + "wantAssertionsSigned": { + "title": "label.want-assertions-signed", + "description": "tooltip.want-assertions-signed", + "type": "boolean", + "default": false + }, + "x509Certificates": { + "title": "label.x509-certificates", + "type": "array", + "items": { + "$ref": "#/definitions/Certificate" + } + } + } + }, + "assertionConsumerServices": { + "title": "label.assertion-consumer-service-endpoints", + "description": "", + "type": "array", + "items": { + "$ref": "#/definitions/AssertionConsumerService" + } + }, + "serviceProviderSsoDescriptor": { + "type": "object", + "properties": { + "protocolSupportEnum": { + "title": "label.protocol-support-enumeration", + "description": "tooltip.protocol-support-enumeration", + "type": "string", + "placeholder": "label.select-protocol", + "oneOf": [ + { + "enum": [ + "SAML 2" + ], + "description": "SAML 2" + }, + { + "enum": [ + "SAML 1.1" + ], + "description": "SAML 1.1" + } + ] + } + }, + "nameIdFormats": { + "$ref": "#/definitions/NameIdFormatList" + } + }, + "logoutEndpoints": { + "title": "label.logout-endpoints", + "description": "tooltip.logout-endpoints", + "type": "array", + "items": { + "$ref": "#/definitions/LogoutEndpoint" + } + }, + "relyingPartyOverrides": { + "type": "object", + "properties": { + "signAssertion": { + "title": "label.sign-the-assertion", + "description": "tooltip.sign-assertion", + "type": "boolean", + "default": false + }, + "dontSignResponse": { + "title": "label.dont-sign-the-response", + "description": "tooltip.dont-sign-response", + "type": "boolean", + "default": false + }, + "turnOffEncryption": { + "title": "label.turn-off-encryption-of-response", + "description": "tooltip.turn-off-encryption", + "type": "boolean", + "default": false + }, + "useSha": { + "title": "label.use-sha1-signing-algorithm", + "description": "tooltip.usa-sha-algorithm", + "type": "boolean", + "default": false + }, + "ignoreAuthenticationMethod": { + "title": "label.ignore-any-sp-requested-authentication-method", + "description": "tooltip.ignore-auth-method", + "type": "boolean", + "default": false + }, + "forceAuthn": { + "title": "label.force-authn", + "description": "tooltip.force-authn", + "type": "boolean", + "default": false + }, + "omitNotBefore": { + "title": "label.omit-not-before-condition", + "type": "boolean", + "description": "tooltip.omit-not-before-condition", + "default": false + }, + "responderId": { + "title": "label.responder-id", + "description": "tooltip.responder-id", + "type": "string" + }, + "nameIdFormats": { + "$ref": "#/definitions/NameIdFormatList" + }, + "authenticationMethods": { + "$ref": "#/definitions/AuthenticationMethodList" + } + } + }, + "attributeRelease": { + "type": "array", + "description": "Attribute release table - select the attributes you want to release (default unchecked)", + "widget": { + "id": "checklist", + "dataUrl": "/customAttributes" + }, + "items": { + "type": "string" + } + }, + "definitions": { + "Contact": { + "type": "object", + "properties": { + "name": { + "title": "label.contact-name", + "description": "tooltip.contact-name", + "type": "string" + }, + "type": { + "title": "label.contact-type", + "description": "tooltip.contact-type", + "type": "string", + "oneOf": [ + { + "enum": [ + "support" + ], + "description": "value.support" + }, + { + "enum": [ + "technical" + ], + "description": "value.technical" + }, + { + "enum": [ + "administrative" + ], + "description": "value.administrative" + }, + { + "enum": [ + "other" + ], + "description": "value.other" + } + ] + }, + "emailAddress": { + "title": "label.contact-email-address", + "description": "tooltip.contact-email", + "type": "string", + "pattern": "^(?=.{1,254}$)(?=.{1,64}@)[-!#$%&'*+/0-9=?A-Z^_`a-z{|}~]+(\\.[-!#$%&'*+/0-9=?A-Z^_`a-z{|}~]+)*@[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?(\\.[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*$" + } + } + }, + "Certificate": { + "name": { + "title": "label.certificate-name-display-only", + "description": "tooltip.certificate-name", + "type": "string" + }, + "type": { + "title": "label.type", + "description": "tooltip.certificate-type", + "type": "string", + "oneOf": [ + { + "enum": [ + "signing" + ], + "description": "value.signing" + }, + { + "enum": [ + "encryption" + ], + "description": "value.encryption" + }, + { + "enum": [ + "both" + ], + "description": "value.both" + } + ], + "default": "both" + }, + "value": { + "title": "label.certificate", + "description": "tooltip.certificate", + "type": "string" + } + }, + "AssertionConsumerService": { + "type": "object", + "properties": { + "locationUrl": { + "title": "label.assertion-consumer-services-location", + "description": "tooltip.assertion-consumer-service-location", + "type": "string", + "widget": { + "id": "string", + "help": "message.valid-url" + } + }, + "binding": { + "title": "label.assertion-consumer-service-location-binding", + "description": "tooltip.assertion-consumer-service-location-binding", + "type": "string", + "oneOf": [ + { + "enum": [ + "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" + ], + "description": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" + }, + { + "enum": [ + "urn:oasis:names:tc:SAML:1.0:profiles:browser-post" + ], + "description": "urn:oasis:names:tc:SAML:1.0:profiles:browser-post" + } + ] + }, + "makeDefault": { + "title": "label.mark-as-default", + "description": "tooltip.mark-as-default", + "type": "boolean" + } + } + }, + "NameIdFormatList": { + "title": "label.nameid-format-to-send", + "placeholder": "label.nameid-format", + "description": "tooltip.nameid-format", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "widget": "datalist", + "data": [ + "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", + "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", + "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent", + "urn:oasis:names:tc:SAML:2.0:nameid-format:transient" + ] + }, + "default": null + }, + "AuthenticationMethodList": { + "title": "label.authentication-methods-to-use", + "description": "tooltip.authentication-methods-to-use", + "type": "array", + "placeholder": "label.authentication-method", + "uniqueItems": true, + "items": { + "type": "string", + "title": "label.authentication-method", + "widget": { + "id": "datalist", + "data": [ + "https://refeds.org/profile/mfa", + "urn:oasis:names:tc:SAML:2.0:ac:classes:TimeSyncToken", + "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport" + ] + } + }, + "default": null + }, + "LogoutEndpoint": { + "title": "label.new-endpoint", + "description": "tooltip.new-endpoint", + "type": "object", + "properties": { + "url": { + "title": "label.url", + "description": "tooltip.url", + "type": "string" + }, + "bindingType": { + "title": "label.binding-type", + "description": "tooltip.binding-type", + "type": "string", + "oneOf": [ + { + "enum": [ + "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" + ], + "description": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" + }, + { + "enum": [ + "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" + ], + "description": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" + } + ] + + } + } + } + } +} \ No newline at end of file From d578ed685232ca594f322e2296f512d680fc0dca Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Fri, 5 Oct 2018 13:58:38 -0400 Subject: [PATCH 2/7] Updated JSON schema --- .../resources/metadata-sources-ui-schema.json | 461 +++++++++--------- 1 file changed, 232 insertions(+), 229 deletions(-) diff --git a/backend/src/main/resources/metadata-sources-ui-schema.json b/backend/src/main/resources/metadata-sources-ui-schema.json index 92211e558..11c5595ac 100644 --- a/backend/src/main/resources/metadata-sources-ui-schema.json +++ b/backend/src/main/resources/metadata-sources-ui-schema.json @@ -1,244 +1,247 @@ { - "entityId": { - "title": "label.entity-id", - "description": "tooltip.entity-id", - "type": "string" - }, - "serviceProviderName": { - "title": "label.service-provider-name", - "description": "tooltip.service-provider-name", - "type": "string" - }, - "serviceEnabled": { - "title": "label.enable-this-service-upon-saving", - "description": "tooltip.enable-this-service-upon-saving", - "type": "boolean" - }, - "organization": { - "type": "object", - "properties": { - "name": { - "title": "label.organization-name", - "description": "tooltip.organization-name", - "type": "string" - }, - "displayName": { - "title": "label.organization-display-name", - "description": "tooltip.organization-display-name", - "type": "string" + "type": "object", + "properties": { + "entityId": { + "title": "label.entity-id", + "description": "tooltip.entity-id", + "type": "string" + }, + "serviceProviderName": { + "title": "label.service-provider-name", + "description": "tooltip.service-provider-name", + "type": "string" + }, + "serviceEnabled": { + "title": "label.enable-this-service-opon-saving", + "description": "tooltip.enable-this-service-upon-saving", + "type": "boolean" + }, + "organization": { + "type": "object", + "properties": { + "name": { + "title": "label.organization-name", + "description": "tooltip.organization-name", + "type": "string" + }, + "displayName": { + "title": "label.organization-display-name", + "description": "tooltip.organization-display-name", + "type": "string" + }, + "url": { + "title": "label.organization-display-name", + "description": "tooltip.organization-display-name", + "type": "string" + } }, - "url": { - "title": "label.organization-display-name", - "description": "tooltip.organization-display-name", - "type": "string" + "dependencies": { + "name": [ + "displayName", + "url" + ], + "displayName": [ + "name", + "url" + ], + "url": [ + "name", + "displayName" + ] } }, - "dependencies": { - "name": [ - "displayName", - "url" - ], - "displayName": [ - "name", - "url" - ], - "url": [ - "name", - "displayName" - ] - } - }, - "contacts": { - "title": "label.contact-information", - "description": "tooltip.contact-information", - "type": "array", - "items": { - "$ref": "#/definitions/Contact" - } - }, - "mdui": { - "type": "object", - "properties": { - "displayName": { - "title": "label.display-name", - "description": "tooltip.mdui-display-name", - "type": "string" - }, - "informationUrl": { - "title": "label.information-url", - "description": "tooltip.mdui-information-url", - "type": "string" - }, - "privacyStatementUrl": { - "title": "label.privacy-statement-url", - "description": "tooltip.mdui-privacy-statement-url", - "type": "string" - }, - "description": { - "title": "label.description", - "description": "tooltip.mdui-description", - "type": "string" - }, - "logoUrl": { - "title": "label.logo-url", - "description": "tooltip.mdui-logo-url", - "type": "string" - }, - "logoHeight": { - "title": "label.logo-height", - "description": "tooltip.mdui-logo-height", - "min": 0, - "type": "integer" - }, - "logoWidth": { - "title": "label.logo-width", - "description": "tooltip.mdui-logo-width", - "min": 0, - "type": "integer" + "contacts": { + "title": "label.contact-information", + "description": "tooltip.contact-information", + "type": "array", + "items": { + "$ref": "#/definitions/Contact" } - } - }, - "securityInfo": { - "type": "object", - "properties": { - "x509CertificateAvailable": { - "title": "label.is-there-a-x509-certificate", - "description": "tooltip.is-there-a-x509-certificate", - "type": "boolean", - "default": false - }, - "authenticationRequestsSigned": { - "title": "label.authentication-requests-signed", - "description": "tooltip.authentication-requests-signed", - "type": "boolean", - "default": false - }, - "wantAssertionsSigned": { - "title": "label.want-assertions-signed", - "description": "tooltip.want-assertions-signed", - "type": "boolean", - "default": false - }, - "x509Certificates": { - "title": "label.x509-certificates", - "type": "array", - "items": { - "$ref": "#/definitions/Certificate" + }, + "mdui": { + "type": "object", + "properties": { + "displayName": { + "title": "label.display-name", + "description": "tooltip.mdui-display-name", + "type": "string" + }, + "informationUrl": { + "title": "label.information-url", + "description": "tooltip.mdui-information-url", + "type": "string" + }, + "privacyStatementUrl": { + "title": "label.privacy-statement-url", + "description": "tooltip.mdui-privacy-statement-url", + "type": "string" + }, + "description": { + "title": "label.description", + "description": "tooltip.mdui-description", + "type": "string" + }, + "logoUrl": { + "title": "label.logo-url", + "description": "tooltip.mdui-logo-url", + "type": "string" + }, + "logoHeight": { + "title": "label.logo-height", + "description": "tooltip.mdui-logo-height", + "min": 0, + "type": "integer" + }, + "logoWidth": { + "title": "label.logo-width", + "description": "tooltip.mdui-logo-width", + "min": 0, + "type": "integer" } } - } - }, - "assertionConsumerServices": { - "title": "label.assertion-consumer-service-endpoints", - "description": "", - "type": "array", - "items": { - "$ref": "#/definitions/AssertionConsumerService" - } - }, - "serviceProviderSsoDescriptor": { - "type": "object", - "properties": { - "protocolSupportEnum": { - "title": "label.protocol-support-enumeration", - "description": "tooltip.protocol-support-enumeration", - "type": "string", - "placeholder": "label.select-protocol", - "oneOf": [ - { - "enum": [ - "SAML 2" - ], - "description": "SAML 2" - }, - { - "enum": [ - "SAML 1.1" - ], - "description": "SAML 1.1" + }, + "securityInfo": { + "type": "object", + "properties": { + "x509CertificateAvailable": { + "title": "label.is-there-a-x509-certificate", + "description": "tooltip.is-there-a-x509-certificate", + "type": "boolean", + "default": false + }, + "authenticationRequestsSigned": { + "title": "label.authentication-requests-signed", + "description": "tooltip.authentication-requests-signed", + "type": "boolean", + "default": false + }, + "wantAssertionsSigned": { + "title": "label.want-assertions-signed", + "description": "tooltip.want-assertions-signed", + "type": "boolean", + "default": false + }, + "x509Certificates": { + "title": "label.x509-certificates", + "type": "array", + "items": { + "$ref": "#/definitions/Certificate" } - ] + } } }, - "nameIdFormats": { - "$ref": "#/definitions/NameIdFormatList" - } - }, - "logoutEndpoints": { - "title": "label.logout-endpoints", - "description": "tooltip.logout-endpoints", - "type": "array", - "items": { - "$ref": "#/definitions/LogoutEndpoint" - } - }, - "relyingPartyOverrides": { - "type": "object", - "properties": { - "signAssertion": { - "title": "label.sign-the-assertion", - "description": "tooltip.sign-assertion", - "type": "boolean", - "default": false - }, - "dontSignResponse": { - "title": "label.dont-sign-the-response", - "description": "tooltip.dont-sign-response", - "type": "boolean", - "default": false - }, - "turnOffEncryption": { - "title": "label.turn-off-encryption-of-response", - "description": "tooltip.turn-off-encryption", - "type": "boolean", - "default": false - }, - "useSha": { - "title": "label.use-sha1-signing-algorithm", - "description": "tooltip.usa-sha-algorithm", - "type": "boolean", - "default": false - }, - "ignoreAuthenticationMethod": { - "title": "label.ignore-any-sp-requested-authentication-method", - "description": "tooltip.ignore-auth-method", - "type": "boolean", - "default": false - }, - "forceAuthn": { - "title": "label.force-authn", - "description": "tooltip.force-authn", - "type": "boolean", - "default": false - }, - "omitNotBefore": { - "title": "label.omit-not-before-condition", - "type": "boolean", - "description": "tooltip.omit-not-before-condition", - "default": false - }, - "responderId": { - "title": "label.responder-id", - "description": "tooltip.responder-id", - "type": "string" + "assertionConsumerServices": { + "title": "label.assertion-consumer-service-endpoints", + "description": "", + "type": "array", + "items": { + "$ref": "#/definitions/AssertionConsumerService" + } + }, + "serviceProviderSsoDescriptor": { + "type": "object", + "properties": { + "protocolSupportEnum": { + "title": "label.protocol-support-enumeration", + "description": "tooltip.protocol-support-enumeration", + "type": "string", + "placeholder": "label.select-protocol", + "oneOf": [ + { + "enum": [ + "SAML 2" + ], + "description": "SAML 2" + }, + { + "enum": [ + "SAML 1.1" + ], + "description": "SAML 1.1" + } + ] + } }, "nameIdFormats": { "$ref": "#/definitions/NameIdFormatList" - }, - "authenticationMethods": { - "$ref": "#/definitions/AuthenticationMethodList" } - } - }, - "attributeRelease": { - "type": "array", - "description": "Attribute release table - select the attributes you want to release (default unchecked)", - "widget": { - "id": "checklist", - "dataUrl": "/customAttributes" }, - "items": { - "type": "string" + "logoutEndpoints": { + "title": "label.logout-endpoints", + "description": "tooltip.logout-endpoints", + "type": "array", + "items": { + "$ref": "#/definitions/LogoutEndpoint" + } + }, + "relyingPartyOverrides": { + "type": "object", + "properties": { + "signAssertion": { + "title": "label.sign-the-assertion", + "description": "tooltip.sign-assertion", + "type": "boolean", + "default": false + }, + "dontSignResponse": { + "title": "label.dont-sign-the-response", + "description": "tooltip.dont-sign-response", + "type": "boolean", + "default": false + }, + "turnOffEncryption": { + "title": "label.turn-off-encryption-of-response", + "description": "tooltip.turn-off-encryption", + "type": "boolean", + "default": false + }, + "useSha": { + "title": "label.use-sha1-signing-algorithm", + "description": "tooltip.usa-sha-algorithm", + "type": "boolean", + "default": false + }, + "ignoreAuthenticationMethod": { + "title": "label.ignore-any-sp-requested-authentication-method", + "description": "tooltip.ignore-auth-method", + "type": "boolean", + "default": false + }, + "forceAuthn": { + "title": "label.force-authn", + "description": "tooltip.force-authn", + "type": "boolean", + "default": false + }, + "omitNotBefore": { + "title": "label.omit-not-before-condition", + "type": "boolean", + "description": "tooltip.omit-not-before-condition", + "default": false + }, + "responderId": { + "title": "label.responder-id", + "description": "tooltip.responder-id", + "type": "string" + }, + "nameIdFormats": { + "$ref": "#/definitions/NameIdFormatList" + }, + "authenticationMethods": { + "$ref": "#/definitions/AuthenticationMethodList" + } + } + }, + "attributeRelease": { + "type": "array", + "description": "Attribute release table - select the attributes you want to release (default unchecked)", + "widget": { + "id": "checklist", + "dataUrl": "/customAttributes" + }, + "items": { + "type": "string" + } } }, "definitions": { @@ -259,7 +262,7 @@ "enum": [ "support" ], - "description": "value.support" + "description": "value.support" }, { "enum": [ @@ -431,9 +434,9 @@ "description": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" } ] - + } } } } -} \ No newline at end of file +} From a9f5cf9ab7ad43c080acb6561bcad5efd30e2bb7 Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Fri, 5 Oct 2018 18:51:09 -0400 Subject: [PATCH 3/7] Work in progress --- ...tadataSourcesUiDefinitionController.groovy | 17 +- .../src/main/resources/application.properties | 2 +- ...efinitionControllerIntegrationTests.groovy | 51 ++ .../metadata-sources-ui-schema_BAD.json | 442 ++++++++++++++++++ 4 files changed, 506 insertions(+), 6 deletions(-) create mode 100644 backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataSourcesUiDefinitionControllerIntegrationTests.groovy create mode 100644 backend/src/test/resources/metadata-sources-ui-schema_BAD.json 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 481d95ac5..6156b97cd 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 @@ -1,31 +1,33 @@ package edu.internet2.tier.shibboleth.admin.ui.controller import groovy.json.JsonSlurper +import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.context.properties.ConfigurationProperties import org.springframework.core.io.ResourceLoader import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RestController +import javax.annotation.PostConstruct + /** * Controller implementing REST resource responsible for exposing structure definition for metadata sources user * interface in terms of JSON schema. * * @author Dmitriy Kopylenko */ -@RestController('/api/ui/metadataSources') +@RestController('/api/ui/MetadataSources') @ConfigurationProperties('shibui') class MetadataSourcesUiDefinitionController { //Configured via @ConfigurationProperties with 'shibui.metadata-sources-ui-schema-location' property and default //value set here if that property is not set - String metadataSourcesUiSchemaLocation = 'classpath:metadata-sources-ui-schema.json' + String metadataSourcesUiSchemaLocation //= 'classpath:metadata-sources-ui-schema.json' URL jsonSchemaUrl - MetadataSourcesUiDefinitionController(ResourceLoader resourceLoader) { - jsonSchemaUrl = resourceLoader.getResource(metadataSourcesUiSchemaLocation).getURL() - } + @Autowired + ResourceLoader resourceLoader @GetMapping ResponseEntity getUiDefinitionJsonSchema() { @@ -33,4 +35,9 @@ class MetadataSourcesUiDefinitionController { def json = new JsonSlurper().parse(this.jsonSchemaUrl) ResponseEntity.ok(json) } + + @PostConstruct + def init() { + jsonSchemaUrl = this.resourceLoader.getResource(this.metadataSourcesUiSchemaLocation).getURL() + } } diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index 4d5baf934..0386e64b1 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -49,7 +49,7 @@ spring.profiles.active=default #shibui.default-password= -shibui.metadata-sources-ui-schema-location=classpath:metadata-sources-ui-schema-location.json +shibui.metadata-sources-ui-schema-location=classpath:metadata-sources-ui-schema.json #Actuator endpoints (info) # Un-comment to get full git details exposed like author, abbreviated SHA-1, commit message diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataSourcesUiDefinitionControllerIntegrationTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataSourcesUiDefinitionControllerIntegrationTests.groovy new file mode 100644 index 000000000..643df654d --- /dev/null +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataSourcesUiDefinitionControllerIntegrationTests.groovy @@ -0,0 +1,51 @@ +package edu.internet2.tier.shibboleth.admin.ui.controller + +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.test.web.client.TestRestTemplate +import org.springframework.core.env.ConfigurableEnvironment +import org.springframework.test.context.ActiveProfiles +import spock.lang.Specification + +/** + * @author Dmitriy Kopylenko + */ +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ActiveProfiles("no-auth") +class MetadataSourcesUiDefinitionControllerIntegrationTests extends Specification { + + @Autowired + private TestRestTemplate restTemplate + + @Autowired + ConfigurableEnvironment environment + + @Autowired + MetadataSourcesUiDefinitionController controllerUnderTest + + static RESOURCE_URI = '/api/ui/MetadataSources' + + def "GET Metadata Sources UI definition schema"() { + when: 'GET request is made for metadata source UI definition schema' + + def result = this.restTemplate.getForEntity(RESOURCE_URI, Object) + + then: "Request completed successfully" + result.statusCodeValue == 200 + result.body.properties.entityId.title == 'label.entity-id' + } + + def "GET Malformed Metadata Sources UI definition schema"() { + when: 'GET request is made for malformed metadata source UI definition schema' + configureMalformedJsonInput() + def result = this.restTemplate.getForEntity(RESOURCE_URI, Object) + + then: "Request results in HTTP 400" + result.statusCodeValue == 200 + } + + private configureMalformedJsonInput() { + controllerUnderTest.metadataSourcesUiSchemaLocation = 'classpath:metadata-sources-ui-schema_BAD.json' + controllerUnderTest.init() + } +} \ No newline at end of file diff --git a/backend/src/test/resources/metadata-sources-ui-schema_BAD.json b/backend/src/test/resources/metadata-sources-ui-schema_BAD.json new file mode 100644 index 000000000..ee055fdf4 --- /dev/null +++ b/backend/src/test/resources/metadata-sources-ui-schema_BAD.json @@ -0,0 +1,442 @@ +{ + "type": "object" + "properties": { + "entityId": { + "title": "label.entity-id", + "description": "tooltip.entity-id", + "type": "string" + }, + "serviceProviderName": { + "title": "label.service-provider-name", + "description": "tooltip.service-provider-name", + "type": "string" + }, + "serviceEnabled": { + "title": "label.enable-this-service-upon-saving", + "description": "tooltip.enable-this-service-upon-saving", + "type": "boolean" + }, + "organization": { + "type": "object", + "properties": { + "name": { + "title": "label.organization-name", + "description": "tooltip.organization-name", + "type": "string" + }, + "displayName": { + "title": "label.organization-display-name", + "description": "tooltip.organization-display-name", + "type": "string" + }, + "url": { + "title": "label.organization-display-name", + "description": "tooltip.organization-display-name", + "type": "string" + } + }, + "dependencies": { + "name": [ + "displayName", + "url" + ], + "displayName": [ + "name", + "url" + ], + "url": [ + "name", + "displayName" + ] + } + }, + "contacts": { + "title": "label.contact-information", + "description": "tooltip.contact-information", + "type": "array", + "items": { + "$ref": "#/definitions/Contact" + } + }, + "mdui": { + "type": "object", + "properties": { + "displayName": { + "title": "label.display-name", + "description": "tooltip.mdui-display-name", + "type": "string" + }, + "informationUrl": { + "title": "label.information-url", + "description": "tooltip.mdui-information-url", + "type": "string" + }, + "privacyStatementUrl": { + "title": "label.privacy-statement-url", + "description": "tooltip.mdui-privacy-statement-url", + "type": "string" + }, + "description": { + "title": "label.description", + "description": "tooltip.mdui-description", + "type": "string" + }, + "logoUrl": { + "title": "label.logo-url", + "description": "tooltip.mdui-logo-url", + "type": "string" + }, + "logoHeight": { + "title": "label.logo-height", + "description": "tooltip.mdui-logo-height", + "min": 0, + "type": "integer" + }, + "logoWidth": { + "title": "label.logo-width", + "description": "tooltip.mdui-logo-width", + "min": 0, + "type": "integer" + } + } + }, + "securityInfo": { + "type": "object", + "properties": { + "x509CertificateAvailable": { + "title": "label.is-there-a-x509-certificate", + "description": "tooltip.is-there-a-x509-certificate", + "type": "boolean", + "default": false + }, + "authenticationRequestsSigned": { + "title": "label.authentication-requests-signed", + "description": "tooltip.authentication-requests-signed", + "type": "boolean", + "default": false + }, + "wantAssertionsSigned": { + "title": "label.want-assertions-signed", + "description": "tooltip.want-assertions-signed", + "type": "boolean", + "default": false + }, + "x509Certificates": { + "title": "label.x509-certificates", + "type": "array", + "items": { + "$ref": "#/definitions/Certificate" + } + } + } + }, + "assertionConsumerServices": { + "title": "label.assertion-consumer-service-endpoints", + "description": "", + "type": "array", + "items": { + "$ref": "#/definitions/AssertionConsumerService" + } + }, + "serviceProviderSsoDescriptor": { + "type": "object", + "properties": { + "protocolSupportEnum": { + "title": "label.protocol-support-enumeration", + "description": "tooltip.protocol-support-enumeration", + "type": "string", + "placeholder": "label.select-protocol", + "oneOf": [ + { + "enum": [ + "SAML 2" + ], + "description": "SAML 2" + }, + { + "enum": [ + "SAML 1.1" + ], + "description": "SAML 1.1" + } + ] + } + }, + "nameIdFormats": { + "$ref": "#/definitions/NameIdFormatList" + } + }, + "logoutEndpoints": { + "title": "label.logout-endpoints", + "description": "tooltip.logout-endpoints", + "type": "array", + "items": { + "$ref": "#/definitions/LogoutEndpoint" + } + }, + "relyingPartyOverrides": { + "type": "object", + "properties": { + "signAssertion": { + "title": "label.sign-the-assertion", + "description": "tooltip.sign-assertion", + "type": "boolean", + "default": false + }, + "dontSignResponse": { + "title": "label.dont-sign-the-response", + "description": "tooltip.dont-sign-response", + "type": "boolean", + "default": false + }, + "turnOffEncryption": { + "title": "label.turn-off-encryption-of-response", + "description": "tooltip.turn-off-encryption", + "type": "boolean", + "default": false + }, + "useSha": { + "title": "label.use-sha1-signing-algorithm", + "description": "tooltip.usa-sha-algorithm", + "type": "boolean", + "default": false + }, + "ignoreAuthenticationMethod": { + "title": "label.ignore-any-sp-requested-authentication-method", + "description": "tooltip.ignore-auth-method", + "type": "boolean", + "default": false + }, + "forceAuthn": { + "title": "label.force-authn", + "description": "tooltip.force-authn", + "type": "boolean", + "default": false + }, + "omitNotBefore": { + "title": "label.omit-not-before-condition", + "type": "boolean", + "description": "tooltip.omit-not-before-condition", + "default": false + }, + "responderId": { + "title": "label.responder-id", + "description": "tooltip.responder-id", + "type": "string" + }, + "nameIdFormats": { + "$ref": "#/definitions/NameIdFormatList" + }, + "authenticationMethods": { + "$ref": "#/definitions/AuthenticationMethodList" + } + } + }, + "attributeRelease": { + "type": "array", + "description": "Attribute release table - select the attributes you want to release (default unchecked)", + "widget": { + "id": "checklist", + "dataUrl": "/customAttributes" + }, + "items": { + "type": "string" + } + } + }, + "definitions": { + "Contact": { + "type": "object", + "properties": { + "name": { + "title": "label.contact-name", + "description": "tooltip.contact-name", + "type": "string" + }, + "type": { + "title": "label.contact-type", + "description": "tooltip.contact-type", + "type": "string", + "oneOf": [ + { + "enum": [ + "support" + ], + "description": "value.support" + }, + { + "enum": [ + "technical" + ], + "description": "value.technical" + }, + { + "enum": [ + "administrative" + ], + "description": "value.administrative" + }, + { + "enum": [ + "other" + ], + "description": "value.other" + } + ] + }, + "emailAddress": { + "title": "label.contact-email-address", + "description": "tooltip.contact-email", + "type": "string", + "pattern": "^(?=.{1,254}$)(?=.{1,64}@)[-!#$%&'*+/0-9=?A-Z^_`a-z{|}~]+(\\.[-!#$%&'*+/0-9=?A-Z^_`a-z{|}~]+)*@[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?(\\.[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*$" + } + } + }, + "Certificate": { + "name": { + "title": "label.certificate-name-display-only", + "description": "tooltip.certificate-name", + "type": "string" + }, + "type": { + "title": "label.type", + "description": "tooltip.certificate-type", + "type": "string", + "oneOf": [ + { + "enum": [ + "signing" + ], + "description": "value.signing" + }, + { + "enum": [ + "encryption" + ], + "description": "value.encryption" + }, + { + "enum": [ + "both" + ], + "description": "value.both" + } + ], + "default": "both" + }, + "value": { + "title": "label.certificate", + "description": "tooltip.certificate", + "type": "string" + } + }, + "AssertionConsumerService": { + "type": "object", + "properties": { + "locationUrl": { + "title": "label.assertion-consumer-services-location", + "description": "tooltip.assertion-consumer-service-location", + "type": "string", + "widget": { + "id": "string", + "help": "message.valid-url" + } + }, + "binding": { + "title": "label.assertion-consumer-service-location-binding", + "description": "tooltip.assertion-consumer-service-location-binding", + "type": "string", + "oneOf": [ + { + "enum": [ + "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" + ], + "description": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" + }, + { + "enum": [ + "urn:oasis:names:tc:SAML:1.0:profiles:browser-post" + ], + "description": "urn:oasis:names:tc:SAML:1.0:profiles:browser-post" + } + ] + }, + "makeDefault": { + "title": "label.mark-as-default", + "description": "tooltip.mark-as-default", + "type": "boolean" + } + } + }, + "NameIdFormatList": { + "title": "label.nameid-format-to-send", + "placeholder": "label.nameid-format", + "description": "tooltip.nameid-format", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "widget": "datalist", + "data": [ + "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", + "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", + "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent", + "urn:oasis:names:tc:SAML:2.0:nameid-format:transient" + ] + }, + "default": null + }, + "AuthenticationMethodList": { + "title": "label.authentication-methods-to-use", + "description": "tooltip.authentication-methods-to-use", + "type": "array", + "placeholder": "label.authentication-method", + "uniqueItems": true, + "items": { + "type": "string", + "title": "label.authentication-method", + "widget": { + "id": "datalist", + "data": [ + "https://refeds.org/profile/mfa", + "urn:oasis:names:tc:SAML:2.0:ac:classes:TimeSyncToken", + "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport" + ] + } + }, + "default": null + }, + "LogoutEndpoint": { + "title": "label.new-endpoint", + "description": "tooltip.new-endpoint", + "type": "object", + "properties": { + "url": { + "title": "label.url", + "description": "tooltip.url", + "type": "string" + }, + "bindingType": { + "title": "label.binding-type", + "description": "tooltip.binding-type", + "type": "string", + "oneOf": [ + { + "enum": [ + "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" + ], + "description": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" + }, + { + "enum": [ + "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" + ], + "description": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" + } + ] + + } + } + } + } +} From 08cf1023818db8adbfefccc678dd3172deb8ba36 Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Fri, 5 Oct 2018 18:52:57 -0400 Subject: [PATCH 4/7] Work in progress --- ...etadataSourcesUiDefinitionControllerIntegrationTests.groovy | 3 --- 1 file changed, 3 deletions(-) diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataSourcesUiDefinitionControllerIntegrationTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataSourcesUiDefinitionControllerIntegrationTests.groovy index 643df654d..abc416b78 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataSourcesUiDefinitionControllerIntegrationTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataSourcesUiDefinitionControllerIntegrationTests.groovy @@ -17,9 +17,6 @@ class MetadataSourcesUiDefinitionControllerIntegrationTests extends Specificatio @Autowired private TestRestTemplate restTemplate - @Autowired - ConfigurableEnvironment environment - @Autowired MetadataSourcesUiDefinitionController controllerUnderTest From 12a91edc2da8d49a57f0fcb6e007b08303e09e63 Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Mon, 8 Oct 2018 09:19:33 -0400 Subject: [PATCH 5/7] WIP --- .../resources/metadata-sources-ui-schema.json | 6 +- .../resources/metadata-sources-ui-schema.json | 442 ++++++++++++++++++ ...metadata-sources-ui-schema_MALFORMED.json} | 6 +- 3 files changed, 448 insertions(+), 6 deletions(-) create mode 100644 backend/src/test/resources/metadata-sources-ui-schema.json rename backend/src/test/resources/{metadata-sources-ui-schema_BAD.json => metadata-sources-ui-schema_MALFORMED.json} (99%) diff --git a/backend/src/main/resources/metadata-sources-ui-schema.json b/backend/src/main/resources/metadata-sources-ui-schema.json index 11c5595ac..5766e298f 100644 --- a/backend/src/main/resources/metadata-sources-ui-schema.json +++ b/backend/src/main/resources/metadata-sources-ui-schema.json @@ -262,7 +262,7 @@ "enum": [ "support" ], - "description": "value.support" + "description": "value.support" }, { "enum": [ @@ -434,9 +434,9 @@ "description": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" } ] - + } } } } -} +} \ No newline at end of file diff --git a/backend/src/test/resources/metadata-sources-ui-schema.json b/backend/src/test/resources/metadata-sources-ui-schema.json new file mode 100644 index 000000000..5766e298f --- /dev/null +++ b/backend/src/test/resources/metadata-sources-ui-schema.json @@ -0,0 +1,442 @@ +{ + "type": "object", + "properties": { + "entityId": { + "title": "label.entity-id", + "description": "tooltip.entity-id", + "type": "string" + }, + "serviceProviderName": { + "title": "label.service-provider-name", + "description": "tooltip.service-provider-name", + "type": "string" + }, + "serviceEnabled": { + "title": "label.enable-this-service-opon-saving", + "description": "tooltip.enable-this-service-upon-saving", + "type": "boolean" + }, + "organization": { + "type": "object", + "properties": { + "name": { + "title": "label.organization-name", + "description": "tooltip.organization-name", + "type": "string" + }, + "displayName": { + "title": "label.organization-display-name", + "description": "tooltip.organization-display-name", + "type": "string" + }, + "url": { + "title": "label.organization-display-name", + "description": "tooltip.organization-display-name", + "type": "string" + } + }, + "dependencies": { + "name": [ + "displayName", + "url" + ], + "displayName": [ + "name", + "url" + ], + "url": [ + "name", + "displayName" + ] + } + }, + "contacts": { + "title": "label.contact-information", + "description": "tooltip.contact-information", + "type": "array", + "items": { + "$ref": "#/definitions/Contact" + } + }, + "mdui": { + "type": "object", + "properties": { + "displayName": { + "title": "label.display-name", + "description": "tooltip.mdui-display-name", + "type": "string" + }, + "informationUrl": { + "title": "label.information-url", + "description": "tooltip.mdui-information-url", + "type": "string" + }, + "privacyStatementUrl": { + "title": "label.privacy-statement-url", + "description": "tooltip.mdui-privacy-statement-url", + "type": "string" + }, + "description": { + "title": "label.description", + "description": "tooltip.mdui-description", + "type": "string" + }, + "logoUrl": { + "title": "label.logo-url", + "description": "tooltip.mdui-logo-url", + "type": "string" + }, + "logoHeight": { + "title": "label.logo-height", + "description": "tooltip.mdui-logo-height", + "min": 0, + "type": "integer" + }, + "logoWidth": { + "title": "label.logo-width", + "description": "tooltip.mdui-logo-width", + "min": 0, + "type": "integer" + } + } + }, + "securityInfo": { + "type": "object", + "properties": { + "x509CertificateAvailable": { + "title": "label.is-there-a-x509-certificate", + "description": "tooltip.is-there-a-x509-certificate", + "type": "boolean", + "default": false + }, + "authenticationRequestsSigned": { + "title": "label.authentication-requests-signed", + "description": "tooltip.authentication-requests-signed", + "type": "boolean", + "default": false + }, + "wantAssertionsSigned": { + "title": "label.want-assertions-signed", + "description": "tooltip.want-assertions-signed", + "type": "boolean", + "default": false + }, + "x509Certificates": { + "title": "label.x509-certificates", + "type": "array", + "items": { + "$ref": "#/definitions/Certificate" + } + } + } + }, + "assertionConsumerServices": { + "title": "label.assertion-consumer-service-endpoints", + "description": "", + "type": "array", + "items": { + "$ref": "#/definitions/AssertionConsumerService" + } + }, + "serviceProviderSsoDescriptor": { + "type": "object", + "properties": { + "protocolSupportEnum": { + "title": "label.protocol-support-enumeration", + "description": "tooltip.protocol-support-enumeration", + "type": "string", + "placeholder": "label.select-protocol", + "oneOf": [ + { + "enum": [ + "SAML 2" + ], + "description": "SAML 2" + }, + { + "enum": [ + "SAML 1.1" + ], + "description": "SAML 1.1" + } + ] + } + }, + "nameIdFormats": { + "$ref": "#/definitions/NameIdFormatList" + } + }, + "logoutEndpoints": { + "title": "label.logout-endpoints", + "description": "tooltip.logout-endpoints", + "type": "array", + "items": { + "$ref": "#/definitions/LogoutEndpoint" + } + }, + "relyingPartyOverrides": { + "type": "object", + "properties": { + "signAssertion": { + "title": "label.sign-the-assertion", + "description": "tooltip.sign-assertion", + "type": "boolean", + "default": false + }, + "dontSignResponse": { + "title": "label.dont-sign-the-response", + "description": "tooltip.dont-sign-response", + "type": "boolean", + "default": false + }, + "turnOffEncryption": { + "title": "label.turn-off-encryption-of-response", + "description": "tooltip.turn-off-encryption", + "type": "boolean", + "default": false + }, + "useSha": { + "title": "label.use-sha1-signing-algorithm", + "description": "tooltip.usa-sha-algorithm", + "type": "boolean", + "default": false + }, + "ignoreAuthenticationMethod": { + "title": "label.ignore-any-sp-requested-authentication-method", + "description": "tooltip.ignore-auth-method", + "type": "boolean", + "default": false + }, + "forceAuthn": { + "title": "label.force-authn", + "description": "tooltip.force-authn", + "type": "boolean", + "default": false + }, + "omitNotBefore": { + "title": "label.omit-not-before-condition", + "type": "boolean", + "description": "tooltip.omit-not-before-condition", + "default": false + }, + "responderId": { + "title": "label.responder-id", + "description": "tooltip.responder-id", + "type": "string" + }, + "nameIdFormats": { + "$ref": "#/definitions/NameIdFormatList" + }, + "authenticationMethods": { + "$ref": "#/definitions/AuthenticationMethodList" + } + } + }, + "attributeRelease": { + "type": "array", + "description": "Attribute release table - select the attributes you want to release (default unchecked)", + "widget": { + "id": "checklist", + "dataUrl": "/customAttributes" + }, + "items": { + "type": "string" + } + } + }, + "definitions": { + "Contact": { + "type": "object", + "properties": { + "name": { + "title": "label.contact-name", + "description": "tooltip.contact-name", + "type": "string" + }, + "type": { + "title": "label.contact-type", + "description": "tooltip.contact-type", + "type": "string", + "oneOf": [ + { + "enum": [ + "support" + ], + "description": "value.support" + }, + { + "enum": [ + "technical" + ], + "description": "value.technical" + }, + { + "enum": [ + "administrative" + ], + "description": "value.administrative" + }, + { + "enum": [ + "other" + ], + "description": "value.other" + } + ] + }, + "emailAddress": { + "title": "label.contact-email-address", + "description": "tooltip.contact-email", + "type": "string", + "pattern": "^(?=.{1,254}$)(?=.{1,64}@)[-!#$%&'*+/0-9=?A-Z^_`a-z{|}~]+(\\.[-!#$%&'*+/0-9=?A-Z^_`a-z{|}~]+)*@[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?(\\.[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*$" + } + } + }, + "Certificate": { + "name": { + "title": "label.certificate-name-display-only", + "description": "tooltip.certificate-name", + "type": "string" + }, + "type": { + "title": "label.type", + "description": "tooltip.certificate-type", + "type": "string", + "oneOf": [ + { + "enum": [ + "signing" + ], + "description": "value.signing" + }, + { + "enum": [ + "encryption" + ], + "description": "value.encryption" + }, + { + "enum": [ + "both" + ], + "description": "value.both" + } + ], + "default": "both" + }, + "value": { + "title": "label.certificate", + "description": "tooltip.certificate", + "type": "string" + } + }, + "AssertionConsumerService": { + "type": "object", + "properties": { + "locationUrl": { + "title": "label.assertion-consumer-services-location", + "description": "tooltip.assertion-consumer-service-location", + "type": "string", + "widget": { + "id": "string", + "help": "message.valid-url" + } + }, + "binding": { + "title": "label.assertion-consumer-service-location-binding", + "description": "tooltip.assertion-consumer-service-location-binding", + "type": "string", + "oneOf": [ + { + "enum": [ + "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" + ], + "description": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" + }, + { + "enum": [ + "urn:oasis:names:tc:SAML:1.0:profiles:browser-post" + ], + "description": "urn:oasis:names:tc:SAML:1.0:profiles:browser-post" + } + ] + }, + "makeDefault": { + "title": "label.mark-as-default", + "description": "tooltip.mark-as-default", + "type": "boolean" + } + } + }, + "NameIdFormatList": { + "title": "label.nameid-format-to-send", + "placeholder": "label.nameid-format", + "description": "tooltip.nameid-format", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "widget": "datalist", + "data": [ + "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", + "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", + "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent", + "urn:oasis:names:tc:SAML:2.0:nameid-format:transient" + ] + }, + "default": null + }, + "AuthenticationMethodList": { + "title": "label.authentication-methods-to-use", + "description": "tooltip.authentication-methods-to-use", + "type": "array", + "placeholder": "label.authentication-method", + "uniqueItems": true, + "items": { + "type": "string", + "title": "label.authentication-method", + "widget": { + "id": "datalist", + "data": [ + "https://refeds.org/profile/mfa", + "urn:oasis:names:tc:SAML:2.0:ac:classes:TimeSyncToken", + "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport" + ] + } + }, + "default": null + }, + "LogoutEndpoint": { + "title": "label.new-endpoint", + "description": "tooltip.new-endpoint", + "type": "object", + "properties": { + "url": { + "title": "label.url", + "description": "tooltip.url", + "type": "string" + }, + "bindingType": { + "title": "label.binding-type", + "description": "tooltip.binding-type", + "type": "string", + "oneOf": [ + { + "enum": [ + "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" + ], + "description": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" + }, + { + "enum": [ + "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" + ], + "description": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" + } + ] + + } + } + } + } +} \ No newline at end of file diff --git a/backend/src/test/resources/metadata-sources-ui-schema_BAD.json b/backend/src/test/resources/metadata-sources-ui-schema_MALFORMED.json similarity index 99% rename from backend/src/test/resources/metadata-sources-ui-schema_BAD.json rename to backend/src/test/resources/metadata-sources-ui-schema_MALFORMED.json index ee055fdf4..6d4d76a24 100644 --- a/backend/src/test/resources/metadata-sources-ui-schema_BAD.json +++ b/backend/src/test/resources/metadata-sources-ui-schema_MALFORMED.json @@ -1,5 +1,5 @@ -{ - "type": "object" + + "type": "object", "properties": { "entityId": { "title": "label.entity-id", @@ -12,7 +12,7 @@ "type": "string" }, "serviceEnabled": { - "title": "label.enable-this-service-upon-saving", + "title": "label.enable-this-service-opon-saving", "description": "tooltip.enable-this-service-upon-saving", "type": "boolean" }, From 47582984d8b4b111f6ffbc605b100658b36d61df Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Mon, 8 Oct 2018 10:37:15 -0400 Subject: [PATCH 6/7] WIP2 --- ...tadataSourcesUiDefinitionController.groovy | 23 ++++++++++++++----- ...efinitionControllerIntegrationTests.groovy | 9 ++++---- .../metadata-sources-ui-schema_MALFORMED.json | 4 ++-- 3 files changed, 24 insertions(+), 12 deletions(-) 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 6156b97cd..0471e40dd 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 @@ -1,6 +1,6 @@ package edu.internet2.tier.shibboleth.admin.ui.controller -import groovy.json.JsonSlurper +import com.fasterxml.jackson.databind.ObjectMapper import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.context.properties.ConfigurationProperties import org.springframework.core.io.ResourceLoader @@ -10,6 +10,8 @@ import org.springframework.web.bind.annotation.RestController import javax.annotation.PostConstruct +import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR + /** * Controller implementing REST resource responsible for exposing structure definition for metadata sources user * interface in terms of JSON schema. @@ -21,19 +23,28 @@ import javax.annotation.PostConstruct class MetadataSourcesUiDefinitionController { //Configured via @ConfigurationProperties with 'shibui.metadata-sources-ui-schema-location' property and default - //value set here if that property is not set - String metadataSourcesUiSchemaLocation //= 'classpath:metadata-sources-ui-schema.json' + //value set here if that property is not explicitly set in application.properties + String metadataSourcesUiSchemaLocation = 'classpath:metadata-sources-ui-schema.json' URL jsonSchemaUrl @Autowired ResourceLoader resourceLoader + @Autowired + ObjectMapper jacksonObjectMapper + @GetMapping ResponseEntity getUiDefinitionJsonSchema() { - //JsonSlurper is not threadsafe, but cheap to init. New instance per-thread is the canonical usage - def json = new JsonSlurper().parse(this.jsonSchemaUrl) - ResponseEntity.ok(json) + try { + def parsedJson = jacksonObjectMapper.readValue(this.jsonSchemaUrl, Map) + return ResponseEntity.ok(parsedJson) + } + catch (Exception e) { + return ResponseEntity.status(INTERNAL_SERVER_ERROR) + .body([jsonParseError : e.getMessage(), + sourceUiSchemaDefinitionFile: this.jsonSchemaUrl]) + } } @PostConstruct diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataSourcesUiDefinitionControllerIntegrationTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataSourcesUiDefinitionControllerIntegrationTests.groovy index abc416b78..9d804ad57 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataSourcesUiDefinitionControllerIntegrationTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataSourcesUiDefinitionControllerIntegrationTests.groovy @@ -3,7 +3,6 @@ package edu.internet2.tier.shibboleth.admin.ui.controller import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest import org.springframework.boot.test.web.client.TestRestTemplate -import org.springframework.core.env.ConfigurableEnvironment import org.springframework.test.context.ActiveProfiles import spock.lang.Specification @@ -37,12 +36,14 @@ class MetadataSourcesUiDefinitionControllerIntegrationTests extends Specificatio configureMalformedJsonInput() def result = this.restTemplate.getForEntity(RESOURCE_URI, Object) - then: "Request results in HTTP 400" - result.statusCodeValue == 200 + then: "Request results in HTTP 500" + result.statusCodeValue == 500 + result.body.jsonParseError + result.body.sourceUiSchemaDefinitionFile } private configureMalformedJsonInput() { - controllerUnderTest.metadataSourcesUiSchemaLocation = 'classpath:metadata-sources-ui-schema_BAD.json' + controllerUnderTest.metadataSourcesUiSchemaLocation = 'classpath:metadata-sources-ui-schema_MALFORMED.json' controllerUnderTest.init() } } \ No newline at end of file diff --git a/backend/src/test/resources/metadata-sources-ui-schema_MALFORMED.json b/backend/src/test/resources/metadata-sources-ui-schema_MALFORMED.json index 6d4d76a24..7ca5220f0 100644 --- a/backend/src/test/resources/metadata-sources-ui-schema_MALFORMED.json +++ b/backend/src/test/resources/metadata-sources-ui-schema_MALFORMED.json @@ -1,5 +1,5 @@ - - "type": "object", +{ + "type": "object" "properties": { "entityId": { "title": "label.entity-id", From 145f18d6a69f99e47ad1af7ea5b73587d9144836 Mon Sep 17 00:00:00 2001 From: Bill Smith Date: Mon, 8 Oct 2018 16:51:28 -0700 Subject: [PATCH 7/7] [SHIBUI-905][SHIBUI-916] Added custom attributes to json schema generation. --- .../MetadataSourcesUiDefinitionController.groovy | 14 ++++++++++++++ 1 file changed, 14 insertions(+) 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 0471e40dd..9d790f362 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 @@ -1,6 +1,8 @@ package edu.internet2.tier.shibboleth.admin.ui.controller import com.fasterxml.jackson.databind.ObjectMapper +import edu.internet2.tier.shibboleth.admin.ui.configuration.CustomAttributesConfiguration +import groovy.json.JsonOutput import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.context.properties.ConfigurationProperties import org.springframework.core.io.ResourceLoader @@ -34,10 +36,22 @@ class MetadataSourcesUiDefinitionController { @Autowired ObjectMapper jacksonObjectMapper + @Autowired + CustomAttributesConfiguration customAttributesConfiguration + @GetMapping ResponseEntity getUiDefinitionJsonSchema() { try { def parsedJson = jacksonObjectMapper.readValue(this.jsonSchemaUrl, Map) + def widget = parsedJson["properties"]["attributeRelease"]["widget"] + def data = [] + customAttributesConfiguration.getAttributes().each { + def attribute = [:] + attribute["key"] = it["name"] + attribute["label"] = it["displayName"] + data << attribute + } + widget["data"] = data return ResponseEntity.ok(parsedJson) } catch (Exception e) {