[java-identity-provider] branch master updated: IDP-1142 - Metadata-driven configuration strategy

Scott Cantor cantor.2 at osu.edu
Tue Mar 28 15:42:52 EDT 2017


This is an automated email from the git hooks/post-receive script.

scantor pushed a commit to branch master
in repository java-identity-provider.

View the commit online:
http://git.shibboleth.net/view/?p=java-identity-provider.git;a=commit;h=04b9b0ec7fe5e4dfdfa6769661ccb1fba4784912

The following commit(s) were added to refs/heads/master by this push:
       new  04b9b0e   IDP-1142 -  Metadata-driven configuration strategy
04b9b0e is described below

commit 04b9b0ec7fe5e4dfdfa6769661ccb1fba4784912
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Tue Mar 28 15:39:44 2017 -0400

    IDP-1142 -  Metadata-driven configuration strategy
    
    https://issues.shibboleth.net/jira/browse/IDP-1142
---
 .../system/conf/relying-party-mddriven.xml         | 479 +++++++++++++++++++++
 .../resources/system/conf/relying-party-system.xml |  71 ++-
 .../src/test/resources/conf/metadata-providers.xml |   4 +
 idp-conf/src/test/resources/conf/relying-party.xml |  53 +--
 ...tractCollectionConfigurationLookupStrategy.java | 142 ++++++
 ...tMetadataDrivenConfigurationLookupStrategy.java | 346 +++++++++++++++
 .../config/AbstractSAMLProfileConfiguration.java   |   2 +-
 .../config/BeanConfigurationLookupStrategy.java    | 145 +++++++
 .../config/BooleanConfigurationLookupStrategy.java | 101 +++++
 .../config/DoubleConfigurationLookupStrategy.java  |  93 ++++
 .../DurationConfigurationLookupStrategy.java       | 116 +++++
 .../config/IntegerConfigurationLookupStrategy.java |  92 ++++
 .../config/ListConfigurationLookupStrategy.java    |  67 +++
 .../config/LongConfigurationLookupStrategy.java    |  98 +++++
 .../config/SetConfigurationLookupStrategy.java     |  68 +++
 .../config/StringConfigurationLookupStrategy.java  | 102 +++++
 16 files changed, 1896 insertions(+), 83 deletions(-)

