[java-plugin-shibd] branch main updated: Sketch out machinery for token consumer flows.
Scott Cantor
cantor.2 at osu.edu
Mon Aug 26 19:37:02 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=1e5fdef1532152644d0b98d88c7dacf271ec8452
The following commit(s) were added to refs/heads/main by this push:
new 1e5fdef Sketch out machinery for token consumer flows.
1e5fdef is described below
commit 1e5fdef1532152644d0b98d88c7dacf271ec8452
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Mon Aug 26 15:36:59 2024 -0400
Sketch out machinery for token consumer flows.
---
.../META-INF/net.shibboleth.idp/postconfig.xml | 6 ++
.../idp/flows/sp/consumer/consumer-flow.xml | 40 ++++++++
.../parse-request-map/parse-request-map-beans.xml | 2 +-
.../shibboleth/idp/flows/sp/ping/ping-beans.xml | 2 +-
.../idp/flows/sp/sealer/sealer-beans.xml | 2 +-
.../session-initiator/session-initiator-beans.xml | 2 +-
.../idp/flows/sp/storage/storage-beans.xml | 2 +-
.../token-consumer-beans.xml} | 12 +--
.../sp/token-consumer/token-consumer-flow.xml | 34 +++++++
.../shibboleth/idp/module/conf/sp/sp.properties | 3 +-
.../net/shibboleth/sp/conf/agents-system.xml | 4 +
.../shibboleth/sp/flows/TokenConsumerFlowText.java | 93 ++++++++++++++++++
.../main/java/net/shibboleth/sp/Application.java | 9 ++
.../profile/BasicTokenConsumerFlowDescriptor.java | 86 +++++++++++++++++
.../sp/profile/TokenConsumerFlowDescriptor.java | 46 +++++++++
.../net/shibboleth/sp/impl/BasicApplication.java | 40 +++++++-
.../sp/profile/impl/SelectTokenConsumerFlow.java | 104 +++++++++++++++++++++
.../impl/TokenConsumerFlowDescriptorManager.java | 41 ++++++++
18 files changed, 515 insertions(+), 13 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 4f8975c..ae73bef 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
@@ -41,4 +41,10 @@
<bean id="shibboleth.RemotedHttpServletRequestSupplier" class="net.shibboleth.sp.messaging.impl.RemotedHttpServletRequestSupplier" />
<bean id="shibboleth.RemotedHttpServletResponseSupplier" class="net.shibboleth.sp.messaging.impl.RemotedlHttpServletResponseSupplier" />
+ <!-- Management bean to collect and expose TokenConsumerFlowDescriptors. -->
+
+ <bean id="shibboleth.TokenConsumerFlowDescriptorManager"
+ class="net.shibboleth.sp.profile.impl.TokenConsumerFlowDescriptorManager"
+ p:components="#{getObject('shibboleth.AvailableTokenConsumerFlows')}" />
+
</beans>
\ No newline at end of file
diff --git a/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/consumer/consumer-flow.xml b/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/consumer/consumer-flow.xml
new file mode 100644
index 0000000..f84660c
--- /dev/null
+++ b/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/consumer/consumer-flow.xml
@@ -0,0 +1,40 @@
+<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">
+
+ <!-- Rudimentary impediment to direct execution of subflow. -->
+ <input name="calledAsSubflow" type="boolean" required="true" />
+
+ <!-- Defines the possible end-state events, and a global transition rule. -->
+
+ <!-- Successful completion of subflow. -->
+ <end-state id="proceed" />
+
+ <!-- Error events to reflect back from this subflow. -->
+ <end-state id="InvalidEvent" />
+ <end-state id="InvalidProfileContext" />
+ <end-state id="InvalidRelyingPartyContext" />
+ <end-state id="InvalidRelyingPartyConfiguration" />
+ <end-state id="ReselectFlow" />
+ <end-state id="RuntimeException" />
+
+ <action-state id="LogRuntimeException">
+ <on-entry>
+ <evaluate expression="LogRuntimeException.log('net.shibboleth.sp.profile.consumer', flowExecutionException)" />
+ </on-entry>
+ <evaluate expression="'RuntimeException'" />
+
+ <transition to="RuntimeException" />
+ </action-state>
+
+ <global-transitions>
+ <transition on-exception="java.lang.RuntimeException" to="LogRuntimeException" />
+ <transition on="InvalidProfileContext" to="InvalidProfileContext" />
+ <transition on="InvalidRelyingPartyContext" to="InvalidRelyingPartyContext" />
+ <transition on="InvalidRelyingPartyConfiguration" to="InvalidRelyingPartyConfiguration" />
+ <transition on="ReselectFlow" to="ReselectFlow" />
+ <transition on="RuntimeException" to="RuntimeException" />
+ </global-transitions>
+
+</flow>
diff --git a/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/parse-request-map/parse-request-map-beans.xml b/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/parse-request-map/parse-request-map-beans.xml
index d817289..361c4b8 100644
--- a/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/parse-request-map/parse-request-map-beans.xml
+++ b/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/parse-request-map/parse-request-map-beans.xml
@@ -8,7 +8,7 @@
default-init-method="initialize" default-destroy-method="destroy">
<bean id="shibboleth.sp.profileId" class="java.lang.String" c:_0="http://shibboleth.net/ns/profiles/sp/parse-request-map" />
- <bean id="shibboleth.sp.loggingId" class="java.lang.String" c:_0="%{idp.service.logging.sp:SPAgent}" />
+ <bean id="shibboleth.sp.loggingId" class="java.lang.String" c:_0="%{sp.service.logging.requestmap:SPAgent.ParseRequestMap}" />
<bean id="ParseAgentRequestMap"
class="net.shibboleth.sp.config.impl.ParseAgentRequestMap" scope="prototype"
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
index b70be65..1fbca80 100644
--- 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
@@ -8,7 +8,7 @@
default-init-method="initialize" default-destroy-method="destroy">
<bean id="shibboleth.sp.profileId" class="java.lang.String" c:_0="http://shibboleth.net/ns/profiles/sp/ping" />
- <bean id="shibboleth.sp.loggingId" class="java.lang.String" c:_0="%{idp.service.logging.sp:SPAgent}" />
+ <bean id="shibboleth.sp.loggingId" class="java.lang.String" c:_0="%{sp.service.logging.ping:SPAgent.Ping}" />
<bean id="DoPing" class="net.shibboleth.sp.profile.impl.DoPing" scope="prototype" />
diff --git a/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/sealer/sealer-beans.xml b/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/sealer/sealer-beans.xml
index 38f3087..c915cc5 100644
--- a/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/sealer/sealer-beans.xml
+++ b/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/sealer/sealer-beans.xml
@@ -8,7 +8,7 @@
default-init-method="initialize" default-destroy-method="destroy">
<bean id="shibboleth.sp.profileId" class="java.lang.String" c:_0="http://shibboleth.net/ns/profiles/sp/parse-request-map" />
- <bean id="shibboleth.sp.loggingId" class="java.lang.String" c:_0="%{idp.service.logging.sp:SPAgent}" />
+ <bean id="shibboleth.sp.loggingId" class="java.lang.String" c:_0="%{sp.service.logging.sealer:SPAgent.Sealer}" />
<bean id="DoSealerOperation"
class="net.shibboleth.sp.profile.impl.DoSealerOperation" scope="prototype"
diff --git a/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/session-initiator/session-initiator-beans.xml b/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/session-initiator/session-initiator-beans.xml
index 90b2971..8b9e448 100644
--- a/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/session-initiator/session-initiator-beans.xml
+++ b/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/session-initiator/session-initiator-beans.xml
@@ -8,7 +8,7 @@
default-init-method="initialize" default-destroy-method="destroy">
<bean id="shibboleth.sp.profileId" class="java.lang.String" c:_0="http://shibboleth.net/ns/profiles/sp/session-initiator" />
- <bean id="shibboleth.sp.loggingId" class="java.lang.String" c:_0="%{idp.service.logging.sp:SPAgent}" />
+ <bean id="shibboleth.sp.loggingId" class="java.lang.String" c:_0="%{sp.service.logging.initiator:SPAgent.SessionInitiator}" />
<bean id="MapResourceToStateToken"
class="net.shibboleth.sp.profile.impl.MapResourceToStateToken" scope="prototype"
diff --git a/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/storage/storage-beans.xml b/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/storage/storage-beans.xml
index e9cec31..df35b1c 100644
--- a/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/storage/storage-beans.xml
+++ b/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/storage/storage-beans.xml
@@ -8,7 +8,7 @@
default-init-method="initialize" default-destroy-method="destroy">
<bean id="shibboleth.sp.profileId" class="java.lang.String" c:_0="http://shibboleth.net/ns/profiles/sp/parse-request-map" />
- <bean id="shibboleth.sp.loggingId" class="java.lang.String" c:_0="%{idp.service.logging.sp:SPAgent}" />
+ <bean id="shibboleth.sp.loggingId" class="java.lang.String" c:_0="%{sp.service.logging.storage:SPAgent.Storage}" />
<bean id="DoStorageOperation"
class="net.shibboleth.sp.profile.impl.DoStorageOperation" scope="prototype"
diff --git a/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/session-initiator/session-initiator-beans.xml b/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/token-consumer/token-consumer-beans.xml
similarity index 72%
copy from sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/session-initiator/session-initiator-beans.xml
copy to sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/token-consumer/token-consumer-beans.xml
index 90b2971..ddf9bcb 100644
--- a/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/session-initiator/session-initiator-beans.xml
+++ b/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/token-consumer/token-consumer-beans.xml
@@ -7,11 +7,11 @@
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="shibboleth.sp.profileId" class="java.lang.String" c:_0="http://shibboleth.net/ns/profiles/sp/session-initiator" />
- <bean id="shibboleth.sp.loggingId" class="java.lang.String" c:_0="%{idp.service.logging.sp:SPAgent}" />
-
- <bean id="MapResourceToStateToken"
- class="net.shibboleth.sp.profile.impl.MapResourceToStateToken" scope="prototype"
- p:createOutputObjects="true" />
+ <bean id="shibboleth.sp.profileId" class="java.lang.String" c:_0="http://shibboleth.net/ns/profiles/sp/token-consumer" />
+ <bean id="shibboleth.sp.loggingId" class="java.lang.String" c:_0="%{sp.service.logging.consumer:SPAgent.TokenConsumer}" />
+
+ <bean id="SelectTokenConsumerFlow"
+ class="net.shibboleth.sp.profile.impl.SelectTokenConsumerFlow" scope="prototype"
+ p:availableFlows="#{getObject('shibboleth.TokenConsumerFlowDescriptorManager').getComponents()}" />
</beans>
diff --git a/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/token-consumer/token-consumer-flow.xml b/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/token-consumer/token-consumer-flow.xml
new file mode 100644
index 0000000..ca582de
--- /dev/null
+++ b/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/token-consumer/token-consumer-flow.xml
@@ -0,0 +1,34 @@
+<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"
+ parent="sp/application">
+
+ <action-state id="InitializeProfileRequestContext">
+ <evaluate expression="InitializeProfileRequestContext" />
+ <evaluate expression="'proceed'" />
+
+ <!-- Branch to parent flow for authentication. -->
+ <transition on="proceed" to="AuthenticationSetup" />
+ </action-state>
+
+ <!-- Resume flow operation after set up by parent. -->
+ <action-state id="DoApplicationOperation">
+ <evaluate expression="SelectTokenConsumerFlow" />
+
+ <!-- Call a subflow with the same ID as the event. -->
+ <transition on="#{currentEvent.id.startsWith('sp/consumer/')}" to="CallConsumerFlow" />
+ </action-state>
+
+ <subflow-state id="CallConsumerFlow" subflow="sp/consumer/#{TokenConsumerFlowID}">
+ <input name="calledAsSubflow" value="true" />
+ <transition on="proceed" to="EncodeAgentResponse" />
+ </subflow-state>
+
+ <action-state id="NoPotentialFlow">
+ <evaluate expression="'NoPotentialFlow'" />
+ </action-state>
+
+ <!-- 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/token-consumer/token-consumer-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 73d3fdb..2c488c4 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
@@ -15,8 +15,9 @@ sp.service.agents.checkInterval = PT5M
# Set to DataSealer to use for remoted data encryption.
#sp.dataSealer = shibboleth.DataSealer
-# Default precedence/set of Session Initiator flows to attempt
+# Default precedence/set of Session Initiator and Token Consumer flows to attempt
#sp.application.sessionInitiators =
+#sp.application.tokenConsumers =
# General SP cookie properties (maxAge only applies to persistent cookies)
#sp.cookie.secure = true
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 5d1f1ac..d6d746b 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
@@ -35,11 +35,15 @@
p:relyingPartyConfigurations-ref="shibboleth.RelyingPartyOverrides"
p:metricName="net.shibboleth.sp.relyingparty.configurations"
p:sessionInitiators-ref="shibboleth.SessionInitiators"
+ p:tokenConsumers-ref="shibboleth.TokenConsumers"
p:stateTokenManager-ref="#{'%{sp.stateToken.Manager:shibboleth.StorageStateTokenManager}'.trim()}" />
<bean id="shibboleth.SessionInitiators" parent="shibboleth.CommaDelimStringArray"
c:_0="%{sp.application.sessionInitiators:}" />
+ <bean id="shibboleth.TokenConsumers" parent="shibboleth.CommaDelimStringArray"
+ c:_0="%{sp.application.tokenConsumers:}" />
+
<bean id="shibboleth.PassthroughStateTokenManager" class="net.shibboleth.sp.impl.PassthroughStateTokenManager" lazy-init="true" />
<bean id="shibboleth.StorageStateTokenManager" class="net.shibboleth.sp.impl.StorageServiceStateTokenManager" lazy-init="true"
diff --git a/sp-conf-impl/src/test/java/net/shibboleth/sp/flows/TokenConsumerFlowText.java b/sp-conf-impl/src/test/java/net/shibboleth/sp/flows/TokenConsumerFlowText.java
new file mode 100644
index 0000000..d387d6c
--- /dev/null
+++ b/sp-conf-impl/src/test/java/net/shibboleth/sp/flows/TokenConsumerFlowText.java
@@ -0,0 +1,93 @@
+/*
+ * 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.flows;
+
+import java.io.IOException;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.profile.action.EventIds;
+import org.springframework.webflow.executor.FlowExecutionResult;
+import org.testng.annotations.Test;
+
+import net.shibboleth.idp.authn.AuthnEventIds;
+import net.shibboleth.sp.ddf.DDF;
+import net.shibboleth.sp.profile.impl.ResolveApplication;
+
+/**
+ * Unit test for the SP token-consumer flow (basics only).
+ */
+public class TokenConsumerFlowText extends AbstractSPFlowTest {
+
+ /** Flow ID. */
+ @Nonnull public static final String FLOW_ID = "sp/token-consumer";
+
+ protected TokenConsumerFlowText() {
+ super(FLOW_ID);
+ }
+
+ /**
+ * Test flow with no application specified.
+ *
+ * @throws IOException
+ */
+ @Test
+ public void testNoApplication() throws IOException {
+ setDefaultAuth();
+ setRequest("POST", new DDF(null));
+ final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+ assertFlowExecutionResult(result, FLOW_ID);
+ assertFlowExecutionOutcome(result.getOutcome());
+ assertOutputMessageEvent(result, EventIds.INVALID_MESSAGE);
+ }
+
+ /**
+ * Test flow with invalid application ID.
+ *
+ * @throws IOException
+ */
+ @Test
+ public void testBadApplication() throws IOException {
+ setDefaultAuth();
+
+ final DDF input = new DDF(null).structure();
+ input.addmember(ResolveApplication.APPLICATION_ID).string("foo");
+ setRequest("POST", input);
+
+ final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+ assertFlowExecutionResult(result, FLOW_ID);
+ assertFlowExecutionOutcome(result.getOutcome());
+ assertOutputMessageEvent(result, EventIds.INVALID_MESSAGE);
+ }
+
+ /**
+ * Test flow with no consumer subflows installed.
+ *
+ * @throws IOException
+ */
+ @Test
+ public void testNoFlows() throws IOException {
+ setDefaultAuth();
+
+ final DDF input = new DDF(null).structure();
+ input.addmember(ResolveApplication.APPLICATION_ID).string(APPLICATION_ID);
+ setRequest("POST", input);
+
+ final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+ assertFlowExecutionResult(result, FLOW_ID);
+ assertFlowExecutionOutcome(result.getOutcome());
+ assertOutputMessageEvent(result, AuthnEventIds.NO_POTENTIAL_FLOW);
+ }
+}
\ No newline at end of file
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/Application.java b/sp-server-api/src/main/java/net/shibboleth/sp/Application.java
index 1d6aa3c..f0d22a4 100644
--- a/sp-server-api/src/main/java/net/shibboleth/sp/Application.java
+++ b/sp-server-api/src/main/java/net/shibboleth/sp/Application.java
@@ -78,6 +78,15 @@ public interface Application extends RelyingPartyConfigurationResolver {
*/
@Nonnull List<String> getSessionInitiators(@Nullable final ProfileRequestContext profileRequestContext);
+ /**
+ * Get an ordered list of token consumer flows to attempt for this application.
+ *
+ * @param profileRequestContext profile request context
+ *
+ * @return ordered list of unprefixed flow IDs
+ */
+ @Nonnull List<String> getTokenConsumers(@Nullable final ProfileRequestContext profileRequestContext);
+
/**
* Get the {@link StateTokenManager} to use for thie application.
*
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/profile/BasicTokenConsumerFlowDescriptor.java b/sp-server-api/src/main/java/net/shibboleth/sp/profile/BasicTokenConsumerFlowDescriptor.java
new file mode 100644
index 0000000..0c1acb3
--- /dev/null
+++ b/sp-server-api/src/main/java/net/shibboleth/sp/profile/BasicTokenConsumerFlowDescriptor.java
@@ -0,0 +1,86 @@
+/*
+ * 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.profile;
+
+import java.util.function.Predicate;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import com.google.common.base.MoreObjects;
+
+import net.shibboleth.shared.component.AbstractIdentifiableInitializableComponent;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.logic.PredicateSupport;
+
+/**
+ * Basic implementation of a descriptor for a token consumer flow.
+ */
+public class BasicTokenConsumerFlowDescriptor extends AbstractIdentifiableInitializableComponent
+ implements TokenConsumerFlowDescriptor {
+
+ /** Whether this flow supports non-browser clients. */
+ @Nonnull private Predicate<ProfileRequestContext> activationCondition;
+
+ /** Constructor. */
+ public BasicTokenConsumerFlowDescriptor() {
+ activationCondition = PredicateSupport.alwaysFalse();
+ }
+
+ /** {@inheritDoc} */
+ @Nonnull public Predicate<ProfileRequestContext> getActivationCondition() {
+ return activationCondition;
+ }
+
+ /**
+ * Set activation condition to determine whether this flow supports a request.
+ *
+ * @param condition condition to use
+ */
+ public void setActivationCondition(@Nonnull final Predicate<ProfileRequestContext> condition) {
+ activationCondition = Constraint.isNotNull(condition, "Activation condition cannot be null");
+ }
+
+ /** {@inheritDoc} */
+ @Override public int hashCode() {
+ return getId().hashCode();
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean equals(final Object obj) {
+ if (obj == null) {
+ return false;
+ }
+
+ if (obj == this) {
+ return true;
+ }
+
+ if (obj instanceof BasicTokenConsumerFlowDescriptor) {
+ return getId().equals(((BasicTokenConsumerFlowDescriptor) obj).getId());
+ }
+
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("flowId", getId())
+ .toString();
+ }
+
+}
\ No newline at end of file
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/profile/TokenConsumerFlowDescriptor.java b/sp-server-api/src/main/java/net/shibboleth/sp/profile/TokenConsumerFlowDescriptor.java
new file mode 100644
index 0000000..c33adf8
--- /dev/null
+++ b/sp-server-api/src/main/java/net/shibboleth/sp/profile/TokenConsumerFlowDescriptor.java
@@ -0,0 +1,46 @@
+/*
+ * 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.profile;
+
+import java.util.function.Predicate;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.idp.profile.FlowDescriptor;
+
+/**
+ * A descriptor for a token consumer subflow.
+ *
+ * <p>Token consumers are the portion of the SP responsible for consuming SSO protocol
+ * responses that result in authenticated access to resources. In SAML, this would be the
+ * "assertion consumer service".</p>
+ *
+ * <p>The primary need for this is to associate each subflow with a condition that can
+ * determine whether a given request is handled by a given subflow. For example, determining
+ * the protocol in use, the parametes included in the request, etc.</p>
+ */
+public interface TokenConsumerFlowDescriptor extends FlowDescriptor {
+
+ /**
+ * Gets the activation condition to run to determine whether this flow can handle the
+ * request.
+ *
+ * @return a condition governing use of this flow
+ */
+ @Nonnull Predicate<ProfileRequestContext> getActivationCondition();
+
+}
\ No newline at end of file
diff --git a/sp-server-impl/src/main/java/net/shibboleth/sp/impl/BasicApplication.java b/sp-server-impl/src/main/java/net/shibboleth/sp/impl/BasicApplication.java
index d3a0e5d..e42dcde 100644
--- a/sp-server-impl/src/main/java/net/shibboleth/sp/impl/BasicApplication.java
+++ b/sp-server-impl/src/main/java/net/shibboleth/sp/impl/BasicApplication.java
@@ -56,7 +56,10 @@ public class BasicApplication extends DefaultRelyingPartyConfigurationResolver i
/** Session initiator list lookup strategy. */
@Nonnull private Function<ProfileRequestContext,List<String>> sessionInitiatorLookupStrategy;
-
+
+ /** Token consumer list lookup strategy. */
+ @Nonnull private Function<ProfileRequestContext,List<String>> tokenConsumerLookupStrategy;
+
/** State token management. */
@NonnullAfterInit private StateTokenManager stateTokenManager;
@@ -77,6 +80,7 @@ public class BasicApplication extends DefaultRelyingPartyConfigurationResolver i
issuerLookupStrategy = FunctionSupport.constant(null);
authenticatingAuthorityLookupStrategy = FunctionSupport.constant(null);
sessionInitiatorLookupStrategy = FunctionSupport.constant(CollectionSupport.emptyList());
+ tokenConsumerLookupStrategy = FunctionSupport.constant(CollectionSupport.emptyList());
}
/** {@inheritDoc} */
@@ -204,7 +208,41 @@ public class BasicApplication extends DefaultRelyingPartyConfigurationResolver i
sessionInitiatorLookupStrategy = Constraint.isNotNull(strategy,
"Session initiators lookup strategy cannot be null");
}
+
+
+ /** {@inheritDoc} */
+ @Nonnull public List<String> getTokenConsumers(@Nullable final ProfileRequestContext profileRequestContext) {
+ final List<String> ret = tokenConsumerLookupStrategy.apply(profileRequestContext);
+ return ret != null ? ret : CollectionSupport.emptyList();
+ }
+
+ /**
+ * Sets the ordered collection of unprefixed token consumer flow IDs to use for this application.
+ *
+ * @param consumers ordered collection of unprefixed token consumer flow IDs
+ */
+ public void setTokenConsumers(@Nullable final Collection<String> consumers) {
+ checkSetterPreconditions();
+
+ if (consumers != null) {
+ tokenConsumerLookupStrategy = FunctionSupport.constant(CollectionSupport.copyToList(consumers));
+ } else {
+ tokenConsumerLookupStrategy = FunctionSupport.constant(CollectionSupport.emptyList());
+ }
+ }
+ /**
+ * Sets the lookup strategy for the ordered collection of unprefixed token consumer flow IDs
+ * to use for this application.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setTokenConsumersLookupStrategy(@Nonnull final Function<ProfileRequestContext,List<String>> strategy) {
+ checkSetterPreconditions();
+
+ tokenConsumerLookupStrategy = Constraint.isNotNull(strategy, "Token consumers lookup strategy cannot be null");
+ }
+
/** {@inheritDoc} */
@Nonnull public StateTokenManager getStateTokenManager() {
checkComponentActive();
diff --git a/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/SelectTokenConsumerFlow.java b/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/SelectTokenConsumerFlow.java
new file mode 100644
index 0000000..4d88c81
--- /dev/null
+++ b/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/SelectTokenConsumerFlow.java
@@ -0,0 +1,104 @@
+/*
+ * 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.profile.impl;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import javax.annotation.Nonnull;
+
+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.AuthnEventIds;
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.primitive.LoggerFactory;
+import net.shibboleth.sp.messaging.impl.RemotedHttpServletRequestResponseContext;
+import net.shibboleth.sp.profile.AbstractApplicationAction;
+import net.shibboleth.sp.profile.TokenConsumerFlowDescriptor;
+
+/**
+ * Action that checks all enabled token consumer flows and returns the flow ID of the first
+ * enabled flow indicating it can handle the request.
+ *
+ * @event {@link EventIds#PROCEED_EVENT_ID}
+ * @event {@link AuthnEventIds#NO_POTENTIAL_FLOW}
+ * @event selected token consumer flow ID
+ */
+public class SelectTokenConsumerFlow extends AbstractApplicationAction {
+
+ /** Class logger. */
+ @Nonnull private Logger log = LoggerFactory.getLogger(SelectTokenConsumerFlow.class);
+
+ /** Map of available consumer flows. */
+ @Nonnull private Map<String,TokenConsumerFlowDescriptor> availableFlows;
+
+ /** Constructor. */
+ public SelectTokenConsumerFlow() {
+ availableFlows = CollectionSupport.emptyMap();
+ }
+
+ /**
+ * Sets the available token consumer flows to consider.
+ *
+ * @param flows available flows
+ */
+ public void setAvailableFlows(@Nonnull final Collection<TokenConsumerFlowDescriptor> flows) {
+ checkSetterPreconditions();
+
+ availableFlows = flows.stream()
+ .collect(CollectionSupport.nonnullCollector(
+ Collectors.toUnmodifiableMap(TokenConsumerFlowDescriptor::getId, Function.identity()))).get();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+
+ // We do the crazy stuff to accomodate flow activation conditions evaluating the servlet request.
+ try {
+ RemotedHttpServletRequestResponseContext.loadCurrent(ensureAgentRequestContext().getRemotedHttpServletRequest(),
+ ensureAgentRequestContext().getRemotedHttpServletResponse());
+
+ final List<String> flows = ensureApplication().getTokenConsumers(profileRequestContext);
+ for (final String flowId : flows) {
+ assert flowId != null;
+ final TokenConsumerFlowDescriptor flowDescriptor = availableFlows.get(flowId);
+ if (flowDescriptor != null) {
+ if (flowDescriptor.getActivationCondition().test(profileRequestContext)) {
+ log.debug("{} Token consumer flow {} handling request", getLogPrefix(), flowId);
+ ActionSupport.buildEvent(profileRequestContext, flowId);
+ return;
+ } else {
+ log.warn("{} Token consumer flow {} cannot handle request", getLogPrefix(), flowId);
+ }
+ } else {
+ log.warn("{} Token consumer flow {} is not available", getLogPrefix(), flowId);
+ }
+ }
+ } finally {
+ RemotedHttpServletRequestResponseContext.clearCurrent();
+ }
+
+ log.warn("{} No token consumer flows were able to handle request", getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_POTENTIAL_FLOW);
+ }
+
+}
\ No newline at end of file
diff --git a/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/TokenConsumerFlowDescriptorManager.java b/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/TokenConsumerFlowDescriptorManager.java
new file mode 100644
index 0000000..c78231e
--- /dev/null
+++ b/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/TokenConsumerFlowDescriptorManager.java
@@ -0,0 +1,41 @@
+/*
+ * 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.profile.impl;
+
+import java.util.Collection;
+
+import javax.annotation.Nullable;
+
+import org.springframework.beans.factory.annotation.Autowired;
+
+import net.shibboleth.shared.spring.config.IdentifiedComponentManager;
+import net.shibboleth.sp.profile.TokenConsumerFlowDescriptor;
+
+/**
+ * Manager of {@link TokenConsumerFlowDescriptor} objects.
+ */
+public class TokenConsumerFlowDescriptorManager extends IdentifiedComponentManager<TokenConsumerFlowDescriptor> {
+
+ /**
+ * Constructor.
+ *
+ * @param freeObjects free-standing objects
+ */
+ @Autowired
+ public TokenConsumerFlowDescriptorManager(@Nullable final Collection<TokenConsumerFlowDescriptor> freeObjects) {
+ super(freeObjects);
+ }
+
+}
\ 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