[java-plugin-shibd] branch main updated: Adjustments to initial design, work on initial abstract flow actions.

Scott Cantor cantor.2 at osu.edu
Tue May 21 19:27:12 UTC 2024


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

scantor pushed a commit to branch main
in repository java-plugin-shibd.

View the commit online:
http://git.shibboleth.net/view/?p=java-plugin-shibd.git;a=commit;h=8a3a9e1be0c52feae28f3ebf45c106fbfb7b0f27

The following commit(s) were added to refs/heads/main by this push:
     new 8a3a9e1  Adjustments to initial design, work on initial abstract flow actions.
8a3a9e1 is described below

commit 8a3a9e1be0c52feae28f3ebf45c106fbfb7b0f27
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Tue May 21 15:27:05 2024 -0400

    Adjustments to initial design, work on initial abstract flow actions.
---
 .../META-INF/net.shibboleth.idp/postconfig.xml     |  20 +--
 .../idp/flows/sp/abstract/sp-abstract-beans.xml    |  34 ++++
 .../idp/flows/sp/abstract/sp-abstract-flow.xml     |  54 ++++++
 .../shibboleth/idp/flows/sp/ping/ping-beans.xml    |  33 ----
 .../net/shibboleth/idp/flows/sp/ping/ping-flow.xml |  30 ----
 .../shibboleth/idp/module/conf/sp/sp.properties    |  11 +-
 .../net/shibboleth/sp/conf/agents-system.xml       |   5 +-
 .../src/main/java/net/shibboleth/sp/Agent.java     |  10 ++
 ...ApplicationResolver.java => AgentResolver.java} |   6 +-
 .../net/shibboleth/sp/ApplicationCriterion.java    |  50 ------
 .../shibboleth/sp/context/AgentRequestContext.java |  78 +++++++++
 .../sp/{config => context}/package-info.java       |   4 +-
 .../main/java/net/shibboleth/sp/package-info.java  |   7 +-
 sp-server-impl/pom.xml                             |  24 ++-
 .../authn/impl/InitializeAgentRequestContext.java  | 183 +++++++++++++++++++++
 .../shibboleth/sp/authn/impl}/package-info.java    |   4 +-
 .../java/net/shibboleth/sp/impl/BasicAgent.java    |  33 ++++
 ...tionResolver.java => DefaultAgentResolver.java} |  48 ++----
 .../impl/InitializeAgentRequestContextTest.java    |  98 +++++++++++
 .../shibboleth/sp/testing/MockAgentResolver.java   |  74 +++++++++
 .../net/shibboleth/sp/testing}/package-info.java   |   4 +-
 21 files changed, 631 insertions(+), 179 deletions(-)

diff --git a/sp-conf-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml b/sp-conf-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
index a0c4a20..513bf41 100644
--- a/sp-conf-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
+++ b/sp-conf-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
@@ -14,22 +14,22 @@
     default-init-method="initialize" default-destroy-method="destroy">
     
 
-    <!-- Application reseolver service. -->
+    <!-- Agent reseolver service. -->
 
-    <bean id="shibboleth.sp.ApplicationResolver" parent="shibboleth.ReloadableService" 
-        c:claz="net.shibboleth.sp.impl.DefaultApplicationResolver"
-        p:serviceConfigurations-ref="ExtendedApplicationResolverResources"
-        p:failFast="%{sp.service.applications.failFast:%{idp.service.failFast:false}}"
-        p:reloadCheckDelay="%{sp.service.applications.checkInterval:PT0S}" />
+    <bean id="shibboleth.AgentResolver" parent="shibboleth.ReloadableService" 
+        c:claz="net.shibboleth.sp.impl.DefaultAgentResolver"
+        p:serviceConfigurations-ref="ExtendedAgentResolverResources"
+        p:failFast="%{sp.service.agents.failFast:%{idp.service.failFast:false}}"
+        p:reloadCheckDelay="%{sp.service.agents.checkInterval:PT0S}" />
 
-    <util:list id="shibboleth.DefaultApplicationResolverResources">
+    <util:list id="shibboleth.DefaultAgentResolverResources">
         <value>conditional:%{idp.home}/conf/sp/agents.xml</value>
     </util:list>
             
     <!-- Auto-append system config files to resource set. -->
-    <bean id ="ExtendedApplicationResolverResources" class="net.shibboleth.shared.spring.factory.CombiningListFactoryBean"
-            p:firstList="#{getObject('%{sp.service.applications.resources:shibboleth.ApplicationResolverResources}'.trim()) ?:
-                getObject('shibboleth.DefaultApplicationResolverResources')}">
+    <bean id ="ExtendedAgentResolverResources" class="net.shibboleth.shared.spring.factory.CombiningListFactoryBean"
+            p:firstList="#{getObject('%{sp.service.agents.resources:shibboleth.AgentResolverResources}'.trim()) ?:
+                getObject('shibboleth.DefaultAgentResolverResources')}">
         <property name="secondList">
             <util:list >
                 <value>classpath:/net/shibboleth/sp/conf/agents-system.xml</value>
