-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
1414: MetadataResolvers JSON schema validation WIP
- Loading branch information
Showing
4 changed files
with
204 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
58 changes: 58 additions & 0 deletions
58
...oovy/edu/internet2/tier/shibboleth/admin/ui/jsonschema/LowLevelJsonSchemaValidator.groovy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| package edu.internet2.tier.shibboleth.admin.ui.jsonschema | ||
|
|
||
| import mjson.Json | ||
| import org.springframework.http.HttpInputMessage | ||
|
|
||
| import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaLocationLookup.dynamicHttpMetadataProviderSchema | ||
| import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaLocationLookup.filesystemMetadataProviderSchema | ||
| import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaLocationLookup.localDynamicMetadataProviderSchema | ||
|
|
||
| /** | ||
| * Currently uses mjson library. | ||
| */ | ||
| class LowLevelJsonSchemaValidator { | ||
|
|
||
| static HttpInputMessage validatePayloadAgainstSchema(HttpInputMessage inputMessage, URI schemaUri) { | ||
| def json = extractJsonPayload(inputMessage) | ||
| def schema = Json.schema(schemaUri) | ||
| doValidate(schema, json) | ||
| } | ||
|
|
||
| static HttpInputMessage validateMetadataResolverTypePayloadAgainstSchema(HttpInputMessage inputMessage, | ||
| JsonSchemaResourceLocationRegistry schemaRegistry) { | ||
| def json = extractJsonPayload(inputMessage) | ||
| def schemaUri = null | ||
| switch (json.asMap()['@type']) { | ||
| case 'LocalDynamicMetadataResolver': | ||
| schemaUri = localDynamicMetadataProviderSchema(schemaRegistry).uri | ||
| break | ||
| case 'DynamicHttpMetadataResolver': | ||
| schemaUri = dynamicHttpMetadataProviderSchema(schemaRegistry).uri | ||
| break | ||
| case 'FilesystemMetadataResolver': | ||
| schemaUri = filesystemMetadataProviderSchema(schemaRegistry).uri | ||
| break | ||
| default: | ||
| break | ||
| } | ||
| if(!schemaUri) { | ||
| return inputMessage | ||
| } | ||
| doValidate(Json.schema(schemaUri), json) | ||
| } | ||
|
|
||
| private static Json extractJsonPayload(HttpInputMessage inputMessage) { | ||
| Json.read(new ByteArrayInputStream(inputMessage.body.bytes).getText()) | ||
| } | ||
|
|
||
| private static HttpInputMessage doValidate(Json.Schema schema, Json json) { | ||
| def validationResult = schema.validate(json) | ||
| if (!validationResult.at('ok')) { | ||
| throw new JsonSchemaValidationFailedException(validationResult.at('errors').asList()) | ||
| } | ||
| return [ | ||
| getBody : { new ByteArrayInputStream(bytes) }, | ||
| getHeaders: { inputMessage.headers } | ||
| ] as HttpInputMessage | ||
| } | ||
| } |
41 changes: 41 additions & 0 deletions
41
...er/shibboleth/admin/ui/jsonschema/MetadataResolverSchemaValidatingControllerAdvice.groovy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| package edu.internet2.tier.shibboleth.admin.ui.jsonschema | ||
|
|
||
|
|
||
| import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver | ||
| import org.springframework.beans.factory.annotation.Autowired | ||
| import org.springframework.core.MethodParameter | ||
| import org.springframework.http.HttpInputMessage | ||
| import org.springframework.http.converter.HttpMessageConverter | ||
| import org.springframework.web.bind.annotation.ControllerAdvice | ||
| import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdviceAdapter | ||
|
|
||
| import java.lang.reflect.Type | ||
|
|
||
| import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.LowLevelJsonSchemaValidator.validateMetadataResolverTypePayloadAgainstSchema | ||
|
|
||
| /** | ||
| * Controller advice implementation for validating metadata resolvers payload coming from UI layer | ||
| * against pre-defined JSON schema for their respected types. Choosing of the appropriate schema based on incoming | ||
| * resolver types is delegated to @{LowLevelJsonSchemaValidator}. | ||
| * | ||
| * @author Dmitriy Kopylenko | ||
| */ | ||
| @ControllerAdvice | ||
| class MetadataResolverSchemaValidatingControllerAdvice extends RequestBodyAdviceAdapter { | ||
|
|
||
| @Autowired | ||
| JsonSchemaResourceLocationRegistry jsonSchemaResourceLocationRegistry | ||
|
|
||
| @Override | ||
| boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) { | ||
| targetType.typeName == MetadataResolver.typeName | ||
| } | ||
|
|
||
| @Override | ||
| HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, | ||
| Type targetType, Class<? extends HttpMessageConverter<?>> converterType) | ||
| throws IOException { | ||
|
|
||
| validateMetadataResolverTypePayloadAgainstSchema(inputMessage, jsonSchemaResourceLocationRegistry) | ||
| } | ||
| } |
100 changes: 100 additions & 0 deletions
100
...eth/admin/ui/controller/MetadataResolverControllerSchemaValidationIntegrationTests.groovy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,100 @@ | ||
| 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.http.HttpEntity | ||
| import org.springframework.http.HttpHeaders | ||
| import org.springframework.test.context.ActiveProfiles | ||
| import spock.lang.Specification | ||
|
|
||
| @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) | ||
| @ActiveProfiles(["no-auth", "dev"]) | ||
| class MetadataResolverControllerSchemaValidationIntegrationTests extends Specification { | ||
|
|
||
| @Autowired | ||
| private TestRestTemplate restTemplate | ||
|
|
||
| static RESOURCE_URI = '/api/MetadataResolvers' | ||
|
|
||
| def 'POST for LocalDynamicMetadataResolver with invalid payload according to schema validation'() { | ||
| given: | ||
| def postedJsonBody = """ | ||
| { | ||
| "createdDate" : null, | ||
| "modifiedDate" : null, | ||
| "createdBy" : null, | ||
| "modifiedBy" : null, | ||
| "name" : null, | ||
| "requireValidMetadata" : true, | ||
| "failFastInitialization" : true, | ||
| "sortKey" : null, | ||
| "criterionPredicateRegistryRef" : null, | ||
| "useDefaultPredicateRegistry" : true, | ||
| "satisfyAnyPredicates" : false, | ||
| "metadataFilters" : [ { | ||
| "createdDate" : null, | ||
| "modifiedDate" : null, | ||
| "createdBy" : null, | ||
| "modifiedBy" : null, | ||
| "name" : "EntityAttributes", | ||
| "filterEnabled" : false, | ||
| "version" : 463855403, | ||
| "entityAttributesFilterTarget" : { | ||
| "createdDate" : null, | ||
| "modifiedDate" : null, | ||
| "createdBy" : null, | ||
| "modifiedBy" : null, | ||
| "entityAttributesFilterTargetType" : "ENTITY", | ||
| "value" : [ "CedewbJJET" ], | ||
| "audId" : null | ||
| }, | ||
| "attributeRelease" : [ "9ktPyjjiCn" ], | ||
| "relyingPartyOverrides" : { | ||
| "signAssertion" : false, | ||
| "dontSignResponse" : true, | ||
| "turnOffEncryption" : true, | ||
| "useSha" : false, | ||
| "ignoreAuthenticationMethod" : false, | ||
| "omitNotBefore" : true, | ||
| "responderId" : null, | ||
| "nameIdFormats" : [ ], | ||
| "authenticationMethods" : [ ] | ||
| }, | ||
| "audId" : null, | ||
| "@type" : "EntityAttributes" | ||
| }, { | ||
| "createdDate" : null, | ||
| "modifiedDate" : null, | ||
| "createdBy" : null, | ||
| "modifiedBy" : null, | ||
| "name" : "EntityRoleWhiteList", | ||
| "filterEnabled" : false, | ||
| "version" : 0, | ||
| "removeRolelessEntityDescriptors" : true, | ||
| "removeEmptyEntitiesDescriptors" : true, | ||
| "retainedRoles" : [ "role1", "role2" ], | ||
| "audId" : null, | ||
| "@type" : "EntityRoleWhiteList" | ||
| } ], | ||
| "version" : 0, | ||
| "sourceDirectory" : "dir", | ||
| "sourceManagerRef" : null, | ||
| "sourceKeyGeneratorRef" : null, | ||
| "audId" : null, | ||
| "@type" : "LocalDynamicMetadataResolver" | ||
| } | ||
| """ | ||
|
|
||
| when: | ||
| def result = this.restTemplate.postForEntity(RESOURCE_URI, createRequestHttpEntityFor { postedJsonBody }, Map) | ||
|
|
||
| then: | ||
| result.statusCodeValue == 400 | ||
| result.body.errorMessage.count('Type mistmatch for null') > 0 | ||
| } | ||
|
|
||
| private static HttpEntity<String> createRequestHttpEntityFor(Closure jsonBodySupplier) { | ||
| new HttpEntity<String>(jsonBodySupplier(), ['Content-Type': 'application/json'] as HttpHeaders) | ||
| } | ||
| } |