[java-sp-server] branch main updated: Profile config refactor.

Scott Cantor cantor.2 at osu.edu
Wed Mar 1 19:25:04 UTC 2023


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

scantor pushed a commit to branch main
in repository java-sp-server.

View the commit online:
http://git.shibboleth.net/view/?p=java-sp-server.git;a=commit;h=aa6f24604da8d105eda214c77bc4c801ffc5be5a

The following commit(s) were added to refs/heads/main by this push:
     new aa6f246  Profile config refactor.
aa6f246 is described below

commit aa6f24604da8d105eda214c77bc4c801ffc5be5a
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Wed Mar 1 14:25:02 2023 -0500

    Profile config refactor.
---
 pom.xml                                            |   8 +
 sp-conf-impl/pom.xml                               |  22 ++
 .../shibboleth/sp/conf/relying-party-system.xml    | 371 +++++++++++++++++++++
 .../net/shibboleth/sp/conf/services-system.xml     |  22 ++
 sp-conf/src/main/resources/conf/credentials.xml    |  65 ++++
 .../src/main/resources/conf/services.properties    |   4 +
 sp-conf/src/main/resources/conf/services.xml       |   4 +
 sp-conf/src/main/resources/conf/sp.properties      |   2 +-
 sp-saml-api/pom.xml                                |   5 +
 .../config/BrowserSSOProfileConfiguration.java}    |  44 +--
 .../profile/config/SAML2ProfileConfiguration.java  |  22 +-
 .../config/SingleLogoutProfileConfiguration.java   |  29 +-
 .../saml2/profile}/config/package-info.java        |   2 +-
 .../SAML2ArtifactConsumerProfileConfiguration.java |  50 ---
 .../sp/saml2/config/SAML2ProfileConfiguration.java |  81 -----
 sp-saml-impl/pom.xml                               |  18 +-
 ...tSAML2ArtifactConsumerProfileConfiguration.java |   5 +-
 ...SAML2AssertionConsumerProfileConfiguration.java |  23 +-
 .../impl}/AbstractSAML2ProfileConfiguration.java   |  42 ++-
 .../impl}/BrowserSSOProfileConfiguration.java      | 161 ++-------
 .../impl}/SingleLogoutProfileConfiguration.java    |  62 +---
 .../saml2/profile/config/impl}/package-info.java   |   5 +-
 .../AbstractSAML2ProfileConfigurationTest.java     |  61 ++++
 .../impl/BrowserSSOProfileConfigurationTest.java   | 180 ++++++++++
 .../impl/SingleLogoutProfileConfigurationTest.java |  68 ++++
 sp-server-api/pom.xml                              |   5 +
 .../main/java/net/shibboleth/sp/Application.java   |  44 ++-
 .../AbstractConditionalProfileConfiguration.java   |  68 ----
 .../sp/config/AbstractProfileConfiguration.java    | 140 --------
 .../net/shibboleth/sp/config/AssertingParty.java   |  68 ----
 .../sp/config/AssertingPartyResolver.java          |  50 ---
 .../net/shibboleth/sp/config/CredentialHolder.java |  63 ----
 .../shibboleth/sp/config/ProfileConfiguration.java |  63 ----
 .../sp/config/impl/BasicAssertingParty.java        | 147 --------
 .../config/impl/DefaultAssertingPartyResolver.java | 304 -----------------
 .../net/shibboleth/sp/impl/BasicApplication.java   |  45 ++-
 .../sp/config/impl/BasicAssertingPartyTest.java    | 119 -------
 .../impl/DefaultAssertingPartyResolverTest.java    | 154 ---------
 38 files changed, 1033 insertions(+), 1593 deletions(-)

diff --git a/pom.xml b/pom.xml
index 525989f..a702ada 100644
--- a/pom.xml
+++ b/pom.xml
@@ -16,6 +16,7 @@
     <packaging>pom</packaging>
     
     <properties>
+        <shib-profile.version>5.0.0-SNAPSHOT</shib-profile.version>
         <shib-attribute.version>5.0.0-SNAPSHOT</shib-attribute.version>
         <shib-metadata.version>5.0.0-SNAPSHOT</shib-metadata.version>
         <opensaml.version>5.0.0-SNAPSHOT</opensaml.version>
@@ -90,6 +91,13 @@
     </dependencies>
     <dependencyManagement>
         <dependencies>
