Skip to content

Commit

Permalink
Merge branch 'master' of bitbucket.org:unicon/shib-idp-ui into bugfix…
Browse files Browse the repository at this point in the history
…/SHIBUI-1002
  • Loading branch information
Jodie Muramoto committed Dec 11, 2018
2 parents 166f86b + de6a5ef commit 4cb61ba
Show file tree
Hide file tree
Showing 26 changed files with 285 additions and 37 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
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.filters.EntityAttributesFilter
import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilterTarget
import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityRoleWhiteListFilter
Expand Down Expand Up @@ -52,6 +53,9 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService {
@Autowired
private MetadataResolversPositionOrderContainerService resolversPositionOrderContainerService

@Autowired
private ShibUIConfiguration shibUIConfiguration

// TODO: enhance
@Override
void reloadFilters(String metadataResolverResourceId) {
Expand All @@ -64,6 +68,13 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService {

List<MetadataFilter> metadataFilters = new ArrayList<>()

// set up namespace protection
if (shibUIConfiguration.protectedAttributeNamespaces && shibUIConfiguration.protectedAttributeNamespaces.size() > 0) {
def target = new org.opensaml.saml.metadata.resolver.filter.impl.EntityAttributesFilter()
target.attributeFilter = new ScriptedPredicate(new EvaluableScript(protectedNamespaceScript()))
metadataFilters.add(target)
}

for (edu.internet2.tier.shibboleth.admin.ui.domain.filters.MetadataFilter metadataFilter : jpaMetadataResolver.getMetadataFilters()) {
if (metadataFilter instanceof EntityAttributesFilter) {
EntityAttributesFilter entityAttributesFilter = (EntityAttributesFilter) metadataFilter
Expand Down Expand Up @@ -104,6 +115,21 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService {
}
}

private String protectedNamespaceScript() {
return """(function (attribute) {
"use strict";
var namespaces = [${shibUIConfiguration.protectedAttributeNamespaces.collect({"\"${it}\""}).join(', ')}];
// check the parameter
if (attribute === null) { return true; }
for (var i in namespaces) {
if (attribute.getName().startsWith(namespaces[i])) {
return false;
}
}
return true;
}(input));"""
}

private class ScriptedPredicate extends net.shibboleth.utilities.java.support.logic.ScriptedPredicate<EntityDescriptor> {
protected ScriptedPredicate(@Nonnull EvaluableScript theScript) {
super(theScript)
Expand Down Expand Up @@ -133,9 +159,18 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService {
if ((mr.type != 'BaseMetadataResolver') && (mr.enabled)) {
constructXmlNodeForResolver(mr, delegate) {
//TODO: enhance
def didNamespaceProtectionFilter = !(shibUIConfiguration.protectedAttributeNamespaces && shibUIConfiguration.protectedAttributeNamespaces.size() > 0)
mr.metadataFilters.each { edu.internet2.tier.shibboleth.admin.ui.domain.filters.MetadataFilter filter ->
if (filter instanceof EntityAttributesFilter && !didNamespaceProtectionFilter) {
constructXmlNodeForEntityAttributeNamespaceProtection(delegate)
didNamespaceProtectionFilter = true
}
constructXmlNodeForFilter(filter, delegate)
}
if (!didNamespaceProtectionFilter) {
constructXmlNodeForEntityAttributeNamespaceProtection(delegate)
didNamespaceProtectionFilter = true
}
}
}
}
Expand All @@ -144,6 +179,16 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService {
}
}

void constructXmlNodeForEntityAttributeNamespaceProtection(def markupBuilderDelegate) {
markupBuilderDelegate.MetadataFilter('xsi:type': 'EntityAttributes') {
AttributeFilterScript() {
Script() {
mkp.yieldUnescaped("\n<![CDATA[\n${protectedNamespaceScript()}\n]]>\n")
}
}
}
}

void constructXmlNodeForFilter(SignatureValidationFilter filter, def markupBuilderDelegate) {
if(filter.xmlShouldBeGenerated()) {
markupBuilderDelegate.MetadataFilter(id: filter.name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
import javax.servlet.http.HttpServletRequest;

@Configuration
@EnableConfigurationProperties(CustomPropertiesConfiguration.class)
@EnableConfigurationProperties({CustomPropertiesConfiguration.class, ShibUIConfiguration.class})
public class CoreShibUiConfiguration {
private static final Logger logger = LoggerFactory.getLogger(CoreShibUiConfiguration.class);

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

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

import java.util.List;

@Configuration
@ConfigurationProperties(prefix = "shibui")
@Getter
@Setter
public class ShibUIConfiguration {
/**
* A list of namespaces that should be excluded from incoming metadata. This is used to prevent third party metadata
* sources from using attributes that they might not have the rights to use.
*/
private List<String> protectedAttributeNamespaces;
}
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,8 @@
"step": 0.01
},
"placeholder": "label.real-number",
"minimum": 0,
"maximum": 1,
"minimum": 0.01,
"maximum": 0.99,
"default": null
},
"minCacheDuration": {
Expand Down Expand Up @@ -222,6 +222,29 @@
"default": null,
"pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$"
},
"removeIdleEntityData": {
"title": "label.remove-idle-entity-data",
"description": "tooltip.remove-idle-entity-data",
"type": "boolean",
"widget": {
"id": "boolean-radio"
},
"oneOf": [
{
"enum": [
true
],
"description": "value.true"
},
{
"enum": [
false
],
"description": "value.false"
}
],
"default": true
},
"cleanupTaskInterval": {
"title": "label.cleanup-task-interval",
"description": "tooltip.cleanup-task-interval",
Expand Down
6 changes: 3 additions & 3 deletions backend/src/main/resources/i18n/messages_en.properties
Original file line number Diff line number Diff line change
Expand Up @@ -483,9 +483,9 @@ tooltip.enable-provider-upon-saving=If checkbox is clicked, the metadata provide
tooltip.max-validity-interval=Defines the window within which the metadata is valid.
tooltip.require-signed-root=If true, this fails to load metadata with no signature on the root XML element.
tooltip.certificate-file=A key used to verify the signature. Conflicts with trustEngineRef and both of the child elements.
tooltip.retained-roles=Controls whether to keep entity descriptors that contain no roles
tooltip.remove-roleless-entity-descriptors=Controls whether to keep entity descriptors that contain no roles.
tooltip.remove-empty-entities-descriptors=Controls whether to keep entities descriptors that contain no entity descriptors.
tooltip.retained-roles=Note that property replacement cannot be used on this element.
tooltip.remove-roleless-entity-descriptors=Controls whether to keep entity descriptors that contain no roles. Note: If this attribute is set to false, the resulting output may not be schema-valid since an <md:EntityDescriptor> element must include at least one role descriptor.
tooltip.remove-empty-entities-descriptors=Controls whether to keep entities descriptors that contain no entity descriptors. Note: If this attribute is set to false, the resulting output may not be schema-valid since an <md:EntitiesDescriptor> element must include at least one child element, either an <md:EntityDescriptor> element or an <md:EntitiesDescriptor> element.

tooltip.min-refresh-delay=Lower bound on the next refresh from the time calculated based on the metadata\u0027s expiration.
tooltip.max-refresh-delay=Upper bound on the next refresh from the time calculated based on the metadata\u0027s expiration.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"properties": {
"name": {
"title": "label.metadata-provider-name-display-only",
"description": "tooltip.metadata-provider-name",
"description": "tooltip.metadata-provider-name-display-only",
"type": "string",
"widget": {
"id": "string",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import edu.internet2.tier.shibboleth.admin.ui.configuration.Internationalization
import edu.internet2.tier.shibboleth.admin.ui.configuration.MetadataResolverConverterConfiguration
import edu.internet2.tier.shibboleth.admin.ui.configuration.PlaceholderResolverComponentsConfiguration
import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration
import edu.internet2.tier.shibboleth.admin.ui.configuration.ShibUIConfiguration
import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilter
import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilterTarget
import edu.internet2.tier.shibboleth.admin.ui.domain.filters.RequiredValidUntilFilter
Expand Down Expand Up @@ -42,7 +43,14 @@ import org.springframework.test.annotation.DirtiesContext
import org.springframework.test.context.ContextConfiguration
import org.xmlunit.builder.DiffBuilder
import org.xmlunit.builder.Input
import spock.lang.Ignore
import spock.lang.Specification
import spock.lang.Unroll

import javax.xml.transform.OutputKeys
import javax.xml.transform.TransformerFactory
import javax.xml.transform.dom.DOMSource
import javax.xml.transform.stream.StreamResult

import static edu.internet2.tier.shibboleth.admin.ui.util.TestHelpers.generatedXmlIsTheSameAsExpectedXml

Expand Down Expand Up @@ -74,6 +82,9 @@ class JPAMetadataResolverServiceImplTests extends Specification {
@Autowired
MetadataResolverConverterService mdrConverterService

@Autowired
ShibUIConfiguration shibUIConfiguration

TestObjectGenerator testObjectGenerator

DOMBuilder domBuilder
Expand Down Expand Up @@ -349,6 +360,51 @@ class JPAMetadataResolverServiceImplTests extends Specification {
generatedXmlIsTheSameAsExpectedXml('/conf/704.3.xml', domBuilder.parseText(writer.toString()))
}

@Unroll
@DirtiesContext(methodMode = DirtiesContext.MethodMode.AFTER_METHOD)
def 'test namespace protection [#namespaces]'() {
setup:
shibUIConfiguration.protectedAttributeNamespaces = namespaces
def resolver = new DynamicHttpMetadataResolver().with {
it.xmlId = 'DynamicHttpMetadataResolver'
it.metadataRequestURLConstructionScheme = new MetadataQueryProtocolScheme().with {
it.content = 'http://mdq-beta.incommon.org/global'
it
}
it
}
metadataResolverRepository.save(resolver)

expect:
generatedXmlIsTheSameAsExpectedXml(filename, metadataResolverService.generateConfiguration())

where:
namespaces | filename
['http://shibboleth.net/ns/profiles'] | '/conf/984.xml'
['http://shibboleth.net/ns/profiles', 'http://scaldingspoon.com/iam'] | '/conf/984-2.xml'
}

@Ignore('there is a bug in org.opensaml.saml.metadata.resolver.filter.impl.EntityAttributesFilter.applyFilter')
def 'test namespace protection internal filtering'() {
setup:
shibUIConfiguration.protectedAttributeNamespaces = ['http://shibboleth.net/ns/profiles']
def resolver = new edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.ResourceBackedMetadataResolver().with {
it.resourceId = 'testme984'
it.name = 'testme984'
it.classpathMetadataResource = new ClasspathMetadataResource('metadata/984-3.xml')
it
}
def x = metadataResolverRepository.save(resolver)
((OpenSamlChainingMetadataResolver)metadataResolver).resolvers.add(mdrConverterService.convertToOpenSamlRepresentation(x))

when:
metadataResolverService.reloadFilters('testme984')
def ed = metadataResolver.resolveSingle(new CriteriaSet(new EntityIdCriterion('http://test.scaldingspoon.org/test1')))

then:
!DiffBuilder.compare(Input.fromStream(this.class.getResourceAsStream('/metadata/984-3-expected.xml'))).withTest(Input.fromString(openSamlObjects.marshalToXmlString(ed))).ignoreComments().ignoreWhitespace().build().hasDifferences()
}

static genXmlSnippet(MarkupBuilder xml, Closure xmlNodeGenerator) {
xml.MetadataProvider('id': 'ShibbolethMetadata',
'xmlns': 'urn:mace:shibboleth:2.0:metadata',
Expand Down
33 changes: 33 additions & 0 deletions backend/src/test/resources/conf/984-2.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<MetadataProvider xmlns="urn:mace:shibboleth:2.0:metadata" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
id="ShibbolethMetadata"
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"
xsi:type="ChainingMetadataProvider">
<MetadataProvider backgroundInitializationFromCacheDelay="PT2S" cleanupTaskInterval="PT30M"
connectionRequestTimeout="PT5S" connectionTimeout="PT5S" id="DynamicHttpMetadataResolver"
maxCacheDuration="PT8H" maxConnectionsPerRoute="100" maxConnectionsTotal="100"
maxIdleEntityData="PT8H" minCacheDuration="PT10M" refreshDelayFactor="0.75"
removeIdleEntityData="true" socketTimeout="PT5S" xsi:type="DynamicHttpMetadataProvider">
<MetadataQueryProtocol>http://mdq-beta.incommon.org/global</MetadataQueryProtocol>
<MetadataFilter xsi:type="EntityAttributes">
<AttributeFilterScript>
<Script>
<![CDATA[
(function (attribute) {
"use strict";
var namespaces = ["http://shibboleth.net/ns/profiles", "http://scaldingspoon.com/iam"];
// check the parameter
if (attribute === null) { return true; }
for (var i in namespaces) {
if (attribute.getName().startsWith(namespaces[i])) {
return false;
}
}
return true;
}(input));
]]>
</Script>
</AttributeFilterScript>
</MetadataFilter>
</MetadataProvider>
</MetadataProvider>
33 changes: 33 additions & 0 deletions backend/src/test/resources/conf/984.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<MetadataProvider xmlns="urn:mace:shibboleth:2.0:metadata" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
id="ShibbolethMetadata"
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"
xsi:type="ChainingMetadataProvider">
<MetadataProvider backgroundInitializationFromCacheDelay="PT2S" cleanupTaskInterval="PT30M"
connectionRequestTimeout="PT5S" connectionTimeout="PT5S" id="DynamicHttpMetadataResolver"
maxCacheDuration="PT8H" maxConnectionsPerRoute="100" maxConnectionsTotal="100"
maxIdleEntityData="PT8H" minCacheDuration="PT10M" refreshDelayFactor="0.75"
removeIdleEntityData="true" socketTimeout="PT5S" xsi:type="DynamicHttpMetadataProvider">
<MetadataQueryProtocol>http://mdq-beta.incommon.org/global</MetadataQueryProtocol>
<MetadataFilter xsi:type="EntityAttributes">
<AttributeFilterScript>
<Script>
<![CDATA[
(function (attribute) {
"use strict";
var namespaces = ["http://shibboleth.net/ns/profiles"];
// check the parameter
if (attribute === null) { return true; }
for (var i in namespaces) {
if (attribute.getName().startsWith(namespaces[i])) {
return false;
}
}
return true;
}(input));
]]>
</Script>
</AttributeFilterScript>
</MetadataFilter>
</MetadataProvider>
</MetadataProvider>
20 changes: 20 additions & 0 deletions backend/src/test/resources/metadata/984-3-expected.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0"?>
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"
xmlns:mdattr="urn:oasis:names:tc:SAML:metadata:attribute"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
entityID="http://test.scaldingspoon.org/test1">
<md:Extensions>
<mdattr:EntityAttributes>
<saml:Attribute Name="http://scaldingspoon.org/realm"
NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
<saml:AttributeValue>internal</saml:AttributeValue>
</saml:Attribute>
</mdattr:EntityAttributes>
</md:Extensions>
<md:SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat>
<md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Location="https://test.scaldingspoon.org/test1/acs"
index="1"/>
</md:SPSSODescriptor>
</md:EntityDescriptor>
Loading

0 comments on commit 4cb61ba

Please sign in to comment.