[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