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 f094ff2f9..34029e28a 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 @@ -1,9 +1,14 @@ package edu.internet2.tier.shibboleth.admin.ui.service import com.google.common.base.Predicate -import edu.internet2.tier.shibboleth.admin.ui.domain.filters.* +import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilter +import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilterTarget +import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityRoleWhiteListFilter +import edu.internet2.tier.shibboleth.admin.ui.domain.filters.RequiredValidUntilFilter +import edu.internet2.tier.shibboleth.admin.ui.domain.filters.SignatureValidationFilter import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.DynamicHttpMetadataResolver import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.FileBackedHttpMetadataResolver +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.FilesystemMetadataResolver import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.LocalDynamicMetadataResolver import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.ResourceBackedMetadataResolver import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects @@ -157,6 +162,30 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { ) } + void constructXmlNodeForResolver(FilesystemMetadataResolver resolver, def markupBuilderDelegate, Closure childNodes) { + markupBuilderDelegate.MetadataProvider(id: resolver.name, + 'xsi:type': 'FilesystemMetadataProvider', + metadataFile: resolver.metadataFile, + + requireValidMetadata: !resolver.requireValidMetadata ?: null, + failFastInitialization: !resolver.failFastInitialization ?: null, + sortKey: resolver.sortKey, + criterionPredicateRegistryRef: resolver.criterionPredicateRegistryRef, + useDefaultPredicateRegistry: !resolver.useDefaultPredicateRegistry ?: null, + satisfyAnyPredicates: resolver.satisfyAnyPredicates ?: null, + + parserPoolRef: resolver.reloadableMetadataResolverAttributes?.parserPoolRef, + minRefreshDelay: resolver.reloadableMetadataResolverAttributes?.minRefreshDelay, + maxRefreshDelay: resolver.reloadableMetadataResolverAttributes?.maxRefreshDelay, + refreshDelayFactor: resolver.reloadableMetadataResolverAttributes?.refreshDelayFactor, + indexesRef: resolver.reloadableMetadataResolverAttributes?.indexesRef, + resolveViaPredicatesOnly: resolver.reloadableMetadataResolverAttributes?.resolveViaPredicatesOnly ?: null, + expirationWarningThreshold: resolver.reloadableMetadataResolverAttributes?.expirationWarningThreshold) { + + childNodes() + } + } + void constructXmlNodeForResolver(DynamicHttpMetadataResolver resolver, def markupBuilderDelegate, Closure childNodes) { markupBuilderDelegate.MetadataProvider(id: resolver.name, 'xsi:type': 'DynamicHttpMetadataProvider', diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/FilesystemMetadataResolver.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/FilesystemMetadataResolver.java new file mode 100644 index 000000000..0e16c8353 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/FilesystemMetadataResolver.java @@ -0,0 +1,29 @@ +package edu.internet2.tier.shibboleth.admin.ui.domain.resolvers; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +import javax.persistence.Embedded; +import javax.persistence.Entity; + +/** + * @author Bill Smith (wsmith@unicon.net) + */ +@Entity +@EqualsAndHashCode(callSuper = true) +@Getter +@Setter +@ToString +public class FilesystemMetadataResolver extends MetadataResolver { + + public FilesystemMetadataResolver() { + type = "FilesystemMetadataResolver"; + } + + private String metadataFile; + + @Embedded + private ReloadableMetadataResolverAttributes reloadableMetadataResolverAttributes; +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataResolver.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataResolver.java index adbb30f0d..e5b1221c1 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataResolver.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataResolver.java @@ -33,7 +33,9 @@ @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "@type", visible = true) @JsonSubTypes({@JsonSubTypes.Type(value = LocalDynamicMetadataResolver.class, name = "LocalDynamicMetadataResolver"), @JsonSubTypes.Type(value = FileBackedHttpMetadataResolver.class, name = "FileBackedHttpMetadataResolver"), - @JsonSubTypes.Type(value = DynamicHttpMetadataResolver.class, name = "DynamicHttpMetadataResolver")}) + @JsonSubTypes.Type(value = DynamicHttpMetadataResolver.class, name = "DynamicHttpMetadataResolver"), + @JsonSubTypes.Type(value = FilesystemMetadataResolver.class, name = "FilesystemMetadataResolver"), + @JsonSubTypes.Type(value = ResourceBackedMetadataResolver.class, name = "ResourceBackedMetadataResolver")}) public class MetadataResolver extends AbstractAuditable { @JsonProperty("@type") diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/ResourceBackedMetadataResolver.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/ResourceBackedMetadataResolver.java index 2133d88eb..5737d0162 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/ResourceBackedMetadataResolver.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/ResourceBackedMetadataResolver.java @@ -18,6 +18,10 @@ @ToString public class ResourceBackedMetadataResolver extends MetadataResolver { + public ResourceBackedMetadataResolver() { + type = "ResourceBackedMetadataResolver"; + } + @Embedded private ReloadableMetadataResolverAttributes reloadableMetadataResolverAttributes; diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversControllerIntegrationTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversControllerIntegrationTests.groovy index 03ae982a5..cb6592990 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversControllerIntegrationTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversControllerIntegrationTests.groovy @@ -1,8 +1,12 @@ package edu.internet2.tier.shibboleth.admin.ui.controller +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.databind.SerializationFeature import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.DynamicHttpMetadataResolver import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.FileBackedHttpMetadataResolver import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository +import edu.internet2.tier.shibboleth.admin.ui.util.TestObjectGenerator +import edu.internet2.tier.shibboleth.admin.util.AttributeUtility import groovy.json.JsonOutput import groovy.json.JsonSlurper import org.opensaml.saml.metadata.resolver.MetadataResolver @@ -18,6 +22,7 @@ import org.springframework.http.HttpHeaders import org.springframework.test.context.ActiveProfiles import spock.lang.Specification +import spock.lang.Unroll import static org.springframework.http.HttpMethod.PUT @@ -34,10 +39,22 @@ class MetadataResolversControllerIntegrationTests extends Specification { @Autowired MetadataResolverRepository metadataResolverRepository + @Autowired + AttributeUtility attributeUtility + + ObjectMapper mapper + TestObjectGenerator generator + JsonSlurper jsonSlurper = new JsonSlurper() static BASE_URI = '/api/MetadataResolvers' + def setup() { + generator = new TestObjectGenerator(attributeUtility) + mapper = new ObjectMapper() + mapper.enable(SerializationFeature.INDENT_OUTPUT) + } + def cleanup() { metadataResolverRepository.deleteAll() } @@ -130,24 +147,31 @@ class MetadataResolversControllerIntegrationTests extends Specification { result.statusCodeValue == 404 } - def "POST new DynamicHttpMetadataResolver -> /api/MetadataResolvers"() { + @Unroll + def "POST new DynamicHttpMetadataResolver of type #resolverType -> /api/MetadataResolvers"(String resolverType) { given: 'New MetadataResolver JSON representation' - def resolver = [name: 'Test DynamicHttpMetadataResolver', '@type': 'DynamicHttpMetadataResolver'] + def resolver = generator.buildRandomMetadataResolverOfType(resolverType) when: 'POST request is made with new DynamicHttpMetadataResolver JSON representation' - def result = this.restTemplate.postForEntity(BASE_URI, createRequestHttpEntityFor { JsonOutput.toJson(resolver) }, String) + def result = this.restTemplate.postForEntity(BASE_URI, createRequestHttpEntityFor { mapper.writeValueAsString(resolver) }, String) then: result.statusCodeValue == 201 result.headers.Location[0].contains(BASE_URI) + + where: + resolverType | _ + 'DynamicHttp' | _ + 'FileBacked' | _ + 'LocalDynamic' | _ + 'ResourceBacked' | _ + 'Filesystem' | _ } - def "PUT concrete MetadataResolver with updated changes -> /api/MetadataResolvers/{resourceId}"() { + @Unroll + def "PUT concrete MetadataResolver of type #resolverType with updated changes -> /api/MetadataResolvers/{resourceId}"(String resolverType) { given: 'One resolver is available in data store' - def resolver = new DynamicHttpMetadataResolver().with { - it.name = 'Test DynamicHttpMetadataResolver' - it - } + def resolver = generator.buildRandomMetadataResolverOfType(resolverType) def resolverResourceId = resolver.resourceId metadataResolverRepository.save(resolver) @@ -171,6 +195,13 @@ class MetadataResolversControllerIntegrationTests extends Specification { then: updatedResolverMap.name == 'Updated DynamicHttpMetadataResolver' + where: + resolverType | _ + 'DynamicHttp' | _ + 'FileBacked' | _ + 'LocalDynamic' | _ + 'ResourceBacked' | _ + 'Filesystem' | _ } def "PUT concrete MetadataResolver with version conflict -> /api/MetadataResolvers/{resourceId}"() { diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImplTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImplTests.groovy index 03b737da9..9c367c1d6 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImplTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImplTests.groovy @@ -215,6 +215,19 @@ class JPAMetadataResolverServiceImplTests extends Specification { generatedXmlIsTheSameAsExpectedXml('/conf/546-classpath.xml', domBuilder.parseText(writer.toString())) } + def 'test generating FilesystemMetadataResolver xml snippet'() { + given: + def resolver = testObjectGenerator.filesystemMetadataResolver() + + when: + genXmlSnippet(markupBuilder) { + JPAMetadataResolverServiceImpl.cast(metadataResolverService).constructXmlNodeForResolver(resolver, it) {} + } + + then: + generatedXmlIsTheSameAsExpectedXml('/conf/520.xml', domBuilder.parseText(writer.toString())) + } + static genXmlSnippet(MarkupBuilder xml, Closure xmlNodeGenerator) { xml.MetadataProvider('id': 'ShibbolethMetadata', 'xmlns': 'urn:mace:shibboleth:2.0:metadata', diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/TestObjectGenerator.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/TestObjectGenerator.groovy index 77e96e82d..4652fe361 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/TestObjectGenerator.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/TestObjectGenerator.groovy @@ -376,6 +376,45 @@ class TestObjectGenerator { return contactPerson } + MetadataResolver buildRandomMetadataResolverOfType(String metadataResolverType) { + def randomResolver + switch (metadataResolverType) { + case 'DynamicHttp': + randomResolver = dynamicHttpMetadataResolver() + break + case 'FileBacked': + randomResolver = fileBackedHttpMetadataResolver() + break + case 'LocalDynamic': + randomResolver = localDynamicMetadataResolver() + break + case 'ResourceBacked': + randomResolver = resourceBackedMetadataResolverForSVN() + break + case 'Filesystem': + randomResolver = filesystemMetadataResolver() + break; + default: + throw new RuntimeException("Did you forget to create a TestObjectGenerator.MetadataResolver method for resolverType: ${metadataResolverType} ?"); + } + randomResolver + } + + FilesystemMetadataResolver filesystemMetadataResolver() { + new FilesystemMetadataResolver().with { + it.name = 'FilesystemMetadata' + it.metadataFile = 'some metadata filename' + + it.reloadableMetadataResolverAttributes = new ReloadableMetadataResolverAttributes().with { + it.minRefreshDelay = 'PT5M' + it.maxRefreshDelay = 'PT1H' + it.refreshDelayFactor = 0.75 + it + } + it + } + } + FileBackedHttpMetadataResolver fileBackedHttpMetadataResolver() { new FileBackedHttpMetadataResolver().with { it.name = 'HTTPMetadata' diff --git a/backend/src/test/resources/conf/520.xml b/backend/src/test/resources/conf/520.xml new file mode 100644 index 000000000..6e813f232 --- /dev/null +++ b/backend/src/test/resources/conf/520.xml @@ -0,0 +1,14 @@ + + + + +