[java-shib-profile] branch main updated: JSPROF-1 - Move RelyingParty "layer" into java-shib-profile

Scott Cantor cantor.2 at osu.edu
Tue Feb 14 13:25:09 UTC 2023


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

scantor pushed a commit to branch main
in repository java-shib-profile.

View the commit online:
http://git.shibboleth.net/view/?p=java-shib-profile.git;a=commit;h=72ef0a9218f204f3f7266b7917c6042420c55bbe

The following commit(s) were added to refs/heads/main by this push:
     new 72ef0a9  JSPROF-1 - Move RelyingParty "layer" into java-shib-profile
72ef0a9 is described below

commit 72ef0a9218f204f3f7266b7917c6042420c55bbe
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Tue Feb 14 08:25:06 2023 -0500

    JSPROF-1 - Move RelyingParty "layer" into java-shib-profile
    
    Migrate in RelayingParty classes and resolver.
    Collapse out criteria-based reslver.
    Add VerifiedProfileCriterion.
    Add testing module.
---
 pom.xml                                            |   4 +-
 .../profile/context/RelyingPartyContext.java       | 228 +++++++++++
 .../shibboleth/profile/context/package-info.java   |  21 +
 .../relyingparty/RelyingPartyConfiguration.java    | 157 ++++++++
 .../RelyingPartyConfigurationResolver.java         |  75 ++++
 .../relyingparty/VerifiedProfileCriterion.java     |  87 ++++
 .../profile/relyingparty/package-info.java         |  21 +
 shib-profile-bom/pom.xml                           |  10 +-
 shib-profile-impl/pom.xml                          |  12 +
 .../DefaultRelyingPartyConfigurationResolver.java  | 440 +++++++++++++++++++++
 .../profile/relyingparty/impl/package-info.java    |  21 +
 ...faultRelyingPartyConfigurationResolverTest.java | 183 +++++++++
 .../impl/RelyingPartyConfigurationTest.java        | 120 ++++++
 .../pom.xml                                        |  47 +--
 .../config/testing/MockProfileConfiguration.java   |  41 ++
 .../profile/config/testing/package-info.java       |  21 +
 16 files changed, 1463 insertions(+), 25 deletions(-)

diff --git a/pom.xml b/pom.xml
index dd9604e..0227b39 100644
--- a/pom.xml
+++ b/pom.xml
@@ -24,9 +24,11 @@
         <module>shib-profile-api</module>
 <!--        <module>shib-saml-profile-api</module>-->
 
-<!--        <module>shib-profile-impl</module>-->
+        <module>shib-profile-impl</module>
 <!--        <module>shib-saml-profile-impl</module>-->
 
+        <module>shib-profile-testing</module>
+
         <module>shib-profile-bom</module>
     </modules>
 