diff --git a/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/abstract/sp-abstract-beans.xml b/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/abstract/sp-abstract-beans.xml
new file mode 100644
index 0000000..49ad8f5
--- /dev/null
+++ b/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/abstract/sp-abstract-beans.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans" xmlns:c="http://www.springframework.org/schema/c"
+    xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
+    xmlns:util="http://www.springframework.org/schema/util" 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">
+    
+    <bean id="InitializeProfileRequestContext"
+        class="net.shibboleth.idp.profile.impl.InitializeProfileRequestContext" scope="prototype"
+        p:profileId-ref="shibboleth.sp.profileId"
+        p:loggingId-ref="shibboleth.sp.loggingId" />
+
+    <bean id="PopulateMetricContext"
+        class="org.opensaml.profile.action.impl.PopulateMetricContext" scope="prototype"
+        p:counterName="#{getObject('shibboleth.metrics.ProfileCounter')}"
+        p:metricStrategy="#{getObject('shibboleth.metrics.MetricStrategy')}" />
+
+    <bean id="FlowStartPopulateAuditContext" parent="shibboleth.AbstractPopulateAuditContext"
+        p:fieldExtractors="#{getObject('shibboleth.FlowStartAuditExtractors') ?: getObject('shibboleth.DefaultFlowStartAuditExtractors')}" />
+
+    <bean id="LogEvent" class="org.opensaml.profile.action.impl.LogEvent" scope="prototype"
+        p:suppressedEvents="#{getObject('shibboleth.SuppressedEvents') ?: getObject('shibboleth.DefaultSuppressedEvents')}">
+        <property name="eventContextLookupStrategy">
+            <bean class="net.shibboleth.idp.profile.context.navigate.WebFlowCurrentEventLookupFunction" />
+        </property>
+    </bean>
+
+    <bean id="ExtractUsernamePasswordFromBasicAuth"
+        class="net.shibboleth.idp.authn.impl.ExtractUsernamePasswordFromBasicAuth" scope="prototype"
+        p:httpServletRequestSupplier-ref="shibboleth.HttpServletRequestSupplier" />
+
+</beans>
diff --git a/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/abstract/sp-abstract-flow.xml b/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/abstract/sp-abstract-flow.xml
new file mode 100644
index 0000000..47c2b43
--- /dev/null
+++ b/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/abstract/sp-abstract-flow.xml
@@ -0,0 +1,54 @@
+<flow xmlns="http://www.springframework.org/schema/webflow" 
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow.xsd"
+    abstract="true">
+
+    <action-state id="AuthenticationSetup">
+        <on-entry>
+            <!-- Needed to allow reuse of various classes. -->
+            <evaluate expression="opensamlProfileRequestContext.ensureSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext))" />
+        </on-entry>
+        <evaluate expression="ExtractUsernamePasswordFromBasicAuth" />
+        <evaluate expression="InitializeAgentRequestContext" />
+        <evaluate expression="ValidateAgentAddress" />
+        <evaluate expression="'proceed'" />
+        
+        <transition on="proceed" to="DoCachedAuthentication" />
+    </action-state>
+    
+    <action-state id="DoCachedAuthentication">
+        <evaluate expression="ValidateCachedAuthentication" />
+        <evaluate expression="'proceed'" />
+        
+        <transition on="proceed" to="DecodeMessage" />
+        <transition on="NoCredentials" to="DoAuthentication" />
+    </action-state>
+
+    <action-state id="DoAuthentication">
+        <evaluate expression="ValidateCredentials" />
+        <evaluate expression="FinalizeAuthentication" />
+        <evaluate expression="'proceed'" />
+        
+        <transition on="proceed" to="DecodeMessage" />
+    </action-state>
+
+    <!-- Passthrough state if an exception is thrown. -->
+    <action-state id="LogRuntimeException">
+        <on-entry>
+            <evaluate
+                expression="T(org.slf4j.LoggerFactory).getLogger('net.shibboleth.sp.profile').error('Uncaught runtime exception', flowExecutionException.getCause())" />
+        </on-entry>
+        <evaluate expression="'RuntimeException'" />
+        <transition to="HandleError" />
+    </action-state>
+
+    <!-- All unhandled non-proceed results are turned into errors. -->
+    <global-transitions>
+        <transition on-exception="java.lang.RuntimeException" to="LogRuntimeException" />
+        <transition on="#{!'proceed'.equals(currentEvent.id)}" to="HandleError" />
+    </global-transitions>
+
+    <!-- The file really exists in this directory, but it's referenced from extending flow-directories -->
+    <bean-import resource="classpath:/META-INF/net/shibboleth/idp/flows/sp/abstract/sp-abstract-beans.xml" />
+
+</flow>
diff --git a/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/ping/ping-beans.xml b/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/ping/ping-beans.xml
deleted file mode 100644
index c8d5026..0000000
--- a/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/ping/ping-beans.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<beans
-    xmlns="http://www.springframework.org/schema/beans"
-    xmlns:c="http://www.springframework.org/schema/c"
-    xmlns:context="http://www.springframework.org/schema/context"
-    xmlns:p="http://www.springframework.org/schema/p"
-    xmlns:util="http://www.springframework.org/schema/util"
-    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">
-
-    <!-- Profile ID for flow. -->
-    <bean id="shibboleth.ProfileId" class="java.lang.String" c:_0="http://shibboleth.net/ns/profiles/sp/echo" />
-    
-    <!-- Profile counter name. -->
-<!--    <bean id="shibboleth.metrics.ProfileCounter" class="java.lang.String" c:_0="net.shibboleth.sp.profiles.echo" />-->
-    
-    <bean id="InitializeProfileRequestContext"
-        class="net.shibboleth.idp.profile.impl.InitializeProfileRequestContext" scope="prototype"
-        p:profileId-ref="shibboleth.ProfileId" />
-    
-    <!--  Work beans. -->
-    
-    <bean id="DoPing" class="net.shibboleth.sp.profile.impl.DoPing" scope="prototype"
-        p:httpServletResponseSupplier-ref="shibboleth.HttpServletResponseSupplier" />
-        
-    <bean id="RecordResponseComplete" class="net.shibboleth.idp.profile.impl.RecordResponseComplete" scope="prototype" />
-    
-    
-</beans>
diff --git a/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/ping/ping-flow.xml b/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/ping/ping-flow.xml
deleted file mode 100644
index 3950542..0000000
--- a/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/ping/ping-flow.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<flow xmlns="http://www.springframework.org/schema/webflow"
-    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-    xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow.xsd">
-
-    <!-- Start action. -->
-        
-    <action-state id="InitializeProfileRequestContext">
-        <evaluate expression="InitializeProfileRequestContext" />
-        <evaluate expression="'proceed'" />
-        
-        <transition on="proceed" to="DoProfileWork" />
-    </action-state>
-
-    <!-- Resume actual flow processing. -->
-
-    <action-state id="DoProfileWork">
-        <evaluate expression="DoPing" />
-        <evaluate expression="RecordResponseComplete" />
-        <evaluate expression="'proceed'" />
-        
-        <transition on="proceed" to="end" />
-    </action-state>
-    
-    <!-- Terminus -->
-
-    <end-state id="end" />
-    
-    <bean-import resource="ping-beans.xml" />
-
-</flow>
diff --git a/sp-conf-impl/src/main/resources/net/shibboleth/idp/module/conf/sp/sp.properties b/sp-conf-impl/src/main/resources/net/shibboleth/idp/module/conf/sp/sp.properties
index 705abcb..3f3c11b 100644
--- a/sp-conf-impl/src/main/resources/net/shibboleth/idp/module/conf/sp/sp.properties
+++ b/sp-conf-impl/src/main/resources/net/shibboleth/idp/module/conf/sp/sp.properties
@@ -1,8 +1,8 @@
-# Controls configuration of new "SP" service.
+# Controls configuration of service to resolve SP agents.
 # Uncomment and define to override service resources