+            <dependency>
+                <groupId>net.shibboleth</groupId>
+                <artifactId>shib-profile-bom</artifactId>
+                <version>${shib-profile.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
             <dependency>
                 <groupId>net.shibboleth</groupId>
                 <artifactId>shib-attribute-bom</artifactId>
diff --git a/sp-conf-impl/pom.xml b/sp-conf-impl/pom.xml
index 219356d..1c049f7 100644
--- a/sp-conf-impl/pom.xml
+++ b/sp-conf-impl/pom.xml
@@ -34,6 +34,28 @@
             <version>${project.version}</version>
             <scope>runtime</scope>
         </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>sp-saml-impl</artifactId>
+            <version>${project.version}</version>
+            <scope>runtime</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>net.shibboleth</groupId>
+            <artifactId>shib-profile-api</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>net.shibboleth</groupId>
+            <artifactId>shib-profile-impl</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>net.shibboleth</groupId>
+            <artifactId>shib-saml-profile-api</artifactId>
+            <scope>runtime</scope>
+        </dependency>
         
         <dependency>
             <groupId>net.shibboleth</groupId>
diff --git a/sp-conf-impl/src/main/resources/net/shibboleth/sp/conf/relying-party-system.xml b/sp-conf-impl/src/main/resources/net/shibboleth/sp/conf/relying-party-system.xml
new file mode 100644
index 0000000..40d04f3
--- /dev/null
+++ b/sp-conf-impl/src/main/resources/net/shibboleth/sp/conf/relying-party-system.xml
@@ -0,0 +1,371 @@
+<?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">
+
+    <context:annotation-config/>
+
+    <!-- This is one of the few properties we rely on that has no default. -->
+    <bean id="entityID" class="java.lang.String" c:_0="%{sp.entityID}" />
+
+    <!-- Pulls together the various components that feed the resolver. -->
+    <bean class="net.shibboleth.profile.relyingparty.impl.DefaultRelyingPartyConfigurationResolver"
+        p:unverifiedConfiguration-ref="shibboleth.UnverifiedRelyingParty"
+        p:defaultConfiguration-ref="shibboleth.DefaultRelyingParty"
+        p:relyingPartyConfigurations-ref="shibboleth.RelyingPartyOverrides"
+        p:defaultSecurityConfiguration-ref="%{sp.security.config:shibboleth.DefaultSecurityConfiguration}" />
+
+    <!--
+    Auto-wiring exposers for credentials to get them loaded into the bean above.
+    The qualifiers control which auto-wiring point is used.
+    -->
+    <bean class="net.shibboleth.spring.security.CredentialHolder"
+            c:_0="#{getObject('shibboleth.SigningCredentials')}">
+        <qualifier value="signing"/>
+    </bean>
+    <bean class="net.shibboleth.spring.security.CredentialHolder"
+            c:_0="#{getObject('shibboleth.EncryptionCredentials')}">
+        <qualifier value="encryption"/>
+    </bean>
+
+    <!-- Parent bean for generic RelyingParty overrides that establishes defaults. -->
+    <bean id="RelyingParty" abstract="true" class="net.shibboleth.profile.relyingparty.RelyingPartyConfiguration" />
+
+    <!-- Parent bean for RelyingParty overrides based on activation by name(s). -->
+    <bean id="RelyingPartyByName" abstract="true" parent="RelyingParty"
+        class="net.shibboleth.saml.relyingparty.RelyingPartyConfigurationSupport" factory-method="byName" />
+
+    <!-- Parent bean for RelyingParty overrides based on activation by group or SAML affiliation. -->
+    <bean id="RelyingPartyByGroup" abstract="true" parent="RelyingParty"
+        class="net.shibboleth.saml.relyingparty.RelyingPartyConfigurationSupport" factory-method="byGroup"
+        c:resolver-ref="shibboleth.MetadataResolver" />
+
+    <!-- Parent bean for RelyingParty overrides based on activation by local containment only. -->
+    <bean id="RelyingPartyByEntitiesDescriptor" abstract="true" parent="RelyingParty"
+            class="net.shibboleth.saml.relyingparty.RelyingPartyConfigurationSupport" factory-method="byGroup">
+        <constructor-arg name="resolver"><null /></constructor-arg>
+    </bean>
+
+    <!-- Parent bean for RelyingParty overrides based on activation by tag. -->
+    <bean id="RelyingPartyByTag" abstract="true" parent="RelyingParty"
+        class="net.shibboleth.saml.relyingparty.RelyingPartyConfigurationSupport" factory-method="byTag"
+        c:trim="false" c:matchAll="false" />
+
+    <!-- Parent bean for RelyingParty overrides based on activation by tag. -->
+    <bean id="RelyingPartyByMappedTag" abstract="true" parent="RelyingParty"
+        class="net.shibboleth.saml.relyingparty.RelyingPartyConfigurationSupport" factory-method="byMappedTag"
+        c:trim="false" c:matchAll="false" />
+
+    <bean id="TagCandidate" abstract="true"
+        class="org.opensaml.saml.common.profile.logic.EntityAttributesPredicate.Candidate" />
+        
+    <!-- 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="SAML2.SSO" lazy-init="true"
+        class="net.shibboleth.sp.saml.saml2.profile.config.impl.BrowserSSOProfileConfiguration"
+        p:encryptionOptionalPredicate="%{sp.encryption.optional:false}" />
+
+    <util:constant id="SAML2.SSO.FEATURE_AUTHNCONTEXT"
+        static-field="net.shibboleth.saml.saml2.profile.config.BrowserSSOProfileConfiguration.FEATURE_AUTHNCONTEXT"/>
+
+    <util:constant id="SAML2.SSO.FEATURE_SCOPING"
+        static-field="net.shibboleth.saml.saml2.profile.config.BrowserSSOProfileConfiguration.FEATURE_SCOPING"/>
+
+    <util:constant id="SAML2.SSO.FEATURE_NAMEIDFORMAT"
+        static-field="net.shibboleth.saml.saml2.profile.config.BrowserSSOProfileConfiguration.FEATURE_NAMEIDFORMAT"/>
+
+    <util:constant id="SAML2.SSO.FEATURE_SPNAMEQUALIFIER"
+        static-field="net.shibboleth.saml.saml2.profile.config.BrowserSSOProfileConfiguration.FEATURE_SPNAMEQUALIFIER"/>
+
+    <util:constant id="SAML2.SSO.FEATURE_FORCEAUTHN"
+        static-field="net.shibboleth.saml.saml2.profile.config.BrowserSSOProfileConfiguration.FEATURE_FORCEAUTHN"/>
+
+    <bean id="SAML2.Logout" lazy-init="true"
+        class="net.shibboleth.sp.saml.saml2.profile.config.impl.SingleLogoutProfileConfiguration"
+        p:encryptionOptionalPredicate="%{sp.encryption.optional:false}"
+        p:asynchronous="%{sp.logout.asynchronous:true}" />
+    
+    <!--
+    Security Configuration Defaults
+    
+    These settings establish the default security configurations for signatures and encryption
+    and loads the default credentials used.
+    -->
+
+    <bean id="shibboleth.DefaultSecurityConfiguration" class="org.opensaml.xmlsec.config.BasicXMLSecurityConfiguration">
+        <property name="signatureSigningConfiguration">
+            <ref bean="#{'%{sp.signing.config:shibboleth.SigningConfiguration.SHA256}'.trim()}" />
+        </property>
+        
+        <property name="signatureValidationConfiguration">
+            <bean parent="shibboleth.BasicSignatureValidationConfiguration" 
+                p:signatureTrustEngine-ref="#{'%{sp.trust.signatures:shibboleth.ChainingSignatureTrustEngine}'.trim()}" />
+        </property>
+
+        <property name="clientTLSValidationConfiguration">
+            <bean parent="shibboleth.BasicClientTLSValidationConfiguration"
+                p:x509TrustEngine-ref="#{'%{sp.trust.certificates:shibboleth.ChainingX509TrustEngine}'.trim()}" />
+        </property>
+        
+        <property name="httpClientSecurityConfiguration">
+            <bean parent="shibboleth.BasicHttpClientSecurityConfiguration"
+                p:clientTLSCredential="#{getObject('shibboleth.DefaultClientTLSCredential')}"
+                p:TLSTrustEngine-ref="#{'%{sp.trust.httpClient.tls:shibboleth.ChainingX509TrustEngine}'.trim()}" />
+        </property>
+        
+        <property name="encryptionConfiguration">
+            <ref bean="#{'%{sp.encryption.config:shibboleth.EncryptionConfiguration.CBC}'.trim()}" />
+        </property>
+
+        <property name="decryptionConfiguration">
+            <bean parent="shibboleth.BasicDecryptionConfiguration"
+                p:KEKKeyInfoCredentialResolver-ref="defaultKeyEncryptionCredentialResolver"
+                p:encryptedKeyResolver-ref="defaultEncryptedKeyResolver" />
+        </property>
+    </bean>
+    
+    <!-- KeyInfoGeneration machinery. Former gets injected into the signing and encryption configs. -->
+    <bean id="NamedKeyInfoGeneratorManager"
+        class="org.opensaml.xmlsec.keyinfo.NamedKeyInfoGeneratorManager"
+        p:useDefaultManager="true" />
+    <bean id="DefaultKeyInfoGeneratorManager"
+        factory-bean="NamedKeyInfoGeneratorManager"
+        factory-method="getDefaultManager" />
+        
+    <!-- These install two default generator managers, for Credential and X509Credential. -->
+    <bean class="org.springframework.beans.factory.config.MethodInvokingBean"
+        p:targetObject-ref="DefaultKeyInfoGeneratorManager"
+        p:targetMethod="registerFactory">
+        <property name="arguments">
+            <list>
+                <ref bean="%{sp.security.basicKeyInfoFactory:shibboleth.BasicKeyInfoGeneratorFactory}" />
+            </list>
+        </property>
+    </bean>
+    <bean class="org.springframework.beans.factory.config.MethodInvokingBean"
+        p:targetObject-ref="DefaultKeyInfoGeneratorManager"
+        p:targetMethod="registerFactory">
+        <property name="arguments">
+            <list>
+                <ref bean="%{sp.security.x509KeyInfoFactory:shibboleth.X509KeyInfoGeneratorFactory}" />
+            </list>
+        </property>
+    </bean>
+    
+    <!-- The default generators, overrideable by inheriting from them and then setting the two properties. -->
+    <bean id="shibboleth.BasicKeyInfoGeneratorFactory" class="org.opensaml.xmlsec.keyinfo.impl.BasicKeyInfoGeneratorFactory"
+        p:emitPublicKeyValue="true"
+        p:emitKeyNames="true"  />
+    <bean id="shibboleth.X509KeyInfoGeneratorFactory" class="org.opensaml.xmlsec.keyinfo.impl.X509KeyInfoGeneratorFactory"
+        p:emitEntityCertificate="true" />
+
+    <!-- Convenience beans for directly injecting SHA-256 or SHA-1 usage.  -->
+    
+    <bean id="shibboleth.SecurityConfiguration.SHA256" parent="shibboleth.DefaultSecurityConfiguration" lazy-init="true"
+        p:signatureSigningConfiguration-ref="shibboleth.SigningConfiguration.SHA256" />
+
+    <bean id="shibboleth.SecurityConfiguration.SHA1" parent="shibboleth.DefaultSecurityConfiguration" lazy-init="true"
+        p:signatureSigningConfiguration-ref="shibboleth.SigningConfiguration.SHA1" />
+
+    <!-- Actual signing configurations for SHA-256 and SHA-1. -->
+    
+    <bean id="shibboleth.SigningConfiguration.SHA256" parent="shibboleth.BasicSignatureSigningConfiguration"
+            p:signingCredentials-ref="shibboleth.DefaultSigningCredential"
+            p:keyInfoGeneratorManager-ref="NamedKeyInfoGeneratorManager">
+        <property name="signatureAlgorithms">
+            <list>
+                <util:constant
+                    static-field="org.opensaml.xmlsec.signature.support.SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256" />
+                <util:constant
+                    static-field="org.opensaml.xmlsec.signature.support.SignatureConstants.ALGO_ID_SIGNATURE_ECDSA_SHA256" />
+            </list>
+        </property>
+        <property name="signatureReferenceDigestMethods">
+            <list>
+                <util:constant
+                    static-field="org.opensaml.xmlsec.signature.support.SignatureConstants.ALGO_ID_DIGEST_SHA256" />
+            </list>
+        </property>
+    </bean>
+
+    <bean id="shibboleth.SigningConfiguration.SHA1" parent="shibboleth.BasicSignatureSigningConfiguration"
+            p:signingCredentials-ref="shibboleth.DefaultSigningCredential"
+            p:keyInfoGeneratorManager-ref="NamedKeyInfoGeneratorManager">
+        <property name="signatureAlgorithms">
+            <list>
+                <util:constant
+                    static-field="org.opensaml.xmlsec.signature.support.SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1" />
+                <util:constant
+                    static-field="org.opensaml.xmlsec.signature.support.SignatureConstants.ALGO_ID_SIGNATURE_ECDSA_SHA1" />
+            </list>
+        </property>
+        <property name="signatureReferenceDigestMethods">
+            <list>
+                <util:constant
+                    static-field="org.opensaml.xmlsec.signature.support.SignatureConstants.ALGO_ID_DIGEST_SHA1" />
+            </list>
+        </property>
+    </bean>
+
+    <!-- Convenience beans for directly injecting AES-CBC or AES-GCM usage.  -->
+    
+    <bean id="shibboleth.SecurityConfiguration.CBC" parent="shibboleth.DefaultSecurityConfiguration" lazy-init="true"
+        p:encryptionConfiguration-ref="shibboleth.EncryptionConfiguration.CBC" />
+
+    <bean id="shibboleth.SecurityConfiguration.GCM" parent="shibboleth.DefaultSecurityConfiguration" lazy-init="true"
+        p:encryptionConfiguration-ref="shibboleth.EncryptionConfiguration.GCM" />
+
+    <!-- Actual encryption configurations for AES-CBC and AES-GCM. -->
+
+    <bean id="shibboleth.EncryptionConfiguration.CBC" parent="shibboleth.BasicEncryptionConfiguration"
+            p:keyTransportKeyInfoGeneratorManager-ref="NamedKeyInfoGeneratorManager">
+        <property name="dataEncryptionAlgorithms">
+            <list>
+                <util:constant
+                    static-field="org.opensaml.xmlsec.encryption.support.EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128" />
+            </list>
+        </property>
+        <property name="keyTransportEncryptionAlgorithms">
+            <list>
+                <util:constant
+                    static-field="org.opensaml.xmlsec.encryption.support.EncryptionConstants.ALGO_ID_KEYTRANSPORT_RSAOAEP" />
+            </list>
+        </property>
+    </bean>
+
+    <bean id="shibboleth.EncryptionConfiguration.GCM" parent="shibboleth.BasicEncryptionConfiguration"
+            p:keyTransportKeyInfoGeneratorManager-ref="NamedKeyInfoGeneratorManager">
+        <property name="dataEncryptionAlgorithms">
+            <list>
+                <util:constant
+                    static-field="org.opensaml.xmlsec.encryption.support.EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128_GCM" />
+            </list>
+        </property>
+        <property name="keyTransportEncryptionAlgorithms">
+            <list>
+                <util:constant
+                    static-field="org.opensaml.xmlsec.encryption.support.EncryptionConstants.ALGO_ID_KEYTRANSPORT_RSAOAEP" />
+            </list>
+        </property>
+    </bean>
+    
+    <!-- Beans related to configuration of key agreement, mostly to provide abstract parent beans for impl classes -->
+    
+    <bean id="shibboleth.KeyAgreement.Configuration" abstract="true"
+        class="org.opensaml.saml.security.SAMLMetadataKeyAgreementEncryptionConfiguration" />
+
+    <bean id="shibboleth.KeyAgreement.ConcatKDF" abstract="true" class="org.opensaml.xmlsec.derivation.impl.ConcatKDF" />
+
+    <bean id="shibboleth.KeyAgreement.PBKDF2" abstract="true" class="org.opensaml.xmlsec.derivation.impl.PBKDF2" />
+
+    <bean id="shibboleth.KeyAgreement.DigestMethod" abstract="true" class="org.opensaml.xmlsec.agreement.impl.DigestMethod" />
+
+    <bean id="shibboleth.KeyAgreement.KANonce" abstract="true" class="org.opensaml.xmlsec.agreement.impl.KANonce" />
+
+    <bean id="shibboleth.MetadataPKIXValidationInformationResolver"
+        class="net.shibboleth.idp.saml.security.impl.MetadataPKIXValidationInformationResolver"
+        c:resolver-ref="shibboleth.RoleDescriptorResolver" />
+
+    <!-- Trust Engine options for message signature evaluation. -->
+        
+    <bean id="shibboleth.ExplicitKeySignatureTrustEngine"
+        class="org.opensaml.xmlsec.signature.support.impl.ExplicitKeySignatureTrustEngine"
+        c:resolver-ref="shibboleth.MetadataCredentialResolver"
+        c:keyInfoResolver-ref="shibboleth.KeyInfoCredentialResolver" />
+
+    <bean id="shibboleth.PKIXSignatureTrustEngine"
+        class="org.opensaml.xmlsec.signature.support.impl.PKIXSignatureTrustEngine" 
+        c:resolver-ref="shibboleth.MetadataPKIXValidationInformationResolver"
+        c:keyInfoResolver-ref="shibboleth.KeyInfoCredentialResolver" />
+
+    <bean id="shibboleth.ChainingSignatureTrustEngine"
+            class="org.opensaml.xmlsec.signature.support.impl.ChainingSignatureTrustEngine">
+        <constructor-arg>
+            <list>
+                <ref bean="shibboleth.ExplicitKeySignatureTrustEngine" />
+                <ref bean="shibboleth.PKIXSignatureTrustEngine" />
+            </list>
+        </constructor-arg>
+    </bean>
+
+    <!-- Trust Engine options for X.509 certificate evaluation (e.g., TLS). -->
+
+    <bean id="shibboleth.ExplicitKeyX509TrustEngine"
+        class="org.opensaml.security.trust.impl.ExplicitKeyTrustEngine"
+        c:resolver-ref="shibboleth.MetadataCredentialResolver" />
+    <!-- Backward compatibility, remove in 5.0. -->
+    <alias name="shibboleth.ExplicitKeyX509TrustEngine" alias="shibboleth.ExplicitX509TrustEngine" />
+
+    <bean id="shibboleth.PKIXX509TrustEngine"
+        class="org.opensaml.security.x509.impl.PKIXX509CredentialTrustEngine"
+        c:resolver-ref="shibboleth.MetadataPKIXValidationInformationResolver" />
+
+    <bean id="shibboleth.ChainingX509TrustEngine" class="org.opensaml.security.trust.impl.ChainingTrustEngine">
+        <constructor-arg>
+            <list>
+                <ref bean="shibboleth.ExplicitKeyX509TrustEngine" />
+                <ref bean="shibboleth.PKIXX509TrustEngine" />
+            </list>
+        </constructor-arg>
+    </bean>
+
+    <!-- Defines decryption keys, along with support for optimizing lookup based on KeyInfo hints. -->
+    <bean id="defaultKeyEncryptionCredentialResolver" class="org.opensaml.xmlsec.keyinfo.impl.ChainingKeyInfoCredentialResolver">
+        <constructor-arg>
+            <list>
+                <bean class="org.opensaml.xmlsec.keyinfo.impl.LocalKeyInfoCredentialResolver">
+                    <constructor-arg name="localCredentialResolver">
+                        <bean class="org.opensaml.xmlsec.keyinfo.impl.CollectionKeyInfoCredentialResolver"
+                            c:credentials-ref="shibboleth.DefaultEncryptionCredentials" />
+                    </constructor-arg>
+                    <constructor-arg name="keyInfoProviders">
+                        <list>
+                            <bean class="org.opensaml.xmlsec.keyinfo.impl.provider.RSAKeyValueProvider" />
+                            <bean class="org.opensaml.xmlsec.keyinfo.impl.provider.DSAKeyValueProvider" />
+                            <bean class="org.opensaml.xmlsec.keyinfo.impl.provider.ECKeyValueProvider" />
+                            <bean class="org.opensaml.xmlsec.keyinfo.impl.provider.DEREncodedKeyValueProvider" />
+                            <bean class="org.opensaml.xmlsec.keyinfo.impl.provider.InlineX509DataProvider" />
+                            <bean class="org.opensaml.xmlsec.keyinfo.impl.provider.AgreementMethodKeyInfoProvider" />
+                        </list>
+                    </constructor-arg>
+                </bean>
+                <bean class="org.opensaml.xmlsec.keyinfo.impl.StaticKeyInfoCredentialResolver"
+                    c:credentials-ref="shibboleth.DefaultEncryptionCredentials" />
+            </list>
+        </constructor-arg>
+    </bean>
+    
+    <!-- Defines how the system locates EncryptedKey elements sent in messages containing encrypted objects. -->
+    <bean id="defaultEncryptedKeyResolver" class="org.opensaml.xmlsec.encryption.support.ChainingEncryptedKeyResolver"
+            c:recipients="#{getObject('shibboleth.DecryptionRecipients') ?: getObject('entityID')}">
+        <constructor-arg name="encKeyResolvers">
+            <list>
+                <bean class="org.opensaml.xmlsec.encryption.support.InlineEncryptedKeyResolver" />
+                <bean class="org.opensaml.saml.saml2.encryption.EncryptedElementTypeEncryptedKeyResolver" />
+                <bean class="org.opensaml.xmlsec.encryption.support.SimpleRetrievalMethodEncryptedKeyResolver" />
+                <bean class="org.opensaml.xmlsec.encryption.support.SimpleKeyInfoReferenceEncryptedKeyResolver" />
+            </list>
+        </constructor-arg>
+    </bean>
+
+    <!-- Wildcard import hook for plugins. -->
+    <import resource="classpath*:/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml" />
+
+</beans>
diff --git a/sp-conf-impl/src/main/resources/net/shibboleth/sp/conf/services-system.xml b/sp-conf-impl/src/main/resources/net/shibboleth/sp/conf/services-system.xml
index fcf4245..fd8d42e 100644
--- a/sp-conf-impl/src/main/resources/net/shibboleth/sp/conf/services-system.xml
+++ b/sp-conf-impl/src/main/resources/net/shibboleth/sp/conf/services-system.xml
@@ -53,6 +53,28 @@
         </property>
     </bean>
 
+    <!-- Relying Party Service -->
+
+    <bean id="shibboleth.RelyingPartyResolverService" parent="shibboleth.ReloadableService"
+            p:serviceConfigurations-ref="ExtendedRelyingPartyResolverResources"
+            p:failFast="%{sp.service.relyingparty.failFast:%{sp.service.failFast:false}}"
+            p:reloadCheckDelay="%{sp.service.relyingparty.checkInterval:PT0S}">
+        <constructor-arg name="claz" value="net.shibboleth.profile.relyingparty.RelyingPartyConfigurationResolver" />
+        <constructor-arg name="strategy">
+            <bean class="net.shibboleth.shared.spring.service.ClassBasedServiceStrategy"
+                c:serviceableClaz="net.shibboleth.profile.relyingparty.impl.DefaultRelyingPartyConfigurationResolver" />
+        </constructor-arg>
+    </bean>
+    <!-- Auto-append system config file to resource set. -->
+    <bean id ="ExtendedRelyingPartyResolverResources" parent="shibboleth.ListCombiner"
+          p:firstList-ref="#{'%{sp.service.relyingparty.resources:shibboleth.RelyingPartyResolverResources}'.trim()}" >
+        <property name="secondList">
+            <util:list >
+                <value>classpath:/net/shibboleth/sp/conf/relying-party-system.xml</value>
+            </util:list>
+        </property>
+    </bean>
+
     <!-- Attribute Filter Service -->
 
     <bean id="shibboleth.AttributeFilterService" parent="shibboleth.ReloadableService"
diff --git a/sp-conf/src/main/resources/conf/credentials.xml b/sp-conf/src/main/resources/conf/credentials.xml
new file mode 100644
index 0000000..f92cf1a
--- /dev/null
+++ b/sp-conf/src/main/resources/conf/credentials.xml
@@ -0,0 +1,65 @@
+<?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 defines the signing and encryption key and certificate pairs referenced by your asserting-party.xml
+    configuration. You don't normally need to touch this, unless you have advanced requirements such as
+    supporting multiple sets of keys for different IdPs, in which case you may want to define all your
+    credentials here for convenience.
+    -->
+
+    <!--
+    The list of ALL of your SP's signing credentials. If you define additional signing credentials,
+    for example for specific IdPs or different key types, make sure to include them within this list.
+    -->
+    <util:list id="shibboleth.SigningCredentials">
+        <ref bean="shibboleth.DefaultSigningCredential" />
+    </util:list>
+    
+    <!-- Your SP's default signing key, set via property file. -->
+    <bean id="shibboleth.DefaultSigningCredential" parent="shibboleth.BasicX509CredentialFactoryBean"
+        p:privateKeyResource="%{sp.signing.key}"
+        p:certificateResource="%{sp.signing.cert}"
+        p:entityId-ref="entityID" />
+        
+    <!-- Your SPs default client TLS credential, by default the same as the default signing credential. -->
+    <alias alias="shibboleth.DefaultClientTLSCredential" name="shibboleth.DefaultSigningCredential" />
+    
+    <!--
+    The list of ALL of your SP's encryption credentials. By default this is just an alias
+    for 'shibboleth.DefaultEncryptionCredentials'. It could be re-defined as
+    a list with additional credentials if needed.
+    -->
+    <alias alias="shibboleth.EncryptionCredentials" name="shibboleth.DefaultEncryptionCredentials" />
+        
+    <!-- Your SP's default encryption (really decryption) keys, set via property file. -->
+    <util:list id="shibboleth.DefaultEncryptionCredentials">
+        <bean parent="shibboleth.BasicX509CredentialFactoryBean"
+            p:privateKeyResource="%{sp.encryption.key}"
+            p:certificateResource="%{sp.encryption.cert}"
+            p:entityId-ref="entityID" />
+
+        <!--
+        For key rollover, uncomment and point to your original keypair, and use the one above
+        to point to your new keypair. Once metadata has propagated, comment this one out again.
+        -->
+        <!--
+        <bean parent="shibboleth.BasicX509CredentialFactoryBean"
+            p:privateKeyResource="%{sp.encryption.key.2}"
+            p:certificateResource="%{sp.encryption.cert.2}"
+            p:entityId-ref="entityID" />
+        -->
+    </util:list>
+
+</beans>
diff --git a/sp-conf/src/main/resources/conf/services.properties b/sp-conf/src/main/resources/conf/services.properties
index f962115..bce22cd 100644
--- a/sp-conf/src/main/resources/conf/services.properties
+++ b/sp-conf/src/main/resources/conf/services.properties
@@ -16,6 +16,10 @@ sp.service.logging.checkInterval = PT5M
 #sp.service.provider.failFast = true
 sp.service.provider.checkInterval = PT5M
 
+#sp.service.assertingparty.resources = shibboleth.AssertingPartyResolverResources
+#sp.service.assertingparty.failFast = false
+sp.service.assertingparty.checkInterval = PT15M
+
 #sp.service.metadata.resources = shibboleth.MetadataResolverResources
 #sp.service.metadata.failFast = false
 #sp.service.metadata.checkInterval = PT0S
diff --git a/sp-conf/src/main/resources/conf/services.xml b/sp-conf/src/main/resources/conf/services.xml
index 2a63d9f..2fd1360 100644
--- a/sp-conf/src/main/resources/conf/services.xml
+++ b/sp-conf/src/main/resources/conf/services.xml
@@ -11,6 +11,10 @@
                                
     <!-- By default we look at resources whose names are derived from %{sp.home}. -->
 
+    <util:list id="shibboleth.AssertingPartyResolverResources">
+        <value>%{sp.home}/conf/asserting-party.xml</value>
+    </util:list>
+
     <util:list id="shibboleth.MetadataResolverResources">
         <value>%{sp.home}/conf/metadata-providers.xml</value>
     </util:list>
diff --git a/sp-conf/src/main/resources/conf/sp.properties b/sp-conf/src/main/resources/conf/sp.properties
index b8f630c..e891307 100644
--- a/sp-conf/src/main/resources/conf/sp.properties
+++ b/sp-conf/src/main/resources/conf/sp.properties
@@ -19,7 +19,7 @@ sp.additionalProperties = /credentials/secrets.properties
 # Uncommented properties are either required or ship non-defaulted.
 
 # Set the entityID of the SP
-sp.entityID = https://idp.example.org
+sp.entityID = https://sp.example.org
 
 # General cookie properties (maxAge only applies to persistent cookies)
 #sp.cookie.secure = true
diff --git a/sp-saml-api/pom.xml b/sp-saml-api/pom.xml
index 268dfed..d2a972a 100644
--- a/sp-saml-api/pom.xml
+++ b/sp-saml-api/pom.xml
@@ -28,6 +28,11 @@
             <scope>compile</scope>
         </dependency>
 
+        <dependency>
+            <groupId>net.shibboleth</groupId>
+            <artifactId>shib-saml-profile-api</artifactId>
+            <scope>compile</scope>
+        </dependency>
         <dependency>
             <groupId>net.shibboleth</groupId>
             <artifactId>shib-attribute-api</artifactId>
diff --git a/sp-saml-api/src/main/java/net/shibboleth/sp/saml2/config/SAML2AssertionConsumerProfileConfiguration.java b/sp-saml-api/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/BrowserSSOProfileConfiguration.java
similarity index 56%
rename from sp-saml-api/src/main/java/net/shibboleth/sp/saml2/config/SAML2AssertionConsumerProfileConfiguration.java
rename to sp-saml-api/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/BrowserSSOProfileConfiguration.java
index f505491..8af1a0f 100644
--- a/sp-saml-api/src/main/java/net/shibboleth/sp/saml2/config/SAML2AssertionConsumerProfileConfiguration.java
+++ b/sp-saml-api/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/BrowserSSOProfileConfiguration.java
@@ -15,52 +15,42 @@
  * limitations under the License.
  */
 
-package net.shibboleth.sp.saml2.config;
+package net.shibboleth.sp.saml.saml2.profile.config;
 
-import java.time.Duration;
-import java.util.Set;
+import java.util.List;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
-import org.opensaml.profile.context.ProfileRequestContext;
-
-import net.shibboleth.shared.annotation.constraint.NonNegative;
+import net.shibboleth.saml.profile.config.SAMLArtifactConsumerProfileConfiguration;
 import net.shibboleth.shared.annotation.constraint.NonnullElements;
 import net.shibboleth.shared.annotation.constraint.NotLive;
 import net.shibboleth.shared.annotation.constraint.Unmodifiable;
 
-/**
- * Interface for configuration of profiles that consume SAML Assertions.
- */
-public interface SAML2AssertionConsumerProfileConfiguration extends SAML2ProfileConfiguration {
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.saml.saml2.core.AuthnContextClassRef;
 
-    /**
-     * Get the predicate used to determine if assertions must be signed.
-     * 
-     * @param profileRequestContext current profile request context
-     * 
-     * @return predicate used to determine if assertions must be signed
-     */
-    boolean isRequireSignedAssertions(@Nullable final ProfileRequestContext profileRequestContext);
+/** Configuration support for SP SAML 2.0 Browser SSO. */
+public interface BrowserSSOProfileConfiguration extends SAMLArtifactConsumerProfileConfiguration,
+        net.shibboleth.saml.saml2.profile.config.BrowserSSOProfileConfiguration {
 
     /**
-     * Get the maximum lifetime of the assertions.
+     * Get the {@link AuthnContextClassRef} values to include in SAML request.
      * 
      * @param profileRequestContext current profile request context
      * 
-     * @return maximum lifetime of the assertions
+     * @return list of class references
      */
-    @Nullable @NonNegative Duration getAssertionLifetime(@Nullable final ProfileRequestContext profileRequestContext);
-    
+    @Nonnull @NonnullElements @NotLive @Unmodifiable List<String> getAuthnContextClassRefs(
+            @Nullable final ProfileRequestContext profileRequestContext);
+
     /**
-     * Get set of audiences to include when validating assertion, in addition to our own entityID.
+     * Get the name identifier format to require via the SAML request.
      * 
      * @param profileRequestContext current profile request context
      * 
-     * @return additional audiences
+     * @return required format
      */
-    @Nonnull @NonnullElements @NotLive @Unmodifiable Set<String> getAdditionalAudiences(
-            @Nullable final ProfileRequestContext profileRequestContext);
-
+    @Nullable String getNameIDFormat(@Nullable final ProfileRequestContext profileRequestContext);
+        
 }
\ No newline at end of file
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/config/ConditionalProfileConfiguration.java b/sp-saml-api/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/SAML2ProfileConfiguration.java
similarity index 61%
rename from sp-server-api/src/main/java/net/shibboleth/sp/config/ConditionalProfileConfiguration.java
rename to sp-saml-api/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/SAML2ProfileConfiguration.java
index ac0e41d..9d2c77c 100644
--- a/sp-server-api/src/main/java/net/shibboleth/sp/config/ConditionalProfileConfiguration.java
+++ b/sp-saml-api/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/SAML2ProfileConfiguration.java
@@ -15,24 +15,24 @@
  * limitations under the License.
  */
 
-package net.shibboleth.sp.config;
+package net.shibboleth.sp.saml.saml2.profile.config;
 
-import java.util.function.Predicate;
-
-import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 
 import org.opensaml.profile.context.ProfileRequestContext;
 
-/**
- * A {@link ProfileConfiguration} supporting an activation condition.
- */
-public interface ConditionalProfileConfiguration extends ProfileConfiguration {
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+
+/** Common interface for SAML 2.0 SP profile configurations. */
+public interface SAML2ProfileConfiguration extends net.shibboleth.saml.saml2.profile.config.SAML2ProfileConfiguration {
 
     /**
-     * Get the condition controlling enablement of this profile.
+     * Get the entityID assigned to this system.
+     * 
+     * @param profileRequestContext current profile request context
      * 
-     * @return condition controlling enablement of this profile
+     * @return our own entityID
      */
-    @Nonnull Predicate<ProfileRequestContext> getActivationCondition();
+    @Nullable @NotEmpty String getEntityID(@Nullable final ProfileRequestContext profileRequestContext);
     
 }
\ No newline at end of file
diff --git a/sp-testing/src/main/java/net/shibboleth/sp/profile/testing/MockProfileConfiguration.java b/sp-saml-api/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/SingleLogoutProfileConfiguration.java
similarity index 53%
rename from sp-testing/src/main/java/net/shibboleth/sp/profile/testing/MockProfileConfiguration.java
rename to sp-saml-api/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/SingleLogoutProfileConfiguration.java
index cbc5240..fbcc8f6 100644
--- a/sp-testing/src/main/java/net/shibboleth/sp/profile/testing/MockProfileConfiguration.java
+++ b/sp-saml-api/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/SingleLogoutProfileConfiguration.java
@@ -15,27 +15,26 @@
  * limitations under the License.
  */
 
-package net.shibboleth.sp.profile.testing;
+package net.shibboleth.sp.saml.saml2.profile.config;
 
-import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 
-import org.opensaml.security.config.BasicSecurityConfiguration;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.saml.ext.saml2aslo.Asynchronous;
 
-import net.shibboleth.shared.annotation.constraint.NotEmpty;
-import net.shibboleth.sp.config.AbstractProfileConfiguration;
-import net.shibboleth.sp.config.ProfileConfiguration;
-
-/** Mock implementation of {@link ProfileConfiguration}. */
-public class MockProfileConfiguration extends AbstractProfileConfiguration {
+/** Configuration support for SP SAML 2.0 Single Logout. */
+public interface SingleLogoutProfileConfiguration
+        extends net.shibboleth.saml.saml2.profile.config.SingleLogoutProfileConfiguration {
 
     /**
-     * Constructor.
+     * Get whether to include the {@link Asynchronous} extension in requests.
+     * 
+     * <p>Defaults to true.</p>
+     * 
+     * @param profileRequestContext current profile request context
      * 
-     * @param id ID of this profile
+     * @return true iff the extension should be included in requests
      */
-    public MockProfileConfiguration(@Nonnull @NotEmpty final String id) {
-        super(id);
-        setSecurityConfiguration(new BasicSecurityConfiguration());
-    }
+    boolean isAsynchronous(@Nullable final ProfileRequestContext profileRequestContext);
     
 }
\ No newline at end of file
diff --git a/sp-saml-api/src/main/java/net/shibboleth/sp/saml2/config/package-info.java b/sp-saml-api/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/package-info.java
similarity index 94%
rename from sp-saml-api/src/main/java/net/shibboleth/sp/saml2/config/package-info.java
rename to sp-saml-api/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/package-info.java
index 09937f8..41fda5b 100644
--- a/sp-saml-api/src/main/java/net/shibboleth/sp/saml2/config/package-info.java
+++ b/sp-saml-api/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/package-info.java
@@ -18,4 +18,4 @@
 /**
  * Configuration of SAML support in SP.
  */
-package net.shibboleth.sp.saml2.config;
\ No newline at end of file
+package net.shibboleth.sp.saml.saml2.profile.config;
\ No newline at end of file
diff --git a/sp-saml-api/src/main/java/net/shibboleth/sp/saml2/config/SAML2ArtifactConsumerProfileConfiguration.java b/sp-saml-api/src/main/java/net/shibboleth/sp/saml2/config/SAML2ArtifactConsumerProfileConfiguration.java
deleted file mode 100644
index 558b3e7..0000000
--- a/sp-saml-api/src/main/java/net/shibboleth/sp/saml2/config/SAML2ArtifactConsumerProfileConfiguration.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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.sp.saml2.config;
-
-import javax.annotation.Nullable;
-
-import org.opensaml.messaging.context.MessageContext;
-
-/**
- * Common interface for SAML profile configurations involving artifact consumption, for example artifact
- * resolution requests.
- * 
- * <p>This is {@link MessageContext}-driven.</p>
- */
-public interface SAML2ArtifactConsumerProfileConfiguration extends SAML2ProfileConfiguration {
-    
-    /**
-     * Get whether artifact resolution requests should be signed.
-     * 
-     * @param messageContext current message context
-     * 
-     * @return whether artifact resolution requests should be signed
-     */
-    boolean isSignArtifactRequests(@Nullable final MessageContext messageContext); 
-
-    /**
-     * Get whether artifact resolution requests should use client TLS.
-     * 
-     * @param messageContext current message context
-     * 
-     * @return whether artifact resolution requests should use client TLS
-     */
-    boolean isClientTLSArtifactRequests(@Nullable final MessageContext messageContext); 
-
-}
\ No newline at end of file
diff --git a/sp-saml-api/src/main/java/net/shibboleth/sp/saml2/config/SAML2ProfileConfiguration.java b/sp-saml-api/src/main/java/net/shibboleth/sp/saml2/config/SAML2ProfileConfiguration.java
deleted file mode 100644
index 3511804..0000000
--- a/sp-saml-api/src/main/java/net/shibboleth/sp/saml2/config/SAML2ProfileConfiguration.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * 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.sp.saml2.config;
-
-import javax.annotation.Nullable;
-
-import org.opensaml.profile.context.ProfileRequestContext;
-
-import net.shibboleth.shared.annotation.constraint.NotEmpty;
-import net.shibboleth.sp.config.ProfileConfiguration;
-
-/** Common interface for SAML 2.0 profile configurations. */
-public interface SAML2ProfileConfiguration extends ProfileConfiguration {
-
-    /**
-     * Get the entityID assigned to this system.
-     * 
-     * @param profileRequestContext current profile request context
-     * 
-     * @return our own entityID
-     */
-    @Nullable @NotEmpty String getEntityID(@Nullable final ProfileRequestContext profileRequestContext);
-    
-    /**
-     * Get the predicate used to determine if generated requests should be signed.
-     * 
-     * @param profileRequestContext current profile request context
-     * 
-     * @return predicate used to determine if generated requests should be signed
-     */
-    boolean isSignRequests(@Nullable final ProfileRequestContext profileRequestContext);
-
-    /**
-     * Get the predicate used to determine if generated responses should be signed.
-     * 
-     * @param profileRequestContext current profile request context
-     * 
-     * @return predicate used to determine if generated responses should be signed
-     */
-    boolean isSignResponses(@Nullable final ProfileRequestContext profileRequestContext);
-    
-    /**
-     * Gets whether to ignore an inability to encrypt due to external factors.
-     * 
-     *  <p>This allows a deployer to signal that encryption is "best effort" and
-     *  can be omitted if an asserting party doesn't possess a key, support a compatible
-     *  algorithm, etc.</p>
-     *  
-     *  <p>Defaults to false.</p>
-     *  
-     * @param profileRequestContext current profile request context
-     * 
-     * @return true iff encryption should be treated as optional
-     */
-    boolean isEncryptionOptional(@Nullable final ProfileRequestContext profileRequestContext);
-
-    /**
-     * Gets the predicate used to determine if name identifiers should be encrypted.
-     * 
-     * @param profileRequestContext current profile request context
-     * 
-     * @return predicate used to determine if name identifiers should be encrypted
-     */
-    boolean isEncryptNameIDs(@Nullable final ProfileRequestContext profileRequestContext);
-    
-}
\ No newline at end of file
diff --git a/sp-saml-impl/pom.xml b/sp-saml-impl/pom.xml
index 76e23c1..ffc226b 100644
--- a/sp-saml-impl/pom.xml
+++ b/sp-saml-impl/pom.xml
@@ -21,21 +21,33 @@
         
     <dependencies>
         <!-- Compile dependencies -->
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>sp-saml-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>net.shibboleth</groupId>
+            <artifactId>shib-profile-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>net.shibboleth</groupId>
+            <artifactId>shib-saml-profile-api</artifactId>
+        </dependency>
+
         <dependency>
             <groupId>net.shibboleth</groupId>
             <artifactId>shib-attribute-api</artifactId>
-            <scope>compile</scope>
         </dependency>
         <dependency>
             <groupId>net.shibboleth</groupId>
             <artifactId>shib-metadata-api</artifactId>
-            <scope>compile</scope>
         </dependency>
 
         <dependency>
             <groupId>net.shibboleth</groupId>
             <artifactId>shib-support</artifactId>
-            <scope>compile</scope>
         </dependency>
         
         <!-- Provided dependencies -->
diff --git a/sp-saml-api/src/main/java/net/shibboleth/sp/saml2/config/AbstractSAML2ArtifactConsumerProfileConfiguration.java b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/impl/AbstractSAML2ArtifactConsumerProfileConfiguration.java
similarity index 95%
rename from sp-saml-api/src/main/java/net/shibboleth/sp/saml2/config/AbstractSAML2ArtifactConsumerProfileConfiguration.java
rename to sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/impl/AbstractSAML2ArtifactConsumerProfileConfiguration.java
index 1f8e654..ed3a22c 100644
--- a/sp-saml-api/src/main/java/net/shibboleth/sp/saml2/config/AbstractSAML2ArtifactConsumerProfileConfiguration.java
+++ b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/impl/AbstractSAML2ArtifactConsumerProfileConfiguration.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package net.shibboleth.sp.saml2.config;
+package net.shibboleth.sp.saml.saml2.profile.config.impl;
 
 import java.util.function.Predicate;
 
@@ -25,6 +25,7 @@ import javax.annotation.Nullable;
 import org.opensaml.messaging.context.MessageContext;
 import org.opensaml.messaging.logic.NoIntegrityMessageChannelPredicate;
 
+import net.shibboleth.saml.profile.config.SAMLArtifactConsumerProfileConfiguration;
 import net.shibboleth.shared.annotation.constraint.NotEmpty;
 import net.shibboleth.shared.logic.Constraint;
 import net.shibboleth.shared.logic.PredicateSupport;
@@ -33,7 +34,7 @@ import net.shibboleth.shared.logic.PredicateSupport;
  * Configuration support for artifact-consuming profiles.
  */
 public abstract class AbstractSAML2ArtifactConsumerProfileConfiguration extends AbstractSAML2ProfileConfiguration
-        implements SAML2ArtifactConsumerProfileConfiguration {
+        implements SAMLArtifactConsumerProfileConfiguration {
     
     /** Predicate used to determine if artifact resolution requests should be signed. */
     @Nonnull private Predicate<MessageContext> signArtifactRequestsPredicate;
diff --git a/sp-saml-api/src/main/java/net/shibboleth/sp/saml2/config/AbstractSAML2AssertionConsumerProfileConfiguration.java b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/impl/AbstractSAML2AssertionConsumerProfileConfiguration.java
similarity index 86%
rename from sp-saml-api/src/main/java/net/shibboleth/sp/saml2/config/AbstractSAML2AssertionConsumerProfileConfiguration.java
rename to sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/impl/AbstractSAML2AssertionConsumerProfileConfiguration.java
index cf822bf..8677ab7 100644
--- a/sp-saml-api/src/main/java/net/shibboleth/sp/saml2/config/AbstractSAML2AssertionConsumerProfileConfiguration.java
+++ b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/impl/AbstractSAML2AssertionConsumerProfileConfiguration.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package net.shibboleth.sp.saml2.config;
+package net.shibboleth.sp.saml.saml2.profile.config.impl;
 
 import java.time.Duration;
 import java.util.Collection;
@@ -26,6 +26,7 @@ import java.util.function.Predicate;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
+import net.shibboleth.saml.profile.config.SAMLAssertionConsumingProfileConfiguration;
 import net.shibboleth.shared.annotation.constraint.NonNegative;
 import net.shibboleth.shared.annotation.constraint.NonnullElements;
 import net.shibboleth.shared.annotation.constraint.NotEmpty;
@@ -41,7 +42,7 @@ import org.opensaml.profile.context.ProfileRequestContext;
 /** Base class for SAML profile configurations. */
 public abstract class AbstractSAML2AssertionConsumerProfileConfiguration
         extends AbstractSAML2ArtifactConsumerProfileConfiguration
-        implements SAML2AssertionConsumerProfileConfiguration {
+        implements SAMLAssertionConsumingProfileConfiguration {
     
     /** Whether to require signed assertions. */
     @Nonnull private Predicate<ProfileRequestContext> requireSignedAssertionsPredicate;
@@ -50,7 +51,7 @@ public abstract class AbstractSAML2AssertionConsumerProfileConfiguration
     @Nonnull private Function<ProfileRequestContext,Duration> assertionLifetimeLookupStrategy;
 
     /** Lookup function to supply additionalAudiences property. */
-    @Nonnull private Function<ProfileRequestContext,Set<String>> additionalAudiencesLookupStrategy;
+    @Nonnull private Function<ProfileRequestContext,Set<String>> assertionAudiencesLookupStrategy;
     
     /**
      * Constructor.
@@ -62,7 +63,7 @@ public abstract class AbstractSAML2AssertionConsumerProfileConfiguration
         
         requireSignedAssertionsPredicate = PredicateSupport.alwaysFalse();
         assertionLifetimeLookupStrategy = FunctionSupport.constant(null);
-        additionalAudiencesLookupStrategy = FunctionSupport.constant(null);
+        assertionAudiencesLookupStrategy = FunctionSupport.constant(null);
     }
 
     /** {@inheritDoc} */
@@ -120,10 +121,10 @@ public abstract class AbstractSAML2AssertionConsumerProfileConfiguration
     }
 
     /** {@inheritDoc} */
-    @Nonnull @NonnullElements @NotLive public Set<String> getAdditionalAudiences(
+    @Nonnull @NonnullElements @NotLive public Set<String> getAssertionAudiences(
             @Nullable final ProfileRequestContext profileRequestContext) {
         
-        final Set<String> audiences = additionalAudiencesLookupStrategy.apply(profileRequestContext);
+        final Set<String> audiences = assertionAudiencesLookupStrategy.apply(profileRequestContext);
         if (audiences != null) {
             return CollectionSupport.copyToSet(audiences);
         }
@@ -135,12 +136,12 @@ public abstract class AbstractSAML2AssertionConsumerProfileConfiguration
      * 
      * @param audiences the additional audiences
      */
-    public void setAdditionalAudiences(@Nullable @NonnullElements final Collection<String> audiences) {
+    public void setAssertionAudiences(@Nullable @NonnullElements final Collection<String> audiences) {
 
         if (audiences == null || audiences.isEmpty()) {
-            additionalAudiencesLookupStrategy = FunctionSupport.constant(null);
+            assertionAudiencesLookupStrategy = FunctionSupport.constant(null);
         } else {
-            additionalAudiencesLookupStrategy = FunctionSupport.constant(
+            assertionAudiencesLookupStrategy = FunctionSupport.constant(
                     CollectionSupport.copyToSet(StringSupport.normalizeStringCollection(audiences)));
         }
     }
@@ -152,9 +153,9 @@ public abstract class AbstractSAML2AssertionConsumerProfileConfiguration
      * 
      * @since 4.0.0
      */
-    public void setAdditionalAudiencesLookupStrategy(
+    public void setAssertionAudiencesLookupStrategy(
             @Nonnull final Function<ProfileRequestContext,Set<String>> strategy) {
-        additionalAudiencesLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
+        assertionAudiencesLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
     }
 
 }
\ No newline at end of file
diff --git a/sp-saml-api/src/main/java/net/shibboleth/sp/saml2/config/AbstractSAML2ProfileConfiguration.java b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/impl/AbstractSAML2ProfileConfiguration.java
similarity index 80%
rename from sp-saml-api/src/main/java/net/shibboleth/sp/saml2/config/AbstractSAML2ProfileConfiguration.java
rename to sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/impl/AbstractSAML2ProfileConfiguration.java
index 677ed74..ea3a7cc 100644
--- a/sp-saml-api/src/main/java/net/shibboleth/sp/saml2/config/AbstractSAML2ProfileConfiguration.java
+++ b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/impl/AbstractSAML2ProfileConfiguration.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package net.shibboleth.sp.saml2.config;
+package net.shibboleth.sp.saml.saml2.profile.config.impl;
 
 import java.util.function.Function;
 import java.util.function.Predicate;
@@ -23,12 +23,14 @@ import java.util.function.Predicate;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
+import net.shibboleth.profile.config.AbstractConditionalProfileConfiguration;
 import net.shibboleth.shared.annotation.constraint.NotEmpty;
 import net.shibboleth.shared.logic.Constraint;
 import net.shibboleth.shared.logic.FunctionSupport;
 import net.shibboleth.shared.logic.PredicateSupport;
-import net.shibboleth.sp.config.AbstractConditionalProfileConfiguration;
+import net.shibboleth.sp.saml.saml2.profile.config.SAML2ProfileConfiguration;
 
+import org.opensaml.messaging.context.MessageContext;
 import org.opensaml.profile.context.ProfileRequestContext;
 
 /** Base class for SAML profile configurations. */
@@ -50,6 +52,10 @@ public abstract class AbstractSAML2ProfileConfiguration extends AbstractConditio
     /** Predicate used to determine if name identifiers should be encrypted. */
     @Nonnull private Predicate<ProfileRequestContext> encryptNameIDsPredicate;
     
+    /** Lookup function to supply a message handler function. */
+    @Nonnull private Function<MessageContext,Function<MessageContext,Exception>> messageHandlerLookupStrategy;
+
+    
     /**
      * Constructor.
      * 
@@ -63,6 +69,13 @@ public abstract class AbstractSAML2ProfileConfiguration extends AbstractConditio
         signResponsesPredicate = PredicateSupport.alwaysFalse();
         encryptionOptionalPredicate = PredicateSupport.alwaysFalse();
         encryptNameIDsPredicate = PredicateSupport.alwaysFalse();
+        
+        messageHandlerLookupStrategy = FunctionSupport.constant(null);
+    }
+    
+    /** {@inheritDoc} */
+    public boolean isIgnoreRequestSignatures(@Nullable ProfileRequestContext profileRequestContext) {
+        throw new UnsupportedOperationException("ignoreRequestSignatures is unsupported");
     }
     
     /** {@inheritDoc} */
@@ -180,4 +193,29 @@ public abstract class AbstractSAML2ProfileConfiguration extends AbstractConditio
         encryptNameIDsPredicate = Constraint.isNotNull(predicate, "Condition cannot be null");
     }
 
+    /** {@inheritDoc} */
+    @Nullable
+    public Function<MessageContext,Exception> getMessageHandler(@Nullable final MessageContext messageContext) {
+        return messageHandlerLookupStrategy.apply(messageContext);
+    }
+    
+    /**
+     * Set a message handler for a SAML message produced or consumed by this profile.
+     * 
+     * @param handler message handler
+     */
+    public void setMessageHandler(@Nullable final Function<MessageContext,Exception> handler) {
+        messageHandlerLookupStrategy = FunctionSupport.constant(handler);
+    }
+    
+    /**
+     * Set a lookup strategy for a message handler for a SAML message produced or consumed by this profile.
+     * 
+     * @param strategy lookup strategy
+     */
+    public void setMessageHandlerLookupStrategy(
+            @Nonnull final Function<MessageContext,Function<MessageContext,Exception>> strategy) {
+        messageHandlerLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
+    }
+    
 }
\ No newline at end of file
diff --git a/sp-saml-api/src/main/java/net/shibboleth/sp/saml2/config/BrowserSSOProfileConfiguration.java b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/impl/BrowserSSOProfileConfiguration.java
similarity index 73%
rename from sp-saml-api/src/main/java/net/shibboleth/sp/saml2/config/BrowserSSOProfileConfiguration.java
rename to sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/impl/BrowserSSOProfileConfiguration.java
index 73d6578..35e8ea7 100644
--- a/sp-saml-api/src/main/java/net/shibboleth/sp/saml2/config/BrowserSSOProfileConfiguration.java
+++ b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/impl/BrowserSSOProfileConfiguration.java
@@ -15,18 +15,18 @@
  * limitations under the License.
  */
 
-package net.shibboleth.sp.saml2.config;
+package net.shibboleth.sp.saml.saml2.profile.config.impl;
 
 import java.time.Duration;
 import java.util.Collection;
 import java.util.List;
-import java.util.function.BiConsumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
+import net.shibboleth.saml.profile.config.SAMLArtifactConsumerProfileConfiguration;
 import net.shibboleth.shared.annotation.constraint.NonNegative;
 import net.shibboleth.shared.annotation.constraint.NonnullElements;
 import net.shibboleth.shared.annotation.constraint.NotEmpty;
@@ -40,28 +40,12 @@ import net.shibboleth.shared.logic.PredicateSupport;
 import org.opensaml.profile.context.ProfileRequestContext;
 import org.opensaml.saml.saml2.core.AuthnContextClassRef;
 import org.opensaml.saml.saml2.core.AuthnContextComparisonTypeEnumeration;
-import org.opensaml.saml.saml2.core.AuthnRequest;
 import org.opensaml.saml.saml2.core.SubjectLocality;
 import org.opensaml.saml.saml2.metadata.RequestedAttribute;
 
-/** Configuration support for SAML 2 Browser SSO. */
+/** Configuration support for SP SAML 2.0 Browser SSO. */
 public class BrowserSSOProfileConfiguration extends AbstractSAML2AssertionConsumerProfileConfiguration
-        implements SAML2ArtifactConsumerProfileConfiguration {
-    
-    /** ID for this profile configuration. */
-    @Nonnull @NotEmpty public static final String PROFILE_ID = "http://shibboleth.net/ns/profiles/saml2/sso/browser";
-    
-    /** Bit constant for RequestedAuthnContext feature. */
-    public static final int FEATURE_AUTHNCONTEXT = 0x1;
-    
-    /** Bit constant for NameIDPolicy Format feature. */
-    public static final int FEATURE_NAMEIDFORMAT = 0x2;
-
-    /** Bit constant for NameIDPolicy SPNameQualifier feature. */
-    public static final int FEATURE_SPNAMEQUALIFIER = 0x4;
-
-    /** Bit constant for ForceAuthn feature. */
-    public static final int FEATURE_FORCEAUTHN = 0x8;
+        implements SAMLArtifactConsumerProfileConfiguration, net.shibboleth.sp.saml.saml2.profile.config.BrowserSSOProfileConfiguration {
 
     /** Whether to mandate forced authentication for the request. */
     @Nonnull private Predicate<ProfileRequestContext> forceAuthnPredicate;
@@ -88,15 +72,11 @@ public class BrowserSSOProfileConfiguration extends AbstractSAML2AssertionConsum
     @Nonnull private Function<ProfileRequestContext,String> nameQualifierLookupStrategy;
 
     /** Lookup function to supply AttributeConsumingServiceIndex. */
-    @Nonnull private Function<ProfileRequestContext,String> attributeIndexLookupStrategy;
+    @Nonnull private Function<ProfileRequestContext,Integer> attributeIndexLookupStrategy;
 
     /** Lookup function to supply RequestedAttributes. */
     @Nonnull private Function<ProfileRequestContext,Collection<RequestedAttribute>> requestedAttributesLookupStrategy;
     
-    /** Lookup function to supply a decorator for the {@link AuthnRequest}. */
-    @Nonnull private Function<ProfileRequestContext,BiConsumer<ProfileRequestContext,AuthnRequest>>
-    requestDecoratorLookupStrategy;
-
     /** Constructor. */
     public BrowserSSOProfileConfiguration() {
         this(PROFILE_ID);
@@ -120,16 +100,9 @@ public class BrowserSSOProfileConfiguration extends AbstractSAML2AssertionConsum
         nameQualifierLookupStrategy = FunctionSupport.constant(null);
         attributeIndexLookupStrategy = FunctionSupport.constant(null);
         requestedAttributesLookupStrategy = FunctionSupport.constant(null);
-        requestDecoratorLookupStrategy = FunctionSupport.constant(null);
     }
 
-    /**
-     * Get whether a fresh user presence proof should be required for this request.
-     * 
-     * @param profileRequestContext current profile request context
-     * 
-     * @return true iff a fresh user presence proof should be required for this request
-     */
+    /** {@inheritDoc} */
     public boolean isForceAuthn(@Nullable final ProfileRequestContext profileRequestContext) {
         return forceAuthnPredicate.test(profileRequestContext);
     }
@@ -152,14 +125,7 @@ public class BrowserSSOProfileConfiguration extends AbstractSAML2AssertionConsum
         forceAuthnPredicate = Constraint.isNotNull(condition, "Forced authentication predicate cannot be null");
     }
 
-    /**
-     * Get whether the client's address must match the address in an inbound {@link SubjectLocality}
-     * element during inbound SSO.
-     * 
-     * @param profileRequestContext current profile request context
-     * 
-     * @return whether to compare addresses
-     */
+    /** {@inheritDoc} */
     public boolean isCheckAddress(@Nullable final ProfileRequestContext profileRequestContext) {
         return checkAddressPredicate.test(profileRequestContext);
     }
@@ -184,15 +150,7 @@ public class BrowserSSOProfileConfiguration extends AbstractSAML2AssertionConsum
         checkAddressPredicate = Constraint.isNotNull(condition, "Address checking predicate cannot be null");
     }
     
-    /**
-     * Get the maximum amount of time allowed to have elapsed since an incoming AuthnInstant.
-     * 
-     * <p>A null or 0 is interpreted as an unlimited amount.</p>
-     * 
-     * @param profileRequestContext current profile request context
-     * 
-     * @return max time since inbound AuthnInstant
-     */
+    /** {@inheritDoc} */
     @Nullable public Duration getMaximumTimeSinceAuthn(@Nullable final ProfileRequestContext profileRequestContext) {
         final Duration amount = maximumTimeSinceAuthnLookupStrategy.apply(profileRequestContext);
         Constraint.isFalse(amount != null && amount.isNegative(),
@@ -224,14 +182,8 @@ public class BrowserSSOProfileConfiguration extends AbstractSAML2AssertionConsum
         maximumTimeSinceAuthnLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
     }
 
-    /**
-     * Gets the maximum number of times an assertion may be proxied to signal in the SAML request.
-     * 
-     * @param profileRequestContext current profile request context
-     * 
-     * @return maximum number of times an assertion may be proxied
-     */
-    @NonNegative @Nullable Integer getProxyCount(@Nullable final ProfileRequestContext profileRequestContext) {
+    /** {@inheritDoc} */
+    @NonNegative @Nullable public Integer getProxyCount(@Nullable final ProfileRequestContext profileRequestContext) {
         final Integer count = proxyCountLookupStrategy.apply(profileRequestContext);
         if (count != null) {
             Constraint.isGreaterThanOrEqual(0, count, "Proxy count must be greater than or equal to 0");
@@ -261,13 +213,7 @@ public class BrowserSSOProfileConfiguration extends AbstractSAML2AssertionConsum
         proxyCountLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
     }
     
-    /**
-     * Get the comparison operator to use when issuing SAML requests containing requested context classes.
-     * 
-     * @param profileRequestContext profile request context
-     * 
-     * @return comparison value or null
-     */
+    /** {@inheritDoc} */
     @Nullable public AuthnContextComparisonTypeEnumeration getAuthnContextComparison(
             @Nullable final ProfileRequestContext profileRequestContext) {
         
@@ -302,13 +248,7 @@ public class BrowserSSOProfileConfiguration extends AbstractSAML2AssertionConsum
         authnContextComparisonLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
     }
 
-    /**
-     * Get the {@link AuthnContextClassRef} values to include in SAML request.
-     * 
-     * @param profileRequestContext current profile request context
-     * 
-     * @return list of class references
-     */
+    /** {@inheritDoc} */
     @Nonnull @NonnullElements @NotLive @Unmodifiable public List<String> getAuthnContextClassRefs(
             @Nullable final ProfileRequestContext profileRequestContext) {
         final Collection<String> refs = authnContextClassRefLookupStrategy.apply(profileRequestContext);
@@ -341,13 +281,7 @@ public class BrowserSSOProfileConfiguration extends AbstractSAML2AssertionConsum
         authnContextClassRefLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
     }
 
-    /**
-     * Get the name identifier format to require via the SAML request.
-     * 
-     * @param profileRequestContext current profile request context
-     * 
-     * @return required format
-     */
+    /** {@inheritDoc} */
     @Nullable public String getNameIDFormat(@Nullable final ProfileRequestContext profileRequestContext) {
         return nameIDFormatLookupStrategy.apply(profileRequestContext);
     }
@@ -371,14 +305,8 @@ public class BrowserSSOProfileConfiguration extends AbstractSAML2AssertionConsum
         nameIDFormatLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
     }
 
-    /**
-     * Get the SPNameQualifier to include in the SAML request.
-     * 
-     * @param profileRequestContext current profile request context
-     * 
-     * @return requested SPNameQualifier
-     */
-    @Nullable public String getNameQualifier(@Nullable final ProfileRequestContext profileRequestContext) {
+    /** {@inheritDoc} */
+    @Nullable public String getSPNameQualifier(@Nullable final ProfileRequestContext profileRequestContext) {
         return nameQualifierLookupStrategy.apply(profileRequestContext);
     }
 
@@ -387,7 +315,7 @@ public class BrowserSSOProfileConfiguration extends AbstractSAML2AssertionConsum
      * 
      * @param qualifier requested SPNameQualifier
      */
-    public void setNameQualifier(@Nullable final String qualifier) {
+    public void setSPNameQualifier(@Nullable final String qualifier) {
         nameQualifierLookupStrategy = FunctionSupport.constant(qualifier);
     }
 
@@ -396,19 +324,13 @@ public class BrowserSSOProfileConfiguration extends AbstractSAML2AssertionConsum
      *
      * @param strategy  lookup strategy
      */
-    public void setNameQualifierLookupStrategy(
+    public void setSPNameQualifierLookupStrategy(
             @Nonnull final Function<ProfileRequestContext,String> strategy) {
         nameQualifierLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
     }
     
-    /**
-     * Get the AttributeConsumingServiceIndex to include in the SAML request.
-     * 
-     * @param profileRequestContext current profile request context
-     * 
-     * @return the AttributeConsumingServiceIndex
-     */
-    @Nullable public String getAttributeIndex(@Nullable final ProfileRequestContext profileRequestContext) {
+    /** {@inheritDoc} */
+    @Nullable public Integer getAttributeIndex(@Nullable final ProfileRequestContext profileRequestContext) {
         return attributeIndexLookupStrategy.apply(profileRequestContext);
     }
 
@@ -417,7 +339,7 @@ public class BrowserSSOProfileConfiguration extends AbstractSAML2AssertionConsum
      * 
      * @param index the AttributeConsumingServiceIndex
      */
-    public void setAttributeIndex(@Nullable final String index) {
+    public void setAttributeIndex(@Nullable final Integer index) {
         attributeIndexLookupStrategy = FunctionSupport.constant(index);
     }
 
@@ -426,17 +348,11 @@ public class BrowserSSOProfileConfiguration extends AbstractSAML2AssertionConsum
      *
      * @param strategy  lookup strategy
      */
-    public void setAttributeIndexLookupStrategy(@Nonnull final Function<ProfileRequestContext,String> strategy) {
+    public void setAttributeIndexLookupStrategy(@Nonnull final Function<ProfileRequestContext,Integer> strategy) {
         attributeIndexLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
     }
     
-    /**
-     * Get the list of {@link RequestedAttribute} objects to include in the SAML request (via extension).
-     * 
-     * @param profileRequestContext current profile request context
-     * 
-     * @return the requested attributes
-     */
+    /** {@inheritDoc} */
     @Nonnull @NonnullElements @Unmodifiable @NotLive public Collection<RequestedAttribute> getRequestedAttributes(
             @Nullable final ProfileRequestContext profileRequestContext) {
         final Collection<RequestedAttribute> attrs = requestedAttributesLookupStrategy.apply(profileRequestContext);
@@ -466,36 +382,5 @@ public class BrowserSSOProfileConfiguration extends AbstractSAML2AssertionConsum
             @Nonnull final Function<ProfileRequestContext,Collection<RequestedAttribute>> strategy) {
         requestedAttributesLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
     }
-    
-    /**
-     * Get a decorator for the SAML request.
-     * 
-     * @param profileRequestContext current profile request context
-     * 
-     * @return request decorator
-     */
-    @Nullable public BiConsumer<ProfileRequestContext,AuthnRequest> getRequestDecorator(
-            @Nullable final ProfileRequestContext profileRequestContext) {
-        return requestDecoratorLookupStrategy.apply(profileRequestContext);
-    }
-    
-    /**
-     * Set a decorator for the SAML request.
-     * 
-     * @param decorator request decorator
-     */
-    public void setRequestDecorator(@Nullable final BiConsumer<ProfileRequestContext,AuthnRequest> decorator) {
-        requestDecoratorLookupStrategy = FunctionSupport.constant(decorator);
-    }
-    
-    /**
-     * Set a lookup strategy for a decorator for the SAML request.
-     * 
-     * @param strategy lookup strategy
-     */
-    public void setRequestDecoratorLookupStrategy(
-            @Nonnull final Function<ProfileRequestContext,BiConsumer<ProfileRequestContext,AuthnRequest>> strategy) {
-        requestDecoratorLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
-    }
-    
+        
 }
\ No newline at end of file
diff --git a/sp-saml-api/src/main/java/net/shibboleth/sp/saml2/config/SingleLogoutProfileConfiguration.java b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/impl/SingleLogoutProfileConfiguration.java
similarity index 81%
rename from sp-saml-api/src/main/java/net/shibboleth/sp/saml2/config/SingleLogoutProfileConfiguration.java
rename to sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/impl/SingleLogoutProfileConfiguration.java
index 941c70e..672bf22 100644
--- a/sp-saml-api/src/main/java/net/shibboleth/sp/saml2/config/SingleLogoutProfileConfiguration.java
+++ b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/impl/SingleLogoutProfileConfiguration.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package net.shibboleth.sp.saml2.config;
+package net.shibboleth.sp.saml.saml2.profile.config.impl;
 
 import java.util.Collection;
 import java.util.List;
@@ -42,11 +42,9 @@ import net.shibboleth.shared.logic.FunctionSupport;
 import net.shibboleth.shared.logic.PredicateSupport;
 import net.shibboleth.shared.primitive.StringSupport;
 
-/** Configuration support for SAML 2 Single Logout. */
-public class SingleLogoutProfileConfiguration extends AbstractSAML2ArtifactConsumerProfileConfiguration {
-
-    /** ID for this profile configuration. */
-    @Nonnull @NotEmpty public static final String PROFILE_ID = "http://shibboleth.net/ns/profiles/saml2/logout";
+/** Configuration support for SP SAML 2.0 Single Logout. */
+public class SingleLogoutProfileConfiguration extends AbstractSAML2ArtifactConsumerProfileConfiguration
+        implements net.shibboleth.sp.saml.saml2.profile.config.SingleLogoutProfileConfiguration {
 
     /** Predicate used to determine if SOAP-based requests should be signed. */
     @Nonnull private Predicate<MessageContext> signSOAPRequestsPredicate;
@@ -84,15 +82,7 @@ public class SingleLogoutProfileConfiguration extends AbstractSAML2ArtifactConsu
         qualifiedNameIDFormatsLookupStrategy = FunctionSupport.constant(null);
     }
 
-    /**
-     * Get whether SOAP-based requests should be signed.
-     * 
-     * @param messageContext current message context
-     * 
-     * @return whether SOAP-based requests should be signed
-     * 
-     * @since 4.0.0
-     */
+    /** {@inheritDoc} */
     public boolean isSignSOAPRequests(@Nullable final MessageContext messageContext) {
         return signSOAPRequestsPredicate.test(messageContext);
     }
@@ -101,8 +91,6 @@ public class SingleLogoutProfileConfiguration extends AbstractSAML2ArtifactConsu
      * Set whether SOAP-based requests should be signed.
      * 
      * @param flag flag to set
-     * 
-     * @since 4.0.0
      */
     public void setSignSOAPRequests(final boolean flag) {
         signSOAPRequestsPredicate = PredicateSupport.constant(flag);
@@ -112,23 +100,13 @@ public class SingleLogoutProfileConfiguration extends AbstractSAML2ArtifactConsu
      * Set the predicate used to determine if SOAP-based requests should be signed.
      * 
      * @param predicate the predicate
-     * 
-     * @since 4.0.0
      */
     public void setSignSOAPRequestsPredicate(@Nonnull final Predicate<MessageContext> predicate) {
         signSOAPRequestsPredicate = Constraint.isNotNull(predicate, 
                 "Predicate used to determine SOAP-based signing cannot be null");
     }
 
-    /**
-     * Get whether SOAP-based requests should use client TLS.
-     * 
-     * @param messageContext current message context
-     * 
-     * @return whether SOAP-based requests should use client TLS
-     * 
-     * @since 4.0.0
-     */
+    /** {@inheritDoc} */
     public boolean isClientTLSSOAPRequests(@Nullable final MessageContext messageContext) {
         return clientTLSSOAPRequestsPredicate.test(messageContext);
     }
@@ -137,8 +115,6 @@ public class SingleLogoutProfileConfiguration extends AbstractSAML2ArtifactConsu
      * Set whether SOAP-based requests should use client TLS.
      * 
      * @param flag flag to set
-     * 
-     * @since 4.0.0
      */
     public void setClientTLSSOAPRequests(final boolean flag) {
         clientTLSSOAPRequestsPredicate = PredicateSupport.constant(flag);
@@ -148,23 +124,13 @@ public class SingleLogoutProfileConfiguration extends AbstractSAML2ArtifactConsu
      * Set the predicate used to determine if SOAP-based requests should use client TLS.
      * 
      * @param predicate the predicate
-     * 
-     * @since 4.0.0
      */
     public void setClientTLSSOAPRequestsPredicate(@Nonnull final Predicate<MessageContext> predicate) {
         clientTLSSOAPRequestsPredicate = Constraint.isNotNull(predicate, 
                 "Predicate used to determine SOAP-based client TLS use cannot be null");
     }
     
-    /**
-     * Get whether to include the {@link Asynchronous} extension in requests.
-     * 
-     * <p>Defaults to true.</p>
-     * 
-     * @param profileRequestContext current profile request context
-     * 
-     * @return true iff the extension should be included in requests
-     */
+    /** {@inheritDoc} */
     public boolean isAsynchronous(@Nullable final ProfileRequestContext profileRequestContext) {
         return asynchronousPredicate.test(profileRequestContext);
     }
@@ -187,19 +153,7 @@ public class SingleLogoutProfileConfiguration extends AbstractSAML2ArtifactConsu
         asynchronousPredicate = Constraint.isNotNull(condition, "Condition cannot be null");
     }
     
-    /**
-     * Get a collection of {@link NameID} Format values for which the use of the NameQualifier and
-     * SPNameQualifier attributes is defined to allow default/implicit values derived from the
-     * asserting and relying parties.
-     * 
-     * <p>In the core standard, only the {@link NameIDType#PERSISTENT} and {@link NameIDType#TRANSIENT}
-     * Formats are defined in this manner. This setting identifies <strong>additional</strong> Formats
-     * that should be handled in this way.</p>
-     * 
-     * @param profileRequestContext current profile request context
-     * 
-     * @return additional Formats for which defaulting of qualifiers is permissable
-     */
+    /** {@inheritDoc} */
     @Nonnull @NonnullElements @NotLive public Collection<String> getQualifiedNameIDFormats(
             @Nullable final ProfileRequestContext profileRequestContext) {
         final Collection<String> formats = qualifiedNameIDFormatsLookupStrategy.apply(profileRequestContext);
diff --git a/sp-testing/src/main/java/net/shibboleth/sp/profile/testing/package-info.java b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/impl/package-info.java
similarity index 89%
rename from sp-testing/src/main/java/net/shibboleth/sp/profile/testing/package-info.java
rename to sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/impl/package-info.java
index bde6abd..54278a5 100644
--- a/sp-testing/src/main/java/net/shibboleth/sp/profile/testing/package-info.java
+++ b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/impl/package-info.java
@@ -16,7 +16,6 @@
  */
 
 /**
- * Testing utility classes.
+ * SAML 2.0 profile configuration classes.
  */
-
-package net.shibboleth.sp.profile.testing;
\ No newline at end of file
+package net.shibboleth.sp.saml.saml2.profile.config.impl;
\ No newline at end of file
diff --git a/sp-saml-impl/src/test/java/net/shibboleth/sp/saml/saml2/profile/config/impl/AbstractSAML2ProfileConfigurationTest.java b/sp-saml-impl/src/test/java/net/shibboleth/sp/saml/saml2/profile/config/impl/AbstractSAML2ProfileConfigurationTest.java
new file mode 100644
index 0000000..4c5d437
--- /dev/null
+++ b/sp-saml-impl/src/test/java/net/shibboleth/sp/saml/saml2/profile/config/impl/AbstractSAML2ProfileConfigurationTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.sp.saml.saml2.profile.config.impl;
+
+import net.shibboleth.shared.logic.PredicateSupport;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+/** Unit test for {@link AbstractSAML2ProfileConfiguration}. */
+ at SuppressWarnings("javadoc")
+public class AbstractSAML2ProfileConfigurationTest {
+
+    @Test
+    public void testEncryptionOptional(){
+        final MockSAML2ProfileConfiguration config = new MockSAML2ProfileConfiguration();
+        Assert.assertFalse(config.isEncryptionOptional(null));
+
+        config.setEncryptionOptional(true);
+        Assert.assertTrue(config.isEncryptionOptional(null));
+    }
+
+    @Test
+    public void testIndirectEncryptionOptional(){
+        final MockSAML2ProfileConfiguration config = new MockSAML2ProfileConfiguration();
+
+        config.setEncryptionOptionalPredicate(PredicateSupport.alwaysTrue());
+        Assert.assertTrue(config.isEncryptionOptional(null));
+    }
+
+    @Test public void testEncryptNameIDsPredicate() {
+        final MockSAML2ProfileConfiguration config = new MockSAML2ProfileConfiguration();
+
+        config.setEncryptNameIDs(true);
+        Assert.assertTrue(config.isEncryptNameIDs(null));
+    }
+
+    /** Mock class for testing {@link AbstractSAML2ProfileConfiguration}. */
+    private static class MockSAML2ProfileConfiguration extends AbstractSAML2ProfileConfiguration {
+
+        /** Constructor. */
+        public MockSAML2ProfileConfiguration() {
+            super("mock");
+        }
+    }
+}
\ No newline at end of file
diff --git a/sp-saml-impl/src/test/java/net/shibboleth/sp/saml/saml2/profile/config/impl/BrowserSSOProfileConfigurationTest.java b/sp-saml-impl/src/test/java/net/shibboleth/sp/saml/saml2/profile/config/impl/BrowserSSOProfileConfigurationTest.java
new file mode 100644
index 0000000..e26e779
--- /dev/null
+++ b/sp-saml-impl/src/test/java/net/shibboleth/sp/saml/saml2/profile/config/impl/BrowserSSOProfileConfigurationTest.java
@@ -0,0 +1,180 @@
+/*
+ * 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.sp.saml.saml2.profile.config.impl;
+
+import net.shibboleth.shared.logic.ConstraintViolationException;
+import net.shibboleth.shared.logic.FunctionSupport;
+
+import java.time.Duration;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+
+/** Unit test for {@link BrowserSSOProfileConfiguration}. */
+ at SuppressWarnings("javadoc")
+public class BrowserSSOProfileConfigurationTest {
+
+    @Test
+    public void testProfileId() {
+        final BrowserSSOProfileConfiguration config = new BrowserSSOProfileConfiguration();
+        Assert.assertEquals(config.getId(), BrowserSSOProfileConfiguration.PROFILE_ID);
+    }
+
+    @Test public void testAssertionLifetime() {
+        final BrowserSSOProfileConfiguration config = new BrowserSSOProfileConfiguration();
+        Assert.assertNull(config.getAssertionLifetime(null));
+
+        config.setAssertionLifetime(Duration.ofMillis(100));
+        Assert.assertEquals(config.getAssertionLifetime(null), Duration.ofMillis(100));
+
+        try {
+            config.setAssertionLifetime(Duration.ofMillis(-100));
+            Assert.fail();
+        } catch (ConstraintViolationException e) {
+            // expected this
+        }
+    }
+
+    @Test public void testIndirectAssertionLifetime() {
+        final BrowserSSOProfileConfiguration config = new BrowserSSOProfileConfiguration();
+        
+        config.setAssertionLifetimeLookupStrategy(FunctionSupport.constant(Duration.ofMillis(500)));
+        Assert.assertEquals(config.getAssertionLifetime(null), Duration.ofMillis(500));
+    }
+
+    @Test public void testAssertionAudiences() {
+        final BrowserSSOProfileConfiguration config = new BrowserSSOProfileConfiguration();
+        Assert.assertNotNull(config.getAssertionAudiences(null));
+        Assert.assertTrue(config.getAssertionAudiences(null).isEmpty());
+
+        config.setAssertionAudiences(Arrays.asList("", null, " foo"));
+
+        final Set<String> audiences = config.getAssertionAudiences(null);
+        Assert.assertNotNull(audiences);
+        Assert.assertEquals(audiences.size(), 1);
+        Assert.assertTrue(audiences.contains("foo"));
+
+        try {
+            audiences.add("bar");
+            Assert.fail();
+        } catch (UnsupportedOperationException e) {
+            // expected this
+        }
+
+        config.setAssertionAudiences(null);
+        Assert.assertNotNull(config.getAssertionAudiences(null));
+        Assert.assertTrue(config.getAssertionAudiences(null).isEmpty());
+    }
+
+    @Test public void testIndirectAssertionAudiences() {
+final BrowserSSOProfileConfiguration config = new BrowserSSOProfileConfiguration();
+        final Set<String> audiences = new HashSet<>();
+        audiences.add("foo");
+        audiences.add("bar");
+        config.setAssertionAudiencesLookupStrategy(FunctionSupport.constant(audiences));
+        Assert.assertEquals(config.getAssertionAudiences(null), audiences);
+    }
+
+    @Test
+    public void testNameIDFormat() {
+        final BrowserSSOProfileConfiguration config = new BrowserSSOProfileConfiguration();
+        Assert.assertNull(config.getNameIDFormat(null));
+
+        config.setNameIDFormat("foo");
+        Assert.assertEquals(config.getNameIDFormat(null), "foo");
+    }
+
+    @Test
+    public void testIndirectNameIDFormat() {
+        final BrowserSSOProfileConfiguration config = new BrowserSSOProfileConfiguration();
+
+        config.setNameIDFormatLookupStrategy(FunctionSupport.constant("foo"));
+        Assert.assertEquals(config.getNameIDFormat(null), "foo");
+    }
+
+    @Test
+    public void testSPNameQualifier() {
+        final BrowserSSOProfileConfiguration config = new BrowserSSOProfileConfiguration();
+        Assert.assertNull(config.getSPNameQualifier(null));
+
+        config.setSPNameQualifier("foo");
+        Assert.assertEquals(config.getSPNameQualifier(null), "foo");
+    }
+
+    @Test
+    public void testIndirectSPNameQualifier() {
+        final BrowserSSOProfileConfiguration config = new BrowserSSOProfileConfiguration();
+
+        config.setSPNameQualifierLookupStrategy(FunctionSupport.constant("foo"));
+        Assert.assertEquals(config.getSPNameQualifier(null), "foo");
+    }
+
+    @Test
+    public void testAttributeIndex() {
+        final BrowserSSOProfileConfiguration config = new BrowserSSOProfileConfiguration();
+        Assert.assertNull(config.getAttributeIndex(null));
+
+        config.setAttributeIndex(10);
+        Assert.assertEquals(config.getAttributeIndex(null), 10);
+    }
+
+    @Test
+    public void testIndirectAttributeIndex() {
+        final BrowserSSOProfileConfiguration config = new BrowserSSOProfileConfiguration();
+
+        config.setAttributeIndexLookupStrategy(FunctionSupport.constant(10));
+        Assert.assertEquals(config.getAttributeIndex(null), 10);
+    }
+
+    @Test
+    public void testSignArtifactRequests() {
+        final BrowserSSOProfileConfiguration config = new BrowserSSOProfileConfiguration();
+        
+        config.setSignArtifactRequests(true);
+        Assert.assertTrue(config.isSignArtifactRequests(null));
+    }
+     
+    @Test
+    public void testClientTLSArtifactRequests() {
+        final BrowserSSOProfileConfiguration config = new BrowserSSOProfileConfiguration();
+        
+        config.setClientTLSArtifactRequests(true);
+        Assert.assertTrue(config.isClientTLSArtifactRequests(null));
+    }
+     
+    
+    @Test public void testProxyCount() {
+        final BrowserSSOProfileConfiguration config = new BrowserSSOProfileConfiguration();
+        Assert.assertNull(config.getProxyCount(null));
+
+        config.setProxyCount(1);
+        Assert.assertEquals(config.getProxyCount(null), Integer.valueOf(1));
+    }
+
+    @Test public void testIndirectProxyCount() {
+        final BrowserSSOProfileConfiguration config = new BrowserSSOProfileConfiguration();
+
+        config.setProxyCountLookupStrategy(FunctionSupport.constant(1));
+        Assert.assertEquals(config.getProxyCount(null), Integer.valueOf(1));
+    }
+
+}
\ No newline at end of file
diff --git a/sp-saml-impl/src/test/java/net/shibboleth/sp/saml/saml2/profile/config/impl/SingleLogoutProfileConfigurationTest.java b/sp-saml-impl/src/test/java/net/shibboleth/sp/saml/saml2/profile/config/impl/SingleLogoutProfileConfigurationTest.java
new file mode 100644
index 0000000..be5582d
--- /dev/null
+++ b/sp-saml-impl/src/test/java/net/shibboleth/sp/saml/saml2/profile/config/impl/SingleLogoutProfileConfigurationTest.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.sp.saml.saml2.profile.config.impl;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import net.shibboleth.shared.logic.PredicateSupport;
+
+/** Unit test for {@link SingleLogoutProfileConfiguration}. */
+ at SuppressWarnings("javadoc")
+public class SingleLogoutProfileConfigurationTest {
+
+    @Test
+    public void testProfileId() {
+        final SingleLogoutProfileConfiguration config = new SingleLogoutProfileConfiguration();
+        Assert.assertEquals(config.getId(), SingleLogoutProfileConfiguration.PROFILE_ID);
+    }
+    
+    @Test
+    public void testSignArtifactRequests() {
+        final SingleLogoutProfileConfiguration config = new SingleLogoutProfileConfiguration();
+        
+        config.setSignArtifactRequests(true);
+        Assert.assertTrue(config.isSignArtifactRequests(null));
+    }
+     
+    @Test
+    public void testClientTLSArtifactRequests() {
+        final SingleLogoutProfileConfiguration config = new SingleLogoutProfileConfiguration();
+        
+        config.setClientTLSArtifactRequests(true);
+        Assert.assertTrue(config.isClientTLSArtifactRequests(null));
+    }
+
+    @Test
+    public void testAsynchronous() {
+        final SingleLogoutProfileConfiguration config = new SingleLogoutProfileConfiguration();
+        Assert.assertTrue(config.isAsynchronous(null));
+        
+        config.setAsynchronous(false);
+        Assert.assertFalse(config.isAsynchronous(null));
+    }
+
+    @Test
+    public void testIndirectAsynchronous() {
+        final SingleLogoutProfileConfiguration config = new SingleLogoutProfileConfiguration();
+        
+        config.setAsynchronousPredicate(PredicateSupport.alwaysFalse());
+        Assert.assertFalse(config.isAsynchronous(null));
+    }
+
+}
\ No newline at end of file
diff --git a/sp-server-api/pom.xml b/sp-server-api/pom.xml
index 63a6a04..67e0bdb 100644
--- a/sp-server-api/pom.xml
+++ b/sp-server-api/pom.xml
@@ -21,6 +21,11 @@
         
     <dependencies>
         <!-- Compile dependencies -->
+        <dependency>
+            <groupId>net.shibboleth</groupId>
+            <artifactId>shib-profile-api</artifactId>
+        </dependency>
+
         <dependency>
             <groupId>net.shibboleth</groupId>
             <artifactId>shib-attribute-resolver-api</artifactId>
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/Application.java b/sp-server-api/src/main/java/net/shibboleth/sp/Application.java
index e36753f..b177834 100644
--- a/sp-server-api/src/main/java/net/shibboleth/sp/Application.java
+++ b/sp-server-api/src/main/java/net/shibboleth/sp/Application.java
@@ -23,9 +23,11 @@ import org.opensaml.saml.metadata.resolver.MetadataResolver;
 import net.shibboleth.idp.attribute.filter.AttributeFilter;
 import net.shibboleth.idp.attribute.resolver.AttributeResolver;
 import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.profile.relyingparty.RelyingPartyConfigurationResolver;
 import net.shibboleth.shared.annotation.constraint.NotEmpty;
 import net.shibboleth.shared.component.IdentifiedComponent;
-import net.shibboleth.shared.service.ReloadableService;
+import net.shibboleth.shared.service.ServiceException;
+import net.shibboleth.shared.service.ServiceableComponent;
 import net.shibboleth.sp.remoting.Endpoint;
 
 /**
@@ -47,34 +49,64 @@ public interface Application extends IdentifiedComponent, Endpoint {
     /** Name of common member to signal receiving component. */
     @Nonnull @NotEmpty static String COMPONENT = "this";
     
-    // TODO: Expose various injected services for use by ApplicationEndpoints.
+    /**
+     * Get {@link RelyingPartyConfigurationResolver} for this {@link Application}.
+     * 
+     * <p>The component is returned in a locked state and must be closed by the caller.</p>
+     * 
+     * @return the {@link RelyingPartyConfigurationResolver} to use
+     * 
+     * @throws ServiceException if no instance can be obtained
+     */
+    @Nonnull ServiceableComponent<RelyingPartyConfigurationResolver> getRelyingPartyConfigurationResolver()
+            throws ServiceException;
     
     /**
      * Get {@link MetadataResolver} for this {@link Application}.
      * 
+     * <p>The component is returned in a locked state and must be closed by the caller.</p>
+     * 
      * @return the metadata source to use
+     * 
+     * @throws ServiceException if no instance can be obtained
      */
-    @Nonnull ReloadableService<MetadataResolver> getMetadataResolver();
+    @Nonnull ServiceableComponent<MetadataResolver> getMetadataResolver()
+            throws ServiceException;
 
     /**
      * Get {@link AttributeTranscoderRegistry} for this {@link Application}.
      * 
+     * <p>The component is returned in a locked state and must be closed by the caller.</p>
+     * 
      * @return the attribute filter to use
+     * 
+     * @throws ServiceException if no instance can be obtained
      */
-    @Nonnull ReloadableService<AttributeTranscoderRegistry> getAttributeTranscoderRegistry();
+    @Nonnull ServiceableComponent<AttributeTranscoderRegistry> getAttributeTranscoderRegistry()
+            throws ServiceException;
 
     /**
      * Get {@link AttributeResolver} for this {@link Application}.
      * 
+     * <p>The component is returned in a locked state and must be closed by the caller.</p>
+     * 
      * @return the attribute resolver to use
+     * 
+     * @throws ServiceException if no instance can be obtained
      */
-    @Nonnull ReloadableService<AttributeResolver> getAttributeResolver();
+    @Nonnull ServiceableComponent<AttributeResolver> getAttributeResolver()
+            throws ServiceException;
 
     /**
      * Get {@link AttributeFilter} for this {@link Application}.
      * 
+     * <p>The component is returned in a locked state and must be closed by the caller.</p>
+     * 
      * @return the attribute filter to use
+     * 
+     * @throws ServiceException if no instance can be obtained
      */
-    @Nonnull ReloadableService<AttributeFilter> getAttributeFilter();
+    @Nonnull ServiceableComponent<AttributeFilter> getAttributeFilter()
+            throws ServiceException;
 
 }
\ No newline at end of file
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/config/AbstractConditionalProfileConfiguration.java b/sp-server-api/src/main/java/net/shibboleth/sp/config/AbstractConditionalProfileConfiguration.java
deleted file mode 100644
index 146da74..0000000
--- a/sp-server-api/src/main/java/net/shibboleth/sp/config/AbstractConditionalProfileConfiguration.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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.sp.config;
-
-import java.util.function.Predicate;
-
-import javax.annotation.Nonnull;
-
-import net.shibboleth.shared.annotation.ParameterName;
-import net.shibboleth.shared.annotation.constraint.NotEmpty;
-import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.shared.logic.PredicateSupport;
-
-import org.opensaml.profile.context.ProfileRequestContext;
-
-/**
- * Base class for {@link ConditionalProfileConfiguration} implementations.
- *
- * @since 3.4.0
- */
-public abstract class AbstractConditionalProfileConfiguration extends AbstractProfileConfiguration
-        implements ConditionalProfileConfiguration {
-
-    /** Activation condition. */
-    @Nonnull private Predicate<ProfileRequestContext> activationCondition;
-
-    /**
-     * Constructor.
-     * 
-     * @param id ID of the communication profile, never null or empty
-     */
-    public AbstractConditionalProfileConfiguration(@Nonnull @NotEmpty @ParameterName(name="id") final String id) {
-        super(id);
-        
-        activationCondition = PredicateSupport.alwaysTrue();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    @Nonnull public Predicate<ProfileRequestContext> getActivationCondition() {
-        return activationCondition;
-    }
-
-    /**
-     * Set an activation condition to control this profile.
-     *
-     * @param condition condition to apply
-     */
-    public void setActivationCondition(@Nonnull final Predicate<ProfileRequestContext> condition) {
-        activationCondition = Constraint.isNotNull(condition, "Activation condition cannot be null");
-    }
-    
-}
\ No newline at end of file
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/config/AbstractProfileConfiguration.java b/sp-server-api/src/main/java/net/shibboleth/sp/config/AbstractProfileConfiguration.java
deleted file mode 100644
index 2a0b713..0000000
--- a/sp-server-api/src/main/java/net/shibboleth/sp/config/AbstractProfileConfiguration.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * 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.sp.config;
-
-import java.util.Objects;
-import java.util.function.Function;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import net.shibboleth.shared.annotation.ParameterName;
-import net.shibboleth.shared.annotation.constraint.NotEmpty;
-import net.shibboleth.shared.component.AbstractIdentifiableInitializableComponent;
-import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.shared.logic.FunctionSupport;
-
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.opensaml.security.config.SecurityConfiguration;
-
-/** Base class for {@link ProfileConfiguration} implementations. */
-public abstract class AbstractProfileConfiguration extends AbstractIdentifiableInitializableComponent
-        implements ProfileConfiguration {
-
-    /** Default value for disallowedFeatures property. */
-    @Nonnull public static final Integer DEFAULT_DISALLOWED_FEATURES = 0; 
-
-    /** Lookup function to supply securityConfiguration property. */
-    @Nonnull private Function<ProfileRequestContext,SecurityConfiguration> securityConfigurationLookupStrategy;
-
-    /** Lookup function to return a bitmask of request features to disallow. */
-    @Nonnull private Function<ProfileRequestContext,Integer> disallowedFeaturesLookupStrategy;
-
-    /**
-     * Constructor.
-     * 
-     * @param id ID of the communication profile, never null or empty
-     */
-    public AbstractProfileConfiguration(@Nonnull @NotEmpty @ParameterName(name="id") final String id) {
-        setId(id);
-        securityConfigurationLookupStrategy = FunctionSupport.constant(null);
-        disallowedFeaturesLookupStrategy = FunctionSupport.constant(DEFAULT_DISALLOWED_FEATURES);
-    }
-
-    /** {@inheritDoc} */
-    @Nullable public SecurityConfiguration getSecurityConfiguration(
-            @Nullable final ProfileRequestContext profileRequestContext) {
-        return securityConfigurationLookupStrategy.apply(profileRequestContext);
-    }
-
-    /**
-     * Sets the security configuration for this profile.
-     * 
-     * @param configuration security configuration for this profile
-     */
-    public void setSecurityConfiguration(@Nullable final SecurityConfiguration configuration) {
-        securityConfigurationLookupStrategy = FunctionSupport.constant(configuration);
-    }
-
-    /**
-     * Set a lookup strategy for the security configuration.
-     *
-     * @param strategy  lookup strategy
-     * 
-     * @since 3.3.0
-     */
-    public void setSecurityConfigurationLookupStrategy(
-            @Nonnull final Function<ProfileRequestContext,SecurityConfiguration> strategy) {
-        securityConfigurationLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
-    }
-    
-    /** {@inheritDoc} */
-    public boolean isFeatureDisallowed(@Nullable final ProfileRequestContext profileRequestContext, final int feature) {
-        return (getDisallowedFeatures(profileRequestContext) & feature) == feature;
-    }
-    
-    /** {@inheritDoc} */
-    public int getDisallowedFeatures(@Nullable final ProfileRequestContext profileRequestContext) {
-        final Integer mask = disallowedFeaturesLookupStrategy.apply(profileRequestContext); 
-        return mask != null ? mask : DEFAULT_DISALLOWED_FEATURES;
-    }
-    
-    /**
-     * Set a bitmask of disallowed features to block.
-     * 
-     * @param mask a bitmask of features to block
-     */
-    public void setDisallowedFeatures(final int mask) {
-        disallowedFeaturesLookupStrategy = FunctionSupport.constant(mask);
-    }
-    
-    /**
-     * Set a lookup strategy for the bitmask of disallowed features to block. 
-     * 
-     * @param strategy lookup strategy
-     */
-    public void setDisallowedFeaturesLookupStrategy(@Nonnull final Function<ProfileRequestContext,Integer> strategy) {
-        disallowedFeaturesLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
-    }
-    
-    /** {@inheritDoc} */
-    @Override
-    public int hashCode() {
-        return getId().hashCode();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public boolean equals(final Object obj) {
-        if (this == obj) {
-            return true;
-        }
-
-        if (obj == null) {
-            return false;
-        }
-
-        if (!(obj instanceof AbstractProfileConfiguration)) {
-            return false;
-        }
-
-        final AbstractProfileConfiguration other = (AbstractProfileConfiguration) obj;
-        return Objects.equals(getId(), other.getId());
-    }
-    
-}
\ No newline at end of file
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/config/AssertingParty.java b/sp-server-api/src/main/java/net/shibboleth/sp/config/AssertingParty.java
deleted file mode 100644
index d24e1c2..0000000
--- a/sp-server-api/src/main/java/net/shibboleth/sp/config/AssertingParty.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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.sp.config;
-
-import java.util.Map;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import org.opensaml.profile.context.ProfileRequestContext;
-
-import net.shibboleth.shared.annotation.constraint.NonnullElements;
-import net.shibboleth.shared.annotation.constraint.NotLive;
-import net.shibboleth.shared.annotation.constraint.Unmodifiable;
-import net.shibboleth.shared.component.IdentifiedComponent;
-
-/**
- * Package of options exposed for configuring behavior when dealing with particular sources
- * of identity.
- * 
- * <p>Historically referred to as a "relying party", renamed for better accuracy.</p>
- */
-public interface AssertingParty extends IdentifiedComponent {
-
-    // TODO: This is entirely speculative but I lean toward this being pretty minimal and not
-    // particularly connected to the "existing" <RelyingParty> settings/concept in the SP.
-    // Rather, this is for hub-side controlled settings geared around technical protocol
-    // settings subject to IT control, modulo a few overrideable options at runtime that
-    // the agent would supply while making subsequent calls.
-    
-    /**
-     * Get map of profile configurations keyed by profile ID.
-     * 
-     * @param profileRequestContext profile request context 
-     * 
-     * @return map of profile configurations
-     */
-    @Nonnull @NonnullElements @NotLive @Unmodifiable Map<String,ProfileConfiguration> getProfileConfigurations(
-            @Nullable final ProfileRequestContext profileRequestContext);
-
-    /**
-     * Get the profile configuration, for the asserting party, for the given profile.
-     * 
-     * @param profileRequestContext current profile request context
-     * @param profileId the ID of the profile
-     * 
-     * @return the configuration for the profile or null if the profile ID was null or empty or there is no
-     *         configuration for the given profile
-     */
-    @Nullable public ProfileConfiguration getProfileConfiguration(
-            @Nullable final ProfileRequestContext profileRequestContext, @Nullable final String profileId);
-
-}
\ No newline at end of file
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/config/AssertingPartyResolver.java b/sp-server-api/src/main/java/net/shibboleth/sp/config/AssertingPartyResolver.java
deleted file mode 100644
index a0aef23..0000000
--- a/sp-server-api/src/main/java/net/shibboleth/sp/config/AssertingPartyResolver.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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.sp.config;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import org.opensaml.profile.criterion.ProfileRequestContextCriterion;
-import org.opensaml.security.config.SecurityConfiguration;
-
-import net.shibboleth.shared.annotation.constraint.NotEmpty;
-import net.shibboleth.shared.resolver.CriteriaSet;
-import net.shibboleth.shared.resolver.Resolver;
-
-/**
- * Interface to a resolution service that identifies the applicable {@link AssertingParty}
- * instance for a request based on extensible criteria.
- * 
- * <p>The {@link ProfileRequestContextCriterion} criterion type MUST be supported; other
- * types are optional.</p>
- * 
- * <p>This service must also expose a default security configuration for supported profiles.</p>
- */
-public interface AssertingPartyResolver extends Resolver<AssertingParty,CriteriaSet> {
-
-    /**
-     * Return the default security configuration for the profile.
-     * 
-     * @param profileId the profile ID
-     * 
-     * @return the default configuration for the profile
-     */
-    @Nullable SecurityConfiguration getDefaultSecurityConfiguration(@Nonnull @NotEmpty final String profileId);
-
-}
\ No newline at end of file
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/config/CredentialHolder.java b/sp-server-api/src/main/java/net/shibboleth/sp/config/CredentialHolder.java
deleted file mode 100644
index efb5770..0000000
--- a/sp-server-api/src/main/java/net/shibboleth/sp/config/CredentialHolder.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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.sp.config;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import org.opensaml.security.credential.Credential;
-
-import net.shibboleth.shared.annotation.constraint.NonnullElements;
-
-/**
- * This is a utility class used as an auto-wiring source for collections of
- * signing and encryption {@link Credential} objects so that other layers of the
- * system can gain access to the complete set of them.
- */
-public class CredentialHolder {
-
-    /** Credentials to expose. */
-    @Nonnull @NonnullElements private final List<Credential> credentials;
-    
-    /**
-     * Constructor.
-     *
-     * @param creds credentials to expose to other components
-     */
-    public CredentialHolder(@Nullable @NonnullElements final Collection<Credential> creds) {
-        if (creds != null) {
-            credentials = List.copyOf(creds);
-        } else {
-            credentials = Collections.emptyList();
-        }
-    }
-    
-    /**
-     * Get the credentials to expose to other components.
-     * 
-     * @return credentials to expose
-     */
-    @Nonnull @NonnullElements public Collection<Credential> getCredentials() {
-        return credentials;
-    }
-    
-}
\ No newline at end of file
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/config/ProfileConfiguration.java b/sp-server-api/src/main/java/net/shibboleth/sp/config/ProfileConfiguration.java
deleted file mode 100644
index 13bc699..0000000
--- a/sp-server-api/src/main/java/net/shibboleth/sp/config/ProfileConfiguration.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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.sp.config;
-
-import javax.annotation.Nullable;
-
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.opensaml.security.config.SecurityConfiguration;
-
-import net.shibboleth.shared.component.IdentifiedComponent;
-
-/**
- * Interface for profile-specific configuration used to customize SP behavior.
- */
-public interface ProfileConfiguration extends IdentifiedComponent {
-
-    /**
-     * Gets the applicable {@link SecurityConfiguration}.
-     * 
-     * @param profileRequestContext profile request context 
-     * 
-     * @return the profile's security configuration
-     */
-    @Nullable SecurityConfiguration getSecurityConfiguration(
-            @Nullable final ProfileRequestContext profileRequestContext);
- 
-    /**
-     * Get a bitmask of disallowed features to block.
-     * 
-     * <p>Individual profiles define their own feature constants.</p>
-     * 
-     * @param profileRequestContext current profile request context
-     * 
-     * @return bitmask of features to block
-     */
-    public int getDisallowedFeatures(@Nullable final ProfileRequestContext profileRequestContext);
-
-    /**
-     * Return true iff the input feature constant is disallowed.
-     * 
-     * @param profileRequestContext current profile request context
-     * @param feature a bit constant
-     * 
-     * @return true iff the input feature constant is disallowed
-     */
-    public boolean isFeatureDisallowed(@Nullable final ProfileRequestContext profileRequestContext, final int feature);
-    
-}
\ No newline at end of file
diff --git a/sp-server-impl/src/main/java/net/shibboleth/sp/config/impl/BasicAssertingParty.java b/sp-server-impl/src/main/java/net/shibboleth/sp/config/impl/BasicAssertingParty.java
deleted file mode 100644
index 90dc6ee..0000000
--- a/sp-server-impl/src/main/java/net/shibboleth/sp/config/impl/BasicAssertingParty.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * 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.sp.config.impl;
-
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.function.Function;
-import java.util.function.Predicate;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import org.opensaml.profile.context.ProfileRequestContext;
-
-import net.shibboleth.shared.annotation.constraint.NonnullElements;
-import net.shibboleth.shared.annotation.constraint.NotLive;
-import net.shibboleth.shared.annotation.constraint.Unmodifiable;
-import net.shibboleth.shared.collection.CollectionSupport;
-import net.shibboleth.shared.component.AbstractIdentifiableInitializableComponent;
-import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.shared.logic.FunctionSupport;
-import net.shibboleth.shared.logic.PredicateSupport;
-import net.shibboleth.shared.primitive.StringSupport;
-import net.shibboleth.sp.config.AssertingParty;
-import net.shibboleth.sp.config.ProfileConfiguration;
-
-/**
- * Concrete implementation of {@link AssertingParty} interface to support the default implementation of
- * a Spring-based configuration resolver.
- */
-public class BasicAssertingParty extends AbstractIdentifiableInitializableComponent implements AssertingParty,
-        Predicate<ProfileRequestContext> {
-
-    /** Lookup function to supply <code>profileConfigurations</code> property. */
-    @Nonnull
-    private Function<ProfileRequestContext,Map<String,ProfileConfiguration>> profileConfigurationsLookupStrategy;
-
-    /** Predicate that must be true for this configuration to be active for a given request. */
-    @Nonnull private Predicate<ProfileRequestContext> activationCondition;
-
-    /** Constructor. */
-    public BasicAssertingParty() {
-        activationCondition = PredicateSupport.alwaysTrue();
-        profileConfigurationsLookupStrategy = FunctionSupport.constant(null);
-    }
-
-    
-    /** {@inheritDoc} */
-    @Nonnull @NonnullElements @Unmodifiable @NotLive public Map<String,ProfileConfiguration> getProfileConfigurations(
-            @Nullable final ProfileRequestContext profileRequestContext) {
-        checkComponentActive();
-        
-        final Map<String,ProfileConfiguration> map = profileConfigurationsLookupStrategy.apply(profileRequestContext);
-        if (map != null) {
-            return CollectionSupport.copyToMap(map);
-        }
-        return CollectionSupport.emptyMap();
-    }
-
-    /** {@inheritDoc} */
-    @Nullable public ProfileConfiguration getProfileConfiguration(
-            @Nullable final ProfileRequestContext profileRequestContext, @Nullable final String profileId) {
-        checkComponentActive();
-        
-        final String trimmedId = StringSupport.trimOrNull(profileId);
-        if (trimmedId == null) {
-            return null;
-        }
-        
-        final Map<String,ProfileConfiguration> configs = profileConfigurationsLookupStrategy.apply(profileRequestContext);
-        if (configs != null) {
-            return configs.get(trimmedId);
-        }
-        
-        return null;
-    }
-
-    /**
-     * Set the profile configurations for this relying party.
-     * 
-     * @param configs the configurations to set
-     */
-    public void setProfileConfigurations(@Nullable @NonnullElements final Collection<ProfileConfiguration> configs) {
-        checkSetterPreconditions();
-        
-        if (configs == null) {
-            profileConfigurationsLookupStrategy = FunctionSupport.constant(null);
-        } else {
-            final HashMap<String,ProfileConfiguration> map = new HashMap<>();
-            for (final ProfileConfiguration config : configs) {
-                final String trimmedId =
-                        Constraint.isNotNull(StringSupport.trimOrNull(config.getId()),
-                                "ID of profile configuration class " + config.getClass().getName() + " cannot be null");
-                map.put(trimmedId, config);
-            }
-            profileConfigurationsLookupStrategy = FunctionSupport.constant(map);
-        }
-    }
-
-    /**
-     * Set a lookup strategy for the <code>profileConfigurations</code> property.
-     * 
-     * @param strategy lookup strategy
-     */
-    public void setProfileConfigurationsLookupStrategy(
-            @Nonnull final Function<ProfileRequestContext,Map<String,ProfileConfiguration>> strategy) {
-        checkSetterPreconditions();
-        
-        profileConfigurationsLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
-    }
-
-    /**
-     * Set the condition under which the relying party configuration should be active.
-     * 
-     * @param condition the activation condition
-     */
-    public void setActivationCondition(@Nonnull final Predicate<ProfileRequestContext> condition) {
-        checkSetterPreconditions();
-        
-        activationCondition =
-                Constraint.isNotNull(condition, "Relying party configuration activation condition cannot be null");
-    }
-
-    /** {@inheritDoc} */
-    public boolean test(@Nullable final ProfileRequestContext input) {
-        checkComponentActive();
-        
-        return activationCondition.test(input);
-    }
-
-}
\ No newline at end of file
diff --git a/sp-server-impl/src/main/java/net/shibboleth/sp/config/impl/DefaultAssertingPartyResolver.java b/sp-server-impl/src/main/java/net/shibboleth/sp/config/impl/DefaultAssertingPartyResolver.java
deleted file mode 100644
index dfdd5ff..0000000
--- a/sp-server-impl/src/main/java/net/shibboleth/sp/config/impl/DefaultAssertingPartyResolver.java
+++ /dev/null
@@ -1,304 +0,0 @@
-/*
- * 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.sp.config.impl;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.function.Predicate;
-import java.util.stream.Collectors;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.opensaml.profile.criterion.ProfileRequestContextCriterion;
-import org.opensaml.security.config.SecurityConfiguration;
-import org.opensaml.security.credential.Credential;
-import org.slf4j.Logger;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Qualifier;
-
-import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
-import net.shibboleth.shared.annotation.constraint.NonnullElements;
-import net.shibboleth.shared.annotation.constraint.NotEmpty;
-import net.shibboleth.shared.annotation.constraint.NotLive;
-import net.shibboleth.shared.annotation.constraint.Unmodifiable;
-import net.shibboleth.shared.collection.CollectionSupport;
-import net.shibboleth.shared.component.AbstractIdentifiableInitializableComponent;
-import net.shibboleth.shared.component.ComponentInitializationException;
-import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.shared.primitive.LoggerFactory;
-import net.shibboleth.shared.resolver.CriteriaSet;
-import net.shibboleth.shared.resolver.Criterion;
-import net.shibboleth.shared.resolver.ResolverException;
-import net.shibboleth.sp.config.AssertingParty;
-import net.shibboleth.sp.config.AssertingPartyResolver;
-import net.shibboleth.sp.config.CredentialHolder;
-
-/**
- * Retrieves a per-asserting party configuration for a given profile request based on the
- * supplied {@link CriteriaSet}.
- *
- * <p>Supported {@link Criterion}:</p>
- * <ul>
- *  <li>{@link ProfileRequestContextCriterion}</li>
- * </ul>
- * 
- * <p>
- * Note that this resolver does not permit more than one {@link AssertingParty} with the same ID.
- * </p>
- */
-public class DefaultAssertingPartyResolver extends AbstractIdentifiableInitializableComponent
-        implements AssertingPartyResolver {
-
-    /** Class logger. */
-    @Nonnull private final Logger log = LoggerFactory.getLogger(DefaultAssertingPartyResolver.class);
-
-    /** Registered relying party configurations. */
-    @Nonnull private List<BasicAssertingParty> assertingParties;
-
-    /** Default asserting party, used if no other verified instance matches. */
-    @NonnullAfterInit private BasicAssertingParty defaultAssertingParty;
-
-    /** The predicate which decides if this request is "verified". */
-    @NonnullAfterInit private Predicate<ProfileRequestContext> verificationPredicate;
-    
-    /** A global default security configuration. */
-    @Nullable private SecurityConfiguration defaultSecurityConfiguration;
-    
-    /** The global list of all configured signing credentials. */
-    @Nonnull private List<Credential> signingCredentials;
-    
-    /** The global list of all configured encryption credentials. */
-    @Nonnull private List<Credential> encryptionCredentials;
-
-    /** Constructor. */
-    public DefaultAssertingPartyResolver() {
-        assertingParties = CollectionSupport.emptyList();
-        signingCredentials = CollectionSupport.emptyList();
-        encryptionCredentials = CollectionSupport.emptyList();
-    }
-    
-    /**
-     * Get an unmodifiable list of verified relying party configurations.
-     * 
-     * @return unmodifiable list of verified relying party configurations
-     */
-    @Nonnull @NonnullElements @Unmodifiable @NotLive public Collection<BasicAssertingParty> getAssertingParties() {
-        return assertingParties;
-    }
-
-    /**
-     * Set the verified relying party configurations.
-     * 
-     * @param configs list of verified relying party configurations
-     */
-    public void setAssertingParties(@Nullable @NonnullElements final Collection<BasicAssertingParty> configs) {
-        checkSetterPreconditions();
-        
-        if (configs != null) {
-            assertingParties = CollectionSupport.copyToList(configs);
-        } else {
-            assertingParties = CollectionSupport.emptyList();
-        }
-    }
-
-    /**
-     * Get the {@link AssertingParty} to use if no other configuration is applicable.
-     * 
-     * @return default configuration
-     */
-    @NonnullAfterInit public BasicAssertingParty getDefaultConfiguration() {
-        return defaultAssertingParty;
-    }
-
-    /**
-     * Set the {@link AssertingParty} to use if no other configuration is applicable.
-     * 
-     * @param configuration default configuration
-     */
-    public void setDefaultConfiguration(@Nonnull final BasicAssertingParty configuration) {
-        checkSetterPreconditions();
-        defaultAssertingParty = Constraint.isNotNull(configuration, "Default AssertingParty cannot be null");
-    }
-
-    /**
-     * Set the global default {@link SecurityConfiguration}.
-     * 
-     * @param config  global default
-     */
-    public void setDefaultSecurityConfiguration(@Nullable final SecurityConfiguration config) {
-        checkSetterPreconditions();
-        defaultSecurityConfiguration = config;
-    }
-
-    /** {@inheritDoc} */
-    @Override protected void doInitialize() throws ComponentInitializationException {
-        super.doInitialize();
-
-        final HashSet<String> configIds = new HashSet<>(assertingParties.size());
-        for (final AssertingParty config : assertingParties) {
-            if (configIds.contains(config.getId())) {
-                throw new ComponentInitializationException("Multiple AssertingParty configurations with ID "
-                        + config.getId() + " detected, IDs must be unique.");
-            }
-            configIds.add(config.getId());
-        }
-    }
-
-    /** {@inheritDoc} */
-    @Nonnull @NonnullElements public Iterable<AssertingParty> resolve(@Nullable final CriteriaSet criteria)
-            throws ResolverException {
-        checkComponentActive();
-
-        final ProfileRequestContext context = getProfileRequestContext(criteria);
-        if (context == null) {
-            return CollectionSupport.emptyList();
-        }
-
-        log.debug("Resolving asserting party configuration");
-
-        final ArrayList<AssertingParty> matches = new ArrayList<>();
-
-        for (final BasicAssertingParty configuration : assertingParties) {
-            log.debug("Checking if asserting party configuration {} is applicable", configuration.getId());
-            if (configuration.test(context)) {
-                log.debug("Asserting party configuration {} is applicable", configuration.getId());
-                matches.add(configuration);
-            } else {
-                log.debug("Asserting party configuration {} is not applicable", configuration.getId());
-            }
-        }
-
-        if (matches.isEmpty()) {
-            log.debug("No matching asserting party configuration applicable, returning default: {}",
-                    getDefaultConfiguration().getId());
-            return CollectionSupport.singleton(getDefaultConfiguration());
-        }
-        return matches;
-    }
-
-    /** {@inheritDoc} */
-    @Nullable public AssertingParty resolveSingle(@Nullable final CriteriaSet criteria) throws ResolverException {
-        checkComponentActive();
-
-        final ProfileRequestContext context = getProfileRequestContext(criteria);
-        if (context == null) {
-            return null;
-        }
-        
-        log.debug("Resolving asserting party configuration");
-
-        for (final BasicAssertingParty configuration : assertingParties) {
-            log.debug("Checking if asserting party configuration {} is applicable", configuration.getId());
-            if (configuration.test(context)) {
-                log.debug("Asserting party configuration {} is applicable", configuration.getId());
-                return configuration;
-            }
-            log.debug("Asserting party configuration {} is not applicable", configuration.getId());
-        }
-
-        log.debug("No asserting party configurations applicable, returning default: {}",
-                getDefaultConfiguration().getId());
-        return getDefaultConfiguration();
-    }
-
-    /** {@inheritDoc} */
-    @Nullable public SecurityConfiguration getDefaultSecurityConfiguration(@Nonnull @NotEmpty final String profileId) {
-        return defaultSecurityConfiguration;
-    }
-    
-    /**
-     * Get the list of all configured signing credentials.
-     * 
-     * @return the list of signing credentials
-     */
-    @Nonnull @NonnullElements @Unmodifiable @NotLive public List<Credential> getSigningCredentials() {
-        return signingCredentials;
-    }
-    
-    /**
-     * Set the list of all configured signing credentials.
-     * 
-     * @param credentials the list of signing credentials, may be null
-     */
-    @Autowired
-    @Qualifier("signing")
-    public void setSigningCredentials(
-            @Nullable @NonnullElements final List<CredentialHolder> credentials) {
-        checkSetterPreconditions();
-        
-        if (credentials != null) {
-            signingCredentials = credentials.stream()
-                    .flatMap(h -> h.getCredentials().stream())
-                    .collect(CollectionSupport.nonnullCollector(Collectors.toUnmodifiableList())).get();
-        } else {
-            signingCredentials = CollectionSupport.emptyList();
-        }
-    }
-
-    /**
-     * Get the list of all configured encryption credentials.
-     * 
-     * @return the list of encryption credentials
-     */
-    @Nonnull @NonnullElements @Unmodifiable @NotLive public List<Credential> getEncryptionCredentials() {
-        return encryptionCredentials;
-    }
-    
-    /**
-     * Set the list of all configured encryption credentials.
-     * 
-     * @param credentials the list of encryption credentials, may be null
-     */
-    @Autowired
-    @Qualifier("encryption")
-    public void setEncryptionCredentials(
-            @Nullable @NonnullElements final List<CredentialHolder> credentials) {
-        checkSetterPreconditions();
-        
-        if (credentials != null) {
-            encryptionCredentials = credentials.stream()
-                    .flatMap(h -> h.getCredentials().stream())
-                    .collect(CollectionSupport.nonnullCollector(Collectors.toUnmodifiableList())).get();
-        } else {
-            encryptionCredentials = CollectionSupport.emptyList();
-        }
-    }
-    
-    /**
-     * Get the {@link ProfileRequestContext} included in the input criteria, if any.
-     * 
-     * @param criteria input criteria
-     * 
-     * @return embedded profile request context or null
-     */
-    @Nullable private ProfileRequestContext getProfileRequestContext(@Nullable final CriteriaSet criteria) {
-        if (criteria != null) {
-            final ProfileRequestContextCriterion prcCriterion = criteria.get(ProfileRequestContextCriterion.class);
-            if (prcCriterion != null) {
-                return prcCriterion.getProfileRequestContext();
-            }
-        }
-        
-        return null;
-    }
-    
-}
\ No newline at end of file
diff --git a/sp-server-impl/src/main/java/net/shibboleth/sp/impl/BasicApplication.java b/sp-server-impl/src/main/java/net/shibboleth/sp/impl/BasicApplication.java
index d8069c8..1c9c6d5 100644
--- a/sp-server-impl/src/main/java/net/shibboleth/sp/impl/BasicApplication.java
+++ b/sp-server-impl/src/main/java/net/shibboleth/sp/impl/BasicApplication.java
@@ -23,12 +23,15 @@ import org.slf4j.Logger;
 import net.shibboleth.idp.attribute.filter.AttributeFilter;
 import net.shibboleth.idp.attribute.resolver.AttributeResolver;
 import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.profile.relyingparty.RelyingPartyConfigurationResolver;
 import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
 import net.shibboleth.shared.annotation.constraint.NotEmpty;
 import net.shibboleth.shared.component.ComponentInitializationException;
 import net.shibboleth.shared.logic.Constraint;
 import net.shibboleth.shared.primitive.LoggerFactory;
 import net.shibboleth.shared.service.ReloadableService;
+import net.shibboleth.shared.service.ServiceException;
+import net.shibboleth.shared.service.ServiceableComponent;
 import net.shibboleth.sp.Application;
 import net.shibboleth.sp.remoting.AbstractEndpoint;
 import net.shibboleth.sp.remoting.ApplicationEndpoint;
@@ -47,16 +50,19 @@ public class BasicApplication extends AbstractEndpoint implements Application {
     /** Component endpoints. */
     @NonnullAfterInit private ApplicationEndpointManager endpointManager;
     
+    /** AssertingParty resolver. */
+    @NonnullAfterInit private ReloadableService<RelyingPartyConfigurationResolver> relyingPartyConfigurationResolver;
+    
     /** Metadata source. */
     @NonnullAfterInit private ReloadableService<MetadataResolver> metadataResolver;
     
     /** Transcoding registry. */
     @NonnullAfterInit private ReloadableService<AttributeTranscoderRegistry> transcodingRegistry;
     
-    /** Attribute source. */
+    /** Attribute resolver. */
     @NonnullAfterInit private ReloadableService<AttributeResolver> attributeResolver;
     
-    /** Filter engine. */
+    /** Attribute filter. */
     @NonnullAfterInit private ReloadableService<AttributeFilter> attributeFilter;
     
     /**
@@ -78,18 +84,32 @@ public class BasicApplication extends AbstractEndpoint implements Application {
         if (endpointManager == null) {
             throw new ComponentInitializationException("ApplicationEndpointManager cannot be null");
         }
-        
-        // TODO: enforce services or install default versions?
     }
     
     /** {@inheritDoc} */
     @Nonnull @NotEmpty public String getAddress() {
         return getId();
     }
+    
+    /** {@inheritDoc} */
+    @Nonnull public ServiceableComponent<RelyingPartyConfigurationResolver> getRelyingPartyConfigurationResolver()
+            throws ServiceException {
+        return relyingPartyConfigurationResolver.getServiceableComponent();
+    }
+    
+    /**
+     * Set the {@link RelyingPartyConfigurationResolver} to use.
+     * 
+     * @param service asserting party resolver service
+     */
+    public void setAssertingPartyResolver(@Nonnull final ReloadableService<RelyingPartyConfigurationResolver> service) {
+        relyingPartyConfigurationResolver = Constraint.isNotNull(service,
+                "RelyingPartyConfigurationResolver service cannot be null");
+    }
 
     /** {@inheritDoc} */
-    @Nonnull public ReloadableService<MetadataResolver> getMetadataResolver() {
-        return metadataResolver;
+    @Nonnull public ServiceableComponent<MetadataResolver> getMetadataResolver() throws ServiceException {
+        return metadataResolver.getServiceableComponent();
     }
     
     /**
@@ -104,8 +124,9 @@ public class BasicApplication extends AbstractEndpoint implements Application {
     }
 
     /** {@inheritDoc} */
-    @Nonnull public ReloadableService<AttributeTranscoderRegistry> getAttributeTranscoderRegistry() {
-        return transcodingRegistry;
+    @Nonnull public ServiceableComponent<AttributeTranscoderRegistry> getAttributeTranscoderRegistry()
+            throws ServiceException {
+        return transcodingRegistry.getServiceableComponent();
     }
     
     /**
@@ -120,8 +141,8 @@ public class BasicApplication extends AbstractEndpoint implements Application {
     }
 
     /** {@inheritDoc} */
-    @Nonnull public ReloadableService<AttributeResolver> getAttributeResolver() {
-        return attributeResolver;
+    @Nonnull public ServiceableComponent<AttributeResolver> getAttributeResolver() throws ServiceException {
+        return attributeResolver.getServiceableComponent();
     }
 
     /**
@@ -136,8 +157,8 @@ public class BasicApplication extends AbstractEndpoint implements Application {
     }
 
     /** {@inheritDoc} */
-    @Nonnull public ReloadableService<AttributeFilter> getAttributeFilter() {
-        return attributeFilter;
+    @Nonnull public ServiceableComponent<AttributeFilter> getAttributeFilter() throws ServiceException {
+        return attributeFilter.getServiceableComponent();
     }
 
     /**
diff --git a/sp-server-impl/src/test/java/net/shibboleth/sp/config/impl/BasicAssertingPartyTest.java b/sp-server-impl/src/test/java/net/shibboleth/sp/config/impl/BasicAssertingPartyTest.java
deleted file mode 100644
index 1802969..0000000
--- a/sp-server-impl/src/test/java/net/shibboleth/sp/config/impl/BasicAssertingPartyTest.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * 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.sp.config.impl;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
-
-import net.shibboleth.shared.component.ComponentInitializationException;
-import net.shibboleth.shared.logic.ConstraintViolationException;
-import net.shibboleth.shared.logic.FunctionSupport;
-import net.shibboleth.sp.config.ProfileConfiguration;
-import net.shibboleth.sp.profile.testing.MockProfileConfiguration;
-
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
-/** Unit test for {@link BasicAssertingParty}. */
- at SuppressWarnings("javadoc")
-public class BasicAssertingPartyTest {
-
-    @Test public void testConstruction() throws ComponentInitializationException {
-        BasicAssertingParty config = new BasicAssertingParty();
-        config.setId("foo");
-        config.initialize();
-        Assert.assertEquals(config.getId(), "foo");
-        Assert.assertTrue(config.getProfileConfigurations(null).isEmpty());
-
-        config = new BasicAssertingParty();
-        config.setId("foo");
-        config.initialize();
-        Assert.assertEquals(config.getId(), "foo");
-        Assert.assertTrue(config.getProfileConfigurations(null).isEmpty());
-
-        ArrayList<ProfileConfiguration> profileConfigs = new ArrayList<>();
-        profileConfigs.add(new MockProfileConfiguration("foo"));
-        profileConfigs.add(new MockProfileConfiguration("bar"));
-
-        config = new BasicAssertingParty();
-        config.setId("foo");
-        config.setProfileConfigurations(profileConfigs);
-        config.initialize();
-        Assert.assertEquals(config.getId(), "foo");
-        Assert.assertEquals(config.getProfileConfigurations(null).size(), 2);
-
-        try {
-            config = new BasicAssertingParty();
-            config.initialize();
-            Assert.fail();
-        } catch (final ComponentInitializationException e) {
-            // expected this
-        }
-
-        try {
-            config = new BasicAssertingParty();
-            config.setId("");
-            config.initialize();
-            Assert.fail();
-        } catch (final ConstraintViolationException e) {
-            // expected this
-        }
-    }
-
-    @Test public void testProfileConfiguration() throws ComponentInitializationException {
-        final ArrayList<ProfileConfiguration> profileConfigs = new ArrayList<>();
-        profileConfigs.add(new MockProfileConfiguration("foo"));
-        profileConfigs.add(new MockProfileConfiguration("bar"));
-
-        final BasicAssertingParty config = new BasicAssertingParty();
-        config.setId("foo");
-        config.setProfileConfigurations(profileConfigs);
-        config.initialize();
-        
-        Assert.assertNotNull(config.getProfileConfiguration(null, "foo"));
-        Assert.assertNotNull(config.getProfileConfiguration(null, "bar"));
-        Assert.assertNull(config.getProfileConfiguration(null, "baz"));
-    }
-
-    @Test public void testIndirectProfileConfiguration() throws ComponentInitializationException {
-        final Map<String,ProfileConfiguration> profileConfigs = new HashMap<>();
-        profileConfigs.put("foo", new MockProfileConfiguration("foo"));
-        profileConfigs.put("bar", new MockProfileConfiguration("bar"));
-
-        BasicAssertingParty config = new BasicAssertingParty();
-        config.setId("foo");
-        config.setProfileConfigurationsLookupStrategy(FunctionSupport.constant(profileConfigs));
-        config.initialize();
-        
-        Assert.assertNotNull(config.getProfileConfiguration(null, "foo"));
-        Assert.assertNotNull(config.getProfileConfiguration(null, "bar"));
-        Assert.assertNull(config.getProfileConfiguration(null, "baz"));
-        
-        config = new BasicAssertingParty();
-        config.setId("foo");
-        config.setProfileConfigurations(profileConfigs.values());
-        config.setProfileConfigurationsLookupStrategy(FunctionSupport.constant(null));
-        config.initialize();
-        
-        Assert.assertNull(config.getProfileConfiguration(null, "foo"));
-        Assert.assertNull(config.getProfileConfiguration(null, "bar"));
-        Assert.assertNull(config.getProfileConfiguration(null, "baz"));
-    }
-
-}
\ No newline at end of file
diff --git a/sp-server-impl/src/test/java/net/shibboleth/sp/config/impl/DefaultAssertingPartyResolverTest.java b/sp-server-impl/src/test/java/net/shibboleth/sp/config/impl/DefaultAssertingPartyResolverTest.java
deleted file mode 100644
index 9a2f6aa..0000000
--- a/sp-server-impl/src/test/java/net/shibboleth/sp/config/impl/DefaultAssertingPartyResolverTest.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * 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.sp.config.impl;
-
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.List;
-
-import net.shibboleth.shared.component.ComponentInitializationException;
-import net.shibboleth.shared.logic.PredicateSupport;
-import net.shibboleth.shared.resolver.CriteriaSet;
-import net.shibboleth.sp.config.AssertingParty;
-
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.opensaml.profile.criterion.ProfileRequestContextCriterion;
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
-/** Unit test for {@link DefaultAssertingPartyResolver}. */
- at SuppressWarnings("javadoc")
-public class DefaultAssertingPartyResolverTest {
-
-    @Test public void testConstruction() throws ComponentInitializationException {
-        final BasicAssertingParty one = new BasicAssertingParty();
-        one.setId("one");
-        one.initialize();
-
-        final BasicAssertingParty two = new BasicAssertingParty();
-        two.setId("two");
-        two.setActivationCondition(PredicateSupport.alwaysFalse());
-        two.initialize();
-
-        final BasicAssertingParty three = new BasicAssertingParty();
-        three.setId("three");
-        three.initialize();
-        
-        final List<BasicAssertingParty> apConfigs = Arrays.asList(one, two, three);
-
-        DefaultAssertingPartyResolver resolver = new DefaultAssertingPartyResolver();
-        resolver.setId("test");
-        resolver.setAssertingParties(apConfigs);
-        Assert.assertEquals(resolver.getId(), "test");
-        Assert.assertEquals(resolver.getAssertingParties().size(), 3);
-
-        resolver = new DefaultAssertingPartyResolver();
-        resolver.setId("test");
-        Assert.assertEquals(resolver.getId(), "test");
-        Assert.assertEquals(resolver.getAssertingParties().size(), 0);
-
-        resolver = new DefaultAssertingPartyResolver();
-        resolver.setId("test");
-        Assert.assertEquals(resolver.getId(), "test");
-        Assert.assertEquals(resolver.getAssertingParties().size(), 0);
-    }
-    
-    @Test public void testDefault() throws Exception {
-        final ProfileRequestContext prc = new ProfileRequestContext();
-        final ProfileRequestContextCriterion crit = new ProfileRequestContextCriterion(prc); 
-
-        final BasicAssertingParty anonRP = new BasicAssertingParty();
-        anonRP.setId("anonRPId");
-        anonRP.initialize();
-        
-        final BasicAssertingParty defaultRP = new BasicAssertingParty();
-        defaultRP.setId("defaultRPId");
-        defaultRP.initialize();
-
-        final DefaultAssertingPartyResolver resolver = new DefaultAssertingPartyResolver();
-        resolver.setId("test");
-        resolver.setDefaultConfiguration(defaultRP);
-        resolver.initialize();
-        
-        final Iterable<AssertingParty> results = resolver.resolve(new CriteriaSet(crit));
-        Assert.assertNotNull(results);
-        
-        final Iterator<AssertingParty> resultItr = results.iterator();        
-        Assert.assertTrue(resultItr.hasNext());
-        Assert.assertSame(resultItr.next(), defaultRP);
-        Assert.assertFalse(resultItr.hasNext());
-        
-        Assert.assertSame(resolver.resolveSingle(new CriteriaSet(crit)), defaultRP);
-    }
-    
-    @Test public void testResolve() throws Exception {
-        final ProfileRequestContext prc = new ProfileRequestContext();
-        final ProfileRequestContextCriterion crit = new ProfileRequestContextCriterion(prc); 
-    
-        final BasicAssertingParty anonRP = new BasicAssertingParty();
-        anonRP.setId("anonRPId");
-        anonRP.initialize();
-        
-        final BasicAssertingParty defaultRP = new BasicAssertingParty();
-        defaultRP.setId("defaultRPId");
-        defaultRP.initialize();
-
-        final BasicAssertingParty one = new BasicAssertingParty();
-        one.setId("one");
-        one.initialize();
-
-        final BasicAssertingParty two = new BasicAssertingParty();
-        two.setId("two");
-        two.setActivationCondition(PredicateSupport.alwaysFalse());
-        two.initialize();
-
-        final BasicAssertingParty three = new BasicAssertingParty();
-        three.setId("three");
-        three.initialize();
-        
-        final List<BasicAssertingParty> apConfigs = Arrays.asList(one, two, three);
-
-        final DefaultAssertingPartyResolver resolver = new DefaultAssertingPartyResolver();
-        resolver.setId("test");
-        resolver.setAssertingParties(apConfigs);
-        resolver.setDefaultConfiguration(defaultRP);
-        resolver.initialize();
-
-        Iterable<AssertingParty> results = resolver.resolve(new CriteriaSet(crit));
-        Assert.assertNotNull(results);
-
-        Iterator<AssertingParty> resultItr = results.iterator();
-        Assert.assertTrue(resultItr.hasNext());
-        Assert.assertSame(resultItr.next(), one);
-        Assert.assertTrue(resultItr.hasNext());
-        Assert.assertSame(resultItr.next(), three);
-        Assert.assertFalse(resultItr.hasNext());
-
-        AssertingParty result = resolver.resolveSingle(new CriteriaSet(crit));
-        Assert.assertSame(result, one);
-
-        results = resolver.resolve(null);
-        Assert.assertNotNull(results);
-
-        resultItr = results.iterator();
-        Assert.assertFalse(resultItr.hasNext());
-
-        result = resolver.resolveSingle(null);
-        Assert.assertNull(result);
-    }
-}
\ 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