diff --git a/shib-profile-api/src/main/java/net/shibboleth/profile/context/RelyingPartyContext.java b/shib-profile-api/src/main/java/net/shibboleth/profile/context/RelyingPartyContext.java
new file mode 100644
index 0000000..e39b2ee
--- /dev/null
+++ b/shib-profile-api/src/main/java/net/shibboleth/profile/context/RelyingPartyContext.java
@@ -0,0 +1,228 @@
+/*
+ * 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.profile.context;
+
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import net.shibboleth.profile.config.ProfileConfiguration;
+import net.shibboleth.profile.relyingparty.RelyingPartyConfiguration;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.primitive.StringSupport;
+
+import org.opensaml.messaging.context.BaseContext;
+
+/**
+ * {@link BaseContext} containing relying party specific information, usually a
+ * subcontext of {@link org.opensaml.profile.context.ProfileRequestContext}.
+ */
+public final class RelyingPartyContext extends BaseContext {
+
+    /** Optional flag indicating whether verification was done. */
+    @Nullable private Boolean verified; 
+    
+    /** The identifier for the relying party. */
+    @Nullable private String relyingPartyId;
+
+    /** A pointer to a context tree containing identifying material for the relying party. */
+    @Nullable private BaseContext relyingPartyIdContextTree;
+
+    /** A lookup strategy for deriving verification based on the context. */
+    @Nullable private Function<RelyingPartyContext,Boolean> verificationLookupStrategy;
+
+    /** A lookup strategy for deriving a relying party ID based on contained information. */
+    @Nullable private Function<RelyingPartyContext,String> relyingPartyIdLookupStrategy;
+    
+    /** The relying party configuration. */
+    @Nullable private RelyingPartyConfiguration relyingPartyConfiguration;
+
+    /** Profile configuration that is in use. */
+    @Nullable private ProfileConfiguration profileConfiguration;
+    
+    /**
+     * Get whether the relying party was verified in some fashion.
+     * 
+     * @return  true iff the relying party's identity was verified
+     */
+    public boolean isVerified() {
+        if (verified != null) {
+            return verified;
+        } else if (verificationLookupStrategy != null) {
+            final Boolean flag = verificationLookupStrategy.apply(this);
+            if (flag != null) {
+                return flag;
+            }
+        }
+        return false;
+    }
+    
+    /**
+     * Set whether the relying party was verified in some fashion.
+     * 
+     * @param flag  explicit value for the verified setting
+     * 
+     * @return this context
+     */
+    @Nonnull public RelyingPartyContext setVerified(@Nullable final Boolean flag) {
+        verified = flag;
+        return this;
+    }
+
+    /**
+     * Get the unique identifier of the relying party.
+     * 
+     * @return unique identifier of the relying party
+     */
+    @Nullable public String getRelyingPartyId() {
+        
+        if (relyingPartyId != null) {
+            return relyingPartyId;
+        } else if (relyingPartyIdLookupStrategy != null) {
+            return relyingPartyIdLookupStrategy.apply(this);
+        } else {
+            return null;
+        }
+    }
+
+
+    /**
+     * Set the unique identifier of the relying party.
+     * 
+     * @param rpId the relying party identifier, or null
+     * 
+     * @return this context
+     */
+    @Nonnull public RelyingPartyContext setRelyingPartyId(@Nullable final String rpId) {
+        relyingPartyId = StringSupport.trimOrNull(rpId);
+        return this;
+    }
+
+    /**
+     * Get the context tree containing identifying information for this relying party.
+     * 
+     * <p>The subtree root may, but need not, be an actual subcontext of this context.</p>
+     * 
+     * @return context tree
+     */
+    @Nullable public BaseContext getRelyingPartyIdContextTree() {
+        return relyingPartyIdContextTree;
+    }
+    
+    /**
+     * Set the context tree containing identifying information for this relying party.
+     * 
+     * <p>The subtree root may, but need not, be an actual subcontext of this context.</p>
+     * 
+     * @param root  root of context tree
+     * 
+     * @return this context
+     */
+    @Nonnull public RelyingPartyContext setRelyingPartyIdContextTree(@Nullable final BaseContext root) {
+        relyingPartyIdContextTree = root;
+        return this;
+    }
+    
+    /**
+     * Get the lookup strategy for a non-explicit verification determination.
+     * 
+     * @return lookup strategy
+     */
+    @Nullable Function<RelyingPartyContext,Boolean> getVerificationLookupStrategy() {
+        return verificationLookupStrategy;
+    }
+    
+    /**
+     * Set the lookup strategy for a non-explicit verification determination.
+     * 
+     * @param strategy  lookup strategy
+     * 
+     * @return this context
+     */
+    @Nonnull public RelyingPartyContext setVerificationLookupStrategy(
+            @Nonnull final Function<RelyingPartyContext,Boolean> strategy) {
+        verificationLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
+        return this;
+    }
+    
+    /**
+     * Get the lookup strategy for a non-explicit relying party ID.
+     * 
+     * @return lookup strategy
+     */
+    @Nullable Function<RelyingPartyContext,String> getRelyingPartyIdLookupStrategy() {
+        return relyingPartyIdLookupStrategy;
+    }
+    
+    /**
+     * Set the lookup strategy for a non-explicit relying party ID.
+     * 
+     * @param strategy  lookup strategy
+     * 
+     * @return this context
+     */
+    @Nonnull public RelyingPartyContext setRelyingPartyIdLookupStrategy(
+            @Nonnull final Function<RelyingPartyContext,String> strategy) {
+        relyingPartyIdLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
+        return this;
+    }
+    
+    /**
+     * Get the relying party configuration.
+     * 
+     * @return the relying party configuration, or null
+     */
+    @Nullable public RelyingPartyConfiguration getConfiguration() {
+        return relyingPartyConfiguration;
+    }
+
+    /**
+     * Set the configuration to use when processing requests for this relying party.
+     * 
+     * @param config configuration to use when processing requests for this relying party, or null
+     * 
+     * @return this context
+     */
+    @Nonnull public RelyingPartyContext setConfiguration(@Nullable final RelyingPartyConfiguration config) {
+        relyingPartyConfiguration = config;
+        return this;
+    }
+
+    /**
+     * Get the configuration for the request profile currently being processed.
+     * 
+     * @return profile configuration for the request profile currently being processed, or null
+     */
+    @Nullable public ProfileConfiguration getProfileConfig() {
+        return profileConfiguration;
+    }
+
+    /**
+     * Set the configuration for the request profile currently being processed.
+     * 
+     * @param config configuration for the request profile currently being processed, or null
+     * 
+     * @return this context
+     */
+    @Nonnull public RelyingPartyContext setProfileConfig(@Nullable final ProfileConfiguration config) {
+        profileConfiguration = config;
+        return this;
+    }
+    
+}
\ No newline at end of file
diff --git a/shib-profile-api/src/main/java/net/shibboleth/profile/context/package-info.java b/shib-profile-api/src/main/java/net/shibboleth/profile/context/package-info.java
new file mode 100644
index 0000000..329b652
--- /dev/null
+++ b/shib-profile-api/src/main/java/net/shibboleth/profile/context/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+/**
+ * Context classes used in profile handling.
+ */
+package net.shibboleth.profile.context;
\ No newline at end of file
diff --git a/shib-profile-api/src/main/java/net/shibboleth/profile/relyingparty/RelyingPartyConfiguration.java b/shib-profile-api/src/main/java/net/shibboleth/profile/relyingparty/RelyingPartyConfiguration.java
new file mode 100644
index 0000000..a802c6e
--- /dev/null
+++ b/shib-profile-api/src/main/java/net/shibboleth/profile/relyingparty/RelyingPartyConfiguration.java
@@ -0,0 +1,157 @@
+/*
+ * 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.profile.relyingparty;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+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.profile.config.ProfileConfiguration;
+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.component.IdentifiedComponent;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.logic.FunctionSupport;
+import net.shibboleth.shared.logic.PredicateSupport;
+import net.shibboleth.shared.primitive.StringSupport;
+
+/**
+ * The configuration that applies to a peer with which the software is communicating.
+ * 
+ * <p>The name "Relying Party" is historical and encompasses both sources and consumers
+ * of identity information.</p>
+ * 
+ * @since 5.0.0
+ */
+public class RelyingPartyConfiguration extends AbstractIdentifiableInitializableComponent implements
+        IdentifiedComponent, 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 RelyingPartyConfiguration() {
+        activationCondition = PredicateSupport.alwaysTrue();
+        profileConfigurationsLookupStrategy = FunctionSupport.constant(null);
+    }
+    
+    /**
+     * Get the unmodifiable set of profile configurations for this relying party.
+     * 
+     * @param profileRequestContext current profile request context
+     * 
+     * @return unmodifiable set of profile configurations for this relying party, never null
+     */
+    @Nonnull @NonnullElements @Unmodifiable @NotLive public Map<String,ProfileConfiguration> getProfileConfigurations(
+            @Nullable final ProfileRequestContext profileRequestContext) {
+        
+        final Map<String,ProfileConfiguration> map = profileConfigurationsLookupStrategy.apply(profileRequestContext);
+        if (map != null) {
+            return CollectionSupport.copyToMap(map);
+        }
+        return CollectionSupport.emptyMap();
+    }
+
+    /**
+     * Get the profile configuration, for the relying party, for the given profile. This is a convenience method and is
+     * equivalent to calling {@link Map#get(Object)} on the return of
+     * {@link #getProfileConfigurations(ProfileRequestContext)}. This map contains no null entries, keys, or values.
+     * 
+     * @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) {
+        final String trimmedId = StringSupport.trimOrNull(profileId);
+        if (trimmedId == null) {
+            return null;
+        }
+
+        return getProfileConfigurations(profileRequestContext).get(trimmedId);
+    }
+
+    /**
+     * 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 : List.copyOf(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.copyOf(map));
+        }
+    }
+
+    /**
+     * Set a lookup strategy for the <code>profileConfigurations</code> property.
+     * 
+     * @param strategy lookup strategy
+     * 
+     * @since 4.0.0
+     */
+    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/shib-profile-api/src/main/java/net/shibboleth/profile/relyingparty/RelyingPartyConfigurationResolver.java b/shib-profile-api/src/main/java/net/shibboleth/profile/relyingparty/RelyingPartyConfigurationResolver.java
new file mode 100644
index 0000000..9b9d255
--- /dev/null
+++ b/shib-profile-api/src/main/java/net/shibboleth/profile/relyingparty/RelyingPartyConfigurationResolver.java
@@ -0,0 +1,75 @@
+/*
+ * 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.profile.relyingparty;
+
+import java.util.Collection;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.criterion.ProfileRequestContextCriterion;
+import org.opensaml.security.config.SecurityConfiguration;
+import org.opensaml.security.credential.Credential;
+
+import net.shibboleth.profile.config.ProfileConfiguration;
+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.component.IdentifiedComponent;
+import net.shibboleth.shared.resolver.CriteriaSet;
+import net.shibboleth.shared.resolver.Resolver;
+
+/**
+ * Interface to a resolution service that identifies the applicable {@link RelyingPartyConfiguration}
+ * 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>
+ * 
+ * @since 5.0.0
+ */
+public interface RelyingPartyConfigurationResolver extends Resolver<RelyingPartyConfiguration,CriteriaSet>,
+        IdentifiedComponent {
+
+    /**
+     * Return the default security configuration for a profile.
+     * 
+     * @param profileId the profile ID (available via {@link ProfileConfiguration#getId()}
+     * 
+     * @return the configured default configuration
+     */
+    @Nullable SecurityConfiguration getDefaultSecurityConfiguration(@Nonnull @NotEmpty final String profileId);
+    
+    /**
+     * Directly expose any configured signing credentials.
+     * 
+     * @return signing credentials
+     */
+    @Nonnull @NonnullElements @Unmodifiable @NotLive public Collection<Credential> getSigningCredentials();
+    
+    /**
+     * Directly expose any configured encryption (really decryption) credentials.
+     * 
+     * @return signing credentials
+     */
+    @Nonnull @NonnullElements @Unmodifiable @NotLive public Collection<Credential> getEncryptionCredentials();
+
+}
\ No newline at end of file
diff --git a/shib-profile-api/src/main/java/net/shibboleth/profile/relyingparty/VerifiedProfileCriterion.java b/shib-profile-api/src/main/java/net/shibboleth/profile/relyingparty/VerifiedProfileCriterion.java
new file mode 100644
index 0000000..6de30d1
--- /dev/null
+++ b/shib-profile-api/src/main/java/net/shibboleth/profile/relyingparty/VerifiedProfileCriterion.java
@@ -0,0 +1,87 @@
+/*
+ * 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.profile.relyingparty;
+
+import javax.annotation.Nonnull;
+
+import net.shibboleth.shared.resolver.Criterion;
+
+/**
+ * {@link Criterion} indicating whether a relying party has been verified in a
+ * profile-specific fashion.
+ *
+ * @since 5.0.0
+ */
+public final class VerifiedProfileCriterion implements Criterion {
+
+    /** Verified flag. */
+    @Nonnull private final boolean verified;
+
+    /**
+     * Constructor.
+     * 
+     * @param flag flag to set
+     */
+    public VerifiedProfileCriterion(final boolean flag) {
+        verified = flag;
+    }
+
+    /**
+     * Gets the profile request context.
+     * 
+     * @return the profile request context
+     */
+    public boolean isVerified() {
+        return verified;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        final StringBuilder builder = new StringBuilder();
+        builder.append("VerifiedProfileCriterion [verified=");
+        builder.append(verified);
+        builder.append("]");
+        return builder.toString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return Boolean.valueOf(verified).hashCode();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj == null) {
+            return false;
+        }
+
+        if (obj instanceof VerifiedProfileCriterion) {
+            return verified == ((VerifiedProfileCriterion) obj).isVerified();
+        }
+
+        return false;
+    }
+    
+}
\ No newline at end of file
diff --git a/shib-profile-api/src/main/java/net/shibboleth/profile/relyingparty/package-info.java b/shib-profile-api/src/main/java/net/shibboleth/profile/relyingparty/package-info.java
new file mode 100644
index 0000000..e3947e9
--- /dev/null
+++ b/shib-profile-api/src/main/java/net/shibboleth/profile/relyingparty/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+/**
+ * Shared RelyingPartyConfiguration and resolver APIs.
+ */
+package net.shibboleth.profile.relyingparty;
\ No newline at end of file
diff --git a/shib-profile-bom/pom.xml b/shib-profile-bom/pom.xml
index f231d5e..fe7c7ce 100644
--- a/shib-profile-bom/pom.xml
+++ b/shib-profile-bom/pom.xml
@@ -27,12 +27,13 @@
                 <artifactId>shib-profile-api</artifactId>
                 <version>${project.version}</version>
             </dependency>