-#sp.service.applications.resources = shibboleth.ApplicationResolverResources
-#sp.service.applications.failFast = false
-sp.service.applications.checkInterval = PT5M
+#sp.service.agents.resources = shibboleth.AgentResolverResources
+#sp.service.agents.failFast = false
+sp.service.agents.checkInterval = PT5M
 
 # General SP cookie properties (maxAge only applies to persistent cookies)
 #sp.cookie.secure = true
@@ -20,3 +20,6 @@ sp.encryption.key = %{idp.home}/credentials/sp/sp-encryption.key
 sp.encryption.cert = %{idp.home}/credentials/sp/sp-encryption.crt
 #sp.encryption.key.2 = %{idp.home}/credentials/sp/sp-encryption-old.key
 #sp.encryption.cert.2 = %{idp.home}/credentials/sp/sp-encryption-old.crt
+
+# Set to Basic to require shared secret authentication
+#sp.agent.authentication = None
diff --git a/sp-conf-impl/src/main/resources/net/shibboleth/sp/conf/agents-system.xml b/sp-conf-impl/src/main/resources/net/shibboleth/sp/conf/agents-system.xml
index de13a90..38da7b3 100644
--- a/sp-conf-impl/src/main/resources/net/shibboleth/sp/conf/agents-system.xml
+++ b/sp-conf-impl/src/main/resources/net/shibboleth/sp/conf/agents-system.xml
@@ -16,11 +16,12 @@
     <context:annotation-config />
 
     <!-- Exposes auto-wired map of Agents. -->
-    <bean id="shibboleth.ApplicationResolver" class="net.shibboleth.sp.impl.DefaultApplicationResolver" />
+    <bean id="shibboleth.AgentResolver" class="net.shibboleth.sp.impl.DefaultAgentResolver" />
 
     <!-- Parent beans for Agents and Applications. -->
 
-    <bean id="shibboleth.Agent" class="net.shibboleth.sp.impl.BasicAgent" abstract="true" />
+    <bean id="shibboleth.Agent" class="net.shibboleth.sp.impl.BasicAgent" abstract="true"
+        p:allowedAddressRanges="#{{ '127.0.0.1/32', '::1/128' }}" />
 
     <bean id="shibboleth.Application" class="net.shibboleth.sp.impl.BasicApplication" abstract="true"
         p:metadataResolver-ref="shibboleth.MetadataResolverService"
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/Agent.java b/sp-server-api/src/main/java/net/shibboleth/sp/Agent.java
index 7985473..d7680f4 100644
--- a/sp-server-api/src/main/java/net/shibboleth/sp/Agent.java
+++ b/sp-server-api/src/main/java/net/shibboleth/sp/Agent.java
@@ -16,6 +16,7 @@
 package net.shibboleth.sp;
 
 import java.util.Collection;
+import java.util.Set;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -25,6 +26,7 @@ import org.opensaml.profile.context.ProfileRequestContext;
 
 import net.shibboleth.shared.annotation.constraint.NotEmpty;
 import net.shibboleth.shared.component.IdentifiedComponent;
+import net.shibboleth.shared.net.IPRange;
 
 /**
  * Represents a deployment of an SP "agent", the stub that operates in a web server or application
@@ -36,6 +38,14 @@ import net.shibboleth.shared.component.IdentifiedComponent;
 @ThreadSafe
 public interface Agent extends IdentifiedComponent {
 
+    /**
+     * Get the network addresses or ranges of addresses from which requests from this agent may
+     * originate.
+     * 
+     * @return set of address ranges
+     */
+    @Nonnull Set<IPRange> getAllowedAddressRanges();
+
     /**
      * Get the issuer value to use in various identity protocols when identifying this agent.
      * 
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/ApplicationResolver.java b/sp-server-api/src/main/java/net/shibboleth/sp/AgentResolver.java
similarity index 80%
rename from sp-server-api/src/main/java/net/shibboleth/sp/ApplicationResolver.java
rename to sp-server-api/src/main/java/net/shibboleth/sp/AgentResolver.java
index 17e8c0a..54b967f 100644
--- a/sp-server-api/src/main/java/net/shibboleth/sp/ApplicationResolver.java
+++ b/sp-server-api/src/main/java/net/shibboleth/sp/AgentResolver.java
@@ -18,12 +18,12 @@ import net.shibboleth.shared.resolver.CriteriaSet;
 import net.shibboleth.shared.resolver.Resolver;
 
 /**
- * Interface to a resolution service that identifies the target {@link Application}
+ * Interface to a resolution service that identifies the target {@link Agent}
  * for a request based on extensible criteria.
  * 
- * <p>The {@link AgentCriterion} {@link ApplicationCriterion} criterion types MUST be supported; other
+ * <p>The {@link AgentCriterion} criterion type MUST be supported; other
  * types are optional.</p>
  */