diff --git a/idp-conf/src/main/resources/system/conf/relying-party-mddriven.xml b/idp-conf/src/main/resources/system/conf/relying-party-mddriven.xml
new file mode 100644
index 0000000..0c30461
--- /dev/null
+++ b/idp-conf/src/main/resources/system/conf/relying-party-mddriven.xml
@@ -0,0 +1,479 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:context="http://www.springframework.org/schema/context"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xmlns:p="http://www.springframework.org/schema/p"
+       xmlns:c="http://www.springframework.org/schema/c"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
+                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
+                           
+       default-init-method="initialize"
+       default-destroy-method="destroy">
+       
+    <!--
+    This is ALOT of wiring to pre-configure support for virtually system-wide support for deriving relying-party
+    settings and profile settings through examination of metadata tags.
+    -->
+
+    <!-- Parent beans for defining metadata-driven config lookup strategies. -->
+
+    <bean id="RelyingParty.MDDriven" abstract="true" parent="RelyingParty">
+        <property name="responderIdLookupStrategy">
+            <bean parent="shibboleth.MDDrivenStringProperty" p:propertyName="responderId" />
+        </property>
+        <property name="profileConfigurationsLookupStrategy">
+            <bean parent="shibboleth.MDDrivenBeanProperty" p:propertyName="profileConfigurations"
+                p:propertyType="#{T(java.util.Map)}" />
+        </property>
+        <property name="detailedErrorsPredicate">
+            <bean class="net.shibboleth.utilities.java.support.logic.PredicateSupport" factory-method="fromFunction">
+                <constructor-arg>
+                    <bean parent="shibboleth.MDDrivenBoolProperty" p:propertyName="detailedErrors" />
+                </constructor-arg>
+                <constructor-arg value="%{idp.errors.detailed:false}" />
+            </bean>
+        </property>
+    </bean>
+    
+    <!-- Parent beans for metadata-driven RelyingParty helpers. -->
+
+    <bean id="RelyingPartyByName" parent="RelyingParty.MDDriven" abstract="true"
+        class="net.shibboleth.idp.saml.relyingparty.impl.RelyingPartyConfigurationSupport" factory-method="byName" />
+
+    <bean id="RelyingPartyByGroup" parent="RelyingParty.MDDriven" abstract="true"
+        class="net.shibboleth.idp.saml.relyingparty.impl.RelyingPartyConfigurationSupport" factory-method="byGroup" />
+
+    <bean id="RelyingPartyByTag" parent="RelyingParty.MDDriven" abstract="true"
+        class="net.shibboleth.idp.saml.relyingparty.impl.RelyingPartyConfigurationSupport" factory-method="byTag"
+        c:trim="false" c:matchAll="false" />
+    
+    <bean id="shibboleth.AbstractMDDrivenProperty" abstract="true"
+        p:metadataLookupStrategy-ref="shibboleth.MDDrivenMetadataLookup"
+        p:profileAliases="#{getObject('shibboleth.MDProfileAliases') ?: getObject('shibboleth.DefaultMDProfileAliases')}" />
+
+    <bean id="shibboleth.DefaultMDProfileAliases"
+            class="org.springframework.beans.factory.config.ListFactoryBean">
+        <property name="sourceList">
+            <list>
+                <value>http://shibboleth.net/ns/profiles</value>
+            </list>
+        </property>
+    </bean>
+
+    <bean id="shibboleth.MDDrivenMetadataLookup" class="com.google.common.base.Functions" factory-method="compose">
+        <constructor-arg name="g">
+            <bean class="org.opensaml.saml.common.messaging.context.navigate.EntityDescriptorLookupFunction" />
+        </constructor-arg>
+        <constructor-arg name="f">
+            <bean class="net.shibboleth.idp.saml.profile.context.navigate.SAMLMetadataContextLookupFunction" />
+        </constructor-arg>
+    </bean>
+
+    <bean id="shibboleth.MDDrivenStringProperty" abstract="true" parent="shibboleth.AbstractMDDrivenProperty"
+        class="net.shibboleth.idp.saml.profile.config.StringConfigurationLookupStrategy" />
+    <bean id="shibboleth.MDDrivenBoolProperty" abstract="true" parent="shibboleth.AbstractMDDrivenProperty"
+        class="net.shibboleth.idp.saml.profile.config.BooleanConfigurationLookupStrategy" />
+    <bean id="shibboleth.MDDrivenIntProperty" abstract="true" parent="shibboleth.AbstractMDDrivenProperty"
+        class="net.shibboleth.idp.saml.profile.config.IntegerConfigurationLookupStrategy" />
+    <bean id="shibboleth.MDDrivenLongProperty" abstract="true" parent="shibboleth.AbstractMDDrivenProperty"
+        class="net.shibboleth.idp.saml.profile.config.LongConfigurationLookupStrategy" />
+    <bean id="shibboleth.MDDrivenDoubleProperty" abstract="true" parent="shibboleth.AbstractMDDrivenProperty"
+        class="net.shibboleth.idp.saml.profile.config.DoubleConfigurationLookupStrategy" />
+    <bean id="shibboleth.MDDrivenDurationProperty" abstract="true" parent="shibboleth.AbstractMDDrivenProperty"
+        class="net.shibboleth.idp.saml.profile.config.DurationConfigurationLookupStrategy" />
+    <bean id="shibboleth.MDDrivenListProperty" abstract="true" parent="shibboleth.AbstractMDDrivenProperty"
+        class="net.shibboleth.idp.saml.profile.config.ListConfigurationLookupStrategy"
+        p:propertyType="#{T(java.lang.String)}" />
+    <bean id="shibboleth.MDDrivenSetProperty" abstract="true" parent="shibboleth.AbstractMDDrivenProperty"
+        class="net.shibboleth.idp.saml.profile.config.SetConfigurationLookupStrategy"
+        p:propertyType="#{T(java.lang.String)}" />
+    <bean id="shibboleth.MDDrivenBeanProperty" abstract="true" parent="shibboleth.AbstractMDDrivenProperty"
+        class="net.shibboleth.idp.saml.profile.config.BeanConfigurationLookupStrategy" />
+
+    <bean id="AbstractMDDrivenProfile" parent="AbstractProfileConfig" abstract="true">
+        <property name="securityConfigurationLookupStrategy">
+            <bean parent="shibboleth.MDDrivenBeanProperty" p:propertyName="securityConfiguration"
+                p:propertyType="#{T(net.shibboleth.idp.profile.config.SecurityConfiguration)}" />
+        </property>
+        <property name="disallowedFeaturesLookupStrategy">
+            <bean parent="shibboleth.MDDrivenIntProperty" p:propertyName="disallowedFeatures" />
+        </property>
+    </bean>
+    
+    <bean id="AbstractMDDrivenSAMLProfile" parent="AbstractMDDrivenProfile" abstract="true">
+        <property name="signRequests">
+            <bean class="net.shibboleth.utilities.java.support.logic.PredicateSupport" factory-method="fromFunction">
+                <constructor-arg>
+                    <bean parent="shibboleth.MDDrivenBoolProperty" p:propertyName="signRequests" />
+                </constructor-arg>
+                <constructor-arg value="false" />
+            </bean>
+        </property>
+        <property name="signResponses">
+            <bean class="net.shibboleth.utilities.java.support.logic.PredicateSupport" factory-method="fromFunction">
+                <constructor-arg>
+                    <bean parent="shibboleth.MDDrivenBoolProperty" p:propertyName="signResponses" />
+                </constructor-arg>
+                <constructor-arg value="false" />
+            </bean>
+        </property>
+        <property name="signAssertions">
+            <bean class="net.shibboleth.utilities.java.support.logic.PredicateSupport" factory-method="fromFunction">
+                <constructor-arg>
+                    <bean parent="shibboleth.MDDrivenBoolProperty" p:propertyName="signAssertions" />
+                </constructor-arg>
+                <constructor-arg value="false" />
+            </bean>
+        </property>
+        <property name="includeConditionsNotBeforePredicate">
+            <bean class="net.shibboleth.utilities.java.support.logic.PredicateSupport" factory-method="fromFunction">
+                <constructor-arg>
+                    <bean parent="shibboleth.MDDrivenBoolProperty" p:propertyName="includeConditionsNotBefore" />
+                </constructor-arg>
+                <constructor-arg value="true" />
+            </bean>
+        </property>
+        <property name="assertionLifetimeLookupStrategy">
+            <bean parent="shibboleth.MDDrivenDurationProperty" p:propertyName="assertionLifetime" />
+        </property>
+        <property name="assertionAudiencesLookupStrategy">
+            <bean parent="shibboleth.MDDrivenSetProperty" p:propertyName="assertionAudiences" />
+        </property>
+    </bean>
+    
+    <bean id="AbstractMDDrivenSAML2Profile" parent="AbstractMDDrivenSAMLProfile" abstract="true">
+        <property name="encryptionOptionalPredicate">
+            <bean class="net.shibboleth.utilities.java.support.logic.PredicateSupport" factory-method="fromFunction">
+                <constructor-arg>
+                    <bean parent="shibboleth.MDDrivenBoolProperty" p:propertyName="encryptionOptional" />
+                </constructor-arg>
+                <constructor-arg value="%{idp.encryption.optional:false}" />
+            </bean>
+        </property>
+        <property name="encryptAssertions">
+            <bean class="net.shibboleth.utilities.java.support.logic.PredicateSupport" factory-method="fromFunction">
+                <constructor-arg>
+                    <bean parent="shibboleth.MDDrivenBoolProperty" p:propertyName="encryptAssertions" />
+                </constructor-arg>
+                <constructor-arg value="false" />
+            </bean>
+        </property>
+        <property name="encryptNameIDs">
+            <bean class="net.shibboleth.utilities.java.support.logic.PredicateSupport" factory-method="fromFunction">
+                <constructor-arg>
+                    <bean parent="shibboleth.MDDrivenBoolProperty" p:propertyName="encryptNameIDs" />
+                </constructor-arg>
+                <constructor-arg value="false" />
+            </bean>
+        </property>
+        <property name="encryptAttributes">
+            <bean class="net.shibboleth.utilities.java.support.logic.PredicateSupport" factory-method="fromFunction">
+                <constructor-arg>
+                    <bean parent="shibboleth.MDDrivenBoolProperty" p:propertyName="encryptAttributes" />
+                </constructor-arg>
+                <constructor-arg value="false" />
+            </bean>
+        </property>
+        <property name="proxyCountLookupStrategy">
+            <bean parent="shibboleth.MDDrivenLongProperty" p:propertyName="proxyCount" />
+        </property>
+        <property name="proxyAudiencesLookupStrategy">
+            <bean parent="shibboleth.MDDrivenSetProperty" p:propertyName="proxyAudiences" />
+        </property>
+    </bean>
+
+    <bean id="AbstractMDDrivenCASProfile" parent="AbstractMDDrivenProfile" abstract="true">
+        <property name="resolveAttributesPredicate">
+            <bean class="net.shibboleth.utilities.java.support.logic.PredicateSupport" factory-method="fromFunction">
+                <constructor-arg>
+                    <bean parent="shibboleth.MDDrivenBoolProperty" p:propertyName="resolveAttributes" />
+                </constructor-arg>
+                <constructor-arg value="true" />
+            </bean>
+        </property>
+        <property name="ticketValidityPeriodLookupStrategy">
+            <bean parent="shibboleth.MDDrivenDurationProperty" p:propertyName="ticketValidityPeriod" />
+        </property>
+    </bean>
+
+    <!-- Concrete profile beans. -->
+
+    <bean id="Shibboleth.SSO.MDDriven" parent="AbstractMDDrivenSAMLProfile" lazy-init="true"
+            class="net.shibboleth.idp.saml.saml1.profile.config.BrowserSSOProfileConfiguration"
+            p:artifactConfiguration-ref="shibboleth.DefaultArtifactConfiguration"
+            p:inboundInterceptorFlows="security-policy/shibboleth-sso">
+        <property name="signResponses">
+            <bean class="net.shibboleth.utilities.java.support.logic.PredicateSupport" factory-method="fromFunction">
+                <constructor-arg>
+                    <bean parent="shibboleth.MDDrivenBoolProperty" p:propertyName="signResponses" />
+                </constructor-arg>
+                <constructor-arg value="true" />
+            </bean>
+        </property>
+        <property name="resolveAttributesPredicate">
+            <bean class="net.shibboleth.utilities.java.support.logic.PredicateSupport" factory-method="fromFunction">
+                <constructor-arg>
+                    <bean parent="shibboleth.MDDrivenBoolProperty" p:propertyName="resolveAttributes" />
+                </constructor-arg>
+                <constructor-arg value="true" />
+            </bean>
+        </property>
+        <property name="includeAttributeStatementPredicate">
+            <bean class="net.shibboleth.utilities.java.support.logic.PredicateSupport" factory-method="fromFunction">
+                <constructor-arg>
+                    <bean parent="shibboleth.MDDrivenBoolProperty" p:propertyName="includeAttributeStatement" />
+                </constructor-arg>
+                <constructor-arg value="false" />
+            </bean>
+        </property>
+        <property name="defaultAuthenticationMethodsLookupStrategy">
+            <bean parent="shibboleth.MDDrivenListProperty" p:propertyName="defaultAuthenticationMethods"
+                p:propertyType="#{T(net.shibboleth.idp.saml.authn.principal.AuthenticationMethodPrincipal)}" />
+        </property>
+        <property name="postAuthenticationFlowsLookupStrategy">
+            <bean parent="shibboleth.MDDrivenListProperty" p:propertyName="postAuthenticationFlows" />
+        </property>
+        <property name="nameIDFormatPrecedenceLookupStrategy">
+            <bean parent="shibboleth.MDDrivenListProperty" p:propertyName="nameIDFormatPrecedence" />
+        </property>
+    </bean>
+    
+    <bean id="SAML1.AttributeQuery.MDDriven" parent="AbstractMDDrivenSAMLProfile" lazy-init="true"
+            class="net.shibboleth.idp.saml.saml1.profile.config.AttributeQueryProfileConfiguration"
+            p:inboundInterceptorFlows="security-policy/saml-soap">
+        <property name="signResponses">
+            <bean class="net.shibboleth.utilities.java.support.logic.PredicateSupport" factory-method="fromFunction">
+                <constructor-arg>
+                    <bean parent="shibboleth.MDDrivenBoolProperty" p:propertyName="signResponses" />
+                </constructor-arg>
+                <constructor-arg>
+                    <bean class="org.opensaml.profile.logic.NoIntegrityMessageChannelPredicate" />
+                </constructor-arg>
+            </bean>
+        </property>
+    </bean>
+
+    <bean id="SAML1.ArtifactResolution.MDDriven" parent="AbstractMDDrivenSAMLProfile" lazy-init="true"
+            class="net.shibboleth.idp.saml.saml1.profile.config.ArtifactResolutionProfileConfiguration"
+            p:inboundInterceptorFlows="security-policy/saml-soap">
+        <property name="signResponses">
+            <bean class="net.shibboleth.utilities.java.support.logic.PredicateSupport" factory-method="fromFunction">
+                <constructor-arg>
+                    <bean parent="shibboleth.MDDrivenBoolProperty" p:propertyName="signResponses" />
+                </constructor-arg>
+                <constructor-arg>
+                    <bean class="org.opensaml.profile.logic.NoIntegrityMessageChannelPredicate" />
+                </constructor-arg>
+            </bean>
+        </property>
+    </bean>
+
+    <bean id="SAML2.SSO.MDDriven" parent="AbstractMDDrivenSAML2Profile" lazy-init="true"
+            class="net.shibboleth.idp.saml.saml2.profile.config.BrowserSSOProfileConfiguration"
+            p:artifactConfiguration-ref="shibboleth.DefaultArtifactConfiguration"
+            p:inboundInterceptorFlows="security-policy/saml2-sso">
+        <property name="signResponses">
+            <bean class="net.shibboleth.utilities.java.support.logic.PredicateSupport" factory-method="fromFunction">
+                <constructor-arg>
+                    <bean parent="shibboleth.MDDrivenBoolProperty" p:propertyName="signResponses" />
+                </constructor-arg>
+                <constructor-arg value="true" />
+            </bean>
+        </property>
+        <property name="encryptAssertions">
+            <bean class="net.shibboleth.utilities.java.support.logic.PredicateSupport" factory-method="fromFunction">
+                <constructor-arg>
+                    <bean parent="shibboleth.MDDrivenBoolProperty" p:propertyName="encryptAssertions" />
+                </constructor-arg>
+                <constructor-arg value="true" />
+            </bean>
+        </property>
+        <property name="resolveAttributesPredicate">
+            <bean class="net.shibboleth.utilities.java.support.logic.PredicateSupport" factory-method="fromFunction">
+                <constructor-arg>
+                    <bean parent="shibboleth.MDDrivenBoolProperty" p:propertyName="resolveAttributes" />
+                </constructor-arg>
+                <constructor-arg value="true" />
+            </bean>
+        </property>
+        <property name="includeAttributeStatementPredicate">
+            <bean class="net.shibboleth.utilities.java.support.logic.PredicateSupport" factory-method="fromFunction">
+                <constructor-arg>
+                    <bean parent="shibboleth.MDDrivenBoolProperty" p:propertyName="includeAttributeStatement" />
+                </constructor-arg>
+                <constructor-arg value="true" />
+            </bean>
+        </property>
+        <property name="skipEndpointValidationWhenSignedPredicate">
+            <bean class="net.shibboleth.utilities.java.support.logic.PredicateSupport" factory-method="fromFunction">
+                <constructor-arg>
+                    <bean parent="shibboleth.MDDrivenBoolProperty" p:propertyName="skipEndpointValidationWhenSigned" />
+                </constructor-arg>
+                <constructor-arg value="false" />
+            </bean>
+        </property>
+        <property name="defaultAuthenticationMethodsLookupStrategy">
+            <bean parent="shibboleth.MDDrivenListProperty" p:propertyName="defaultAuthenticationMethods"
+                p:propertyType="#{T(net.shibboleth.idp.saml.authn.principal.AuthnContextClassRefPrincipal)}" />
+        </property>
+        <property name="postAuthenticationFlowsLookupStrategy">
+            <bean parent="shibboleth.MDDrivenListProperty" p:propertyName="postAuthenticationFlows" />
+        </property>
+        <property name="nameIDFormatPrecedenceLookupStrategy">
+            <bean parent="shibboleth.MDDrivenListProperty" p:propertyName="nameIDFormatPrecedence" />
+        </property>
+        <property name="maximumSPSessionLifetimeLookupStrategy">
+            <bean parent="shibboleth.MDDrivenDurationProperty" p:propertyName="maximumSPSessionLifetime" />
+        </property>
+        <property name="allowDelegation">
+            <bean class="net.shibboleth.utilities.java.support.logic.PredicateSupport" factory-method="fromFunction">
+                <constructor-arg>
+                    <bean parent="shibboleth.MDDrivenBoolProperty" p:propertyName="allowDelegation" />
+                </constructor-arg>
+                <constructor-arg value="false" />
+            </bean>
+        </property>
+        <property name="maximumTokenDelegationChainLengthLookupStrategy">
+            <bean parent="shibboleth.MDDrivenLongProperty" p:propertyName="maximumTokenDelegationChainLength" />
+        </property>
+    </bean>
+
+    <bean id="SAML2.ECP.MDDriven" parent="SAML2.SSO.MDDriven" lazy-init="true"
+            class="net.shibboleth.idp.saml.saml2.profile.config.ECPProfileConfiguration"
+            p:inboundInterceptorFlows="security-policy/saml2-ecp">
+        <property name="localEventsLookupStrategy">
+            <bean parent="shibboleth.MDDrivenSetProperty" p:propertyName="localEvents" />
+        </property>
+    </bean>
+
+    <bean id="SAML2.Logout.MDDriven" parent="AbstractMDDrivenSAML2Profile" lazy-init="true"
+            class="net.shibboleth.idp.saml.saml2.profile.config.SingleLogoutProfileConfiguration"
+            p:artifactConfiguration-ref="shibboleth.DefaultArtifactConfiguration"
+            p:inboundInterceptorFlows="security-policy/saml2-slo">
+        <property name="signRequests">
+            <bean class="net.shibboleth.utilities.java.support.logic.PredicateSupport" factory-method="fromFunction">
+                <constructor-arg>
+                    <bean parent="shibboleth.MDDrivenBoolProperty" p:propertyName="signRequests" />
+                </constructor-arg>
+                <constructor-arg>
+                    <bean class="org.opensaml.profile.logic.NoIntegrityMessageChannelPredicate" />
+                </constructor-arg>
+            </bean>
+        </property>
+        <property name="signResponses">
+            <bean class="net.shibboleth.utilities.java.support.logic.PredicateSupport" factory-method="fromFunction">
+                <constructor-arg>
+                    <bean parent="shibboleth.MDDrivenBoolProperty" p:propertyName="signResponses" />
+                </constructor-arg>
+                <constructor-arg>
+                    <bean class="org.opensaml.profile.logic.NoIntegrityMessageChannelPredicate" />
+                </constructor-arg>
+            </bean>
+        </property>
+        <property name="encryptNameIDs">
+            <bean class="net.shibboleth.utilities.java.support.logic.PredicateSupport" factory-method="fromFunction">
+                <constructor-arg>
+                    <bean parent="shibboleth.MDDrivenBoolProperty" p:propertyName="encryptNameIDs" />
+                </constructor-arg>
+                <constructor-arg>
+                    <bean class="org.opensaml.profile.logic.NoConfidentialityMessageChannelPredicate" />
+                </constructor-arg>
+            </bean>
+        </property>
+    </bean>
+
+    <bean id="SAML2.AttributeQuery.MDDriven" parent="AbstractMDDrivenSAML2Profile" lazy-init="true"
+            class="net.shibboleth.idp.saml.saml2.profile.config.AttributeQueryProfileConfiguration"
+            p:inboundInterceptorFlows="security-policy/saml-soap">
+        <property name="signResponses">
+            <bean class="net.shibboleth.utilities.java.support.logic.PredicateSupport" factory-method="fromFunction">
+                <constructor-arg>
+                    <bean parent="shibboleth.MDDrivenBoolProperty" p:propertyName="signResponses" />
+                </constructor-arg>
+                <constructor-arg>
+                    <bean class="org.opensaml.profile.logic.NoIntegrityMessageChannelPredicate" />
+                </constructor-arg>
+            </bean>
+        </property>
+        <property name="encryptAssertions">
+            <bean class="net.shibboleth.utilities.java.support.logic.PredicateSupport" factory-method="fromFunction">
+                <constructor-arg>
+                    <bean parent="shibboleth.MDDrivenBoolProperty" p:propertyName="encryptAssertions" />
+                </constructor-arg>
+                <constructor-arg>
+                    <bean class="org.opensaml.profile.logic.NoConfidentialityMessageChannelPredicate" />
+                </constructor-arg>
+            </bean>
+        </property>
+    </bean>
+
+    <bean id="SAML2.ArtifactResolution.MDDriven" parent="AbstractMDDrivenSAML2Profile" lazy-init="true"
+            class="net.shibboleth.idp.saml.saml2.profile.config.ArtifactResolutionProfileConfiguration"
+            p:inboundInterceptorFlows="security-policy/saml-soap">
+        <property name="signResponses">
+            <bean class="net.shibboleth.utilities.java.support.logic.PredicateSupport" factory-method="fromFunction">
+                <constructor-arg>
+                    <bean parent="shibboleth.MDDrivenBoolProperty" p:propertyName="signResponses" />
+                </constructor-arg>
+                <constructor-arg>
+                    <bean class="org.opensaml.profile.logic.NoIntegrityMessageChannelPredicate" />
+                </constructor-arg>
+            </bean>
+        </property>
+        <property name="encryptAssertions">
+            <bean class="net.shibboleth.utilities.java.support.logic.PredicateSupport" factory-method="fromFunction">
+                <constructor-arg>
+                    <bean parent="shibboleth.MDDrivenBoolProperty" p:propertyName="encryptAssertions" />
+                </constructor-arg>
+                <constructor-arg>
+                    <bean class="org.opensaml.profile.logic.NoConfidentialityMessageChannelPredicate" />
+                </constructor-arg>
+            </bean>
+        </property>
+    </bean>
+
+    <bean id="Liberty.SSOS.MDDriven" parent="SAML2.SSO.MDDriven" lazy-init="true"
+            class="net.shibboleth.idp.saml.idwsf.profile.config.SSOSProfileConfiguration"
+            p:inboundInterceptorFlows="security-policy/saml2-idwsf-ssos">
+        <property name="delegationPredicate">
+            <bean class="net.shibboleth.utilities.java.support.logic.PredicateSupport" factory-method="fromFunction">
+                <constructor-arg>
+                    <bean parent="shibboleth.MDDrivenBoolProperty" p:propertyName="delegation" />
+                </constructor-arg>
+                <constructor-arg value="false" />
+            </bean>
+        </property>
+    </bean>
+
+    <bean id="CAS.LoginConfiguration.MDDriven" parent="AbstractMDDrivenCASProfile" lazy-init="true"
+            class="net.shibboleth.idp.cas.config.impl.LoginConfiguration">
+        <property name="defaultAuthenticationMethodsLookupStrategy">
+            <bean parent="shibboleth.MDDrivenListProperty" p:propertyName="defaultAuthenticationMethods"
+                p:propertyType="#{T(net.shibboleth.idp.saml.authn.principal.AuthnContextClassRefPrincipal)}" />
+        </property>
+        <property name="postAuthenticationFlowsLookupStrategy">
+            <bean parent="shibboleth.MDDrivenListProperty" p:propertyName="postAuthenticationFlows" />
+        </property>
+        <property name="nameIDFormatPrecedenceLookupStrategy">
+            <bean parent="shibboleth.MDDrivenListProperty" p:propertyName="nameIDFormatPrecedence" />
+        </property>
+    </bean>
+
+    <bean id="CAS.ProxyConfiguration.MDDriven" parent="AbstractMDDrivenCASProfile" lazy-init="true"
+        class="net.shibboleth.idp.cas.config.impl.ProxyConfiguration" />
+
+    <!-- TODO: ticketValidityPeriod default here needs its default overridden, see IDP-1150. -->
+    <bean id="CAS.ValidateConfiguration.MDDriven" parent="AbstractMDDrivenCASProfile" lazy-init="true"
+            class="net.shibboleth.idp.cas.config.impl.ValidateConfiguration">
+        <property name="serviceComparatorLookupStrategy">
+            <bean parent="shibboleth.MDDrivenBeanProperty" p:propertyName="serviceComparator"
+                p:propertyType="#{T(java.util.Comparator)}" />
+        </property>
+        <property name="userAttributeLookupStrategy">
+            <bean parent="shibboleth.MDDrivenStringProperty" p:propertyName="userAttribute" />
+        </property>
+    </bean>
+
+</beans>
diff --git a/idp-conf/src/main/resources/system/conf/relying-party-system.xml b/idp-conf/src/main/resources/system/conf/relying-party-system.xml
index 65dc43f..26e1820 100644
--- a/idp-conf/src/main/resources/system/conf/relying-party-system.xml
+++ b/idp-conf/src/main/resources/system/conf/relying-party-system.xml
@@ -31,48 +31,49 @@
         p:detailedErrorsPredicate="%{idp.errors.detailed:false}" />
 
     <!-- Parent bean for RelyingParty overrides based on activation by name(s). -->
