diff --git a/backend/build.gradle b/backend/build.gradle index 71ab2c763..83d2c8dd8 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -331,6 +331,12 @@ task generateSources { processLine(it['@className'].toString(), 'src/main/templates/AlgorithmBuilderTemplate.java') } } + + new XmlSlurper().parse(file('src/main/resources/jpa-signature-config.xml')).with { builders -> + builders.ObjectProviders.ObjectProvider.BuilderClass.each { + processLine(it['@className'].toString(), 'src/main/templates/SignatureBuilderTemplate.java') + } + } } } diff --git a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/AlgorithmFilterUiDefinitionController.groovy b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/AlgorithmFilterUiDefinitionController.groovy new file mode 100644 index 000000000..9a7005fa7 --- /dev/null +++ b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/AlgorithmFilterUiDefinitionController.groovy @@ -0,0 +1,57 @@ +package edu.internet2.tier.shibboleth.admin.ui.controller + +import com.fasterxml.jackson.databind.ObjectMapper +import edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation +import edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocationRegistry +import edu.internet2.tier.shibboleth.admin.ui.service.JsonSchemaBuilderService +import groovy.util.logging.Slf4j +import io.swagger.v3.oas.annotations.tags.Tag +import io.swagger.v3.oas.annotations.tags.Tags +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController + +import javax.annotation.PostConstruct + +import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaLocationLookup.algorithmFilterSchema +import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR + +/** + * Controller implementing REST resource responsible for exposing structure definition for algorithm format filter user + * interface in terms of JSON schema. + */ +@RestController +@RequestMapping('/api/ui/AlgorithmFilter') +@Slf4j +@Tags(value = [@Tag(name = "ui")]) +class AlgorithmFilterUiDefinitionController { + + @Autowired + JsonSchemaResourceLocationRegistry jsonSchemaResourceLocationRegistry + + JsonSchemaResourceLocation jsonSchemaLocation + + @Autowired + ObjectMapper jacksonObjectMapper + + @Autowired + JsonSchemaBuilderService jsonSchemaBuilderService + + @GetMapping + ResponseEntity getUiDefinitionJsonSchema() { + try { + def parsedJson = jacksonObjectMapper.readValue(this.jsonSchemaLocation.url, Map) + return ResponseEntity.ok(parsedJson) + } catch (Exception e) { + log.error(e.getMessage(), e) + return ResponseEntity.status(INTERNAL_SERVER_ERROR).body([jsonParseError : e.getMessage(), sourceUiSchemaDefinitionFile: this.jsonSchemaLocation.url]) + } + } + + @PostConstruct + void init() { + this.jsonSchemaLocation = algorithmFilterSchema(this.jsonSchemaResourceLocationRegistry) + } +} \ No newline at end of file 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 170a45fa8..2ac4ecafe 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 @@ -2,6 +2,8 @@ package edu.internet2.tier.shibboleth.admin.ui.service import com.google.common.base.Predicate import edu.internet2.tier.shibboleth.admin.ui.configuration.ShibUIConfiguration +import edu.internet2.tier.shibboleth.admin.ui.domain.EncryptionMethod +import edu.internet2.tier.shibboleth.admin.ui.domain.EncryptionMethodBuilder import edu.internet2.tier.shibboleth.admin.ui.domain.exceptions.MetadataFileNotFoundException import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilter import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilterTarget @@ -9,6 +11,8 @@ import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityRoleWhiteList import edu.internet2.tier.shibboleth.admin.ui.domain.filters.NameIdFormatFilter 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.filters.AlgorithmFilter +import edu.internet2.tier.shibboleth.admin.ui.domain.filters.AlgorithmFilterTarget import edu.internet2.tier.shibboleth.admin.ui.domain.filters.opensaml.OpenSamlNameIdFormatFilter import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.DynamicHttpMetadataResolver import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.ExternalMetadataResolver @@ -89,6 +93,69 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { } } + void constructXmlNodeForFilter(AlgorithmFilter filter, def markupBuilderDelegate) { + if (!filter.isFilterEnabled()) { + return + } + markupBuilderDelegate.MetadataFilter( + 'xsi:type': 'Algorithm', + 'xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance', + 'xsi:schemaLocation': 'urn:mace:shibboleth:2.0:metadata http://shibboleth.net/schema/idp/shibboleth-metadata.xsd urn:mace:shibboleth:2.0:security http://shibboleth.net/schema/idp/shibboleth-security.xsd urn:oasis:names:tc:SAML:2.0:assertion http://docs.oasis-open.org/security/saml/v2.0/saml-schema-assertion-2.0.xsd urn:oasis:names:tc:SAML:2.0:metadata http://docs.oasis-open.org/security/saml/v2.0/saml-schema-metadata-2.0.xsd urn:oasis:names:tc:SAML:metadata:algsupport https://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-metadata-algsupport-v1.0.xsd http://www.w3.org/2000/09/xmldsig# https://www.w3.org/TR/2002/REC-xmldsig-core-20020212/xmldsig-core-schema.xsd http://www.w3.org/2009/xmlenc11# https://www.w3.org/TR/xmlenc-core1/xenc-schema-11.xsd', + 'xmlns:md': 'urn:oasis:names:tc:SAML:2.0:metadata', + 'xmlns': 'urn:mace:shibboleth:2.0:metadata', + 'xmlns:security': 'urn:mace:shibboleth:2.0:security', + 'xmlns:saml2': 'urn:oasis:names:tc:SAML:2.0:assertion', + 'xmlns:xenc11': 'http://www.w3.org/2009/xmlenc11#', + 'xmlns:alg': 'urn:oasis:names:tc:SAML:metadata:algsupport', + 'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#' + ) { + for (String algValue : filter.getAlgorithms()) { + EncryptionMethod method = new EncryptionMethodBuilder().buildObject(); + method.setAlgorithm(algValue) + mkp.yieldUnescaped(openSamlObjects.marshalToXmlString(method, false)) + } + switch (filter.algorithmFilterTarget.targetType) { + case AlgorithmFilterTarget.AlgorithmFilterTargetType.ENTITY: + filter.algorithmFilterTarget.value.each { + Entity(it) + } + break + case AlgorithmFilterTarget.AlgorithmFilterTargetType.CONDITION_REF: + ConditionRef(xmlObject.getValue()) + break + case AlgorithmFilterTarget.AlgorithmFilterTargetType.CONDITION_SCRIPT: + ConditionScript() { + Script() { + def script = filter.getAlgorithmFilterTarget().value[0] + mkp.yieldUnescaped("\n\n") + } + } + break + default: + // do nothing, we'd have exploded elsewhere previously. + break + } +// filter.unknownXMLObjects.each { xmlObject -> +// { +// if (xmlObject instanceof Entity) { +// Entity(xmlObject.getValue()) +// } else if (xmlObject instanceof ConditionRef) { +// ConditionRef(xmlObject.getValue()) +// } else if (xmlObject instanceof ConditionScript) { +// ConditionScript() { +// Script() { +// def script = xmlObject.getValue() +// mkp.yieldUnescaped("\n\n") +// } +// } +// } else { +// mkp.yieldUnescaped(openSamlObjects.marshalToXmlString(xmlObject, false)) +// } +// } +// } + } + } + void constructXmlNodeForFilter(EntityAttributesFilter filter, def markupBuilderDelegate) { if (!filter.isFilterEnabled()) { return } markupBuilderDelegate.MetadataFilter('xsi:type': 'EntityAttributes') { @@ -103,10 +170,8 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { Entity(it) } break - case EntityAttributesFilterTarget - .EntityAttributesFilterTargetType.CONDITION_SCRIPT: - case EntityAttributesFilterTarget - .EntityAttributesFilterTargetType.REGEX: + case EntityAttributesFilterTarget.EntityAttributesFilterTargetType.CONDITION_SCRIPT: + case EntityAttributesFilterTarget.EntityAttributesFilterTargetType.REGEX: ConditionScript() { Script() { def script diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/JsonSchemaComponentsConfiguration.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/JsonSchemaComponentsConfiguration.java index 34c5c1eaf..23fbaaa30 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/JsonSchemaComponentsConfiguration.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/JsonSchemaComponentsConfiguration.java @@ -2,23 +2,22 @@ import com.fasterxml.jackson.databind.ObjectMapper; import edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocationRegistry; -import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserRepository; import edu.internet2.tier.shibboleth.admin.ui.security.service.UserService; import edu.internet2.tier.shibboleth.admin.ui.service.JsonSchemaBuilderService; import lombok.Setter; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ResourceLoader; -import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.*; +import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.JsonSchemaLocationBuilder; +import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.ALGORITHM_FILTER; +import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.DYNAMIC_HTTP_METADATA_RESOLVER; import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.ENTITY_ATTRIBUTES_FILTERS; import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.EXTERNAL_METADATA_RESOLVER; -import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.METADATA_SOURCES; import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.FILESYSTEM_METADATA_RESOLVER; import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.LOCAL_DYNAMIC_METADATA_RESOLVER; -import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.DYNAMIC_HTTP_METADATA_RESOLVER; +import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.METADATA_SOURCES; import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.NAME_ID_FORMAT_FILTER; /** @@ -58,8 +57,16 @@ public class JsonSchemaComponentsConfiguration { @Setter private String nameIdFormatFilterUiSchemaLocation = "classpath:nameid-filter.schema.json"; + //Configured via @ConfigurationProperties (using setter method) with 'shibui.external-metadata-resolver-ui-schema-location' property and + // default value set here if that property is not explicitly set in application.properties + @Setter private String externalMetadataResolverUiSchemaLocation = "classpath:external.schema.json"; + //Configured via @ConfigurationProperties (using setter method) with 'shibui.algorithm-filter-ui-schema-location' property and + // default value set here if that property is not explicitly set in application.properties + @Setter + private String algorithmFilterUiSchemaLocation = "classpath:algorithm-filter.schema.json"; + @Bean public JsonSchemaResourceLocationRegistry jsonSchemaResourceLocationRegistry(ResourceLoader resourceLoader, ObjectMapper jacksonMapper) { return JsonSchemaResourceLocationRegistry.inMemory() @@ -104,6 +111,12 @@ public JsonSchemaResourceLocationRegistry jsonSchemaResourceLocationRegistry(Res .resourceLoader(resourceLoader) .jacksonMapper(jacksonMapper) .detectMalformedJson(true) + .build()) + .register(ALGORITHM_FILTER, JsonSchemaLocationBuilder.with() + .jsonSchemaLocation(algorithmFilterUiSchemaLocation) + .resourceLoader(resourceLoader) + .jacksonMapper(jacksonMapper) + .detectMalformedJson(true) .build()); } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AbstractAlgorithmIdentifierType.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AbstractAlgorithmIdentifierType.java new file mode 100644 index 000000000..3a3eeef77 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AbstractAlgorithmIdentifierType.java @@ -0,0 +1,36 @@ +package edu.internet2.tier.shibboleth.admin.ui.domain; + +import edu.internet2.tier.shibboleth.admin.ui.domain.AbstractXMLObject; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import org.hibernate.envers.Audited; +import org.opensaml.core.xml.XMLObject; +import org.opensaml.xmlsec.encryption.AlgorithmIdentifierType; + +import javax.annotation.Nullable; +import javax.persistence.Entity; + +@Entity +@Audited +@Getter +@Setter +@ToString +@EqualsAndHashCode(callSuper = true) +public abstract class AbstractAlgorithmIdentifierType extends AbstractXMLObject implements AlgorithmIdentifierType { + private String algorithm; + + @Nullable + @Override + public XMLObject getParameters() { + // implement? + return null; + } + + @Override + public void setParameters(@Nullable final XMLObject newParameters) { + // do nothing? + } + +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AlgorithmDigestMethod.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AlgorithmDigestMethod.java new file mode 100644 index 000000000..509f9613d --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AlgorithmDigestMethod.java @@ -0,0 +1,29 @@ +package edu.internet2.tier.shibboleth.admin.ui.domain; + +import lombok.EqualsAndHashCode; + +import javax.annotation.Nullable; +import javax.persistence.Entity; + +@Entity(name = "DigestMethod") // for backwards compatibility instead of dealing with renaming the table +@EqualsAndHashCode(callSuper = true) +public class AlgorithmDigestMethod extends AbstractElementExtensibleXMLObject implements org.opensaml.saml.ext.saml2alg.DigestMethod { + private String algorithm; + + public AlgorithmDigestMethod() {} + + public AlgorithmDigestMethod(String algorithm) { + this.algorithm = algorithm; + } + + @Nullable + @Override + public String getAlgorithm() { + return this.algorithm; + } + + @Override + public void setAlgorithm(@Nullable String value) { + this.algorithm = value; + } +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EncryptionMethod.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EncryptionMethod.java index 06a47da63..16a122883 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EncryptionMethod.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EncryptionMethod.java @@ -2,12 +2,14 @@ import lombok.EqualsAndHashCode; import org.hibernate.envers.Audited; +import org.opensaml.core.xml.XMLObject; import org.opensaml.xmlsec.encryption.KeySize; import org.opensaml.xmlsec.encryption.OAEPparams; import javax.annotation.Nullable; import javax.persistence.Embedded; import javax.persistence.Entity; +import java.util.List; @Entity @EqualsAndHashCode(callSuper = true) @@ -16,12 +18,9 @@ public class EncryptionMethod extends AbstractElementExtensibleXMLObject impleme private String algorithm; - @Embedded - private KeySize keySize; - - @Embedded - private OAEPparams oaePparams; + @Embedded private KeySize keySize; + @Embedded private OAEPparams oaePparams; @Nullable @Override @@ -55,4 +54,9 @@ public org.opensaml.xmlsec.encryption.OAEPparams getOAEPparams() { public void setOAEPparams(@Nullable org.opensaml.xmlsec.encryption.OAEPparams oaePparams) { this.oaePparams = oaePparams; } -} + + @Override + public List getOrderedChildren() { + return this.getUnknownXMLObjects(); + } +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/MGF.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/MGF.java new file mode 100644 index 000000000..e145231af --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/MGF.java @@ -0,0 +1,24 @@ +package edu.internet2.tier.shibboleth.admin.ui.domain; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import org.hibernate.envers.Audited; +import org.opensaml.xmlsec.encryption.support.EncryptionConstants; + +import javax.persistence.Entity; + +@Entity +@Audited +@Getter +@Setter +@ToString +@EqualsAndHashCode(callSuper = true) +public class MGF extends AbstractAlgorithmIdentifierType { + public MGF() { + setElementLocalName("MGF"); + setNamespaceURI(EncryptionConstants.XMLENC11_NS); + setNamespacePrefix(EncryptionConstants.XMLENC11_PREFIX); + } +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/OtherSource.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/OtherSource.java new file mode 100644 index 000000000..0fbd26a16 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/OtherSource.java @@ -0,0 +1,26 @@ +package edu.internet2.tier.shibboleth.admin.ui.domain; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import org.hibernate.envers.Audited; +import org.opensaml.xmlsec.encryption.support.EncryptionConstants; + +import javax.persistence.Entity; + +@Entity +@Audited +@Getter +@Setter +@ToString +@EqualsAndHashCode(callSuper = true) +public class OtherSource extends AbstractAlgorithmIdentifierType { + public OtherSource() { + { + setElementLocalName("OtherSource"); + setNamespaceURI(EncryptionConstants.XMLENC11_NS); + setNamespacePrefix(EncryptionConstants.XMLENC11_PREFIX); + } + } +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/PRF.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/PRF.java new file mode 100644 index 000000000..876c11561 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/PRF.java @@ -0,0 +1,24 @@ +package edu.internet2.tier.shibboleth.admin.ui.domain; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import org.hibernate.envers.Audited; +import org.opensaml.xmlsec.encryption.support.EncryptionConstants; + +import javax.persistence.Entity; + +@Entity +@Audited +@Getter +@Setter +@ToString +@EqualsAndHashCode(callSuper = true) +public class PRF extends AbstractAlgorithmIdentifierType { + public PRF() { + setElementLocalName("PRF"); + setNamespaceURI(EncryptionConstants.XMLENC11_NS); + setNamespacePrefix(EncryptionConstants.XMLENC11_PREFIX); + } +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/DigestMethod.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/SignatureDigestMethod.java similarity index 66% rename from backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/DigestMethod.java rename to backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/SignatureDigestMethod.java index 199947f88..519df1faf 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/DigestMethod.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/SignatureDigestMethod.java @@ -1,18 +1,19 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.opensaml.xmlsec.signature.DigestMethod; import javax.annotation.Nullable; import javax.persistence.Entity; @Entity @EqualsAndHashCode(callSuper = true) -public class DigestMethod extends AbstractElementExtensibleXMLObject implements org.opensaml.saml.ext.saml2alg.DigestMethod { +public class SignatureDigestMethod extends AbstractElementExtensibleXMLObject implements DigestMethod { private String algorithm; - public DigestMethod() {} + public SignatureDigestMethod() {} - public DigestMethod(String algorithm) { + public SignatureDigestMethod(String algorithm) { this.algorithm = algorithm; } @@ -26,4 +27,4 @@ public String getAlgorithm() { public void setAlgorithm(@Nullable String value) { this.algorithm = value; } -} +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/AbstractFilterTarget.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/AbstractFilterTarget.java new file mode 100644 index 000000000..a26c06e88 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/AbstractFilterTarget.java @@ -0,0 +1,39 @@ +package edu.internet2.tier.shibboleth.admin.ui.domain.filters; + +import edu.internet2.tier.shibboleth.admin.ui.domain.AbstractAuditable; +import lombok.EqualsAndHashCode; + +import javax.persistence.Column; +import javax.persistence.ElementCollection; +import javax.persistence.FetchType; +import javax.persistence.MappedSuperclass; +import javax.persistence.OrderColumn; +import java.util.ArrayList; +import java.util.List; + +@MappedSuperclass +@EqualsAndHashCode(callSuper = true) +public abstract class AbstractFilterTarget extends AbstractAuditable implements IFilterTarget { + @ElementCollection(fetch = FetchType.EAGER) + @OrderColumn + @Column(length = 760, name="target_value") + protected List value; + + @Override + public List getValue() { + return value == null ? new ArrayList<>() : value; + } + + @Override + public void setSingleValue(String value) { + List values = new ArrayList<>(); + values.add(value); + this.value = values; + } + + @Override + public void setValue(List value) { + this.value = value; + } + +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/AlgorithmFilter.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/AlgorithmFilter.java new file mode 100644 index 000000000..062ffa8c6 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/AlgorithmFilter.java @@ -0,0 +1,48 @@ +package edu.internet2.tier.shibboleth.admin.ui.domain.filters; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import org.hibernate.envers.Audited; + +import javax.persistence.CascadeType; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.OneToOne; +import javax.persistence.OrderColumn; +import java.util.List; + +@Entity +@Audited +@Getter +@Setter +@ToString +@EqualsAndHashCode(callSuper = true) +public class AlgorithmFilter extends MetadataFilter implements ITargetable { + @OneToOne(cascade = CascadeType.ALL) private AlgorithmFilterTarget algorithmFilterTarget; + + @ElementCollection + @OrderColumn + private List algorithms; + + public AlgorithmFilter() { + type = "Algorithm"; + } + + @Override + public IFilterTarget getTarget() { + return algorithmFilterTarget; + } + + private AlgorithmFilter updateConcreteFilterTypeData(AlgorithmFilter filterToBeUpdated) { + filterToBeUpdated.setAlgorithms(getAlgorithms()); + filterToBeUpdated.setAlgorithmFilterTarget(getAlgorithmFilterTarget()); + return filterToBeUpdated; + } + + @Override + public MetadataFilter updateConcreteFilterTypeData(MetadataFilter filterToBeUpdated) { + return updateConcreteFilterTypeData((AlgorithmFilter) filterToBeUpdated); + } +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/AlgorithmFilterTarget.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/AlgorithmFilterTarget.java new file mode 100644 index 000000000..0b881a987 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/AlgorithmFilterTarget.java @@ -0,0 +1,34 @@ +package edu.internet2.tier.shibboleth.admin.ui.domain.filters; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import edu.internet2.tier.shibboleth.admin.ui.domain.AbstractAuditable; +import edu.internet2.tier.shibboleth.admin.ui.domain.filters.AbstractFilterTarget; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.hibernate.envers.AuditOverride; +import org.hibernate.envers.Audited; + +import javax.persistence.Entity; + +@Entity +@EqualsAndHashCode(callSuper = true) +@ToString +@Audited +@AuditOverride(forClass = AbstractAuditable.class) +@JsonIgnoreProperties({"handler", "hibernateLazyInitializer"}) +public class AlgorithmFilterTarget extends AbstractFilterTarget { + private AlgorithmFilterTargetType targetType; + + @Override + public String getTargetTypeValue() { + return targetType == null ? "NONE" : targetType.name(); + } + + public void setAlgorithmFilterTargetType(AlgorithmFilterTargetType type) { + this.targetType = type; + } + + public enum AlgorithmFilterTargetType { + ENTITY, CONDITION_SCRIPT, CONDITION_REF + } +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/EntityAttributesFilterTarget.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/EntityAttributesFilterTarget.java index e2ed028a0..b2ffb6b0d 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/EntityAttributesFilterTarget.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/EntityAttributesFilterTarget.java @@ -4,31 +4,21 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import edu.internet2.tier.shibboleth.admin.ui.domain.AbstractAuditable; import lombok.EqualsAndHashCode; +import lombok.ToString; import org.hibernate.envers.AuditOverride; import org.hibernate.envers.Audited; -import javax.persistence.Column; -import javax.persistence.ElementCollection; import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.OrderColumn; -import java.util.ArrayList; -import java.util.List; @Entity @EqualsAndHashCode(callSuper = true) +@ToString @Audited @AuditOverride(forClass = AbstractAuditable.class) @JsonIgnoreProperties({"handler", "hibernateLazyInitializer"}) -public class EntityAttributesFilterTarget extends AbstractAuditable implements IFilterTarget { - +public class EntityAttributesFilterTarget extends AbstractFilterTarget { private EntityAttributesFilterTargetType entityAttributesFilterTargetType; - @ElementCollection (fetch = FetchType.EAGER) - @OrderColumn - @Column(length = 760, name="target_value") - private List value; - public EntityAttributesFilterTargetType getEntityAttributesFilterTargetType() { return entityAttributesFilterTargetType; } @@ -39,33 +29,10 @@ public String getTargetTypeValue() { return entityAttributesFilterTargetType == null ? "NONE" : entityAttributesFilterTargetType.name(); } - @Override - public List getValue() { - return value == null ? new ArrayList<>() : value; - } - public void setEntityAttributesFilterTargetType(EntityAttributesFilterTargetType entityAttributesFilterTarget) { this.entityAttributesFilterTargetType = entityAttributesFilterTarget; } - public void setSingleValue(String value) { - List values = new ArrayList<>(); - values.add(value); - this.value = values; - } - - public void setValue(List value) { - this.value = value; - } - - @Override - public String toString() { - return "EntityAttributesFilterTarget{" + - "entityAttributesFilterTargetType=" + entityAttributesFilterTargetType + - ", value=" + value + - '}'; - } - public enum EntityAttributesFilterTargetType { ENTITY, CONDITION_SCRIPT, CONDITION_REF, REGEX } 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 548c97356..c566576df 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 @@ -50,7 +50,7 @@ public abstract class MetadataFilter extends AbstractAuditable implements IConcr @JsonProperty("@type") @Transient - String type; + protected String type; @Transient private transient Integer version; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/NameIdFormatFilterTarget.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/NameIdFormatFilterTarget.java index 3a5bfe9da..c5441a053 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/NameIdFormatFilterTarget.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/NameIdFormatFilterTarget.java @@ -8,29 +8,18 @@ import org.hibernate.envers.AuditOverride; import org.hibernate.envers.Audited; -import javax.persistence.Column; -import javax.persistence.ElementCollection; import javax.persistence.Entity; -import javax.persistence.OrderColumn; -import javax.persistence.Transient; -import java.util.ArrayList; -import java.util.List; @Entity @EqualsAndHashCode(callSuper = true) @ToString @Audited @AuditOverride(forClass = AbstractAuditable.class) -@JsonIgnoreProperties({"handler", "hibernateLazyInitializer"}) -public class NameIdFormatFilterTarget extends AbstractAuditable implements IFilterTarget { +@JsonIgnoreProperties({ "handler", "hibernateLazyInitializer" }) +public class NameIdFormatFilterTarget extends AbstractFilterTarget { private NameIdFormatFilterTargetType nameIdFormatFilterTargetType; - @ElementCollection - @OrderColumn - @Column(name="target_value") - private List value; - public NameIdFormatFilterTargetType getNameIdFormatFilterTargetType() { return nameIdFormatFilterTargetType; } @@ -41,24 +30,10 @@ public String getTargetTypeValue() { return nameIdFormatFilterTargetType.name(); } - public List getValue() { - return value; - } - public void setNameIdFormatFilterTargetType(NameIdFormatFilterTargetType nameIdFormatFilterTargetType) { this.nameIdFormatFilterTargetType = nameIdFormatFilterTargetType; } - public void setSingleValue(String value) { - List values = new ArrayList<>(); - values.add(value); - this.value = values; - } - - public void setValue(List value) { - this.value = value; - } - public enum NameIdFormatFilterTargetType { ENTITY, CONDITION_SCRIPT, REGEX } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/JsonSchemaLocationLookup.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/JsonSchemaLocationLookup.java index b44e4e7ce..bdb781d9a 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/JsonSchemaLocationLookup.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/JsonSchemaLocationLookup.java @@ -1,11 +1,12 @@ package edu.internet2.tier.shibboleth.admin.ui.jsonschema; +import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.ALGORITHM_FILTER; +import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.DYNAMIC_HTTP_METADATA_RESOLVER; import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.ENTITY_ATTRIBUTES_FILTERS; import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.EXTERNAL_METADATA_RESOLVER; -import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.METADATA_SOURCES; import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.FILESYSTEM_METADATA_RESOLVER; import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.LOCAL_DYNAMIC_METADATA_RESOLVER; -import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.DYNAMIC_HTTP_METADATA_RESOLVER; +import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.METADATA_SOURCES; import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.NAME_ID_FORMAT_FILTER; /** @@ -105,4 +106,16 @@ public static JsonSchemaResourceLocation nameIdFormatFilterSchema(JsonSchemaReso .lookup(NAME_ID_FORMAT_FILTER) .orElseThrow(() -> new IllegalStateException("JSON schema resource location for name id format filter is not registered.")); } + + /** + * Searches algorithm filter JSON schema resource location object in the given location registry. + * + * @param resourceLocationRegistry + * @return algorithm filter JSON schema resource location object + * @throws IllegalStateException if schema is not found in the given registry + */ + public static JsonSchemaResourceLocation algorithmFilterSchema(JsonSchemaResourceLocationRegistry resourceLocationRegistry) { + return resourceLocationRegistry.lookup(ALGORITHM_FILTER) + .orElseThrow(() -> new IllegalStateException("JSON schema resource location for algorithm filter is not registered.")); + } } \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/JsonSchemaResourceLocation.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/JsonSchemaResourceLocation.java index 1b9054cd3..02e3da1d8 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/JsonSchemaResourceLocation.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/JsonSchemaResourceLocation.java @@ -99,6 +99,7 @@ public enum SchemaType { // filter types ENTITY_ATTRIBUTES_FILTERS("EntityAttributesFilters"), NAME_ID_FORMAT_FILTER("NameIdFormatFilter"), + ALGORITHM_FILTER("AlgorithmFilter"), // resolver types FILE_BACKED_HTTP_METADATA_RESOLVER("FileBackedHttpMetadataResolver"), diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/opensaml/OpenSamlObjects.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/opensaml/OpenSamlObjects.java index 8a25b0855..bd149c9ff 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/opensaml/OpenSamlObjects.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/opensaml/OpenSamlObjects.java @@ -88,11 +88,11 @@ public void init() throws ComponentInitializationException { this.unmarshallerFactory = registry.getUnmarshallerFactory(); } - public String marshalToXmlString(XMLObject ed, boolean includeXMLDeclaration) throws MarshallingException { - Marshaller marshaller = this.marshallerFactory.getMarshaller(ed); - ed.releaseDOM(); - ed.releaseChildrenDOM(true); - String entityDescriptorXmlString = null; + public String marshalToXmlString(XMLObject xmlObject, boolean includeXMLDeclaration) throws MarshallingException { + Marshaller marshaller = this.marshallerFactory.getMarshaller(xmlObject); + xmlObject.releaseDOM(); + xmlObject.releaseChildrenDOM(true); + String resultString = null; if (marshaller != null) { try (StringWriter writer = new StringWriter()) { Transformer transformer = TransformerFactory.newInstance().newTransformer(); @@ -101,22 +101,22 @@ public String marshalToXmlString(XMLObject ed, boolean includeXMLDeclaration) th if (!includeXMLDeclaration) { transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); } - transformer.transform(new DOMSource(marshaller.marshall(ed)), new StreamResult(writer)); - entityDescriptorXmlString = writer.toString(); + transformer.transform(new DOMSource(marshaller.marshall(xmlObject)), new StreamResult(writer)); + resultString = writer.toString(); } catch (TransformerException | IOException e) { logger.error(e.getMessage(), e); } } - if (entityDescriptorXmlString == null) { + if (resultString == null) { //Figure out the best way to deal with this case - throw new RuntimeException("Unable to marshal EntityDescriptor"); + throw new RuntimeException("Unable to marshal xmlObject"); } - return entityDescriptorXmlString; + return resultString; } - public String marshalToXmlString(XMLObject ed) throws MarshallingException { - return this.marshalToXmlString(ed, true); + public String marshalToXmlString(XMLObject xmlObject) throws MarshallingException { + return this.marshalToXmlString(xmlObject, true); } public EntityDescriptor unmarshalFromXml(byte[] entityDescriptorXml) throws Exception { @@ -152,4 +152,4 @@ public T buildDefaultInstanceOfType(Class type) { throw new RuntimeException("there was a problem building an instance", e); } } -} +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/opensaml/config/JPAXMLObjectProviderInitializer.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/opensaml/config/JPAXMLObjectProviderInitializer.java index 25000ae67..24757d560 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/opensaml/config/JPAXMLObjectProviderInitializer.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/opensaml/config/JPAXMLObjectProviderInitializer.java @@ -8,17 +8,17 @@ public class JPAXMLObjectProviderInitializer extends AbstractXMLObjectProviderIn protected String[] getConfigResources() { return new String[]{ "/jpa-default-config.xml", - "/jpa-saml2-metadata-config.xml", - "/jpa-saml2-metadata-attr-config.xml", + "/encryption-config.xml", "/jpa-saml2-assertion-config.xml", - "/jpa-schema-config.xml", - "/jpa-saml2-metadata-ui-config.xml", - "/jpa-signature-config.xml", "/jpa-saml2-metadata-algorithm-config.xml", - "/encryption-config.xml", + "/jpa-saml2-metadata-attr-config.xml", + "/jpa-saml2-metadata-config.xml", "/jpa-saml2-metadata-reqinit-config.xml", + "/jpa-saml2-metadata-ui-config.xml", + "/jpa-schema-config.xml", + "/jpa-signature-config.xml", "/saml2-protocol-config.xml", "/modified-saml2-assertion-config.xml" }; } -} +} \ No newline at end of file diff --git a/backend/src/main/resources/algorithm-filter.schema.json b/backend/src/main/resources/algorithm-filter.schema.json new file mode 100644 index 000000000..f570dacb7 --- /dev/null +++ b/backend/src/main/resources/algorithm-filter.schema.json @@ -0,0 +1,65 @@ +{ + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "title": "label.filter-name", + "description": "tooltip.filter-name", + "type": "string" + }, + "filterEnabled": { + "title": "label.enable-filter", + "description": "tooltip.enable-filter", + "type": "boolean", + "default": false + }, + "AlgorithmFilterTarget": { + "title": "label.search-criteria", + "description": "tooltip.search-criteria", + "type": "object", + "properties": { + "nameIdFormatFilterTargetType": { + "title": "label.filter-target-type", + "type": "string", + "default": "ENTITY", + "enum": [ + "ENTITY", + "CONDITION_REF", + "CONDITION_SCRIPT" + ], + "enumNames": [ + "value.entity-id", + "value.reference", + "value.script" + ] + }, + "value": { + "title": "label.filter-target-value", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "type": "string" + } + } + }, + "required": [ + "value", + "algorithmFilterTargetType" + ] + }, + "@type": { + "type": "string", + "default": "Algorithm" + }, + "version": { + "type": "integer" + }, + "resourceId": { + "type": "string" + } + }, + "definitions": {} +} \ No newline at end of file diff --git a/backend/src/main/resources/i18n/messages.properties b/backend/src/main/resources/i18n/messages.properties index 3b3e67e83..77ca7f957 100644 --- a/backend/src/main/resources/i18n/messages.properties +++ b/backend/src/main/resources/i18n/messages.properties @@ -90,6 +90,7 @@ value.false=false value.regex=Regex value.script=Script value.entity-id=Entity ID +value.reference=Condition Ref value.support=Support value.technical=Technical diff --git a/backend/src/main/resources/i18n/messages_en.properties b/backend/src/main/resources/i18n/messages_en.properties index ca93cdbb3..d0637f6eb 100644 --- a/backend/src/main/resources/i18n/messages_en.properties +++ b/backend/src/main/resources/i18n/messages_en.properties @@ -59,6 +59,7 @@ value.false=false value.regex=Regex value.script=Script value.entity-id=Entity ID +value.reference=Condition Ref value.support=Support value.technical=Technical diff --git a/backend/src/main/resources/jpa-encryption-config.xml b/backend/src/main/resources/jpa-encryption-config.xml new file mode 100644 index 000000000..05adde279 --- /dev/null +++ b/backend/src/main/resources/jpa-encryption-config.xml @@ -0,0 +1,284 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backend/src/main/resources/jpa-saml2-metadata-algorithm-config.xml b/backend/src/main/resources/jpa-saml2-metadata-algorithm-config.xml index f6432a71a..c37c788c1 100644 --- a/backend/src/main/resources/jpa-saml2-metadata-algorithm-config.xml +++ b/backend/src/main/resources/jpa-saml2-metadata-algorithm-config.xml @@ -6,13 +6,13 @@ - + - + @@ -31,4 +31,4 @@ - + \ No newline at end of file diff --git a/backend/src/main/templates/SignatureBuilderTemplate.java b/backend/src/main/templates/SignatureBuilderTemplate.java new file mode 100644 index 000000000..d8ba87cb1 --- /dev/null +++ b/backend/src/main/templates/SignatureBuilderTemplate.java @@ -0,0 +1,22 @@ +package edu.internet2.tier.shibboleth.admin.ui.domain; + +import edu.internet2.tier.shibboleth.admin.ui.opensaml.xml.AbstractXMLObjectBuilder; +import org.opensaml.xmlsec.signature.support.SignatureConstants; + +public class {{TOKEN}}Builder extends AbstractXMLObjectBuilder<{{TOKEN}}> { + public {{TOKEN}}Builder() { + } + + public {{TOKEN}} buildObject() { + return buildObject(SignatureConstants.XMLSIG_NS, {{TOKEN}}.DEFAULT_ELEMENT_LOCAL_NAME, + SignatureConstants.XMLSIG_PREFIX); + } + + public {{TOKEN}} buildObject(final String namespaceURI, final String localName, final String namespacePrefix) { + {{TOKEN}} o = new {{TOKEN}}(); + o.setNamespaceURI(namespaceURI); + o.setElementLocalName(localName); + o.setNamespacePrefix(namespacePrefix); + return o; + } +} \ No newline at end of file diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/BadJSONMetadataSourcesUiDefinitionControllerIntegrationTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/BadJSONMetadataSourcesUiDefinitionControllerIntegrationTests.groovy index b9789201f..14f5d9a16 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/BadJSONMetadataSourcesUiDefinitionControllerIntegrationTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/BadJSONMetadataSourcesUiDefinitionControllerIntegrationTests.groovy @@ -13,6 +13,7 @@ import org.springframework.test.context.ActiveProfiles import spock.lang.Specification import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.* +import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.ALGORITHM_FILTER import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.DYNAMIC_HTTP_METADATA_RESOLVER import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.ENTITY_ATTRIBUTES_FILTERS import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.FILESYSTEM_METADATA_RESOLVER @@ -87,6 +88,12 @@ class BadJSONMetadataSourcesUiDefinitionControllerIntegrationTests extends Speci .jacksonMapper(jacksonMapper) .detectMalformedJson(false) .build()) + .register(ALGORITHM_FILTER, JsonSchemaLocationBuilder.with() + .jsonSchemaLocation('classpath:algorithm-filter.schema.json') + .resourceLoader(resourceLoader) + .jacksonMapper(jacksonMapper) + .detectMalformedJson(false) + .build()) } } diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/opensaml/config/JPAXMLObjectProviderInitializerForTest.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/opensaml/config/JPAXMLObjectProviderInitializerForTest.groovy new file mode 100644 index 000000000..f84afdcc5 --- /dev/null +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/opensaml/config/JPAXMLObjectProviderInitializerForTest.groovy @@ -0,0 +1,12 @@ +package edu.internet2.tier.shibboleth.admin.ui.opensaml.config + +import org.opensaml.core.xml.config.AbstractXMLObjectProviderInitializer + +class JPAXMLObjectProviderInitializerForTest extends AbstractXMLObjectProviderInitializer { + @Override + protected String[] getConfigResources() { + return new String[]{ + "/jpa-saml2-metadata-config.xml", "jpa-saml2-metadata-algorithm-config.xml", "jpa-encryption-config.xml", "jpa-signature-config.xml" + } + } +} \ No newline at end of file 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 9905dbcea..f5f37da1f 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 @@ -1,16 +1,17 @@ package edu.internet2.tier.shibboleth.admin.ui.service -import com.google.common.collect.Lists import edu.internet2.tier.shibboleth.admin.ui.AbstractBaseDataJpaTest import edu.internet2.tier.shibboleth.admin.ui.configuration.PlaceholderResolverComponentsConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.ShibUIConfiguration -import edu.internet2.tier.shibboleth.admin.ui.domain.Attribute -import edu.internet2.tier.shibboleth.admin.ui.domain.AttributeValue +import edu.internet2.tier.shibboleth.admin.ui.domain.AlgorithmDigestMethod +import edu.internet2.tier.shibboleth.admin.ui.domain.EncryptionMethod +import edu.internet2.tier.shibboleth.admin.ui.domain.SignatureDigestMethod import edu.internet2.tier.shibboleth.admin.ui.domain.XSString 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.MetadataFilter import edu.internet2.tier.shibboleth.admin.ui.domain.filters.RequiredValidUntilFilter +import edu.internet2.tier.shibboleth.admin.ui.domain.filters.AlgorithmFilterTarget import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.ClasspathMetadataResource import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.DynamicHttpMetadataResolver import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.ExternalMetadataResolver @@ -21,6 +22,7 @@ import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.SvnMetadataResour import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.TemplateScheme import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.opensaml.OpenSamlChainingMetadataResolver import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects +import edu.internet2.tier.shibboleth.admin.ui.opensaml.config.JPAXMLObjectProviderInitializerForTest 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 @@ -29,9 +31,11 @@ import groovy.xml.MarkupBuilder import net.shibboleth.ext.spring.resource.ResourceHelper import net.shibboleth.utilities.java.support.resolver.CriteriaSet import org.opensaml.core.criterion.EntityIdCriterion +import org.opensaml.saml.common.xml.SAMLConstants import org.opensaml.saml.metadata.resolver.MetadataResolver import org.opensaml.saml.metadata.resolver.filter.MetadataFilterChain import org.opensaml.saml.metadata.resolver.impl.ResourceBackedMetadataResolver +import org.opensaml.xmlsec.signature.support.SignatureConstants import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.TestConfiguration import org.springframework.context.annotation.Bean @@ -48,7 +52,7 @@ import java.time.Instant import static edu.internet2.tier.shibboleth.admin.ui.util.TestHelpers.generatedXmlIsTheSameAsExpectedXml -@ContextConfiguration(classes=[ JPAMRSIConfig, PlaceholderResolverComponentsConfiguration ]) +@ContextConfiguration(classes=[ JPAMRSIConfig, PlaceholderResolverComponentsConfiguration, JPAXMLObjectProviderInitializerForTest ]) class JPAMetadataResolverServiceImplTests extends AbstractBaseDataJpaTest { @Autowired @@ -184,6 +188,47 @@ class JPAMetadataResolverServiceImplTests extends AbstractBaseDataJpaTest { !diff.hasDifferences() } + def 'test generating AlgorithmFilter shibui-2268 entities'() { + given: + def filter = TestObjectGenerator.algorithmFilter() + ArrayList algs = new ArrayList<>() + algs.add("http://www.w3.org/2001/04/xmlenc#aes128-cbc") + filter.setAlgorithms(algs) + + AlgorithmFilterTarget target = new AlgorithmFilterTarget() + target.setAlgorithmFilterTargetType(AlgorithmFilterTarget.AlgorithmFilterTargetType.ENTITY) + ArrayList entities = new ArrayList<>() + entities.add("https://broken.example.org/sp") + entities.add("https://also-broken.example.org/sp") + target.setValue(entities) + filter.setAlgorithmFilterTarget(target) + + when: + genXmlSnippet(markupBuilder) { JPAMetadataResolverServiceImpl.cast(metadataResolverService).constructXmlNodeForFilter(filter, it) } + + then: + generatedXmlIsTheSameAsExpectedXml('/conf/2268-entity.xml', domBuilder.parseText(writer.toString())) + } + + def 'test generating AlgorithmFilter shibui-2268 script'() { + given: + def filter = TestObjectGenerator.algorithmFilter() + ArrayList algs = new ArrayList<>() + algs.add("http://www.w3.org/2001/04/xmlenc#aes128-cbc") + filter.setAlgorithms(algs) + + AlgorithmFilterTarget target = new AlgorithmFilterTarget() + target.setAlgorithmFilterTargetType(AlgorithmFilterTarget.AlgorithmFilterTargetType.CONDITION_SCRIPT) + target.setSingleValue("\"use strict\";\nfalse;") + filter.setAlgorithmFilterTarget(target) + + when: + genXmlSnippet(markupBuilder) { JPAMetadataResolverServiceImpl.cast(metadataResolverService).constructXmlNodeForFilter(filter, it) } + + then: + generatedXmlIsTheSameAsExpectedXml('/conf/2268-script.xml', domBuilder.parseText(writer.toString())) + } + def 'test generating EntityAttributesFilter xml snippet with condition script'() { given: def filter = testObjectGenerator.entityAttributesFilterWithConditionScript() @@ -506,6 +551,34 @@ class JPAMetadataResolverServiceImplTests extends AbstractBaseDataJpaTest { !DiffBuilder.compare(Input.fromStream(this.class.getResourceAsStream('/metadata/984-3-expected.xml'))).withTest(Input.fromString(openSamlObjects.marshalToXmlString(ed))).ignoreComments().ignoreWhitespace().build().hasDifferences() } + private EncryptionMethod getEncryptionMethod(String algorithm){ + EncryptionMethod encryptionMethod = new EncryptionMethod() + encryptionMethod.setElementLocalName(EncryptionMethod.DEFAULT_ELEMENT_LOCAL_NAME) + encryptionMethod.setNamespacePrefix(SAMLConstants.SAML20MD_PREFIX) + encryptionMethod.setNamespaceURI(SAMLConstants.SAML20MD_NS) + encryptionMethod.setSchemaLocation(SAMLConstants.SAML20MD_SCHEMA_LOCATION) + encryptionMethod.setAlgorithm(algorithm) + return encryptionMethod + } + + private AlgorithmDigestMethod getDigestMethod(String algorithm) { + AlgorithmDigestMethod dm = new AlgorithmDigestMethod() + dm.setNamespaceURI(SAMLConstants.SAML20ALG_NS) + dm.setElementLocalName(AlgorithmDigestMethod.DEFAULT_ELEMENT_LOCAL_NAME) + dm.setNamespacePrefix(SAMLConstants.SAML20ALG_PREFIX) + dm.setAlgorithm(algorithm) + return dm + } + + private SignatureDigestMethod getSignatureDigestMethod(String algorithm) { + SignatureDigestMethod dm = new SignatureDigestMethod() + dm.setNamespaceURI(SignatureConstants.XMLSIG_NS) + dm.setElementLocalName(SignatureDigestMethod.DEFAULT_ELEMENT_LOCAL_NAME) + dm.setNamespacePrefix(SignatureConstants.XMLSIG_PREFIX) + dm.setAlgorithm(algorithm) + return dm + } + 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 7b2865462..972120ab4 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 @@ -17,6 +17,7 @@ import edu.internet2.tier.shibboleth.admin.ui.domain.filters.NameIdFormatFilter import edu.internet2.tier.shibboleth.admin.ui.domain.filters.NameIdFormatFilterTarget 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.filters.AlgorithmFilter 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.resolvers.ClasspathMetadataResource @@ -182,6 +183,14 @@ class TestObjectGenerator { randomFilter } + static AlgorithmFilter algorithmFilter() { + return new AlgorithmFilter().with { + it.name = "Algorithm" + it.enabled = true; + it + } + } + SignatureValidationFilter signatureValidationFilter() { new SignatureValidationFilter().with { it.name = 'SignatureValidation' diff --git a/backend/src/test/resources/META-INF/services/org.opensaml.core.config.Initializer b/backend/src/test/resources/META-INF/services/org.opensaml.core.config.Initializer new file mode 100644 index 000000000..c7c2e7cb3 --- /dev/null +++ b/backend/src/test/resources/META-INF/services/org.opensaml.core.config.Initializer @@ -0,0 +1 @@ +edu.internet2.tier.shibboleth.admin.ui.opensaml.config.JPAXMLObjectProviderInitializerForTest \ No newline at end of file diff --git a/backend/src/test/resources/conf/2268-entity.xml b/backend/src/test/resources/conf/2268-entity.xml new file mode 100644 index 000000000..daf8c5fac --- /dev/null +++ b/backend/src/test/resources/conf/2268-entity.xml @@ -0,0 +1,22 @@ + + + + + + + https://broken.example.org/sp + https://also-broken.example.org/sp + + + \ No newline at end of file diff --git a/backend/src/test/resources/conf/2268-script.xml b/backend/src/test/resources/conf/2268-script.xml new file mode 100644 index 000000000..3283e883c --- /dev/null +++ b/backend/src/test/resources/conf/2268-script.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/backend/src/test/resources/jpa-encryption-config.xml b/backend/src/test/resources/jpa-encryption-config.xml new file mode 100644 index 000000000..05adde279 --- /dev/null +++ b/backend/src/test/resources/jpa-encryption-config.xml @@ -0,0 +1,284 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backend/src/test/resources/jpa-saml2-metadata-algorithm-config.xml b/backend/src/test/resources/jpa-saml2-metadata-algorithm-config.xml new file mode 100644 index 000000000..c37c788c1 --- /dev/null +++ b/backend/src/test/resources/jpa-saml2-metadata-algorithm-config.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/backend/src/test/resources/jpa-saml2-metadata-config.xml b/backend/src/test/resources/jpa-saml2-metadata-config.xml new file mode 100644 index 000000000..b008809ce --- /dev/null +++ b/backend/src/test/resources/jpa-saml2-metadata-config.xml @@ -0,0 +1,372 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/backend/src/test/resources/jpa-saml2-metadata-ds-config.xml b/backend/src/test/resources/jpa-saml2-metadata-ds-config.xml new file mode 100644 index 000000000..3a0eed8f0 --- /dev/null +++ b/backend/src/test/resources/jpa-saml2-metadata-ds-config.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/backend/src/test/resources/jpa-signature-config.xml b/backend/src/test/resources/jpa-signature-config.xml new file mode 100644 index 000000000..9a8da32e8 --- /dev/null +++ b/backend/src/test/resources/jpa-signature-config.xml @@ -0,0 +1,313 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file