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 09f54ab75..64f33a01d 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,11 @@
-package edu.internet2.tier.shibboleth.admin.ui.service;
-
-import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.DynamicHttpMetadataResolver
-import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.LocalDynamicMetadataResolver
+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.filters.EntityRoleWhiteListFilter
-
-import edu.internet2.tier.shibboleth.admin.ui.domain.filters.RequiredValidUntilFilter
-
+import edu.internet2.tier.shibboleth.admin.ui.domain.filters.*
+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.LocalDynamicMetadataResolver
import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects
-
import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository
import groovy.util.logging.Slf4j
import groovy.xml.DOMBuilder
@@ -27,9 +19,7 @@ import org.opensaml.saml.metadata.resolver.filter.MetadataFilter
import org.opensaml.saml.metadata.resolver.filter.MetadataFilterChain
import org.opensaml.saml.saml2.core.Attribute
import org.opensaml.saml.saml2.metadata.EntityDescriptor
-
import org.springframework.beans.factory.annotation.Autowired
-
import org.w3c.dom.Document
@Slf4j
@@ -117,6 +107,52 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService {
}
}
+ void constructXmlNodeForFilter(SignatureValidationFilter filter, def markupBuilderDelegate) {
+ markupBuilderDelegate.MetadataFilter(id: filter.name,
+ 'xsi:type': 'SignatureValidation',
+ 'xmlns:md': 'urn:oasis:names:tc:SAML:2.0:metadata',
+ 'requireSignedRoot': !filter.requireSignedRoot ?: null,
+ 'certificateFile': filter.certificateFile,
+ 'defaultCriteriaRef': filter.defaultCriteriaRef,
+ 'signaturePrevalidatorRef': filter.signaturePrevalidatorRef,
+ 'dynamicTrustedNamesStrategyRef': filter.dynamicTrustedNamesStrategyRef,
+ 'trustEngineRef': filter.trustEngineRef,
+ 'publicKey': filter.publicKey)
+ }
+
+ void constructXmlNodeForFilter(EntityAttributesFilter filter, def markupBuilderDelegate) {
+ markupBuilderDelegate.MetadataFilter('xsi:type': 'EntityAttributes') {
+ // TODO: enhance. currently this does weird things with namespaces
+ filter.attributes.each { attribute ->
+ mkp.yieldUnescaped(openSamlObjects.marshalToXmlString(attribute, false))
+ }
+ if (filter.entityAttributesFilterTarget.entityAttributesFilterTargetType == EntityAttributesFilterTarget
+ .EntityAttributesFilterTargetType.ENTITY) {
+ filter.entityAttributesFilterTarget.value.each {
+ Entity(it)
+ }
+ }
+ }
+ }
+
+ void constructXmlNodeForFilter(EntityRoleWhiteListFilter filter, def markupBuilderDelegate) {
+ markupBuilderDelegate.MetadataFilter(
+ 'xsi:type': 'EntityRoleWhiteList',
+ 'xmlns:md': 'urn:oasis:names:tc:SAML:2.0:metadata'
+ ) {
+ filter.retainedRoles.each {
+ markupBuilderDelegate.RetainedRole(it)
+ }
+ }
+ }
+
+ void constructXmlNodeForFilter(RequiredValidUntilFilter filter, def markupBuilderDelegate) {
+ markupBuilderDelegate.MetadataFilter(
+ 'xsi:type': 'RequiredValidUntil',
+ maxValidityInterval: filter.maxValidityInterval
+ )
+ }
+
void constructXmlNodeForResolver(DynamicHttpMetadataResolver resolver, def markupBuilderDelegate, Closure childNodes) {
markupBuilderDelegate.MetadataProvider(id: resolver.name,
'xsi:type': 'DynamicHttpMetadataProvider',
@@ -164,39 +200,6 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService {
}
}
- void constructXmlNodeForFilter(EntityAttributesFilter filter, def markupBuilderDelegate) {
- markupBuilderDelegate.MetadataFilter('xsi:type': 'EntityAttributes') {
- // TODO: enhance. currently this does weird things with namespaces
- filter.attributes.each { attribute ->
- mkp.yieldUnescaped(openSamlObjects.marshalToXmlString(attribute, false))
- }
- if (filter.entityAttributesFilterTarget.entityAttributesFilterTargetType == EntityAttributesFilterTarget
- .EntityAttributesFilterTargetType.ENTITY) {
- filter.entityAttributesFilterTarget.value.each {
- Entity(it)
- }
- }
- }
- }
-
- void constructXmlNodeForFilter(EntityRoleWhiteListFilter filter, def markupBuilderDelegate) {
- markupBuilderDelegate.MetadataFilter(
- 'xsi:type': 'EntityRoleWhiteList',
- 'xmlns:md': 'urn:oasis:names:tc:SAML:2.0:metadata'
- ) {
- filter.retainedRoles.each {
- markupBuilderDelegate.RetainedRole(it)
- }
- }
- }
-
- 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 75eea0984..7d873afa6 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
@@ -4,10 +4,9 @@
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.filters.SignatureValidationFilter;
import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver;
import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository;
-import edu.internet2.tier.shibboleth.admin.ui.service.FilterService;
import edu.internet2.tier.shibboleth.admin.ui.service.MetadataResolverService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -135,6 +134,8 @@ public ResponseEntity> update(@PathVariable String metadataResolverId,
MetadataFilter persistedFilter =
convertIntoTransientRepresentationIfNecessary(persistedMr.getMetadataFilters().stream(), updatedFilter.getResourceId());
+ persistedFilter.setVersion(persistedFilter.hashCode());
+
return ResponseEntity.ok().body(persistedFilter);
}
@@ -155,6 +156,9 @@ private MetadataFilter convertIntoTransientRepresentationIfNecessary(Stream {
+
+ SignatureValidationFilter findByName(String name);
+
+ SignatureValidationFilter findByResourceId(String resourceId);
+
+ boolean deleteByResourceId(String resourceId);
+}
diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersControllerTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersControllerTests.groovy
index 3cd5f00c0..d160411db 100644
--- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersControllerTests.groovy
+++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersControllerTests.groovy
@@ -21,10 +21,10 @@ import org.springframework.boot.autoconfigure.domain.EntityScan
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest
import org.springframework.data.jpa.repository.config.EnableJpaRepositories
import org.springframework.test.context.ContextConfiguration
-import org.springframework.test.web.servlet.result.MockMvcResultHandlers
import org.springframework.test.web.servlet.setup.MockMvcBuilders
import org.w3c.dom.Document
import spock.lang.Specification
+import spock.lang.Unroll
import static org.hamcrest.CoreMatchers.containsString
import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8
@@ -94,7 +94,6 @@ class MetadataFiltersControllerTests extends Specification {
when:
def result = mockMvc.perform(get("$BASE_URI/foo/Filters"))
- println(mapper.writeValueAsString(expectedContent))
then:
result.andExpect(expectedHttpResponseStatus)
@@ -122,9 +121,10 @@ class MetadataFiltersControllerTests extends Specification {
.andExpect(content().json(mapper.writeValueAsString(expectedFilter)))
}
- def "FilterController.create creates the desired filter"() {
+ @Unroll
+ def "FilterController.create creates the desired filter (filterType: #filterType)"(String filterType) {
given:
- def randomFilter = testObjectGenerator.entityAttributesFilter()
+ def randomFilter = testObjectGenerator.buildRandomFilterOfType(filterType)
def metadataResolver = new MetadataResolver()
metadataResolver.setResourceId(randomGenerator.randomId())
metadataResolver.setMetadataFilters(testObjectGenerator.buildAllTypesOfFilterList())
@@ -142,9 +142,6 @@ class MetadataFiltersControllerTests extends Specification {
def expectedResponseHeaderValue = "$BASE_URI/$expectedMetadataResolverUUID/Filters/$expectedFilterUUID"
def expectedJsonBody = mapper.writeValueAsString(randomFilter)
def postedJsonBody = expectedJsonBody - ~/"id":.*?,/ // remove the "id:,"
- println postedJsonBody
- def filter = mapper.readValue(postedJsonBody, MetadataFilter)
- println filter
when:
def result = mockMvc.perform(
@@ -156,13 +153,22 @@ class MetadataFiltersControllerTests extends Specification {
result.andExpect(status().isCreated())
.andExpect(content().json(expectedJsonBody, true))
.andExpect(header().string(expectedResponseHeader, containsString(expectedResponseHeaderValue)))
+
+ where:
+ filterType | _
+ 'entityAttributes' | _
+ 'entityRoleWhiteList' | _
+ 'signatureValidation' | _
+ 'requiredValidUntil' | _
}
- def "FilterController.update updates the target EntityAttributes filter as desired"() {
+ @Unroll
+ def "FilterController.update updates the target #filterType filter as desired"(String filterType) {
given:
- def originalFilter = testObjectGenerator.entityAttributesFilter()
+ def originalFilter = testObjectGenerator.buildRandomFilterOfType(filterType)
def updatedFilter = testObjectGenerator.copyOf(originalFilter)
updatedFilter.name = 'Updated Filter'
+ updatedFilter.version = originalFilter.hashCode()
def postedJsonBody = mapper.writeValueAsString(updatedFilter)
def originalMetadataResolver = new MetadataResolver()
@@ -188,10 +194,19 @@ class MetadataFiltersControllerTests extends Specification {
then:
def expectedJson = new JsonSlurper().parseText(postedJsonBody)
- updatedFilter.fromTransientRepresentation()
+ if (filterType == 'entityAttributes') {
+ EntityAttributesFilter.cast(updatedFilter).fromTransientRepresentation()
+ }
expectedJson << [version: updatedFilter.hashCode()]
result.andExpect(status().isOk())
.andExpect(content().json(JsonOutput.toJson(expectedJson), true))
+
+ where:
+ filterType | _
+ 'entityAttributes' | _
+ 'entityRoleWhiteList' | _
+ 'signatureValidation' | _
+ 'requiredValidUntil' | _
}
def "FilterController.update filter 409's if the version numbers don't match"() {
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 b2dd89727..fe5be3277 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
@@ -1,18 +1,13 @@
package edu.internet2.tier.shibboleth.admin.ui.util
import edu.internet2.tier.shibboleth.admin.ui.domain.*
-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.MetadataFilter
-import edu.internet2.tier.shibboleth.admin.ui.domain.filters.RequiredValidUntilFilter
+import edu.internet2.tier.shibboleth.admin.ui.domain.filters.*
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
@@ -121,11 +116,46 @@ class TestObjectGenerator {
(1..generator.randomInt(4, 10)).each {
filterList.add(buildFilter { entityAttributesFilter() })
filterList.add(buildFilter { entityRoleWhitelistFilter() })
+ filterList.add(buildFilter { signatureValidationFilter() })
filterList.add(buildFilter { requiredValidUntilFilter() })
}
return filterList
}
+ MetadataFilter buildRandomFilterOfType(String filterType) {
+ def randomFilter
+ switch (filterType) {
+ case 'entityAttributes':
+ randomFilter = entityAttributesFilter()
+ break
+ case 'entityRoleWhiteList':
+ randomFilter = entityRoleWhitelistFilter()
+ break
+ case 'signatureValidation':
+ randomFilter = signatureValidationFilter()
+ break
+ case 'requiredValidUntil':
+ randomFilter = requiredValidUntilFilter()
+ break
+ default:
+ throw new RuntimeException("Did you forget to create a TestObjectGenerator.copyOf method for filtertype: ${filterType} ?");
+ }
+ randomFilter
+ }
+
+ SignatureValidationFilter signatureValidationFilter() {
+ new SignatureValidationFilter().with {
+ it.name = 'SignatureValidation'
+ it.requireSignedRoot = generator.randomBoolean()
+ it.certificateFile = generator.randomString(50)
+ it.defaultCriteriaRef = generator.randomString(10)
+ it.signaturePrevalidatorRef = generator.randomString(10)
+ it.dynamicTrustedNamesStrategyRef = generator.randomString(10)
+ it.trustEngineRef = generator.randomString(10)
+ it.publicKey = generator.randomString(50)
+ it
+ }
+ }
EntityRoleWhiteListFilter entityRoleWhitelistFilter() {
new EntityRoleWhiteListFilter().with {
it.name = 'EntityRoleWhiteList'
@@ -152,6 +182,41 @@ class TestObjectGenerator {
}
}
+ RequiredValidUntilFilter copyOf(RequiredValidUntilFilter requiredValidUntilFilter) {
+ new RequiredValidUntilFilter().with {
+ it.name = requiredValidUntilFilter.name
+ it.resourceId = requiredValidUntilFilter.resourceId
+ it.maxValidityInterval = requiredValidUntilFilter.maxValidityInterval
+ it
+ }
+ }
+
+ SignatureValidationFilter copyOf(SignatureValidationFilter signatureValidationFilter) {
+ new SignatureValidationFilter().with {
+ it.name = signatureValidationFilter.name
+ it.resourceId = signatureValidationFilter.resourceId
+ it.trustEngineRef = signatureValidationFilter.trustEngineRef
+ it.signaturePrevalidatorRef = signatureValidationFilter.signaturePrevalidatorRef
+ it.publicKey = signatureValidationFilter.publicKey
+ it.dynamicTrustedNamesStrategyRef = signatureValidationFilter.dynamicTrustedNamesStrategyRef
+ it.requireSignedRoot = signatureValidationFilter.requireSignedRoot
+ it.certificateFile = signatureValidationFilter.certificateFile
+ it.defaultCriteriaRef = signatureValidationFilter.defaultCriteriaRef
+ it
+ }
+ }
+
+ EntityRoleWhiteListFilter copyOf(EntityRoleWhiteListFilter entityRoleWhiteListFilter) {
+ new EntityRoleWhiteListFilter().with {
+ it.name = entityRoleWhiteListFilter.name
+ it.resourceId = entityRoleWhiteListFilter.resourceId
+ it.removeEmptyEntitiesDescriptors = entityRoleWhiteListFilter.removeEmptyEntitiesDescriptors
+ it.removeRolelessEntityDescriptors = entityRoleWhiteListFilter.removeRolelessEntityDescriptors
+ it.retainedRoles = entityRoleWhiteListFilter.retainedRoles
+ it
+ }
+ }
+
EntityAttributesFilter copyOf(EntityAttributesFilter entityAttributesFilter) {
new EntityAttributesFilter().with {
it.name = entityAttributesFilter.name