-            <!--
+
             <dependency>
                 <groupId>${project.groupId}</groupId>
                 <artifactId>shib-profile-impl</artifactId>
                 <version>${project.version}</version>
             </dependency>
+            <!--
             <dependency>
                 <groupId>${project.groupId}</groupId>
                 <artifactId>shib-saml-profile-api</artifactId>
@@ -44,6 +45,13 @@
                 <version>${project.version}</version>
             </dependency>
             -->
+
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>shib-profile-testing</artifactId>
+                <version>${project.version}</version>
+                <scope>test</scope>
+            </dependency>
         </dependencies>
     </dependencyManagement>
     
diff --git a/shib-profile-impl/pom.xml b/shib-profile-impl/pom.xml
index 958d164..bf13f73 100644
--- a/shib-profile-impl/pom.xml
+++ b/shib-profile-impl/pom.xml
@@ -28,6 +28,12 @@
             <version>${project.version}</version>
         </dependency>
 
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>shib-metadata-spring</artifactId>
+            <version>${shib-metadata.version}</version>
+        </dependency>
+
         <dependency>
             <groupId>${opensaml.groupId}</groupId>
             <artifactId>opensaml-profile-api</artifactId>
@@ -66,6 +72,12 @@
         <!-- Runtime Dependencies -->
 
         <!-- Test Dependencies -->
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>shib-profile-testing</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>${shib-shared.groupId}</groupId>
             <artifactId>shib-testing</artifactId>
