[java-plugin-shibd] 02/02: Master flow for session-initiator loop.

Scott Cantor cantor.2 at osu.edu
Mon Jul 8 17:31:14 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=0121aa8b166212af7b9a9d12d4fa69135c906dfc

commit 0121aa8b166212af7b9a9d12d4fa69135c906dfc
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Mon Jul 8 13:31:09 2024 -0400

    Master flow for session-initiator loop.
---
 .../session-initiator/session-initiator-beans.xml  | 13 ++++
 .../session-initiator/session-initiator-flow.xml   | 42 ++++++++++
 .../shibboleth/idp/module/conf/sp/sp.properties    |  3 +
 .../net/shibboleth/sp/conf/agents-system.xml       |  6 +-
 .../sp/flows/SessionInitiatorFlowTest.java         | 90 ++++++++++++++++++++++
 .../shibboleth/idp/module/conf/sp/test-agents.xml  |  7 +-
 .../main/java/net/shibboleth/sp/Application.java   | 11 +++
 .../net/shibboleth/sp/impl/BasicApplication.java   | 46 ++++++++++-
 8 files changed, 214 insertions(+), 4 deletions(-)

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
new file mode 100644
index 0000000..5a13abe
--- /dev/null
+++ b/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/session-initiator/session-initiator-beans.xml
@@ -0,0 +1,13 @@
+<?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="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}" />
+
+</beans>
diff --git a/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/session-initiator/session-initiator-flow.xml b/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/session-initiator/session-initiator-flow.xml
new file mode 100644
index 0000000..de61b5a
--- /dev/null
+++ b/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/session-initiator/session-initiator-flow.xml
@@ -0,0 +1,42 @@
+<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">
+        <on-entry>
+            <evaluate expression="opensamlProfileRequestContext.ensureSubcontext(T(net.shibboleth.sp.context.AgentRequestContext)).getApplication().getSessionInitiators(opensamlProfileRequestContext).iterator()" result="flowScope.SessionInitiatorIterator" />
+        </on-entry>
+        <evaluate expression="'proceed'" />
+        
+        <!-- Branch to child flow for actual work. -->
+        <transition on="proceed" to="CheckIterator" />
+    </action-state>
+
+    <decision-state id="CheckIterator">
+        <if test="SessionInitiatorIterator.hasNext()" then="CallInitiatorFlow" else="NoPotentialFlow" />
+    </decision-state>
+    
+    <subflow-state id="CallInitiatorFlow" subflow="sp/initiator/#{SessionInitiatorIterator.next()}">
+        <input name="calledAsSubflow" value="true" />
+        <transition on="proceed" to="EncodeAgentResponse" />
+        <transition on="ReselectFlow" to="CheckIterator" />
+    </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/session-initiator/session-initiator-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 98253af..4f5cb7e 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
@@ -31,3 +31,6 @@ sp.encryption.cert = %{idp.home}/credentials/sp/sp-encryption.crt
 #sp.storageService = shibboleth.StorageService
 # Set to DataSealer to use for remoted data encryption.
 #sp.dataSealer = shibboleth.DataSealer
+
+# Default precedence/set of Session Initiator flows to attempt
+#sp.application.sessionInitiators = saml2
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 2954c1b..f5ec11f 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
@@ -33,7 +33,11 @@
         p:unverifiedConfiguration-ref="shibboleth.UnverifiedRelyingParty"
         p:defaultConfiguration-ref="shibboleth.DefaultRelyingParty"
         p:relyingPartyConfigurations-ref="shibboleth.RelyingPartyOverrides"
-        p:metricName="net.shibboleth.sp.relyingparty.configurations" />
+        p:metricName="net.shibboleth.sp.relyingparty.configurations"
+        p:sessionInitiators-ref="shibboleth.SessionInitiators" />
+
+    <bean id="shibboleth.SessionInitiators" parent="shibboleth.CommaDelimStringArray"
+        c:_0="%{sp.application.sessionInitiators:saml2}" />
 
     <!-- Wildcard import hook for plugins. -->
     <import resource="classpath*:/META-INF/net/shibboleth/sp/service/application/postconfig.xml" />