-    <bean id="RelyingPartyByName" abstract="true"
-        class="net.shibboleth.idp.saml.relyingparty.impl.RelyingPartyConfigurationSupport" factory-method="byName"
-        p:servletRequest-ref="shibboleth.HttpServletRequest"
-        p:responderId="#{getObject('entityID')}" p:detailedErrorsPredicate="%{idp.errors.detailed:false}" />
+    <bean id="RelyingPartyByName" abstract="true" parent="RelyingParty"
+        class="net.shibboleth.idp.saml.relyingparty.impl.RelyingPartyConfigurationSupport" factory-method="byName" />
 
     <!-- Parent bean for RelyingParty overrides based on activation by group. -->
-    <bean id="RelyingPartyByGroup" abstract="true"
-        class="net.shibboleth.idp.saml.relyingparty.impl.RelyingPartyConfigurationSupport" factory-method="byGroup"
-        p:servletRequest-ref="shibboleth.HttpServletRequest"
-        p:responderId="#{getObject('entityID')}" p:detailedErrorsPredicate="%{idp.errors.detailed:false}" />
+    <bean id="RelyingPartyByGroup" abstract="true" parent="RelyingParty"
+        class="net.shibboleth.idp.saml.relyingparty.impl.RelyingPartyConfigurationSupport" factory-method="byGroup" />
 
     <!-- Parent bean for RelyingParty overrides based on activation by tag. -->
