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 34e73233c..09f54ab75 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,19 +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.EntityAttributesFilter; -import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilterTarget import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.DynamicHttpMetadataResolver import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.LocalDynamicMetadataResolver -import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects; - -import com.google.common.base.Predicate import com.google.common.base.Predicate 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.resolvers.FileBackedHttpMetadataResolver @@ -111,10 +106,6 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { 'requireSignedRoot': 'true', 'certificateFile': '%{idp.home}/credentials/inc-md-cert.pem' ) - MetadataFilter( - 'xsi:type': 'RequiredValidUntil', - 'maxValidityInterval': 'P14D' - ) //TODO: enhance mr.metadataFilters.each { edu.internet2.tier.shibboleth.admin.ui.domain.filters.MetadataFilter filter -> constructXmlNodeForFilter(filter, delegate) @@ -199,6 +190,13 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { } } + void constructXmlNodeForFilter(RequiredValidUntilFilter filter, def markupBuilderDelegate) { + markupBuilderDelegate.MetadataFilter( + 'xsi:type': 'RequiredValidUntil', + maxValidityInterval: filter.maxValidityInterval + ) + } + void constructXmlNodeForResolver(FileBackedHttpMetadataResolver resolver, def markupBuilderDelegate, Closure childNodes) { markupBuilderDelegate.MetadataProvider(id: resolver.name, 'xsi:type': 'FileBackedHTTPMetadataProvider', diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersController.java index 40de7ea46..9735b708f 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersController.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersController.java @@ -3,6 +3,7 @@ import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilter; import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityRoleWhiteListFilter; import edu.internet2.tier.shibboleth.admin.ui.domain.filters.MetadataFilter; +import edu.internet2.tier.shibboleth.admin.ui.domain.filters.RequiredValidUntilFilter; import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.FilterRepresentation; import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver; import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository; @@ -168,6 +169,11 @@ else if(filterWithUpdatedData instanceof EntityRoleWhiteListFilter) { toFilter.setRemoveRolelessEntityDescriptors(fromFilter.getRemoveRolelessEntityDescriptors()); toFilter.setRetainedRoles(fromFilter.getRetainedRoles()); } + else if(filterWithUpdatedData instanceof RequiredValidUntilFilter) { + RequiredValidUntilFilter toFilter = RequiredValidUntilFilter.class.cast(filterToBeUpdated); + RequiredValidUntilFilter fromFilter = RequiredValidUntilFilter.class.cast(filterWithUpdatedData); + toFilter.setMaxValidityInterval(fromFilter.getMaxValidityInterval()); + } //TODO: add other types of concrete filters update here } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/MetadataFilter.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/MetadataFilter.java index aa5b063a4..3702bba3d 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/MetadataFilter.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/MetadataFilter.java @@ -29,7 +29,8 @@ @ToString @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "@type", visible = true) @JsonSubTypes({@JsonSubTypes.Type(value=EntityRoleWhiteListFilter.class, name="EntityRoleWhiteList"), - @JsonSubTypes.Type(value=EntityAttributesFilter.class, name="EntityAttributes")}) + @JsonSubTypes.Type(value=EntityAttributesFilter.class, name="EntityAttributes"), + @JsonSubTypes.Type(value=RequiredValidUntilFilter.class, name="RequiredValidUntil")}) public class MetadataFilter extends AbstractAuditable { @JsonProperty("@type") diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/RequiredValidUntilFilter.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/RequiredValidUntilFilter.java new file mode 100644 index 000000000..86bc03426 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/RequiredValidUntilFilter.java @@ -0,0 +1,22 @@ +package edu.internet2.tier.shibboleth.admin.ui.domain.filters; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +import javax.persistence.Entity; + +@Entity +@EqualsAndHashCode(callSuper = true) +@Getter +@Setter +@ToString +public class RequiredValidUntilFilter extends MetadataFilter { + + public RequiredValidUntilFilter() { + type = "RequiredValidUntil"; + } + + private String maxValidityInterval; +} diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/domain/PolymorphicFiltersJacksonHandlingTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/domain/PolymorphicFiltersJacksonHandlingTests.groovy index c9cef2cb3..4e5899f49 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/domain/PolymorphicFiltersJacksonHandlingTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/domain/PolymorphicFiltersJacksonHandlingTests.groovy @@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.SerializationFeature import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilter import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityRoleWhiteListFilter import edu.internet2.tier.shibboleth.admin.ui.domain.filters.MetadataFilter +import edu.internet2.tier.shibboleth.admin.ui.domain.filters.RequiredValidUntilFilter import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects import edu.internet2.tier.shibboleth.admin.ui.util.TestObjectGenerator import edu.internet2.tier.shibboleth.admin.util.AttributeUtility @@ -74,28 +75,54 @@ class PolymorphicFiltersJacksonHandlingTests extends Specification { simulatedPrePersistentFilter.relyingPartyOverrides = simulatedPersistentFilter.relyingPartyOverrides simulatedPrePersistentFilter.fromTransientRepresentation() + expect: + simulatedPersistentFilter.attributes.size() == simulatedPrePersistentFilter.attributes.size() + } + + def "Correct polymorphic serialization of RequiredValidUntilFilter"() { + given: + def givenFilterJson = """ + { + "@type" : "RequiredValidUntil", + "createdDate" : null, + "modifiedDate" : null, + "createdBy" : null, + "modifiedBy" : null, + "name" : null, + "resourceId" : "9667ae04-8c36-4741-be62-dd325e7d6790", + "filterEnabled" : true, + "version" : 0, + "maxValidityInterval" : "P14D" + + } + """ + when: - def jsonFromPersistentFilter = mapper.writeValueAsString(simulatedPersistentFilter) - def jsonFromPrePersistentFilter = mapper.writeValueAsString(simulatedPrePersistentFilter) - println("JSON from persistent filter -> $jsonFromPersistentFilter") - println("JSON from PRE persistent filter -> $jsonFromPrePersistentFilter") - println("Attributes from persistent filter -> $simulatedPersistentFilter.attributes") - println("Attributes from PRE persistent filter -> $simulatedPrePersistentFilter.attributes") + def deSerializedFilter = mapper.readValue(givenFilterJson, MetadataFilter) + def json = mapper.writeValueAsString(deSerializedFilter) + println(json) + def roundTripFilter = mapper.readValue(json, MetadataFilter) then: - simulatedPersistentFilter.attributes.size() == simulatedPrePersistentFilter.attributes.size() + roundTripFilter == deSerializedFilter + + and: + deSerializedFilter instanceof RequiredValidUntilFilter + roundTripFilter instanceof RequiredValidUntilFilter } def "List of filters with correct types"() { given: - def filters = testObjectGenerator.buildAllTypesOfFilterList(); + def filters = testObjectGenerator.buildAllTypesOfFilterList() when: def json = mapper.writeValueAsString(filters) println(json) then: - json + json.contains('EntityAttributes') + json.contains('RequiredValidUntil') + json.contains('EntityAttributes') } diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/IncommonJPAMetadataResolverServiceImplTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/IncommonJPAMetadataResolverServiceImplTests.groovy index 6a90b2507..4e4600ae9 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/IncommonJPAMetadataResolverServiceImplTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/IncommonJPAMetadataResolverServiceImplTests.groovy @@ -4,6 +4,8 @@ import edu.internet2.tier.shibboleth.admin.ui.configuration.CoreShibUiConfigurat import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration 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.opensaml.OpenSamlObjects import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository import edu.internet2.tier.shibboleth.admin.ui.util.TestObjectGenerator @@ -19,8 +21,7 @@ import org.springframework.context.annotation.Bean import org.springframework.data.jpa.repository.config.EnableJpaRepositories import org.springframework.test.annotation.DirtiesContext import org.springframework.test.context.ContextConfiguration -import org.xmlunit.builder.DiffBuilder -import org.xmlunit.builder.Input + import spock.lang.Specification import static edu.internet2.tier.shibboleth.admin.ui.util.TestHelpers.* @@ -43,6 +44,10 @@ class IncommonJPAMetadataResolverServiceImplTests extends Specification { def 'simple test generation of metadata-providers.xml'() { when: + def mr = metadataResolverRepository.findAll().iterator().next() + mr.metadataFilters << requiredValidUntilFilterForXmlGenerationTests() + mr.metadataFilters << entityRoleWhiteListFilterForXmlGenerationTests() + metadataResolverRepository.save(mr) def output = metadataResolverService.generateConfiguration() println(output.documentElement) @@ -55,6 +60,7 @@ class IncommonJPAMetadataResolverServiceImplTests extends Specification { when: //TODO: this might break later def mr = metadataResolverRepository.findAll().iterator().next() + mr.metadataFilters << requiredValidUntilFilterForXmlGenerationTests() mr.metadataFilters.add(new EntityAttributesFilter().with { it.entityAttributesFilterTarget = new EntityAttributesFilterTarget().with { it.entityAttributesFilterTargetType = EntityAttributesFilterTarget.EntityAttributesFilterTargetType.ENTITY @@ -70,6 +76,7 @@ class IncommonJPAMetadataResolverServiceImplTests extends Specification { it.attributes = [attribute] it }) + mr.metadataFilters << entityRoleWhiteListFilterForXmlGenerationTests() metadataResolverRepository.save(mr) def output = metadataResolverService.generateConfiguration() @@ -78,6 +85,20 @@ class IncommonJPAMetadataResolverServiceImplTests extends Specification { generatedXmlIsTheSameAsExpectedXml('/conf/278.2.xml', output) } + EntityRoleWhiteListFilter entityRoleWhiteListFilterForXmlGenerationTests() { + new EntityRoleWhiteListFilter().with { + it.retainedRoles = ['md:SPSSODescriptor'] + it + } + } + + RequiredValidUntilFilter requiredValidUntilFilterForXmlGenerationTests() { + new RequiredValidUntilFilter().with { + it.maxValidityInterval = 'P14D' + it + } + } + //TODO: check that this configuration is sufficient @TestConfiguration static class TestConfig { 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 546315d6f..e548d26b5 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 @@ -4,6 +4,7 @@ import edu.internet2.tier.shibboleth.admin.ui.configuration.CoreShibUiConfigurat import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration 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.RequiredValidUntilFilter import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository @@ -139,6 +140,21 @@ class JPAMetadataResolverServiceImplTests extends Specification { then: generatedXmlIsTheSameAsExpectedXml('/conf/533.xml', domBuilder.parseText(writer.toString())) + + } + + def 'test generating RequiredValidUntilFilter xml snippet'() { + given: + def filter = new RequiredValidUntilFilter().with { + it.maxValidityInterval = 'P14D' + it + } + + when: + genXmlSnippet(markupBuilder) { JPAMetadataResolverServiceImpl.cast(metadataResolverService).constructXmlNodeForFilter(filter, it) } + + then: + generatedXmlIsTheSameAsExpectedXml('/conf/552.xml', domBuilder.parseText(writer.toString())) } def 'test generating FileBackedHttMetadataResolver xml snippet'() { diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/TestHelpers.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/TestHelpers.groovy index c2c6f7761..04066c581 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/TestHelpers.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/TestHelpers.groovy @@ -27,8 +27,11 @@ class TestHelpers { } static void generatedXmlIsTheSameAsExpectedXml(String expectedXmlResource, Document generatedXml) { - assert !DiffBuilder.compare(Input.fromStream(TestHelpers.getResourceAsStream(expectedXmlResource))).withTest(Input.fromDocument(generatedXml)) - .ignoreComments().ignoreWhitespace().build().hasDifferences() - + assert !DiffBuilder.compare(Input.fromStream(TestHelpers.getResourceAsStream(expectedXmlResource))) + .withTest(Input.fromDocument(generatedXml)) + .ignoreComments() + .ignoreWhitespace() + .build() + .hasDifferences() } } 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 4069ab849..b2dd89727 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 @@ -5,12 +5,14 @@ import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFil 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.MetadataFilter +import edu.internet2.tier.shibboleth.admin.ui.domain.filters.RequiredValidUntilFilter import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.FilterRepresentation import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.FilterTargetRepresentation import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.RelyingPartyOverridesRepresentation import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.* import edu.internet2.tier.shibboleth.admin.util.AttributeUtility import edu.internet2.tier.shibboleth.admin.util.MDDCConstants + import org.opensaml.saml.saml2.metadata.Organization import java.util.function.Supplier @@ -119,6 +121,7 @@ class TestObjectGenerator { (1..generator.randomInt(4, 10)).each { filterList.add(buildFilter { entityAttributesFilter() }) filterList.add(buildFilter { entityRoleWhitelistFilter() }) + filterList.add(buildFilter { requiredValidUntilFilter() }) } return filterList } @@ -142,6 +145,13 @@ class TestObjectGenerator { } } + RequiredValidUntilFilter requiredValidUntilFilter() { + return new RequiredValidUntilFilter().with { + it.maxValidityInterval = 'P14D' + it + } + } + EntityAttributesFilter copyOf(EntityAttributesFilter entityAttributesFilter) { new EntityAttributesFilter().with { it.name = entityAttributesFilter.name diff --git a/backend/src/test/resources/conf/278.2.xml b/backend/src/test/resources/conf/278.2.xml index b50d8e3d7..bd6c3b082 100644 --- a/backend/src/test/resources/conf/278.2.xml +++ b/backend/src/test/resources/conf/278.2.xml @@ -32,6 +32,9 @@ https://sp1.example.org + + md:SPSSODescriptor + - - - + \ No newline at end of file diff --git a/backend/src/test/resources/conf/278.xml b/backend/src/test/resources/conf/278.xml index be69c5487..7241943ab 100644 --- a/backend/src/test/resources/conf/278.xml +++ b/backend/src/test/resources/conf/278.xml @@ -25,6 +25,9 @@ xsi:type="DynamicHttpMetadataProvider"> + + md:SPSSODescriptor + - - - + \ No newline at end of file diff --git a/backend/src/test/resources/conf/552.xml b/backend/src/test/resources/conf/552.xml new file mode 100644 index 000000000..6253b2323 --- /dev/null +++ b/backend/src/test/resources/conf/552.xml @@ -0,0 +1,9 @@ + + + + +