-public interface ApplicationResolver extends Resolver<Application,CriteriaSet> {
+public interface AgentResolver extends Resolver<Agent,CriteriaSet> {
 
 }
\ No newline at end of file
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/ApplicationCriterion.java b/sp-server-api/src/main/java/net/shibboleth/sp/ApplicationCriterion.java
deleted file mode 100644
index f9fda54..0000000
--- a/sp-server-api/src/main/java/net/shibboleth/sp/ApplicationCriterion.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Licensed 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;
-
-import javax.annotation.Nonnull;
-
-import net.shibboleth.shared.annotation.constraint.NotEmpty;
-import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.shared.primitive.StringSupport;
-import net.shibboleth.shared.resolver.Criterion;
-
-/**
- * Resolver {@link Criterion} identifying an application.
- */
-public class ApplicationCriterion implements Criterion {
-    
-    /** Application ID. */
-    @Nonnull @NotEmpty final String applicationId;
-
-    /**
-     * Constructor.
-     *
-     * @param id application ID
-     */
-    public ApplicationCriterion(@Nonnull @NotEmpty final String id) {
-        applicationId = Constraint.isNotNull(StringSupport.trimOrNull(id), "Application ID cannot be null or empty");
-    }
-    
-    /**
-     * Get the application ID specified.
-     * 
-     * @return application ID
-     */
-    @Nonnull @NotEmpty public String getId() {
-        return applicationId;
-    }
-
-}
\ No newline at end of file
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/context/AgentRequestContext.java b/sp-server-api/src/main/java/net/shibboleth/sp/context/AgentRequestContext.java
new file mode 100644
index 0000000..2b575b9
--- /dev/null
+++ b/sp-server-api/src/main/java/net/shibboleth/sp/context/AgentRequestContext.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed 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.context;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.BaseContext;
+
+import net.shibboleth.sp.Agent;
+import net.shibboleth.sp.Application;
+
+/**
+ * General context for an agent request tracking basic information about the request.
+ */
+public class AgentRequestContext extends BaseContext {
+
+    /** Agent making the request. */
+    @Nullable private Agent agent;
+    
+    /** Application for which the request was made. */
+    @Nullable private Application application;
+
+    /**
+     * Get the agent making the request.
+     * 
+     * @return the agent
+     */
+    @Nullable public Agent getAgent() {
+        return agent;
+    }
+
+    /**
+     * Set the agent making the request.
+     * 
+     * @param a agent to set
+     * 
+     * @return this context
+     */
+    @Nonnull public AgentRequestContext setAgent(Agent a) {
+        agent = a;
+        return this;
+    }
+
+    /**
+     * Get the application for which the request was made.
+     * 
+     * @return the application
+     */
+    @Nullable public Application getApplication() {
+        return application;
+    }
+
+    /**
+     * Set the application for which the request was made.
+     * 
+     * @param app application to set
+     * 
+     * @return this context
+     */
+    @Nonnull public AgentRequestContext setApplication(Application app) {
+        application = app;
+        return this;
+    }
+    
+}
\ No newline at end of file
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/config/package-info.java b/sp-server-api/src/main/java/net/shibboleth/sp/context/package-info.java
similarity index 80%
copy from sp-server-api/src/main/java/net/shibboleth/sp/config/package-info.java
copy to sp-server-api/src/main/java/net/shibboleth/sp/context/package-info.java
index 61ef139..4650a66 100644
--- a/sp-server-api/src/main/java/net/shibboleth/sp/config/package-info.java
+++ b/sp-server-api/src/main/java/net/shibboleth/sp/context/package-info.java
@@ -13,6 +13,6 @@
  */
 
 /**
- * Core configuration interfaces and classes.
+ * General {@link org.opensaml.messaging.context.BaseContext} subclasses defined by the SP plugin.
  */
-package net.shibboleth.sp.config;
\ No newline at end of file
+package net.shibboleth.sp.context;
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/package-info.java b/sp-server-api/src/main/java/net/shibboleth/sp/package-info.java
index dd814b6..b7543b4 100644
--- a/sp-server-api/src/main/java/net/shibboleth/sp/package-info.java
+++ b/sp-server-api/src/main/java/net/shibboleth/sp/package-info.java
@@ -13,7 +13,10 @@
  */
 
 /**
- * Core APIs for the SP.
+ * Core APIs for the SP plugin.
  */
 
-package net.shibboleth.sp;
\ No newline at end of file
+ at NonnullElements
+package net.shibboleth.sp;
+
+import net.shibboleth.shared.annotation.constraint.NonnullElements;
diff --git a/sp-server-impl/pom.xml b/sp-server-impl/pom.xml
index 39257db..5caa179 100644
--- a/sp-server-impl/pom.xml
+++ b/sp-server-impl/pom.xml
@@ -28,11 +28,15 @@
             <scope>compile</scope>
         </dependency>
 
+        <dependency>
+            <groupId>${idp.groupId}</groupId>
+            <artifactId>idp-authn-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
         <dependency>
             <groupId>${idp.groupId}</groupId>
             <artifactId>idp-profile-api</artifactId>
-            <version>${idp.version}</version>
-            <scope>compile</scope>
+            <scope>provided</scope>
         </dependency>
 
         <!-- Provided dependencies -->
@@ -67,6 +71,16 @@
             <artifactId>opensaml-core-api</artifactId>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>${opensaml.groupId}</groupId>
+            <artifactId>opensaml-messaging-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>${opensaml.groupId}</groupId>
+            <artifactId>opensaml-profile-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
         <dependency>
             <groupId>${opensaml.groupId}</groupId>
             <artifactId>opensaml-xmlsec-api</artifactId>
@@ -113,6 +127,12 @@
             <version>${project.version}</version>
             <scope>test</scope>
         </dependency>
+
+        <dependency>
+            <groupId>${idp.groupId}</groupId>
+            <artifactId>idp-testing</artifactId>
+            <scope>test</scope>
+        </dependency>
         
         <dependency>
             <groupId>${spring.groupId}</groupId>
diff --git a/sp-server-impl/src/main/java/net/shibboleth/sp/authn/impl/InitializeAgentRequestContext.java b/sp-server-impl/src/main/java/net/shibboleth/sp/authn/impl/InitializeAgentRequestContext.java
new file mode 100644
index 0000000..03b2583
--- /dev/null
+++ b/sp-server-impl/src/main/java/net/shibboleth/sp/authn/impl/InitializeAgentRequestContext.java
@@ -0,0 +1,183 @@
+/*
+ * Licensed 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.authn.impl;
+
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.profile.action.ActionSupport;
+import org.opensaml.profile.action.EventIds;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+
+import net.shibboleth.idp.authn.AbstractAuthenticationAction;
+import net.shibboleth.idp.authn.AuthnEventIds;
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.authn.context.UsernamePasswordContext;
+import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
+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.ResolverException;
+import net.shibboleth.shared.service.ReloadableService;
+import net.shibboleth.shared.service.ServiceException;
+import net.shibboleth.shared.service.ServiceableComponent;
+import net.shibboleth.sp.Agent;
+import net.shibboleth.sp.AgentCriterion;
+import net.shibboleth.sp.AgentResolver;
+import net.shibboleth.sp.context.AgentRequestContext;
+
+/**
+ * An authentication action that relies on extracted HTTP authentication credentials
+ * to resolve the appropriate {@link Agent} and create an {@link AgentRequestContext}.
+ * 
+ * @event {@link EventIds#PROCEED_EVENT_ID}
+ * @event {@link EventIds#INVALID_PROFILE_CTX}
+ * @event {@link EventIds#IO_ERROR}
+ * @event {@link AuthnEventIds#UNKNOWN_USERNAME}
+ * @pre <pre>ProfileRequestContext.getSubcontext(AuthenticationContext.class) != null</pre>
+ * @post <pre>ProfileRequestContext.ensureSubcontext(AgentRequestContext.class).getAgent() != null</pre>
+ */
+public class InitializeAgentRequestContext extends AbstractAuthenticationAction {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(InitializeAgentRequestContext.class);
+    
+    /** Lookup strategy for {@link UsernamePasswordContext}. */
+    @Nonnull private Function<ProfileRequestContext,UsernamePasswordContext> usernamePasswordContextLookupStrategy;
+
+    /** Creation strategy for {@link AgentRequestContext}. */
+    @Nonnull private Function<ProfileRequestContext,AgentRequestContext> agentRequestContextCreationStrategy;
+    
+    /** The agent resolver. */
+    @NonnullAfterInit ReloadableService<AgentResolver> agentResolver;
+    
+    /** Cached agent ID from "username". */
+    @NonnullBeforeExec private String agentId;
+
+    /** Constructor. */
+    @SuppressWarnings("null")
+    public InitializeAgentRequestContext() {
+        usernamePasswordContextLookupStrategy = new ChildContextLookup<>(UsernamePasswordContext.class).compose(
+                new ChildContextLookup<>(AuthenticationContext.class));
+        agentRequestContextCreationStrategy = new ChildContextLookup<>(AgentRequestContext.class, true);
+    }
+    
+    /**
+     * Sets the lookup strategy for the {@link UsernamePasswordContext}.
+     * 
+     * @param strategy lookup strategy
+     */
+    public void setUsernamePasswordContextLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext,UsernamePasswordContext> strategy) {
+        checkSetterPreconditions();
+        
+        usernamePasswordContextLookupStrategy = Constraint.isNotNull(strategy,
+                "UsernamePasswordContext lookup strategy cannot be null");
+    }
+    
+    /**
+     * Sets the creation strategy for the {@link AgentRequestContext}.
+     * 
+     * @param strategy creation strategy
+     */
+    public void setAgentRequestContextCreationStrategy(
+            @Nonnull final Function<ProfileRequestContext,AgentRequestContext> strategy) {
+        checkSetterPreconditions();
+        
+        agentRequestContextCreationStrategy = Constraint.isNotNull(strategy,
+                "AgentRequestContext creation strategy cannot be null");
+    }
+    
+    /**
+     * Sets the {@link AgentResolver} service to use.
+     * 
+     * @param service the {@link AgentResolver} service
+     */
+    public void setAgentResolver(@Nonnull final ReloadableService<AgentResolver> service) {
+        checkSetterPreconditions();
+        
+        agentResolver = Constraint.isNotNull(service, "AgentResolver service cannot be null");
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    protected void doInitialize() throws ComponentInitializationException {
+        if (agentResolver == null) {
+            throw new ComponentInitializationException("AgentResolver service cannot be null");
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext,
+            @Nonnull final AuthenticationContext authenticationContext) {
+        
+        if (!super.doPreExecute(profileRequestContext, authenticationContext)) {
+            return false;
+        }
+        
+        final UsernamePasswordContext usernamePasswordContext =
+                usernamePasswordContextLookupStrategy.apply(profileRequestContext);
+        if (usernamePasswordContext != null) {
+            agentId = usernamePasswordContext.getUsername();
+        }
+        
+        if (agentId == null) {
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.UNKNOWN_USERNAME);
+            return false;
+        }
+        
+        return true;
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
+            @Nonnull final AuthenticationContext authenticationContext) {
+        
+        try (final ServiceableComponent<AgentResolver> resolver = agentResolver.getServiceableComponent()) {
+            
+            final Agent agent = resolver.getComponent().resolveSingle(new CriteriaSet(new AgentCriterion(agentId)));
+            if (agent == null) {
+                log.warn("{} Unknown agent: '{}'", getLogPrefix(), agentId);
+                ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.UNKNOWN_USERNAME);
+                return;
+            }
+            
+            final AgentRequestContext agentRequestContext =
+                    agentRequestContextCreationStrategy.apply(profileRequestContext);
+            if (agentRequestContext == null) {
+                log.error("{} Unable to create AgentRequestContext", getLogPrefix());
+                ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
+                return;
+            }
+            
+            agentRequestContext.setAgent(agent);
+            
+        } catch (final ServiceException e) {
+            log.error("{} Unable to obtain AgentResolver component", getLogPrefix(), e);
+            ActionSupport.buildEvent(profileRequestContext, EventIds.IO_ERROR);
+        } catch (final ResolverException e) {
+            log.error("{} Error resolving Agent '{}'", getLogPrefix(), agentId, e);
+            ActionSupport.buildEvent(profileRequestContext, EventIds.IO_ERROR);
+        }
+    }
+    
+}
\ No newline at end of file
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/config/package-info.java b/sp-server-impl/src/main/java/net/shibboleth/sp/authn/impl/package-info.java
similarity index 85%
copy from sp-server-api/src/main/java/net/shibboleth/sp/config/package-info.java
copy to sp-server-impl/src/main/java/net/shibboleth/sp/authn/impl/package-info.java
index 61ef139..e0b3fcd 100644
--- a/sp-server-api/src/main/java/net/shibboleth/sp/config/package-info.java
+++ b/sp-server-impl/src/main/java/net/shibboleth/sp/authn/impl/package-info.java
@@ -13,6 +13,6 @@
  */
 
 /**
- * Core configuration interfaces and classes.
+ * Classes implementing authentication of agent requests.
  */