-    <bean id="RelyingPartyByTag" abstract="true"
+    <bean id="RelyingPartyByTag" abstract="true" parent="RelyingParty"
         class="net.shibboleth.idp.saml.relyingparty.impl.RelyingPartyConfigurationSupport" factory-method="byTag"
-        p:servletRequest-ref="shibboleth.HttpServletRequest"
-        p:responderId="#{getObject('entityID')}" p:detailedErrorsPredicate="%{idp.errors.detailed:false}"
         c:trim="false" c:matchAll="false" />
 
     <bean id="TagCandidate" abstract="true"
         class="org.opensaml.saml.common.profile.logic.EntityAttributesPredicate.Candidate" />
         
-    <!-- Default Profile Configurations, also usable as parent beans in overrides. -->
+    <bean id="AbstractProfileConfig" abstract="true"
+        p:servletRequest-ref="shibboleth.HttpServletRequest" />
+        
+    <!-- Import parent beans for metadata-driven configuration approach. -->
+    <import resource="relying-party-mddriven.xml" />
+        
+    <!--
+    Default Profile Configurations, also usable as parent beans in overrides.
+    
+    Each profile shows up twice, once as a basic bean and again as a child bean that adds
+    a suite of metadata-driven lookup strategies pre-injected.
+    -->
     
-    <bean id="Shibboleth.SSO"
+    <bean id="Shibboleth.SSO" parent="AbstractProfileConfig"
         class="net.shibboleth.idp.saml.saml1.profile.config.BrowserSSOProfileConfiguration"
-        p:servletRequest-ref="shibboleth.HttpServletRequest"
         p:artifactConfiguration-ref="shibboleth.DefaultArtifactConfiguration"
         p:inboundInterceptorFlows="security-policy/shibboleth-sso" />
-
-    <bean id="SAML1.AttributeQuery"
+        
+    <bean id="SAML1.AttributeQuery" parent="AbstractProfileConfig"
         class="net.shibboleth.idp.saml.saml1.profile.config.AttributeQueryProfileConfiguration"
-        p:servletRequest-ref="shibboleth.HttpServletRequest"
         p:inboundInterceptorFlows="security-policy/saml-soap" />
 
-    <bean id="SAML1.ArtifactResolution"
+    <bean id="SAML1.ArtifactResolution" parent="AbstractProfileConfig"
         class="net.shibboleth.idp.saml.saml1.profile.config.ArtifactResolutionProfileConfiguration"
-        p:servletRequest-ref="shibboleth.HttpServletRequest"
         p:inboundInterceptorFlows="security-policy/saml-soap" />
 
-    <bean id="SAML2.SSO"
+    <bean id="SAML2.SSO" parent="AbstractProfileConfig"
         class="net.shibboleth.idp.saml.saml2.profile.config.BrowserSSOProfileConfiguration"
-        p:servletRequest-ref="shibboleth.HttpServletRequest"
         p:artifactConfiguration-ref="shibboleth.DefaultArtifactConfiguration"
         p:inboundInterceptorFlows="security-policy/saml2-sso"
         p:encryptionOptionalPredicate="%{idp.encryption.optional:false}" />
@@ -80,49 +81,41 @@
     <util:constant id="SAML2.SSO.FEATURE_AUTHNCONTEXT"
         static-field="net.shibboleth.idp.saml.saml2.profile.config.BrowserSSOProfileConfiguration.FEATURE_AUTHNCONTEXT"/>
 
-    <bean id="SAML2.ECP"
+    <bean id="SAML2.ECP" parent="AbstractProfileConfig"
         class="net.shibboleth.idp.saml.saml2.profile.config.ECPProfileConfiguration"
-        p:servletRequest-ref="shibboleth.HttpServletRequest"
         p:inboundInterceptorFlows="security-policy/saml2-ecp"
         p:encryptionOptionalPredicate="%{idp.encryption.optional:false}" />
 
-    <bean id="SAML2.Logout"
+    <bean id="SAML2.Logout" parent="AbstractProfileConfig"
         class="net.shibboleth.idp.saml.saml2.profile.config.SingleLogoutProfileConfiguration"
-        p:servletRequest-ref="shibboleth.HttpServletRequest"
         p:artifactConfiguration-ref="shibboleth.DefaultArtifactConfiguration"
         p:inboundInterceptorFlows="security-policy/saml2-slo"
         p:encryptionOptionalPredicate="%{idp.encryption.optional:false}" />
 
-    <bean id="SAML2.AttributeQuery"
+    <bean id="SAML2.AttributeQuery" parent="AbstractProfileConfig"
         class="net.shibboleth.idp.saml.saml2.profile.config.AttributeQueryProfileConfiguration"
-        p:servletRequest-ref="shibboleth.HttpServletRequest"
         p:inboundInterceptorFlows="security-policy/saml-soap"
         p:encryptionOptionalPredicate="%{idp.encryption.optional:false}" />
 
-    <bean id="SAML2.ArtifactResolution"
+    <bean id="SAML2.ArtifactResolution" parent="AbstractProfileConfig"
         class="net.shibboleth.idp.saml.saml2.profile.config.ArtifactResolutionProfileConfiguration"
-        p:servletRequest-ref="shibboleth.HttpServletRequest"
         p:inboundInterceptorFlows="security-policy/saml-soap"
         p:encryptionOptionalPredicate="%{idp.encryption.optional:false}" />
 
-    <bean id="Liberty.SSOS"
+    <bean id="Liberty.SSOS" parent="AbstractProfileConfig"
         class="net.shibboleth.idp.saml.idwsf.profile.config.SSOSProfileConfiguration"
-        p:servletRequest-ref="shibboleth.HttpServletRequest"
         p:inboundInterceptorFlows="security-policy/saml2-idwsf-ssos"
         p:encryptionOptionalPredicate="%{idp.encryption.optional:false}" />
 
-    <bean id="CAS.LoginConfiguration"
-        class="net.shibboleth.idp.cas.config.impl.LoginConfiguration"
-        p:servletRequest-ref="shibboleth.HttpServletRequest" />
+    <bean id="CAS.LoginConfiguration" parent="AbstractProfileConfig"
+        class="net.shibboleth.idp.cas.config.impl.LoginConfiguration" />
 
-    <bean id="CAS.ProxyConfiguration"
-        class="net.shibboleth.idp.cas.config.impl.ProxyConfiguration"
-        p:servletRequest-ref="shibboleth.HttpServletRequest" />
+    <bean id="CAS.ProxyConfiguration" parent="AbstractProfileConfig"
+        class="net.shibboleth.idp.cas.config.impl.ProxyConfiguration" />
 
     <!-- Ticket validity period applies to proxy-granting tickets created upon successful proxy callback validation -->
-    <bean id="CAS.ValidateConfiguration"
+    <bean id="CAS.ValidateConfiguration" parent="AbstractProfileConfig"
         class="net.shibboleth.idp.cas.config.impl.ValidateConfiguration"
-        p:servletRequest-ref="shibboleth.HttpServletRequest"
         p:ticketValidityPeriod="PT12H" />
 
     <!-- Artifact Configuration Defaults -->
diff --git a/idp-conf/src/test/resources/conf/metadata-providers.xml b/idp-conf/src/test/resources/conf/metadata-providers.xml
index a5ee3b9..55e373d 100644
--- a/idp-conf/src/test/resources/conf/metadata-providers.xml
+++ b/idp-conf/src/test/resources/conf/metadata-providers.xml
@@ -5,6 +5,7 @@
 	xmlns:security="urn:mace:shibboleth:2.0:security"
 	xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"
     xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
+    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
     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:resource http://shibboleth.net/schema/idp/shibboleth-resource.xsd 
@@ -43,6 +44,9 @@
                 <saml:AttributeValue>foo</saml:AttributeValue>
                 <saml:AttributeValue>bar</saml:AttributeValue>
             </saml:Attribute>
+            <saml:Attribute Name="http://shibboleth.net/ns/profiles/saml1/sso/browser/includeAttributeStatement">
+                <saml:AttributeValue xsi:type="xsd:boolean">1</saml:AttributeValue>
+            </saml:Attribute>
             <Entity>https://sp.example.org</Entity>
         </MetadataFilter>
         <MetadataResource xsi:type="resource:ClasspathResource" file="/metadata/example-metadata.xml" />
diff --git a/idp-conf/src/test/resources/conf/relying-party.xml b/idp-conf/src/test/resources/conf/relying-party.xml
index 42c2e03..894bc88 100644
--- a/idp-conf/src/test/resources/conf/relying-party.xml
+++ b/idp-conf/src/test/resources/conf/relying-party.xml
@@ -30,56 +30,23 @@
 
     <!-- Default configuration, with default settings applied for all profiles. -->
 
-    <bean id="shibboleth.DefaultRelyingParty" parent="RelyingParty">
+    <bean id="shibboleth.DefaultRelyingParty" parent="RelyingParty.MDDriven">
         <property name="profileConfigurations">
             <util:list>
-                <ref bean="Shibboleth.SSO" />
-                <ref bean="SAML1.AttributeQuery" />
-                <ref bean="SAML1.ArtifactResolution" />
-                <ref bean="SAML2.SSO" />
-                <ref bean="SAML2.ECP" />
-                <ref bean="SAML2.Logout" />
-                <ref bean="SAML2.AttributeQuery" />
-                <ref bean="SAML2.ArtifactResolution" />
+                <ref bean="Shibboleth.SSO.MDDriven" />
+                <ref bean="SAML1.AttributeQuery.MDDriven" />
+                <ref bean="SAML1.ArtifactResolution.MDDriven" />
+                <ref bean="SAML2.SSO.MDDriven" />
+                <ref bean="SAML2.ECP.MDDriven" />
+                <ref bean="SAML2.Logout.MDDriven" />
+                <ref bean="SAML2.AttributeQuery.MDDriven" />
+                <ref bean="SAML2.ArtifactResolution.MDDriven" />
             </util:list>
         </property>
     </bean>
 
     <util:list id="shibboleth.RelyingPartyOverrides">
