Skip to content

Commit

Permalink
Merged in SHIBUI-525 (pull request #95)
Browse files Browse the repository at this point in the history
SHIBUI-525

Approved-by: Shibui Jenkins <shibui.jenkins@gmail.com>
  • Loading branch information
Bill Smith authored and Jonathan Johnson committed Jun 26, 2018
2 parents 6c7f3ef + 101fcd3 commit 3659adc
Show file tree
Hide file tree
Showing 7 changed files with 221 additions and 64 deletions.
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}

Expand All @@ -155,6 +156,9 @@ private MetadataFilter convertIntoTransientRepresentationIfNecessary(Stream<Meta
* Add else if instanceof block here for each concrete filter types we add in the future
*/
private void updateConcreteFilterTypeData(MetadataFilter filterToBeUpdated, MetadataFilter filterWithUpdatedData) {
//TODO: Could we maybe use Dozer here before things get out of control? https://dozermapper.github.io
// Mapper mapper = new net.sf.dozer.Mapper(); // or autowire one
// mapper.map(fromFilter, toFilter);
if(filterWithUpdatedData instanceof EntityAttributesFilter) {
EntityAttributesFilter toFilter = EntityAttributesFilter.class.cast(filterToBeUpdated);
EntityAttributesFilter fromFilter = EntityAttributesFilter.class.cast(filterWithUpdatedData);
Expand All @@ -169,6 +173,17 @@ else if(filterWithUpdatedData instanceof EntityRoleWhiteListFilter) {
toFilter.setRemoveRolelessEntityDescriptors(fromFilter.getRemoveRolelessEntityDescriptors());
toFilter.setRetainedRoles(fromFilter.getRetainedRoles());
}
else if (filterWithUpdatedData instanceof SignatureValidationFilter) {
SignatureValidationFilter toFilter = SignatureValidationFilter.class.cast(filterToBeUpdated);
SignatureValidationFilter fromFilter = SignatureValidationFilter.class.cast(filterWithUpdatedData);
toFilter.setRequireSignedRoot(fromFilter.getRequireSignedRoot());
toFilter.setCertificateFile(fromFilter.getCertificateFile());
toFilter.setDefaultCriteriaRef(fromFilter.getDefaultCriteriaRef());
toFilter.setSignaturePrevalidatorRef(fromFilter.getSignaturePrevalidatorRef());
toFilter.setDynamicTrustedNamesStrategyRef(fromFilter.getDynamicTrustedNamesStrategyRef());
toFilter.setTrustEngineRef(fromFilter.getTrustEngineRef());
toFilter.setPublicKey(fromFilter.getPublicKey());
}
else if(filterWithUpdatedData instanceof RequiredValidUntilFilter) {
RequiredValidUntilFilter toFilter = RequiredValidUntilFilter.class.cast(filterToBeUpdated);
RequiredValidUntilFilter fromFilter = RequiredValidUntilFilter.class.cast(filterWithUpdatedData);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
@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=SignatureValidationFilter.class, name="SignatureValidation"),
@JsonSubTypes.Type(value=RequiredValidUntilFilter.class, name="RequiredValidUntil")})
public class MetadataFilter extends AbstractAuditable {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package edu.internet2.tier.shibboleth.admin.ui.domain.filters;

import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.OrderColumn;
import java.util.ArrayList;
import java.util.List;

@Entity
@EqualsAndHashCode(callSuper = true)
@Getter
@Setter
@ToString
public class SignatureValidationFilter extends MetadataFilter {

public SignatureValidationFilter() {
type = "SignatureValidation";
}

private Boolean requireSignedRoot = true;

private String certificateFile;

private String defaultCriteriaRef;

private String signaturePrevalidatorRef;

private String dynamicTrustedNamesStrategyRef;

private String trustEngineRef;

private String publicKey;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package edu.internet2.tier.shibboleth.admin.ui.repository;

import edu.internet2.tier.shibboleth.admin.ui.domain.filters.SignatureValidationFilter;
import org.springframework.data.repository.CrudRepository;

/**
* Spring Data CRUD repository for instances of {@link SignatureValidationFilter}s.
*/
public interface SignatureValidationFilterRepository extends CrudRepository<SignatureValidationFilter, Long> {

SignatureValidationFilter findByName(String name);

SignatureValidationFilter findByResourceId(String resourceId);

boolean deleteByResourceId(String resourceId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ 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
Expand Down Expand Up @@ -94,7 +95,6 @@ class MetadataFiltersControllerTests extends Specification {

when:
def result = mockMvc.perform(get('/api/MetadataResolver/foo/Filters'))
println(mapper.writeValueAsString(expectedContent))

then:
result.andExpect(expectedHttpResponseStatus)
Expand Down Expand Up @@ -122,9 +122,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())
Expand All @@ -142,9 +143,6 @@ class MetadataFiltersControllerTests extends Specification {
def expectedResponseHeaderValue = "/api/MetadataResolver/$expectedMetadataResolverUUID/Filters/$expectedFilterUUID"
def expectedJsonBody = mapper.writeValueAsString(randomFilter)
def postedJsonBody = expectedJsonBody - ~/"id":.*?,/ // remove the "id:<foo>,"
println postedJsonBody
def filter = mapper.readValue(postedJsonBody, MetadataFilter)
println filter
when:
def result = mockMvc.perform(
Expand All @@ -156,13 +154,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()
Expand All @@ -188,10 +195,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"() {
Expand Down
Loading

0 comments on commit 3659adc

Please sign in to comment.