-package net.shibboleth.sp.config;
\ No newline at end of file
+package net.shibboleth.sp.authn.impl;
\ No newline at end of file
diff --git a/sp-server-impl/src/main/java/net/shibboleth/sp/impl/BasicAgent.java b/sp-server-impl/src/main/java/net/shibboleth/sp/impl/BasicAgent.java
index a3073ad..84d1b29 100644
--- a/sp-server-impl/src/main/java/net/shibboleth/sp/impl/BasicAgent.java
+++ b/sp-server-impl/src/main/java/net/shibboleth/sp/impl/BasicAgent.java
@@ -15,6 +15,7 @@
 
 package net.shibboleth.sp.impl;
 
+import java.net.InetAddress;
 import java.util.Collection;
 import java.util.Map;
 import java.util.Objects;
@@ -35,6 +36,7 @@ import net.shibboleth.shared.annotation.constraint.NotEmpty;
 import net.shibboleth.shared.collection.CollectionSupport;
 import net.shibboleth.shared.logic.Constraint;
 import net.shibboleth.shared.logic.FunctionSupport;
+import net.shibboleth.shared.net.IPRange;
 import net.shibboleth.sp.Agent;
 import net.shibboleth.sp.Application;
 
@@ -43,6 +45,9 @@ import net.shibboleth.sp.Application;
  */
 public class BasicAgent extends DefaultRelyingPartyConfigurationResolver implements Agent {
 
+    /** Allowed address ranges. */
+    @Nonnull private Set<IPRange> allowedAddressRanges;
+    
     /** Application map. */
     @Nonnull private Map<String,Application> applicationMap;
     
@@ -51,9 +56,37 @@ public class BasicAgent extends DefaultRelyingPartyConfigurationResolver impleme
     
     /** Constructor. */
     public BasicAgent() {
+        allowedAddressRanges = CollectionSupport.emptySet();
         applicationMap = CollectionSupport.emptyMap();
         issuerLookupStrategy = FunctionSupport.constant(null);
     }
+    
+    /** {@inheritDoc} */
+    @Nonnull public Set<IPRange> getAllowedAddressRanges() {
+        return allowedAddressRanges;
+    }
+    
+    /**
+     * Set the network ranges from which this agent may make requests.
+     * 
+     * @param ranges allowed ranges
+     */
+    public void setAllowedAddressRanges(@Nullable final Set<IPRange> ranges) {
+        checkSetterPreconditions();
+        
+        allowedAddressRanges = ranges != null ? CollectionSupport.copyToSet(ranges) : CollectionSupport.emptySet();
+    }
+    
+    /**
+     * Returns true iff the supplied address matches one of the allowed ranges.
+     * 
+     * @param address address to test
+     * 
+     * @return true iff the supplied address matches one of the allowed ranges
+     */
+    public boolean isAllowed(@Nonnull final InetAddress address) {
+        return allowedAddressRanges.stream().anyMatch(r -> r.contains(address));
+    }
 
     /** {@inheritDoc} */
     @Nullable @NotEmpty public String getIssuer(@Nullable final ProfileRequestContext profileRequestContext) {
diff --git a/sp-server-impl/src/main/java/net/shibboleth/sp/impl/DefaultApplicationResolver.java b/sp-server-impl/src/main/java/net/shibboleth/sp/impl/DefaultAgentResolver.java
similarity index 58%
rename from sp-server-impl/src/main/java/net/shibboleth/sp/impl/DefaultApplicationResolver.java
rename to sp-server-impl/src/main/java/net/shibboleth/sp/impl/DefaultAgentResolver.java
index adc9623..1215876 100644
--- a/sp-server-impl/src/main/java/net/shibboleth/sp/impl/DefaultApplicationResolver.java
+++ b/sp-server-impl/src/main/java/net/shibboleth/sp/impl/DefaultAgentResolver.java
@@ -27,27 +27,19 @@ import org.springframework.beans.factory.annotation.Autowired;
 import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
 import net.shibboleth.shared.collection.CollectionSupport;
 import net.shibboleth.shared.component.ComponentInitializationException;
-import net.shibboleth.shared.component.IdentifiableComponent;
 import net.shibboleth.shared.component.InitializableComponent;
-import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.shared.primitive.StringSupport;
 import net.shibboleth.shared.resolver.CriteriaSet;
 import net.shibboleth.shared.resolver.ResolverException;
 import net.shibboleth.shared.spring.config.IdentifiedComponentManager;
 import net.shibboleth.sp.Agent;
 import net.shibboleth.sp.AgentCriterion;
-import net.shibboleth.sp.Application;
-import net.shibboleth.sp.ApplicationCriterion;
-import net.shibboleth.sp.ApplicationResolver;
+import net.shibboleth.sp.AgentResolver;
 
 /**
- * Default implementation of {@link ApplicationResolver}.
+ * Default implementation of {@link AgentResolver}.
  */
-public class DefaultApplicationResolver extends IdentifiedComponentManager<Agent>
-        implements ApplicationResolver, InitializableComponent, IdentifiableComponent {
-    
-    /** Identifier. */
-    @Nullable String id;
+public class DefaultAgentResolver extends IdentifiedComponentManager<Agent>
+        implements AgentResolver, InitializableComponent {
     
     /** Tracks init state. */
     private boolean initialized;
@@ -61,22 +53,10 @@ public class DefaultApplicationResolver extends IdentifiedComponentManager<Agent
      * @param agents agents to autowire into resolver
      */
     @Autowired
-    public DefaultApplicationResolver(@Nullable final Collection<Agent> agents) {
+    public DefaultAgentResolver(@Nullable final Collection<Agent> agents) {
         super(agents);
     }
-    
-    /** {@inheritDoc} */
-    @Nullable public String getId() {
-        return id;
-    }
-
-    /** {@inheritDoc} */
-    public void setId(@Nonnull final String componentId) {
-        if (!initialized) {
-            id = Constraint.isNotNull(StringSupport.trimOrNull(componentId), "ID cannot be null or empty");
-        }
-    }
-    
+        
     /** {@inheritDoc} */
     public void initialize() throws ComponentInitializationException {
 
@@ -95,18 +75,12 @@ public class DefaultApplicationResolver extends IdentifiedComponentManager<Agent
     }
 
     /** {@inheritDoc} */
-    @Nullable public Application resolveSingle(@Nullable final CriteriaSet criteria) throws ResolverException {
+    @Nullable public Agent resolveSingle(@Nullable final CriteriaSet criteria) throws ResolverException {
         
         if (criteria != null) {
             final AgentCriterion c_agent = criteria.get(AgentCriterion.class);
             if (c_agent != null) {
-                final Agent agent = agentMap.get(c_agent.getId());
-                if (agent != null) {
-                    final ApplicationCriterion c_app = criteria.get(ApplicationCriterion.class);
-                    if (c_app != null) {
-                        return agent.getApplication(c_app.getId());
-                    }
-                }
+                return agentMap.get(c_agent.getId());
             }
         }
         
@@ -114,10 +88,10 @@ public class DefaultApplicationResolver extends IdentifiedComponentManager<Agent
     }
 
     /** {@inheritDoc} */
-    @Nonnull public Iterable<Application> resolve(@Nullable final CriteriaSet criteria) throws ResolverException {
+    @Nonnull public Iterable<Agent> resolve(@Nullable final CriteriaSet criteria) throws ResolverException {
         
-        final Application app = resolveSingle(criteria);
-        return app != null ? CollectionSupport.singletonList(app) : CollectionSupport.emptyList();
+        final Agent agent = resolveSingle(criteria);
+        return agent != null ? CollectionSupport.singletonList(agent) : CollectionSupport.emptyList();
     }
 
 }
\ No newline at end of file
diff --git a/sp-server-impl/src/test/java/net/shibboleth/sp/authn/impl/InitializeAgentRequestContextTest.java b/sp-server-impl/src/test/java/net/shibboleth/sp/authn/impl/InitializeAgentRequestContextTest.java
new file mode 100644
index 0000000..dd9c37b
--- /dev/null
+++ b/sp-server-impl/src/test/java/net/shibboleth/sp/authn/impl/InitializeAgentRequestContextTest.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed 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.authn.impl;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.springframework.webflow.execution.Event;
+import org.springframework.webflow.execution.RequestContext;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import net.shibboleth.idp.authn.AuthnEventIds;
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.authn.context.UsernamePasswordContext;
+import net.shibboleth.idp.profile.context.navigate.WebflowRequestContextProfileRequestContextLookup;
+import net.shibboleth.idp.profile.testing.ActionTestingSupport;
+import net.shibboleth.idp.profile.testing.RequestContextBuilder;
+import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.sp.context.AgentRequestContext;
+import net.shibboleth.sp.impl.BasicAgent;
+import net.shibboleth.sp.testing.MockAgentResolver;
+
+/**
+ * Unit test for {@link InitializeAgentRequestContext} action.
+ */
+ at SuppressWarnings("javadoc")
+public class InitializeAgentRequestContextTest {
+
+    private RequestContext src;
+    private ProfileRequestContext prc;
+    private BasicAgent agent;
+    private InitializeAgentRequestContext action;
+    
+    @BeforeMethod
+    public void setUp() throws ComponentInitializationException {
+        src = new RequestContextBuilder().buildRequestContext();
+        prc = new WebflowRequestContextProfileRequestContextLookup().apply(src);
+        prc.ensureSubcontext(AuthenticationContext.class);
+        
+        agent = new BasicAgent();
+        agent.setId("foo");
+        agent.initialize();
+        
+        action = new InitializeAgentRequestContext();
+        action.setAgentResolver(MockAgentResolver.getMockResolverService(agent));
+        action.initialize();
+    }
+
+    @Test
+    public void testNoUsername() {
+        
+        // No context.
+        
+        Event event = action.execute(src);
+        ActionTestingSupport.assertEvent(event, AuthnEventIds.UNKNOWN_USERNAME);
+        
+        // Empty context.
+        prc.ensureSubcontext(AuthenticationContext.class).ensureSubcontext(UsernamePasswordContext.class);
+        event = action.execute(src);
+        ActionTestingSupport.assertEvent(event, AuthnEventIds.UNKNOWN_USERNAME);
+    }
+
+    @Test
+    public void testNoAgent() {
+        final UsernamePasswordContext upCtx =
+                prc.ensureSubcontext(AuthenticationContext.class).ensureSubcontext(UsernamePasswordContext.class);
+        upCtx.setUsername("bogus");
+        
+        final Event event = action.execute(src);
+        ActionTestingSupport.assertEvent(event, AuthnEventIds.UNKNOWN_USERNAME);
+    }
+    
+    @Test
+    public void testSuccess() {
+        final UsernamePasswordContext upCtx =
+                prc.ensureSubcontext(AuthenticationContext.class).ensureSubcontext(UsernamePasswordContext.class);
+        upCtx.setUsername("foo");
+        
+        final Event event = action.execute(src);
+        ActionTestingSupport.assertProceedEvent(event);
+        
+        final AgentRequestContext agentCtx = prc.getSubcontext(AgentRequestContext.class);
+        assert agentCtx != null;
+        Assert.assertEquals(agent, agentCtx.getAgent());
+    }
+}
\ No newline at end of file
diff --git a/sp-testing/src/main/java/net/shibboleth/sp/testing/MockAgentResolver.java b/sp-testing/src/main/java/net/shibboleth/sp/testing/MockAgentResolver.java
new file mode 100644
index 0000000..1a1a792
--- /dev/null
+++ b/sp-testing/src/main/java/net/shibboleth/sp/testing/MockAgentResolver.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed 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.testing;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.resolver.CriteriaSet;
+import net.shibboleth.shared.resolver.ResolverException;
+import net.shibboleth.shared.service.ReloadableService;
+import net.shibboleth.shared.testing.MockReloadableService;
+import net.shibboleth.shared.testing.MockResolver;
+import net.shibboleth.sp.Agent;
+import net.shibboleth.sp.AgentCriterion;
+import net.shibboleth.sp.AgentResolver;
+
+/**
+ * Mock {@link AgentResolver} for tests.
+ */
+public class MockAgentResolver extends MockResolver<Agent> implements AgentResolver {
+
+    /** Agent to return. */
+    private final Agent agent;
+    
+    /**
+     * Constructor.
+     *
+     * @param a agent to return if a match
+     */
+    public MockAgentResolver(@Nonnull final Agent a) {
+        agent = Constraint.isNotNull(a, "Agent cannot be null");
+    }
+    
+    /** {@inheritDoc} */
+    @Nullable public Agent resolveSingle(@Nullable final CriteriaSet criteria) throws ResolverException {
+        super.resolveSingle(criteria);
+        
+        final AgentCriterion agentCrit = criteria != null ? criteria.get(AgentCriterion.class) : null;
+        if (agentCrit == null) {
+            throw new ResolverException("No AgentCriterion provided.");
+        }
+        
+        if (agentCrit.getId().equals(agent.getId())) {
+            return agent;
+        }
+        
+        return null;
+    }
+    
+    /**
+     * Wrap a new instance of the mock class in a mocked reloadable service.
+     * 
+     * @param a agent to return if a match
+     * 
+     * @return mock service
+     */
+    @Nonnull static public ReloadableService<AgentResolver> getMockResolverService(@Nonnull final Agent a) {
+        return new MockReloadableService<>(new MockAgentResolver(a));
+    }
+
+}
\ No newline at end of file
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/config/package-info.java b/sp-testing/src/main/java/net/shibboleth/sp/testing/package-info.java
similarity index 87%
rename from sp-server-api/src/main/java/net/shibboleth/sp/config/package-info.java
rename to sp-testing/src/main/java/net/shibboleth/sp/testing/package-info.java
index 61ef139..926f92d 100644
--- a/sp-server-api/src/main/java/net/shibboleth/sp/config/package-info.java
+++ b/sp-testing/src/main/java/net/shibboleth/sp/testing/package-info.java
@@ -13,6 +13,6 @@
  */
 
 /**
- * Core configuration interfaces and classes.
+ * Shared SP testing classes.
  */
-package net.shibboleth.sp.config;
\ No newline at end of file
+package net.shibboleth.sp.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