-    
-        <bean parent="RelyingPartyByName" c:relyingPartyIds="https://sp.example.org">
-            <property name="profileConfigurations">
-                <util:list>
-                    <bean parent="Shibboleth.SSO" p:includeAttributeStatement="true">
-                        <!--
-                        <property name="defaultAuthenticationMethods">
-                            <list>
-                                <bean parent="shibboleth.SAML1AuthenticationMethod"
-                                    c:method="urn:oasis:names:tc:SAML:1.0:am:password" />
-                            </list>
-                        </property>
-                        -->
-                    </bean>
-                    <ref bean="SAML1.AttributeQuery" />
-                    <ref bean="SAML1.ArtifactResolution" />
-                    <bean parent="SAML2.SSO">
-                        <!--
-                        <property name="defaultAuthenticationMethods">
-                            <list>
-                                <bean parent="shibboleth.SAML2AuthnContextClassRef"
-                                    c:classRef="urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport" />
-                            </list>
-                        </property>
-                        -->
-                    </bean>
-                    <ref bean="SAML2.ECP" />
-                    <ref bean="SAML2.Logout" />
-                    <ref bean="SAML2.AttributeQuery" />
-                    <ref bean="SAML2.ArtifactResolution" />
-                </util:list>
-            </property>
-        </bean>
-        
+
     </util:list>
 
 </beans>
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/AbstractCollectionConfigurationLookupStrategy.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/AbstractCollectionConfigurationLookupStrategy.java
new file mode 100644
index 0000000..8771a40
--- /dev/null
+++ b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/AbstractCollectionConfigurationLookupStrategy.java
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.saml.profile.config;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.joda.time.DateTime;
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.core.xml.schema.XSAny;
+import org.opensaml.core.xml.schema.XSBase64Binary;
+import org.opensaml.core.xml.schema.XSBoolean;
+import org.opensaml.core.xml.schema.XSBooleanValue;
+import org.opensaml.core.xml.schema.XSDateTime;
+import org.opensaml.core.xml.schema.XSInteger;
+import org.opensaml.core.xml.schema.XSString;
+import org.opensaml.core.xml.schema.XSURI;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+/**
+ * A strategy function that examines SAML metadata associated with a relying party and derives List<String>-valued
+ * configuration settings based on EntityAttribute extension tags.
+ * 
+ * @param <T1> type of collection member
+ * @param <T2> type of collection itself
+ * 
+ * @since 3.4.0
+ */
+public abstract class AbstractCollectionConfigurationLookupStrategy<T1,T2>
+    extends AbstractMetadataDrivenConfigurationLookupStrategy<T2> {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(AbstractCollectionConfigurationLookupStrategy.class);
+
+    /** Type of bean in collection. */
+    @NonnullAfterInit private Class<T1> propertyType;
+    
+    /**
+     * Get the type of object to coerce collection elements into.
+     * 
+     * @return object type
+     */
+    @NonnullAfterInit public Class<T1> getPropertyType() {
+        return propertyType;
+    }
+    
+    /**
+     * Set the type of object to coerce collection elements into.
+     * 
+     * @param type object type
+     */
+    public void setPropertyType(@Nonnull final Class<T1> type) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        propertyType = Constraint.isNotNull(type, "Property type cannot be null");
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    protected void doInitialize() throws ComponentInitializationException {
+        super.doInitialize();
+        
+        if (propertyType == null) {
+            throw new ComponentInitializationException("Property type cannot be null");
+        }
+    }
+
+    /**
+     * Helper method to manufacture instance of object using a string constructor or a cast.
+     * 
+     * @param input the input string
+     * 
+     * @return the new object or the existing object if casting is possible
+     * 
+     * @throws ReflectiveOperationException if the attempt fails
+     */
+    protected T1 createInstanceFromString(@Nonnull @NotEmpty final String input) throws ReflectiveOperationException {
+        if (propertyType.isAssignableFrom(input.getClass())) {
+            return propertyType.cast(input);
+        }
+        
+        return propertyType.getConstructor(String.class).newInstance(input);
+    }
+    
+ // Checkstyle: CyclomaticComplexity OFF
+    /**
+     * Convert an XMLObject to a String if the type is supported.
+     * 
+     * @param object object to convert
+     * 
+     * @return the converted value, or null
+     */
+    @Nullable protected String xmlObjectToString(@Nonnull final XMLObject object) {
+        if (object instanceof XSString) {
+            return ((XSString) object).getValue();
+        } else if (object instanceof XSURI) {
+            return ((XSURI) object).getValue();
+        } else if (object instanceof XSBoolean) {
+            final XSBooleanValue value = ((XSBoolean) object).getValue();
+            return value != null ? (value.getValue() ? "1" : "0") : null;
+        } else if (object instanceof XSInteger) {
+            final Integer value = ((XSInteger) object).getValue();
+            return value != null ? value.toString() : null;
+        } else if (object instanceof XSDateTime) {
+            final DateTime dt = ((XSDateTime) object).getValue();
+            return dt != null ? Long.toString(dt.getMillis()) : null;
+        } else if (object instanceof XSBase64Binary) {
+            return ((XSBase64Binary) object).getValue();
+        } else if (object instanceof XSAny) {
+            final XSAny wc = (XSAny) object;
+            if (wc.getUnknownAttributes().isEmpty() && wc.getUnknownXMLObjects().isEmpty()) {
+                return wc.getTextContent();
+            }
+        }
+        
+        log.error("Unsupported conversion to String from XMLObject type ({})", object.getClass().getName());
+        return null;
+    }
+// Checkstyle: CyclomaticComplexity ON
+}
\ No newline at end of file
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/AbstractMetadataDrivenConfigurationLookupStrategy.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/AbstractMetadataDrivenConfigurationLookupStrategy.java
new file mode 100644
index 0000000..ee5493c
--- /dev/null
+++ b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/AbstractMetadataDrivenConfigurationLookupStrategy.java
@@ -0,0 +1,346 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.saml.profile.config;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.messaging.context.BaseContext;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.saml.common.messaging.context.navigate.EntityDescriptorLookupFunction;
+import org.opensaml.saml.ext.saml2mdattr.EntityAttributes;
+import org.opensaml.saml.saml2.core.Attribute;
+import org.opensaml.saml.saml2.metadata.EntitiesDescriptor;
+import org.opensaml.saml.saml2.metadata.EntityDescriptor;
+import org.opensaml.saml.saml2.metadata.Extensions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Function;
+import com.google.common.base.Functions;
+import com.google.common.collect.Collections2;
+
+import net.shibboleth.idp.saml.profile.context.navigate.SAMLMetadataContextLookupFunction;
+import net.shibboleth.utilities.java.support.annotation.constraint.Live;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
+import net.shibboleth.utilities.java.support.component.AbstractInitializableComponent;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
+
+/**
+ * A strategy function that examines SAML metadata associated with a relying party and derives configuration
+ * settings based on EntityAttribute extension tags.
+ * 
+ * <p>The function is tailored with properties that determine what tag it looks for, with subclasses
+ * handling the specific type conversion logic.</p>
+ * 
+ * <p>If a specific property is unavailable, then null is returned.</p>
+ * 
+ * @param <T> type of property being returned
+ * 
+ * @since 3.4.0
+ */
+public abstract class AbstractMetadataDrivenConfigurationLookupStrategy<T> extends AbstractInitializableComponent
+        implements Function<ProfileRequestContext,T> {
+
+    /** Class logger. */
+    @Nonnull
+    private final Logger log = LoggerFactory.getLogger(AbstractMetadataDrivenConfigurationLookupStrategy.class);
+    
+    /** Require use of URI attribute name format. */
+    private boolean strictNameFormat;
+    
+    /** Cache the lookup in the context tree. */
+    private boolean enableCaching;
+    
+    /** Base name of property to produce. */
+    @NonnullAfterInit @NotEmpty private String propertyName;
+    
+    /** Alternative "full" property identifiers to support. */
+    @NonnullAfterInit @NonnullElements private Collection<String> propertyAliases;
+        
+    /** Strategy for obtaining metadata to check. */
+    @Nonnull private Function<ProfileRequestContext,EntityDescriptor> metadataLookupStrategy;
+    
+    /** Constructor. */
+    public AbstractMetadataDrivenConfigurationLookupStrategy() {
+        enableCaching = true;
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    protected void doInitialize() throws ComponentInitializationException {
+        super.doInitialize();
+        
+        if (propertyName == null) {
+            throw new ComponentInitializationException("Property name cannot be null or empty");
+        } else if (propertyAliases == null) {
+            propertyAliases = Collections.emptyList();
+        } else if (metadataLookupStrategy == null) {
+            metadataLookupStrategy = Functions.compose(new EntityDescriptorLookupFunction(),
+                    new SAMLMetadataContextLookupFunction());
+        }
+    }
+
+    /**
+     * Set whether tag matching should examine and require an Attribute NameFormat of the URI type.
+     * 
+     * <p>Default is false.</p>
+     * 
+     * @param flag flag to set
+     */
+    public void setStrictNameFormat(final boolean flag) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        strictNameFormat = flag;
+    }
+    
+    /**
+     * Set whether property lookup should be cached in the profile context tree.
+     * 
+     * <p>Default is true.</p>
+     * 
+     * @param flag flag to set
+     */
+    public void setEnableCaching(final boolean flag) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        enableCaching = flag;
+    }
+    
+    /**
+     * Set the "base" name of the property/setting to derive.
+     * 
+     * @param name base property name
+     */
+    public void setPropertyName(@Nonnull @NotEmpty final String name) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        propertyName = Constraint.isNotNull(StringSupport.trimOrNull(name), "Property name cannot be null or empty");
+    }
+    
+    /**
+     * Set profile ID aliases to include when checking for metadata tags (the property name is suffixed to the
+     * aliases).
+     * 
+     * <p>This allows alternative tag names to be checked.</p>
+     * 
+     * @param aliases alternative profile IDs
+     */
+    public void setProfileAliases(@Nonnull @NonnullElements final Collection<String> aliases) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        Constraint.isNotNull(aliases, "Alias collection cannot be null");
+        
+        propertyAliases = Collections2.transform(StringSupport.normalizeStringCollection(aliases),
+                new Function<String,String>() {
+                    public String apply(final String input) {
+                        return input + (input.endsWith("/") ? propertyName : '/' + propertyName);
+                    }
+                });
+    }
+    
+    /**
+     * Set lookup strategy for metadata to examine.
+     * 
+     * @param strategy  lookup strategy
+     */
+    public void setMetadataLookupStrategy(@Nonnull final Function<ProfileRequestContext,EntityDescriptor> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        metadataLookupStrategy = Constraint.isNotNull(strategy, "Metadata lookup strategy cannot be null");
+    }
+
+    /** {@inheritDoc} */
+    @Nullable public T apply(@Nullable final ProfileRequestContext input) {
+        ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
+        
+        CachedConfigurationContext cacheContext = null;
+        
+        if (enableCaching) {
+            cacheContext = input.getSubcontext(CachedConfigurationContext.class, true);
+            if (cacheContext.getPropertyMap().containsKey(propertyName)) {
+                log.debug("Returning cached property '{}'", propertyName);
+                return (T) cacheContext.getPropertyMap().get(propertyName);
+            }
+        }
+        
+        final EntityDescriptor entity = metadataLookupStrategy.apply(input);
+        if (entity == null) {
+            log.debug("No metadata available for relying party, no setting returned for '{}'", propertyName);
+            return null;
+        }
+        
+        // Look for "primary" tag name based on profile/property.
+        Attribute attribute = findMatchingTag(entity, input.getProfileId() + '/' + propertyName);
+        if (attribute != null) {
+            log.debug("Found matching tag '{}' for property '{}'", attribute.getName(), propertyName);
+            final T result = translate(input, attribute);
+            if (enableCaching) {
+                cacheContext.getPropertyMap().put(propertyName, result);
+            }
+            return result;
+        }
+        
+        for (final String alias : propertyAliases) {
+            attribute = findMatchingTag(entity, alias);
+            if (attribute != null) {
+                log.debug("Found matching tag '{}' for property '{}'", attribute.getName(), propertyName);
+                final T result = translate(input, attribute);
+                if (enableCaching) {
+                    cacheContext.getPropertyMap().put(propertyName, result);
+                }
+                return result;
+            }
+        }
+        
+        log.debug("No applicable tag, no setting returned for '{}'", propertyName);
+        if (enableCaching) {
+            cacheContext.getPropertyMap().put(propertyName, null);
+        }
+        return null;
+    }
+    
+    /**
+     * Translate the value(s) into a setting of the appropriate type.
+     * 
+     * @param profileRequestContext current profile request context
+     * @param tag tag to translate
+     * 
+     * @return the setting derived from the tag's value(s)
+     */
+    @Nullable private T translate(@Nullable final ProfileRequestContext profileRequestContext,
+            @Nonnull final Attribute tag) {
+        
+        final List<XMLObject> values = tag.getAttributeValues();
+        if (values == null || values.isEmpty()) {
+            log.debug("Tag '{}' contained no values, no setting returned for '{}'", tag.getName(), propertyName);
+            return null;
+        }
+        
+        return doTranslate(profileRequestContext, tag);
+    }
+    
+    /**
+     * Translate the value(s) into a setting of the appropriate type.
+     * 
+     * <p>Overrides of this function can assume a non-zero collection of values.</p>
+     * 
+     * @param profileRequestContext current profile request context
+     * @param tag tag to translate
+     * 
+     * @return the setting derived from the tag's value(s)
+     */
+    @Nullable protected abstract T doTranslate(@Nullable final ProfileRequestContext profileRequestContext,
+            @Nonnull final Attribute tag); 
+    
+    /**
+     * Find a matching entity attribute in the input metadata.
+     * 
+     * @param entity the metadata to examine
+     * @param name the tag name to search for
+     * 
+     * @return matching attribute or null
+     */
+    @Nullable private Attribute findMatchingTag(@Nonnull final EntityDescriptor entity,
+            @Nonnull @NotEmpty final String name) {
+        
+        // Check for a tag match in the EntityAttributes extension of the entity and its parent(s).
+        Extensions exts = entity.getExtensions();
+        if (exts != null) {
+            final List<XMLObject> children = exts.getUnknownXMLObjects(EntityAttributes.DEFAULT_ELEMENT_NAME);
+            if (!children.isEmpty() && children.get(0) instanceof EntityAttributes) {
+                final Attribute tag = findMatchingTag((EntityAttributes) children.get(0), name);
+                if (tag != null) {
+                    return tag;
+                }
+            }
+        }
+
+        EntitiesDescriptor group = (EntitiesDescriptor) entity.getParent();
+        while (group != null) {
+            exts = group.getExtensions();
+            if (exts != null) {
+                final List<XMLObject> children = exts.getUnknownXMLObjects(EntityAttributes.DEFAULT_ELEMENT_NAME);
+                if (!children.isEmpty() && children.get(0) instanceof EntityAttributes) {
+                    final Attribute tag = findMatchingTag((EntityAttributes) children.get(0), name);
+                    if (tag != null) {
+                        return tag;
+                    }
+                }
+            }
+            group = (EntitiesDescriptor) group.getParent();
+        }
+        
+        return null;
+    }
+    
+    /**
+     * Find a matching entity attribute in the input metadata.
+     * 
+     * @param entityAttributes the metadata to examine
+     * @param name the tag name to search for
+     * 
+     * @return matching attribute or null
+     */
+    @Nullable private Attribute findMatchingTag(@Nonnull final EntityAttributes entityAttributes,
+            @Nonnull @NotEmpty final String name) {
+        
+        for (final Attribute tag : entityAttributes.getAttributes()) {
+            if (Objects.equals(tag.getName(), name)
+                    && (!strictNameFormat || Objects.equals(tag.getNameFormat(), Attribute.URI_REFERENCE))) {
+                return tag;
+            }
+        }
+
+        return null;
+    }
+    
+    /** A child context that caches derived configuration properties. */
+    public static final class CachedConfigurationContext extends BaseContext {
+        
+        /** Cached property map. */
+        @Nonnull private Map<String,Object> propertyMap;
+        
+        /** Constructor. */
+        public CachedConfigurationContext() {
+            propertyMap = new HashMap<>();
+        }
+        
+        /**
+         * Get cached property map.
+         * 
+         * @return cached property map
+         */
+        @Nonnull @Live Map<String,Object> getPropertyMap() {
+            return propertyMap;
+        }
+    }
+    
+}
\ No newline at end of file
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/AbstractSAMLProfileConfiguration.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/AbstractSAMLProfileConfiguration.java
index 0320e17..6e67e94 100644
--- a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/AbstractSAMLProfileConfiguration.java
+++ b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/AbstractSAMLProfileConfiguration.java
@@ -210,7 +210,7 @@ public abstract class AbstractSAMLProfileConfiguration extends AbstractProfileCo
      * 
      * @since 3.3.0
      */