diff --git a/shib-profile-impl/src/main/java/net/shibboleth/profile/relyingparty/impl/DefaultRelyingPartyConfigurationResolver.java b/shib-profile-impl/src/main/java/net/shibboleth/profile/relyingparty/impl/DefaultRelyingPartyConfigurationResolver.java
new file mode 100644
index 0000000..e248705
--- /dev/null
+++ b/shib-profile-impl/src/main/java/net/shibboleth/profile/relyingparty/impl/DefaultRelyingPartyConfigurationResolver.java
@@ -0,0 +1,440 @@
+/*
+ * 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.profile.relyingparty.impl;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.core.criterion.EntityIdCriterion;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.profile.criterion.ProfileRequestContextCriterion;
+import org.opensaml.saml.common.messaging.context.SAMLMetadataContext;
+import org.opensaml.saml.common.messaging.context.SAMLPeerEntityContext;
+import org.opensaml.saml.criterion.RoleDescriptorCriterion;
+import org.opensaml.saml.saml2.metadata.EntityDescriptor;
+import org.opensaml.saml.saml2.metadata.RoleDescriptor;
+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.profile.context.RelyingPartyContext;
+import net.shibboleth.profile.relyingparty.RelyingPartyConfiguration;
+import net.shibboleth.profile.relyingparty.RelyingPartyConfigurationResolver;
+import net.shibboleth.profile.relyingparty.VerifiedProfileCriterion;
+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.spring.security.CredentialHolder;
+
+/**
+ * Retrieves a per-relying party configuration for a given profile request based on the
+ * supplied {@link CriteriaSet}.
+ *
+ * <p>Supported {@link Criterion}:</p>
+ * <ul>
+ *  <li>{@link ProfileRequestContextCriterion}</li>
+ *  <li>{@link EntityIdCriterion}</li>
+ *  <li>{@link RoleDescriptorCriterion}</li>
+ *  <li>{@link VerifiedProfileCriterion}</li>
+ * </ul>
+ * 
+ * <p>
+ * Note that this resolver does not permit more than one {@link RelyingPartyConfiguration} with the same ID.
+ * </p>
+ * 
+ * @since 5.0.0
+ */
+public class DefaultRelyingPartyConfigurationResolver extends AbstractIdentifiableInitializableComponent
+        implements RelyingPartyConfigurationResolver {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(DefaultRelyingPartyConfigurationResolver.class);
+
+    /** Registered relying party configurations. */
+    @Nonnull private List<RelyingPartyConfiguration> rpConfigurations;
+
+    /** Default relying party, used if no other verified instance matches. */
+    @NonnullAfterInit private RelyingPartyConfiguration defaultRelyingPartyConfiguration;
+
+    /** Unverified relying party configuration, used if the request is unverified. */
+    @Nullable private RelyingPartyConfiguration unverifiedConfiguration;
+    
+    /** 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 DefaultRelyingPartyConfigurationResolver() {
+        rpConfigurations = 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<? extends RelyingPartyConfiguration> getRelyingPartyConfigurations() {
+        return rpConfigurations;
+    }
+
+    /**
+     * Set the verified relying party configurations.
+     * 
+     * @param configs list of verified relying party configurations
+     */
+    public void setRelyingPartyConfigurations(
+            @Nullable @NonnullElements final Collection<? extends RelyingPartyConfiguration> configs) {
+        checkSetterPreconditions();
+        
+        if (configs != null) {
+            rpConfigurations = CollectionSupport.copyToList(configs);
+        } else {
+            rpConfigurations = CollectionSupport.emptyList();
+        }
+    }
+
+    /**
+     * Get the {@link RelyingPartyConfiguration} to use if no other configuration is applicable.
+     * 
+     * @return default configuration
+     */
+    @NonnullAfterInit public RelyingPartyConfiguration getDefaultConfiguration() {
+        return defaultRelyingPartyConfiguration;
+    }
+
+    /**
+     * Set the {@link RelyingPartyConfiguration} to use if no other configuration is applicable.
+     * 
+     * @param configuration default configuration
+     */
+    public void setDefaultConfiguration(@Nonnull final RelyingPartyConfiguration configuration) {
+        checkSetterPreconditions();
+        
+        defaultRelyingPartyConfiguration = Constraint.isNotNull(configuration, "Default RelyingPartyConfiguration cannot be null");
+    }
+
+    /**
+     * Set the global default {@link SecurityConfiguration}.
+     * 
+     * @param config  global default
+     */
+    public void setDefaultSecurityConfiguration(@Nullable final SecurityConfiguration config) {
+        checkSetterPreconditions();
+        
+        defaultSecurityConfiguration = config;
+    }
+
+    /**
+     * Get the {@link RelyingPartyConfiguration} to use if the configuration is found to be "unverified"
+     * (via use of {@link VerifiedProfileCriterion}).
+     * 
+     * @return unverified configuration
+     */
+    @Nullable public RelyingPartyConfiguration getUnverifiedConfiguration() {
+        return unverifiedConfiguration;
+    }
+
+    /**
+     * Set the {@link RelyingPartyConfiguration} to use if the configuration is found to be "unverified"
+     * (via use of {@link VerifiedProfileCriterion}).
+     * 
+     * @param configuration unverified configuration
+     */
+    public void setUnverifiedConfiguration(@Nullable final RelyingPartyConfiguration configuration) {
+        checkSetterPreconditions();
+        
+        unverifiedConfiguration = configuration;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void doInitialize() throws ComponentInitializationException {
+        super.doInitialize();
+
+        final HashSet<String> configIds = new HashSet<>(rpConfigurations.size());
+        for (final RelyingPartyConfiguration config : rpConfigurations) {
+            if (configIds.contains(config.getId())) {
+                throw new ComponentInitializationException("Multiple RelyingPartyConfiguration configurations with ID "
+                        + config.getId() + " detected, IDs must be unique.");
+            }
+            configIds.add(config.getId());
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Nonnull @NonnullElements public Iterable<RelyingPartyConfiguration> resolve(@Nullable final CriteriaSet criteria)
+            throws ResolverException {
+        checkComponentActive();
+
+        log.debug("Resolving relying party configuration");
+
+        if (criteria == null) {
+            return CollectionSupport.emptyList();
+        }
+
+        final VerifiedProfileCriterion vpc = criteria.get(VerifiedProfileCriterion.class);
+        if (vpc == null || !vpc.isVerified()) {
+            final RelyingPartyConfiguration uvc = getUnverifiedConfiguration();
+            if (uvc == null) {
+                log.warn("Profile request was unverified, but no such configuration is available");
+                return CollectionSupport.emptyList();
+            }
+            log.debug("Profile request is unverified, returning configuration {}", uvc.getId());
+            return CollectionSupport.singleton(uvc);
+        }
+        
+        final ArrayList<RelyingPartyConfiguration> matches = new ArrayList<>();
+
+        final ProfileRequestContext context = getProfileRequestContext(criteria);
+
+        for (final RelyingPartyConfiguration configuration : rpConfigurations) {
+            log.debug("Checking if relying party configuration {} is applicable", configuration.getId());
+            if (configuration.test(context)) {
+                log.debug("Relying party configuration {} is applicable", configuration.getId());
+                matches.add(configuration);
+            } else {
+                log.debug("Relying party configuration {} is not applicable", configuration.getId());
+            }
+        }
+
+        if (matches.isEmpty()) {
+            log.debug("No matching relying party configuration applicable, returning default: {}",
+                    getDefaultConfiguration().getId());
+            return CollectionSupport.singleton(getDefaultConfiguration());
+        }
+        return matches;
+    }
+
+    /** {@inheritDoc} */
+    @Nullable public RelyingPartyConfiguration resolveSingle(@Nullable final CriteriaSet criteria) throws ResolverException {
+        checkComponentActive();
+
+        log.debug("Resolving relying party configuration");
+
+        if (criteria == null) {
+            return null;
+        }
+
+        final VerifiedProfileCriterion vpc = criteria.get(VerifiedProfileCriterion.class);
+        if (vpc == null || !vpc.isVerified()) {
+            final RelyingPartyConfiguration uvc = getUnverifiedConfiguration();
+            if (uvc == null) {
+                log.warn("Profile request was unverified, but no such configuration is available");
+                return null;
+            }
+            log.debug("Profile request is unverified, returning configuration {}", uvc.getId());
+            return uvc;
+        }
+
+        final ProfileRequestContext context = getProfileRequestContext(criteria);
+
+        for (final RelyingPartyConfiguration configuration : rpConfigurations) {
+            log.debug("Checking if relying party configuration {} is applicable", configuration.getId());
+            if (configuration.test(context)) {
+                log.debug("Relying party configuration {} is applicable", configuration.getId());
+                return configuration;
+            }
+            log.debug("Relying party configuration {} is not applicable", configuration.getId());
+        }
+
+        log.debug("No relying party configurations applicable, returning default: {}",
+                getDefaultConfiguration().getId());
+        return getDefaultConfiguration();
+    }
+
+    /** {@inheritDoc} */
+    @Nullable public SecurityConfiguration getDefaultSecurityConfiguration(@Nonnull @NotEmpty final String profileId) {
+        return defaultSecurityConfiguration;
+    }
+
+    /** {@inheritDoc} */
+    @Nonnull @NonnullElements @Unmodifiable @NotLive public Collection<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();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Nonnull @NonnullElements @Unmodifiable @NotLive public Collection<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(@Nonnull final CriteriaSet criteria) {
+        
+        final ProfileRequestContextCriterion prcCriterion = criteria.get(ProfileRequestContextCriterion.class);
+        if (prcCriterion != null) {
+            return prcCriterion.getProfileRequestContext();
+        }
+        
+        final String entityID = resolveEntityID(criteria);
+        log.debug("Resolved effective entityID from criteria: {}", entityID);
+
+        final EntityDescriptor entityDescriptor = resolveEntityDescriptor(criteria);
+        log.debug("Resolved effective entity descriptor from criteria: {}", entityDescriptor);
+
+        final RoleDescriptor roleDescriptor = resolveRoleDescriptor(criteria);
+        log.debug("Resolved effective role descriptor from criteria: {}", roleDescriptor);
+
+        if (entityID != null || entityDescriptor != null || roleDescriptor != null) {
+            final ProfileRequestContext prc = new ProfileRequestContext();
+            final RelyingPartyContext rpc = prc.getOrCreateSubcontext(RelyingPartyContext.class);
+            rpc.setVerified(true);
+
+            rpc.setRelyingPartyId(entityID);
+
+            if (entityDescriptor != null || roleDescriptor != null) {
+                final SAMLPeerEntityContext peerContext = prc.getOrCreateSubcontext(SAMLPeerEntityContext.class);
+                rpc.setRelyingPartyIdContextTree(peerContext);
+
+                peerContext.setEntityId(entityID);
+
+                if (roleDescriptor != null) {
+                    peerContext.setRole(roleDescriptor.getSchemaType() != null
+                            ? roleDescriptor.getSchemaType() : roleDescriptor.getElementQName());
+                }
+
+                final SAMLMetadataContext metadataContext = peerContext.getOrCreateSubcontext(SAMLMetadataContext.class);
+                metadataContext.setEntityDescriptor(entityDescriptor);
+                metadataContext.setRoleDescriptor(roleDescriptor);
+            }
+            return prc;
+        }
+        return null;
+    }
+    
+    /**
+     * Resolve the entityID from the criteria.
+     * 
+     * @param criteria the input criteria
+     * @return the input entityID criterion or null if could not be resolved
+     */
+    @Nullable private String resolveEntityID(@Nonnull final CriteriaSet criteria) {
+        final EntityIdCriterion eic = criteria.get(EntityIdCriterion.class);
+        if (eic != null) {
+            return eic.getEntityId();
+        }
+
+        final EntityDescriptor ed = resolveEntityDescriptor(criteria);
+        if (ed != null) {
+            return ed.getEntityID();
+        }
+
+        return null;
+    }
+
+    /**
+     * Resolve the EntityDescriptor from the criteria.
+     *
+     * @param criteria the input criteria
+     * @return the input entity descriptor criterion, or null if could not be resolved
+     */
+    @Nullable private EntityDescriptor resolveEntityDescriptor(@Nonnull final CriteriaSet criteria) {
+        final RoleDescriptor rd = resolveRoleDescriptor(criteria);
+        if (rd != null && rd.getParent() != null && rd.getParent() instanceof EntityDescriptor) {
+            return (EntityDescriptor)rd.getParent();
+        }
+
+        return null;
+    }
+
+    /**
+     * Resolve the RoleDescriptor from the criteria.
+     *
+     * @param criteria the input criteria
+     * @return the input role descriptor criterion or null if could not be resolved
+     */
+    @Nullable private RoleDescriptor resolveRoleDescriptor(@Nonnull final CriteriaSet criteria) {
+        final RoleDescriptorCriterion rdc = criteria.get(RoleDescriptorCriterion.class);
+        if (rdc != null) {
+            return rdc.getRole();
+        }
+
+        return null;
+    }
+
+}
\ No newline at end of file
diff --git a/shib-profile-impl/src/main/java/net/shibboleth/profile/relyingparty/impl/package-info.java b/shib-profile-impl/src/main/java/net/shibboleth/profile/relyingparty/impl/package-info.java
new file mode 100644
index 0000000..a394c02
--- /dev/null
+++ b/shib-profile-impl/src/main/java/net/shibboleth/profile/relyingparty/impl/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+/**
+ * Implementation of relying party resolution.
+ */
+package net.shibboleth.profile.relyingparty.impl;
\ No newline at end of file
diff --git a/shib-profile-impl/src/test/java/net/shibboleth/profile/relyingparty/impl/DefaultRelyingPartyConfigurationResolverTest.java b/shib-profile-impl/src/test/java/net/shibboleth/profile/relyingparty/impl/DefaultRelyingPartyConfigurationResolverTest.java
new file mode 100644
index 0000000..594b984
--- /dev/null
+++ b/shib-profile-impl/src/test/java/net/shibboleth/profile/relyingparty/impl/DefaultRelyingPartyConfigurationResolverTest.java
@@ -0,0 +1,183 @@
+/*
+ * 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.profile.relyingparty.impl;
+
+import java.util.Iterator;
+import java.util.List;
+
+import net.shibboleth.profile.relyingparty.RelyingPartyConfiguration;
+import net.shibboleth.profile.relyingparty.VerifiedProfileCriterion;
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.shared.logic.PredicateSupport;
+import net.shibboleth.shared.resolver.CriteriaSet;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+/** Unit test for {@link DefaultRelyingPartyConfigurationResolver}. */
+ at SuppressWarnings("javadoc")
+public class DefaultRelyingPartyConfigurationResolverTest {
+
+    @Test public void testConstruction() throws ComponentInitializationException {
+        final RelyingPartyConfiguration one = new RelyingPartyConfiguration();
+        one.setId("one");
+        one.initialize();
+
+        final RelyingPartyConfiguration two = new RelyingPartyConfiguration();
+        two.setId("two");
+        two.setActivationCondition(PredicateSupport.alwaysFalse());
+        two.initialize();
+
+        final RelyingPartyConfiguration three = new RelyingPartyConfiguration();
+        three.setId("three");
+        three.initialize();
+        
+        final List<RelyingPartyConfiguration> rpConfigs = CollectionSupport.listOf(one, two, three);
+
+        DefaultRelyingPartyConfigurationResolver resolver = new DefaultRelyingPartyConfigurationResolver();
+        resolver.setId("test");
+        resolver.setRelyingPartyConfigurations(rpConfigs);
+        Assert.assertEquals(resolver.getId(), "test");
+        Assert.assertEquals(resolver.getRelyingPartyConfigurations().size(), 3);
+
+        resolver = new DefaultRelyingPartyConfigurationResolver();
+        resolver.setId("test");
+        Assert.assertEquals(resolver.getId(), "test");
+        Assert.assertEquals(resolver.getRelyingPartyConfigurations().size(), 0);
+
+        resolver = new DefaultRelyingPartyConfigurationResolver();
+        resolver.setId("test");
+        Assert.assertEquals(resolver.getId(), "test");
+        Assert.assertEquals(resolver.getRelyingPartyConfigurations().size(), 0);
+    }
+    
+    @Test public void testDefault() throws Exception {
+        final RelyingPartyConfiguration anonRP = new RelyingPartyConfiguration();
+        anonRP.setId("anonRPId");
+        anonRP.initialize();
+        
+        final RelyingPartyConfiguration defaultRP = new RelyingPartyConfiguration();
+        defaultRP.setId("defaultRPId");
+        defaultRP.initialize();
+
+        final DefaultRelyingPartyConfigurationResolver resolver = new DefaultRelyingPartyConfigurationResolver();
+        resolver.setId("test");
+        resolver.setUnverifiedConfiguration(anonRP);
+        resolver.setDefaultConfiguration(defaultRP);
+        resolver.initialize();
+        
+        final CriteriaSet criteria = new CriteriaSet();
+        criteria.add(new VerifiedProfileCriterion(true));
+        
+        final Iterable<RelyingPartyConfiguration> results = resolver.resolve(criteria);
+        Assert.assertNotNull(results);
+        
+        final Iterator<RelyingPartyConfiguration> resultItr = results.iterator();        
+        Assert.assertTrue(resultItr.hasNext());
+        Assert.assertSame(resultItr.next(), defaultRP);
+        Assert.assertFalse(resultItr.hasNext());
+        
+        Assert.assertSame(resolver.resolveSingle(criteria), defaultRP);
+    }
+    
+    @Test public void testAnon() throws Exception {
+        
+        final RelyingPartyConfiguration anonRP = new RelyingPartyConfiguration();
+        anonRP.setId("anonRPId");
+        anonRP.initialize();
+        
+        final RelyingPartyConfiguration defaultRP = new RelyingPartyConfiguration();
+        defaultRP.setId("defaultRPId");
+        defaultRP.initialize();
+
+        final DefaultRelyingPartyConfigurationResolver resolver = new DefaultRelyingPartyConfigurationResolver();
+        resolver.setId("test");
+        resolver.setUnverifiedConfiguration(anonRP);
+        resolver.setDefaultConfiguration(defaultRP);
+        resolver.initialize();
+        
+        final Iterable<RelyingPartyConfiguration> results = resolver.resolve(new CriteriaSet());
+        Assert.assertNotNull(results);
+        
+        final Iterator<RelyingPartyConfiguration> resultItr = results.iterator();        
+        Assert.assertTrue(resultItr.hasNext());
+        Assert.assertSame(resultItr.next(), anonRP);
+        Assert.assertFalse(resultItr.hasNext());
+
+        Assert.assertSame(resolver.resolveSingle(new CriteriaSet()), anonRP);
+    }
+
+    @Test public void testResolve() throws Exception {
+        final RelyingPartyConfiguration anonRP = new RelyingPartyConfiguration();
+        anonRP.setId("anonRPId");
+        anonRP.initialize();
+        
+        final RelyingPartyConfiguration defaultRP = new RelyingPartyConfiguration();
+        defaultRP.setId("defaultRPId");
+        defaultRP.initialize();
+
+        final RelyingPartyConfiguration one = new RelyingPartyConfiguration();
+        one.setId("one");
+        one.initialize();
+
+        final RelyingPartyConfiguration two = new RelyingPartyConfiguration();
+        two.setId("two");
+        two.setActivationCondition(PredicateSupport.alwaysFalse());
+        two.initialize();
+
+        final RelyingPartyConfiguration three = new RelyingPartyConfiguration();
+        three.setId("three");
+        three.initialize();
+        
+        final List<RelyingPartyConfiguration> rpConfigs = CollectionSupport.listOf(one, two, three);
+
+        final DefaultRelyingPartyConfigurationResolver resolver = new DefaultRelyingPartyConfigurationResolver();
+        resolver.setId("test");
+        resolver.setRelyingPartyConfigurations(rpConfigs);
+        resolver.setUnverifiedConfiguration(anonRP);
+        resolver.setDefaultConfiguration(defaultRP);
+        resolver.initialize();
+
+        final CriteriaSet criteria = new CriteriaSet();
+        criteria.add(new VerifiedProfileCriterion(true));
+        
+        Iterable<RelyingPartyConfiguration> results = resolver.resolve(criteria);
+        Assert.assertNotNull(results);
+
+        Iterator<RelyingPartyConfiguration> 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());
+
+        RelyingPartyConfiguration result = resolver.resolveSingle(criteria);
+        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
diff --git a/shib-profile-impl/src/test/java/net/shibboleth/profile/relyingparty/impl/RelyingPartyConfigurationTest.java b/shib-profile-impl/src/test/java/net/shibboleth/profile/relyingparty/impl/RelyingPartyConfigurationTest.java
new file mode 100644
index 0000000..a2fd530
--- /dev/null
+++ b/shib-profile-impl/src/test/java/net/shibboleth/profile/relyingparty/impl/RelyingPartyConfigurationTest.java
@@ -0,0 +1,120 @@
+/*
+ * 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.profile.relyingparty.impl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+import net.shibboleth.profile.config.ProfileConfiguration;
+import net.shibboleth.profile.config.testing.MockProfileConfiguration;
+import net.shibboleth.profile.relyingparty.RelyingPartyConfiguration;
+import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.shared.logic.ConstraintViolationException;
+import net.shibboleth.shared.logic.FunctionSupport;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+/** Unit test for {@link RelyingPartyConfiguration}. */
+ at SuppressWarnings("javadoc")
+public class RelyingPartyConfigurationTest {
+
+    @Test public void testConstruction() throws ComponentInitializationException {
+        RelyingPartyConfiguration config = new RelyingPartyConfiguration();
+        config.setId("foo");
+        config.initialize();
+        Assert.assertEquals(config.getId(), "foo");
+        Assert.assertTrue(config.getProfileConfigurations(null).isEmpty());
+
+        config = new RelyingPartyConfiguration();
+        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 RelyingPartyConfiguration();
+        config.setId("foo");
+        config.setProfileConfigurations(profileConfigs);
+        config.initialize();
+        Assert.assertEquals(config.getId(), "foo");
+        Assert.assertEquals(config.getProfileConfigurations(null).size(), 2);
+
+        try {
+            config = new RelyingPartyConfiguration();
+            config.initialize();
+            Assert.fail();
+        } catch (final ComponentInitializationException e) {
+            // expected this
+        }
+
+        try {
+            config = new RelyingPartyConfiguration();
+            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 RelyingPartyConfiguration config = new RelyingPartyConfiguration();
+        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"));
+
+        RelyingPartyConfiguration config = new RelyingPartyConfiguration();
+        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 RelyingPartyConfiguration();
+        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/shib-profile-impl/pom.xml b/shib-profile-testing/pom.xml
similarity index 73%
copy from shib-profile-impl/pom.xml
copy to shib-profile-testing/pom.xml
index 958d164..9ab4a6e 100644
--- a/shib-profile-impl/pom.xml
+++ b/shib-profile-testing/pom.xml
@@ -10,14 +10,14 @@
         <version>5.0.0-SNAPSHOT</version>
     </parent>
 
-    <name>Shibboleth Profile :: Profile Implementation</name>
-    <description>Core Profile Implementation</description>
-    <artifactId>shib-profile-impl</artifactId>
+    <name>Shibboleth Profile :: Testing API</name>
+    <description>Core Profile Testing API</description>
+    <artifactId>shib-profile-testing</artifactId>
     <packaging>jar</packaging>
 
     <properties>
         <checkstyle.configLocation>${project.basedir}/../resources/checkstyle/checkstyle.xml</checkstyle.configLocation>
-        <automatic.module.name>net.shibboleth.profile.impl</automatic.module.name>
+        <automatic.module.name>net.shibboleth.profile.config.testing</automatic.module.name>
     </properties>
 
     <dependencies>
@@ -27,50 +27,51 @@
             <artifactId>shib-profile-api</artifactId>
             <version>${project.version}</version>
         </dependency>
-
+        
+        <dependency>
+            <groupId>${opensaml.groupId}</groupId>
+            <artifactId>opensaml-core-api</artifactId>
+        </dependency>
         <dependency>
             <groupId>${opensaml.groupId}</groupId>
             <artifactId>opensaml-profile-api</artifactId>
         </dependency>
+        <dependency>
+            <groupId>${opensaml.groupId}</groupId>
+            <artifactId>opensaml-messaging-api</artifactId>
+        </dependency>
 
+        <!-- Normally test scope. -->
         <dependency>
-            <groupId>${spring.groupId}</groupId>
-            <artifactId>spring-beans</artifactId>
+            <groupId>${opensaml.groupId}</groupId>
+            <artifactId>opensaml-testing</artifactId>
+            <scope>compile</scope>
         </dependency>
         <dependency>
-            <groupId>${spring.groupId}</groupId>
-            <artifactId>spring-context</artifactId>
+            <groupId>${shib-shared.groupId}</groupId>
+            <artifactId>shib-testing</artifactId>
+            <scope>compile</scope>
         </dependency>
+
         <dependency>
             <groupId>${spring.groupId}</groupId>
             <artifactId>spring-core</artifactId>
         </dependency>
-        
         <dependency>
-            <groupId>${shib-shared.groupId}</groupId>
-            <artifactId>shib-service</artifactId>
+            <groupId>${spring.groupId}</groupId>
+            <artifactId>spring-context</artifactId>
         </dependency>
 
         <dependency>
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
         </dependency>
-        
+
         <!-- Provided Dependencies -->
-        <dependency>
-            <groupId>jakarta.servlet</groupId>
-            <artifactId>jakarta.servlet-api</artifactId>
-            <scope>provided</scope>
-        </dependency>
 
         <!-- Runtime Dependencies -->
 
         <!-- Test Dependencies -->
-        <dependency>
-            <groupId>${shib-shared.groupId}</groupId>
-            <artifactId>shib-testing</artifactId>
-            <scope>test</scope>
-        </dependency>
 
     </dependencies>
 
diff --git a/shib-profile-testing/src/main/java/net/shibboleth/profile/config/testing/MockProfileConfiguration.java b/shib-profile-testing/src/main/java/net/shibboleth/profile/config/testing/MockProfileConfiguration.java
new file mode 100644
index 0000000..ff220f9
--- /dev/null
+++ b/shib-profile-testing/src/main/java/net/shibboleth/profile/config/testing/MockProfileConfiguration.java
@@ -0,0 +1,41 @@
+/*
+ * 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.profile.config.testing;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.security.config.BasicSecurityConfiguration;
+
+import net.shibboleth.profile.config.AbstractConditionalProfileConfiguration;
+import net.shibboleth.profile.config.ProfileConfiguration;
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+
+/** Mock implementation of {@link ProfileConfiguration}. */
+public class MockProfileConfiguration extends AbstractConditionalProfileConfiguration {
+
+    /**
+     * Constructor.
+     * 
+     * @param id ID of this profile
+     */
+    public MockProfileConfiguration(@Nonnull @NotEmpty final String id) {
+        super(id);
+        setSecurityConfiguration(new BasicSecurityConfiguration());
+    }
+    
+}
\ No newline at end of file
diff --git a/shib-profile-testing/src/main/java/net/shibboleth/profile/config/testing/package-info.java b/shib-profile-testing/src/main/java/net/shibboleth/profile/config/testing/package-info.java
new file mode 100644
index 0000000..b54ca69
--- /dev/null
+++ b/shib-profile-testing/src/main/java/net/shibboleth/profile/config/testing/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+/**
+ * Profile testing APIs.
+ */
+package net.shibboleth.profile.config.testing;
\ 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