Skip to content

Commit

Permalink
Merged in feature/shibui-2268 (pull request #603)
Browse files Browse the repository at this point in the history
Feature/shibui 2268

Approved-by: Dmitriy Kopylenko
Approved-by: Bill Smith
Former-commit-id: 105ca18483cddc45cb75d5953b9efb9f135efe2d
  • Loading branch information
chasegawa committed Aug 24, 2022
2 parents 885a85b + 68d6fb5 commit e00de96
Show file tree
Hide file tree
Showing 52 changed files with 2,338 additions and 125 deletions.
6 changes: 6 additions & 0 deletions backend/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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')
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ class SeleniumSIDETest extends Specification {
'SHIBUI-1674: Verify metadata source tooltips' | '/SHIBUI-1674-1.side'
'SHIBUI-1674: Verify metadata provider tooltips' | '/SHIBUI-1674-2.side'
'SHIBUI-1674: Verify advanced menu tooltips' | '/SHIBUI-1674-3.side'
'SHIBUI-2268: Verify Algorithm Filter' | '/SHIBUI-2268.side'
'SHIBUI-2269: Verify XML generation of external filters' | '/SHIBUI-2269.side'
}
}
Original file line number Diff line number Diff line change
@@ -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)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@ 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
import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityRoleWhiteListFilter
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
Expand Down Expand Up @@ -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.algorithmFilterTargetType) {
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<![CDATA[\n${script}\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<![CDATA[\n${script}\n]]>\n")
// }
// }
// } else {
// mkp.yieldUnescaped(openSamlObjects.marshalToXmlString(xmlObject, false))
// }
// }
// }
}
}

void constructXmlNodeForFilter(EntityAttributesFilter filter, def markupBuilderDelegate) {
if (!filter.isFilterEnabled()) { return }
markupBuilderDelegate.MetadataFilter('xsi:type': 'EntityAttributes') {
Expand All @@ -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
Expand Down Expand Up @@ -485,6 +550,45 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService {
}
}

@Override
Document generateSingleMetadataConfiguration(edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver mr) {
new StringWriter().withCloseable { writer ->
def xml = new MarkupBuilder(writer)
xml.omitEmptyAttributes = true
xml.omitNullAttributes = true

xml.MetadataProvider(id: 'ShibbolethIdPUIGeneratedMetadata',
xmlns: 'urn:mace:shibboleth:2.0:metadata',
'xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
'xsi:type': 'ChainingMetadataProvider',
'xsi:schemaLocation': 'urn:mace:shibboleth:2.0:metadata http://shibboleth.net/schema/idp/shibboleth-metadata.xsd urn:mace:shibboleth:2.0:resource http://shibboleth.net/schema/idp/shibboleth-resource.xsd urn:mace:shibboleth:2.0:security http://shibboleth.net/schema/idp/shibboleth-security.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:2.0:assertion http://docs.oasis-open.org/security/saml/v2.0/saml-schema-assertion-2.0.xsd'
) {
// We do not currently marshall the internal incommon chaining resolver (with BaseMetadataResolver type)
// We do not want to include the custom type: ExternalMetadataResolver
if ((mr.type != 'BaseMetadataResolver') && (mr.type != 'ExternalMetadataResolver') && (mr.enabled)) {
constructXmlNodeForResolver(mr, delegate) {
//TODO: enhance
def didNamespaceProtectionFilter = !(shibUIConfiguration.protectedAttributeNamespaces && shibUIConfiguration.protectedAttributeNamespaces.size() > 0)
def doNamespaceProtectionFilter = { def filter ->
if (mr.type in ['FileBackedMetadataResolver', 'DynamicHttpMetadataResolver'] && (filter == null || filter instanceof EntityAttributesFilter) && !didNamespaceProtectionFilter) {
constructXmlNodeForEntityAttributeNamespaceProtection(delegate)
didNamespaceProtectionFilter = true
}
}
mr.metadataFilters.each { edu.internet2.tier.shibboleth.admin.ui.domain.filters.MetadataFilter filter ->
if (filter.isFilterEnabled()) {
doNamespaceProtectionFilter()
constructXmlNodeForFilter(filter, delegate)
}
}
doNamespaceProtectionFilter()
}
}
}
return DOMBuilder.newInstance().parseText(writer.toString())
}
}

@Override
Document generateExternalMetadataFilterConfiguration() {
// TODO: this can probably be a better writer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@

import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
Expand Down Expand Up @@ -102,6 +103,22 @@ public ResponseEntity<?> getXml() throws IOException, TransformerException {
}
}

@GetMapping(value = "/MetadataResolvers/{resourceId}", produces = "application/xml")
@Transactional(readOnly = true)
public ResponseEntity<?> getOneXml(@PathVariable String resourceId) throws TransformerException {
MetadataResolver resolver = resolverRepository.findByResourceId(resourceId);
if (resolver == null) {
return ResponseEntity.notFound().build();
}
StringWriter writer = new StringWriter();
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");

transformer.transform(new DOMSource(metadataResolverService.generateSingleMetadataConfiguration(resolver)), new StreamResult(writer));
return ResponseEntity.ok(writer.toString());
}

@GetMapping(value = "/MetadataResolvers/External", produces = "application/xml")
@Transactional(readOnly = true)
public ResponseEntity<?> getExternalXml() throws IOException, TransformerException {
Expand Down
Original file line number Diff line number Diff line change
@@ -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?
}

}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Loading

0 comments on commit e00de96

Please sign in to comment.