diff --git a/sp-conf-impl/src/test/java/net/shibboleth/sp/flows/SessionInitiatorFlowTest.java b/sp-conf-impl/src/test/java/net/shibboleth/sp/flows/SessionInitiatorFlowTest.java
new file mode 100644
index 0000000..1bbf23f
--- /dev/null
+++ b/sp-conf-impl/src/test/java/net/shibboleth/sp/flows/SessionInitiatorFlowTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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 session-initiator flow (basics only).
+ */
+public class SessionInitiatorFlowTest extends AbstractSPFlowTest {
+    
+    /** Flow ID. */
+    @Nonnull public static final String FLOW_ID = "sp/session-initiator";
+
+    protected SessionInitiatorFlowTest() {
+        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 initiator subflows installed.
+     * @throws IOException 
+     */
+    @Test
+    public void testNoFlows() throws IOException {
+        setDefaultAuth();
+        
+        final DDF input = new DDF(null).structure();
+        input.addmember(ResolveApplication.APPLICATION_ID).string("no-initiators");
+        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-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/test-agents.xml b/sp-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/test-agents.xml
index 18ac6c1..aa95a7a 100644
--- a/sp-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/test-agents.xml
+++ b/sp-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/test-agents.xml
@@ -15,7 +15,12 @@
     <bean id="testsp.example.org" parent="shibboleth.Agent" p:sharedSecrets="foo">
         <property name="applications">
             <set>
-                <bean p:id="default" parent="shibboleth.Application" p:issuer="https://testsp.example.org"/>
+                <bean p:id="default" parent="shibboleth.Application"
+                    p:issuer="https://testsp.example.org"/>
+                    
+                <bean p:id="no-initiators" parent="shibboleth.Application"
+                    p:issuer="https://testsp.example.org"
+                    p:sessionInitiators="#{{}}" />
             </set>
         </property>
     </bean>
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 0a34bc7..e694c8c 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
@@ -15,6 +15,8 @@
 
 package net.shibboleth.sp;
 
+import java.util.List;
+
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 import javax.annotation.concurrent.ThreadSafe;
@@ -58,6 +60,15 @@ public interface Application extends RelyingPartyConfigurationResolver {
      */
     @Nullable @NotEmpty String getIssuer(@Nonnull final ProfileRequestContext profileRequestContext);
     
+    /**
+     * Get an ordered list of session initiator flows to attempt for this application.
+     * 
+     * @param profileRequestContext profile request context
+     *  
+     * @return ordered list of unprefixed flow IDs
+     */
+    @Nonnull List<String> getSessionInitiators(@Nullable final ProfileRequestContext profileRequestContext);
+    
     /**
      * Get {@link MetadataResolver} for this {@link Application}.
      * 
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 f922b79..c7bbac0 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
@@ -15,6 +15,8 @@
 
 package net.shibboleth.sp.impl;
 
+import java.util.Collection;
+import java.util.List;
 import java.util.Objects;
 import java.util.function.Function;
 
@@ -33,6 +35,7 @@ import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
 import net.shibboleth.profile.relyingparty.impl.DefaultRelyingPartyConfigurationResolver;
 import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
 import net.shibboleth.shared.annotation.constraint.NotEmpty;
+import net.shibboleth.shared.collection.CollectionSupport;
 import net.shibboleth.shared.component.ComponentInitializationException;
 import net.shibboleth.shared.logic.Constraint;
 import net.shibboleth.shared.logic.FunctionSupport;
@@ -48,7 +51,10 @@ public class BasicApplication extends DefaultRelyingPartyConfigurationResolver i
 
     /** Lookup strategy for issuer. */
     @Nonnull private Function<ProfileRequestContext,String> issuerLookupStrategy;
-        
+    
+    /** Session initiator list lookup strategy. */
+    @Nonnull private Function<ProfileRequestContext,List<String>> sessionInitiatorLookupStrategy;
+    
     /** Metadata source. */
     @NonnullAfterInit private ReloadableService<MetadataResolver> metadataResolver;
     
@@ -64,6 +70,7 @@ public class BasicApplication extends DefaultRelyingPartyConfigurationResolver i
     /** Constructor. */
     public BasicApplication() {
         issuerLookupStrategy = FunctionSupport.constant(null);
+        sessionInitiatorLookupStrategy = FunctionSupport.constant(CollectionSupport.emptyList());
     }
     
     /** {@inheritDoc} */
@@ -117,11 +124,46 @@ public class BasicApplication extends DefaultRelyingPartyConfigurationResolver i
      * 
      * @param strategy lookup strategy
      */
-    public void setIssuer(@Nonnull final Function<ProfileRequestContext,String> strategy) {
+    public void setIssuerLookupStrategy(@Nonnull final Function<ProfileRequestContext,String> strategy) {
         checkSetterPreconditions();
         
         issuerLookupStrategy = Constraint.isNotNull(strategy, "Issuer lookup strategy cannot be null");
     }
+    
+    /** {@inheritDoc} */
+    @Nonnull public List<String> getSessionInitiators(@Nullable final ProfileRequestContext profileRequestContext) {
+        final List<String> ret = sessionInitiatorLookupStrategy.apply(profileRequestContext);
+        return ret != null ? ret : CollectionSupport.emptyList(); 
+    }
+    
+    /**
+     * Sets the ordered collection of unprefixed session initiator flow IDs to use for this application.
+     * 
+     * @param initiators ordered collection of unprefixed session initiator flow IDs
+     */
+    public void setSessionInitiators(@Nullable final Collection<String> initiators) {
+        checkSetterPreconditions();
+        
+        if (initiators != null) {
+            sessionInitiatorLookupStrategy = FunctionSupport.constant(CollectionSupport.copyToList(initiators));
+        } else {
+            sessionInitiatorLookupStrategy = FunctionSupport.constant(CollectionSupport.emptyList());
+        }
+    }
+    
+    /**
+     * Sets the lookup strategy for the ordered collection of unprefixed session initiator flow IDs
+     * to use for this application.
+     * 
+     * @param strategy lookup strategy
+     */
+    public void setSessionInitiatorsLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext,List<String>> strategy) {
+        checkSetterPreconditions();
+        
+        sessionInitiatorLookupStrategy = Constraint.isNotNull(strategy,
+                "Session initiators lookup strategy cannot be null");
+    }
         
     /** {@inheritDoc} */
     @Nonnull public ServiceableComponent<MetadataResolver> getMetadataResolver() throws ServiceException {

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list