-    public void setIncludeConditionsNotBeforePredicate(@Nullable final Predicate<ProfileRequestContext> condition) {
+    public void setIncludeConditionsNotBeforePredicate(@Nonnull final Predicate<ProfileRequestContext> condition) {
         includeNotBeforePredicate = Constraint.isNotNull(condition, "NotBefore predicate cannot be null");
     }
 
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/BeanConfigurationLookupStrategy.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/BeanConfigurationLookupStrategy.java
new file mode 100644
index 0000000..3e874cd
--- /dev/null
+++ b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/BeanConfigurationLookupStrategy.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.saml.profile.config;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.core.xml.schema.XSAny;
+import org.opensaml.core.xml.schema.XSString;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.saml.saml2.core.Attribute;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.BeansException;
+import org.springframework.webflow.execution.RequestContext;
+
+import net.shibboleth.idp.profile.context.SpringRequestContext;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+/**
+ * A strategy function that examines SAML metadata associated with a relying party and derives bean-based
+ * configuration settings based on EntityAttribute extension tags.
+ * 
+ * <p>Defaults to no caching of the result to avoid bean lifecycle issues if relying party config is reloaded.</p>
+ * 
+ * @param <T> type of bean
+ * 
+ * @since 3.4.0
+ */
+public class BeanConfigurationLookupStrategy<T> extends AbstractMetadataDrivenConfigurationLookupStrategy<T> {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(BeanConfigurationLookupStrategy.class);
+
+    /** Type of bean to return. */
+    @NonnullAfterInit private Class<T> propertyType;
+    
+    /** Constructor. */
+    public BeanConfigurationLookupStrategy() {
+        setEnableCaching(false);
+    }
+    
+    /**
+     * Set the type of bean to search for.
+     * 
+     * @param type bean type
+     */
+    public void setPropertyType(@Nonnull final Class<T> type) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        propertyType = Constraint.isNotNull(type, "Property type cannot be null");
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    protected void doInitialize() throws ComponentInitializationException {
+        super.doInitialize();
+        
+        if (propertyType == null) {
+            throw new ComponentInitializationException("Property type cannot be null");
+        }
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    @Nullable protected T doTranslate(@Nullable final ProfileRequestContext profileRequestContext,
+            @Nonnull final Attribute tag) {
+        
+        final List<XMLObject> values = tag.getAttributeValues();
+        if (values.size() != 1) {
+            log.error("Tag '{}' contained multiple values, returning none");
+            return null;
+        }
+        
+        log.debug("Converting tag '{}' to Bean property of tyoe '{}'", tag.getName(), propertyType.getSimpleName());
+        return xmlObjectToBean(profileRequestContext, values.get(0));
+    }
+    
+    /**
+     * Convert an XMLObject to a Spring bean reference if the type is supported.
+     * 
+     * @param profileRequestContext current profile request context
+     * @param object object to convert
+     * 
+     * @return the converted value, or null
+     */
+    @Nullable private T xmlObjectToBean(@Nullable final ProfileRequestContext profileRequestContext,
+            @Nonnull final XMLObject object) {
+        String value = null;
+        if (object instanceof XSString) {
+            value = ((XSString) object).getValue();
+        } else if (object instanceof XSAny) {
+            final XSAny wc = (XSAny) object;
+            if (wc.getUnknownAttributes().isEmpty() && wc.getUnknownXMLObjects().isEmpty()) {
+                value = wc.getTextContent();
+            }
+        }
+        
+        if (value != null) {
+            if (profileRequestContext != null) {
+                final SpringRequestContext springContext =
+                        profileRequestContext.getSubcontext(SpringRequestContext.class);
+                if (springContext != null) {
+                    final RequestContext requestContext = springContext.getRequestContext();
+                    if (requestContext != null) {
+                        try {
+                            return requestContext.getActiveFlow().getApplicationContext().getBean(value, propertyType);
+                        } catch (final BeansException e) {
+                            log.error("Error locating appropriately typed bean named {}", value, e);
+                            return null;
+                        }
+                    }
+                }
+            }
+            
+            log.error("Unable to access Spring ApplicationContext to search for bean reference");
+            return null;
+        }
+        
+        log.error("Unsupported conversion to Spring bean from XMLObject type ({})", object.getClass().getName());
+        return null;
+    }
+    
+}
\ No newline at end of file
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/BooleanConfigurationLookupStrategy.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/BooleanConfigurationLookupStrategy.java
new file mode 100644
index 0000000..2c3420d
--- /dev/null
+++ b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/BooleanConfigurationLookupStrategy.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.saml.profile.config;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.core.xml.schema.XSAny;
+import org.opensaml.core.xml.schema.XSBoolean;
+import org.opensaml.core.xml.schema.XSBooleanValue;
+import org.opensaml.core.xml.schema.XSInteger;
+import org.opensaml.core.xml.schema.XSString;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.saml.saml2.core.Attribute;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A strategy function that examines SAML metadata associated with a relying party and derives Boolean-valued
+ * configuration settings based on EntityAttribute extension tags.
+ * 
+ * @since 3.4.0
+ */
+public class BooleanConfigurationLookupStrategy extends AbstractMetadataDrivenConfigurationLookupStrategy<Boolean> {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(BooleanConfigurationLookupStrategy.class);
+
+    /** {@inheritDoc} */
+    @Override
+    @Nullable protected Boolean doTranslate(@Nullable final ProfileRequestContext profileRequestContext,
+            @Nonnull final Attribute tag) {
+        
+        final List<XMLObject> values = tag.getAttributeValues();
+        if (values.size() != 1) {
+            log.error("Tag '{}' contained multiple values, returning none");
+            return null;
+        }
+        
+        log.debug("Converting tag '{}' to Boolean property", tag.getName());
+        return xmlObjectToBoolean(values.get(0));
+    }
+    
+// Checkstyle: CyclomaticComplexity OFF
+    /**
+     * Convert an XMLObject to a Boolean if the type is supported.
+     * 
+     * @param object object to convert
+     * 
+     * @return the converted value, or null
+     */
+    @Nullable private Boolean xmlObjectToBoolean(@Nonnull final XMLObject object) {
+        if (object instanceof XSString) {
+            final String value = ((XSString) object).getValue();
+            if (value != null) {
+                return XSBooleanValue.valueOf(value).getValue();
+            } else {
+                return null;
+            }
+        } else if (object instanceof XSBoolean) {
+            final XSBooleanValue value = ((XSBoolean) object).getValue();
+            return value != null ? value.getValue() : null;
+        } else if (object instanceof XSInteger) {
+            final Integer value = ((XSInteger) object).getValue();
+            return value != null ? value != 0 : null;
+        } else if (object instanceof XSAny) {
+            final XSAny wc = (XSAny) object;
+            if (wc.getUnknownAttributes().isEmpty() && wc.getUnknownXMLObjects().isEmpty()) {
+                final String value = wc.getTextContent();
+                if (value != null) {
+                    return XSBooleanValue.valueOf(value).getValue();
+                } else {
+                    return null;
+                }
+            }
+        }
+        
+        log.error("Unsupported conversion to Boolean from XMLObject type ({})", object.getClass().getName());
+        return null;
+    }
+// Checkstyle: CyclomaticComplexity ON
+    
+}
\ No newline at end of file
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/DoubleConfigurationLookupStrategy.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/DoubleConfigurationLookupStrategy.java
new file mode 100644
index 0000000..640aa38
--- /dev/null
+++ b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/DoubleConfigurationLookupStrategy.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.saml.profile.config;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.core.xml.schema.XSAny;
+import org.opensaml.core.xml.schema.XSBoolean;
+import org.opensaml.core.xml.schema.XSBooleanValue;
+import org.opensaml.core.xml.schema.XSInteger;
+import org.opensaml.core.xml.schema.XSString;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.saml.saml2.core.Attribute;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A strategy function that examines SAML metadata associated with a relying party and derives Double-valued
+ * configuration settings based on EntityAttribute extension tags.
+ * 
+ * @since 3.4.0
+ */
+public class DoubleConfigurationLookupStrategy extends AbstractMetadataDrivenConfigurationLookupStrategy<Double> {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(DoubleConfigurationLookupStrategy.class);
+
+    /** {@inheritDoc} */
+    @Override
+    @Nullable protected Double doTranslate(@Nullable final ProfileRequestContext profileRequestContext,
+            @Nonnull final Attribute tag) {
+        
+        final List<XMLObject> values = tag.getAttributeValues();
+        if (values.size() != 1) {
+            log.error("Tag '{}' contained multiple values, returning none");
+            return null;
+        }
+        
+        log.debug("Converting tag '{}' to Double property", tag.getName());
+        return xmlObjectToDouble(values.get(0));
+    }
+    
+// Checkstyle: CyclomaticComplexity OFF
+    /**
+     * Convert an XMLObject to a Double if the type is supported.
+     * 
+     * @param object object to convert
+     * 
+     * @return the converted value, or null
+     */
+    @Nullable private Double xmlObjectToDouble(@Nonnull final XMLObject object) {
+        if (object instanceof XSString) {
+            final String value = ((XSString) object).getValue();
+            return value != null ? Double.valueOf(value) : null;
+        } else if (object instanceof XSBoolean) {
+            final XSBooleanValue value = ((XSBoolean) object).getValue();
+            return value != null ? (value.getValue() ? 1.0 : 0.0) : null;
+        } else if (object instanceof XSInteger) {
+            final Integer value = ((XSInteger) object).getValue(); 
+            return value != null ? value.doubleValue() : null;
+        } else if (object instanceof XSAny) {
+            final XSAny wc = (XSAny) object;
+            if (wc.getUnknownAttributes().isEmpty() && wc.getUnknownXMLObjects().isEmpty()) {
+                final String value = wc.getTextContent();
+                return value != null ? Double.valueOf(value) : null;
+            }
+        }
+        
+        log.error("Unsupported conversion to Double from XMLObject type ({})", object.getClass().getName());
+        return null;
+    }
+// Checkstyle: CyclomaticComplexity ON
+    
+}
\ No newline at end of file
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/DurationConfigurationLookupStrategy.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/DurationConfigurationLookupStrategy.java
new file mode 100644
index 0000000..8dea0ff
--- /dev/null
+++ b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/DurationConfigurationLookupStrategy.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.saml.profile.config;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.core.xml.schema.XSAny;
+import org.opensaml.core.xml.schema.XSInteger;
+import org.opensaml.core.xml.schema.XSString;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.saml.saml2.core.Attribute;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.shibboleth.ext.spring.config.DurationToLongConverter;
+
+/**
+ * A strategy function that examines SAML metadata associated with a relying party and derives Long-valued
+ * configuration settings that are durations, based on EntityAttribute extension tags.
+ * 
+ * @since 3.4.0
+ */
+public class DurationConfigurationLookupStrategy extends AbstractMetadataDrivenConfigurationLookupStrategy<Long> {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(DurationConfigurationLookupStrategy.class);
+
+    /** Converter to handle duration strings. */
+    @Nonnull private final DurationToLongConverter durationConverter;
+
+    /** Constructor. */
+    public DurationConfigurationLookupStrategy() {
+        durationConverter = new DurationToLongConverter();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @Nullable protected Long doTranslate(@Nullable final ProfileRequestContext profileRequestContext,
+            @Nonnull final Attribute tag) {
+        
+        final List<XMLObject> values = tag.getAttributeValues();
+        if (values.size() != 1) {
+            log.error("Tag '{}' contained multiple values, returning none");
+            return null;
+        }
+        
+        log.debug("Converting tag '{}' to Long duration property", tag.getName());
+        return xmlObjectToDuration(values.get(0));
+    }
+    
+// Checkstyle: CyclomaticComplexity OFF
+    /**
+     * Convert an XMLObject to a Long based on a duration if the type is supported.
+     * 
+     * @param object object to convert
+     * 
+     * @return the converted value, or null
+     */
+    @Nullable private Long xmlObjectToDuration(@Nonnull final XMLObject object) {
+        if (object instanceof XSString) {
+            final String value = ((XSString) object).getValue();
+            if (value != null) {
+                try {
+                    return durationConverter.convert(value);
+                } catch (final IllegalArgumentException e) {
+                    log.error("Error converting duration", e);
+                    return null;
+                }
+            } else {
+                return null;
+            }
+        } else if (object instanceof XSInteger) {
+            final Integer value = ((XSInteger) object).getValue();
+            return value != null ? Integer.toUnsignedLong(value) : null;
+        } else if (object instanceof XSAny) {
+            final XSAny wc = (XSAny) object;
+            if (wc.getUnknownAttributes().isEmpty() && wc.getUnknownXMLObjects().isEmpty()) {
+                final String value = wc.getTextContent();
+                if (value != null) {
+                    try {
+                        return durationConverter.convert(value);
+                    } catch (final IllegalArgumentException e) {
+                        log.error("Error converting duration", e);
+                        return null;
+                    }
+                } else {
+                    return null;
+                }
+            }
+        }
+        
+        log.error("Unsupported conversion to Long duration from XMLObject type ({})", object.getClass().getName());
+        return null;
+    }
+// Checkstyle: CyclomaticComplexity ON
+    
+}
\ No newline at end of file
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/IntegerConfigurationLookupStrategy.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/IntegerConfigurationLookupStrategy.java
new file mode 100644
index 0000000..de0fe07
--- /dev/null
+++ b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/IntegerConfigurationLookupStrategy.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.saml.profile.config;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.core.xml.schema.XSAny;
+import org.opensaml.core.xml.schema.XSBoolean;
+import org.opensaml.core.xml.schema.XSBooleanValue;
+import org.opensaml.core.xml.schema.XSInteger;
+import org.opensaml.core.xml.schema.XSString;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.saml.saml2.core.Attribute;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A strategy function that examines SAML metadata associated with a relying party and derives Integer-valued
+ * configuration settings based on EntityAttribute extension tags.
+ * 
+ * @since 3.4.0
+ */
+public class IntegerConfigurationLookupStrategy extends AbstractMetadataDrivenConfigurationLookupStrategy<Integer> {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(IntegerConfigurationLookupStrategy.class);
+
+    /** {@inheritDoc} */
+    @Override
+    @Nullable protected Integer doTranslate(@Nullable final ProfileRequestContext profileRequestContext,
+            @Nonnull final Attribute tag) {
+        
+        final List<XMLObject> values = tag.getAttributeValues();
+        if (values.size() != 1) {
+            log.error("Tag '{}' contained multiple values, returning none");
+            return null;
+        }
+        
+        log.debug("Converting tag '{}' to Integer property", tag.getName());
+        return xmlObjectToInteger(values.get(0));
+    }
+    
+// Checkstyle: CyclomaticComplexity OFF
+    /**
+     * Convert an XMLObject to an Integer if the type is supported.
+     * 
+     * @param object object to convert
+     * 
+     * @return the converted value, or null
+     */
+    @Nullable private Integer xmlObjectToInteger(@Nonnull final XMLObject object) {
+        if (object instanceof XSString) {
+            final String value = ((XSString) object).getValue();
+            return value != null ? Integer.decode(value) : null;
+        } else if (object instanceof XSBoolean) {
+            final XSBooleanValue value = ((XSBoolean) object).getValue();
+            return value != null ? (value.getValue() ? 1 : 0) : null;
+        } else if (object instanceof XSInteger) {
+            return ((XSInteger) object).getValue();
+        } else if (object instanceof XSAny) {
+            final XSAny wc = (XSAny) object;
+            if (wc.getUnknownAttributes().isEmpty() && wc.getUnknownXMLObjects().isEmpty()) {
+                final String value = wc.getTextContent();
+                return value != null ? Integer.decode(value) : null;
+            }
+        }
+        
+        log.error("Unsupported conversion to Integer from XMLObject type ({})", object.getClass().getName());
+        return null;
+    }
+// Checkstyle: CyclomaticComplexity ON
+    
+}
\ No newline at end of file
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/ListConfigurationLookupStrategy.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/ListConfigurationLookupStrategy.java
new file mode 100644
index 0000000..86d8238
--- /dev/null
+++ b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/ListConfigurationLookupStrategy.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.saml.profile.config;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.saml.saml2.core.Attribute;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A strategy function that examines SAML metadata associated with a relying party and derives List<String>-valued
+ * configuration settings based on EntityAttribute extension tags.
+ * 
+ * @param <T> type of object in list
+ * 
+ * @since 3.4.0
+ */
+public class ListConfigurationLookupStrategy<T> extends AbstractCollectionConfigurationLookupStrategy<T,List<T>> {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(ListConfigurationLookupStrategy.class);
+
+    /** {@inheritDoc} */
+    @Override
+    @Nullable protected List<T> doTranslate(@Nullable final ProfileRequestContext profileRequestContext,
+            @Nonnull final Attribute tag) {
+
+        log.debug("Converting tag '{}' to List<{}> property", tag.getName(), getPropertyType().getSimpleName());
+        
+        final List<XMLObject> values = tag.getAttributeValues();
+        final List<T> result = new ArrayList<>(values.size());
+        for (final XMLObject value : values) {
+            final String converted = xmlObjectToString(value);
+            if (converted != null) {
+                try {
+                    result.add(createInstanceFromString(converted));
+                } catch (final Exception e) {
+                    log.error("Error converting tag value into {}", getPropertyType().getSimpleName(), e);
+                }
+            }
+        }
+        return result;
+    }
+    
+}
\ No newline at end of file
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/LongConfigurationLookupStrategy.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/LongConfigurationLookupStrategy.java
new file mode 100644
index 0000000..64fa254
--- /dev/null
+++ b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/LongConfigurationLookupStrategy.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.saml.profile.config;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.joda.time.DateTime;
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.core.xml.schema.XSAny;
+import org.opensaml.core.xml.schema.XSBoolean;
+import org.opensaml.core.xml.schema.XSBooleanValue;
+import org.opensaml.core.xml.schema.XSDateTime;
+import org.opensaml.core.xml.schema.XSInteger;
+import org.opensaml.core.xml.schema.XSString;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.saml.saml2.core.Attribute;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A strategy function that examines SAML metadata associated with a relying party and derives Long-valued
+ * configuration settings based on EntityAttribute extension tags.
+ * 
+ * @since 3.4.0
+ */
+public class LongConfigurationLookupStrategy extends AbstractMetadataDrivenConfigurationLookupStrategy<Long> {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(LongConfigurationLookupStrategy.class);
+
+    /** {@inheritDoc} */
+    @Override
+    @Nullable protected Long doTranslate(@Nullable final ProfileRequestContext profileRequestContext,
+            @Nonnull final Attribute tag) {
+        
+        final List<XMLObject> values = tag.getAttributeValues();
+        if (values.size() != 1) {
+            log.error("Tag '{}' contained multiple values, returning none");
+            return null;
+        }
+        
+        log.debug("Converting tag '{}' to Long property", tag.getName());
+        return xmlObjectToLong(values.get(0));
+    }
+    
+// Checkstyle: CyclomaticComplexity OFF
+    /**
+     * Convert an XMLObject to a Long if the type is supported.
+     * 
+     * @param object object to convert
+     * 
+     * @return the converted value, or null
+     */
+    @Nullable private Long xmlObjectToLong(@Nonnull final XMLObject object) {
+        if (object instanceof XSString) {
+            final String value = ((XSString) object).getValue();
+            return value != null ? Long.decode(value) : null;
+        } else if (object instanceof XSBoolean) {
+            final XSBooleanValue value = ((XSBoolean) object).getValue();
+            return value != null ? (value.getValue() ? 1L : 0L) : null;
+        } else if (object instanceof XSInteger) {
+            final Integer value = ((XSInteger) object).getValue(); 
+            return value != null ? value.longValue() : null;
+        } else if (object instanceof XSDateTime) {
+            final DateTime dt = ((XSDateTime) object).getValue();
+            return dt != null ? dt.getMillis() : null;
+        } else if (object instanceof XSAny) {
+            final XSAny wc = (XSAny) object;
+            if (wc.getUnknownAttributes().isEmpty() && wc.getUnknownXMLObjects().isEmpty()) {
+                final String value = wc.getTextContent();
+                return value != null ? Long.decode(value) : null;
+            }
+        }
+        
+        log.error("Unsupported conversion to Long from XMLObject type ({})", object.getClass().getName());
+        return null;
+    }
+// Checkstyle: CyclomaticComplexity ON
+    
+}
\ No newline at end of file
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/SetConfigurationLookupStrategy.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/SetConfigurationLookupStrategy.java
new file mode 100644
index 0000000..a74ddb4
--- /dev/null
+++ b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/SetConfigurationLookupStrategy.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.saml.profile.config;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.saml.saml2.core.Attribute;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A strategy function that examines SAML metadata associated with a relying party and derives Set<String>-valued
+ * configuration settings based on EntityAttribute extension tags.
+ * 
+ * @param <T> type of object in list
+ * 
+ * @since 3.4.0
+ */
+public class SetConfigurationLookupStrategy<T> extends AbstractCollectionConfigurationLookupStrategy<T,Set<T>> {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(SetConfigurationLookupStrategy.class);
+
+    /** {@inheritDoc} */
+    @Override
+    @Nullable protected Set<T> doTranslate(@Nullable final ProfileRequestContext profileRequestContext,
+            @Nonnull final Attribute tag) {
+        
+        log.debug("Converting tag '{}' to Set<String> property", tag.getName());
+        
+        final List<XMLObject> values = tag.getAttributeValues();
+        final Set<T> result = new HashSet<>(values.size());
+        for (final XMLObject value : values) {
+            final String converted = xmlObjectToString(value);
+            if (converted != null) {
+                try {
+                    result.add(createInstanceFromString(converted));
+                } catch (final Exception e) {
+                    log.error("Error converting tag value into {}", getPropertyType().getSimpleName(), e);
+                }
+            }
+        }
+        return result;
+    }
+        
+}
\ No newline at end of file
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/StringConfigurationLookupStrategy.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/StringConfigurationLookupStrategy.java
new file mode 100644
index 0000000..714722c
--- /dev/null
+++ b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/StringConfigurationLookupStrategy.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.saml.profile.config;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.joda.time.DateTime;
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.core.xml.schema.XSAny;
+import org.opensaml.core.xml.schema.XSBase64Binary;
+import org.opensaml.core.xml.schema.XSBoolean;
+import org.opensaml.core.xml.schema.XSBooleanValue;
+import org.opensaml.core.xml.schema.XSDateTime;
+import org.opensaml.core.xml.schema.XSInteger;
+import org.opensaml.core.xml.schema.XSString;
+import org.opensaml.core.xml.schema.XSURI;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.saml.saml2.core.Attribute;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A strategy function that examines SAML metadata associated with a relying party and derives String-valued
+ * configuration settings based on EntityAttribute extension tags.
+ * 
+ * @since 3.4.0
+ */
+public class StringConfigurationLookupStrategy extends AbstractMetadataDrivenConfigurationLookupStrategy<String> {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(StringConfigurationLookupStrategy.class);
+
+    /** {@inheritDoc} */
+    @Override
+    @Nullable protected String doTranslate(@Nullable final ProfileRequestContext profileRequestContext,
+            @Nonnull final Attribute tag) {
+        
+        final List<XMLObject> values = tag.getAttributeValues();
+        if (values.size() != 1) {
+            log.error("Tag '{}' contained multiple values, returning none");
+            return null;
+        }
+        
+        log.debug("Converting tag '{}' to String property", tag.getName());
+        return xmlObjectToString(values.get(0));
+    }
+    
+// Checkstyle: CyclomaticComplexity OFF
+    /**
+     * Convert an XMLObject to a String if the type is supported.
+     * 
+     * @param object object to convert
+     * 
+     * @return the converted value, or null
+     */
+    @Nullable private String xmlObjectToString(@Nonnull final XMLObject object) {
+        if (object instanceof XSString) {
+            return ((XSString) object).getValue();
+        } else if (object instanceof XSURI) {
+            return ((XSURI) object).getValue();
+        } else if (object instanceof XSBoolean) {
+            final XSBooleanValue value = ((XSBoolean) object).getValue();
+            return value != null ? (value.getValue() ? "1" : "0") : null;
+        } else if (object instanceof XSInteger) {
+            final Integer value = ((XSInteger) object).getValue();
+            return value != null ? value.toString() : null;
+        } else if (object instanceof XSDateTime) {
+            final DateTime dt = ((XSDateTime) object).getValue();
+            return dt != null ? Long.toString(dt.getMillis()) : null;
+        } else if (object instanceof XSBase64Binary) {
+            return ((XSBase64Binary) object).getValue();
+        } else if (object instanceof XSAny) {
+            final XSAny wc = (XSAny) object;
+            if (wc.getUnknownAttributes().isEmpty() && wc.getUnknownXMLObjects().isEmpty()) {
+                return wc.getTextContent();
+            }
+        }
+        
+        log.error("Unsupported conversion to String from XMLObject type ({})", object.getClass().getName());
+        return null;
+    }
+// Checkstyle: CyclomaticComplexity ON
+    
+}
\ No newline at end of file

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list