[java-plugin-shibd] branch main updated: Beginnings of working flow machinery.

Scott Cantor cantor.2 at osu.edu
Thu May 30 15:51:18 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=81c6afb8a7d857c31f7b35e3a327f32ee42fd7b8

The following commit(s) were added to refs/heads/main by this push:
     new 81c6afb  Beginnings of working flow machinery.
81c6afb is described below

commit 81c6afb8a7d857c31f7b35e3a327f32ee42fd7b8
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Thu May 30 11:51:15 2024 -0400

    Beginnings of working flow machinery.
---
 .../idp/flows/sp/abstract/sp-abstract-beans.xml    |  13 ++-
 .../idp/flows/sp/abstract/sp-abstract-flow.xml     |  17 ++-
 .../shibboleth/idp/flows/sp/ping/ping-beans.xml    |  15 +++
 .../net/shibboleth/idp/flows/sp/ping/ping-flow.xml |  26 +++++
 .../net/shibboleth/idp/module/conf/sp/agents.xml   |   2 +-
 .../sp/profile/impl/DecodeAgentRequest.java        |  36 +++---
 .../net/shibboleth/sp/profile/impl/DoPing.java     |  16 +--
 .../sp/profile/impl/PrepareAgentErrorResponse.java | 121 +++++++++++++++++++++
 8 files changed, 215 insertions(+), 31 deletions(-)

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
index ebb5805..3ac8dbb 100644
--- 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
@@ -17,16 +17,13 @@
         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" />
@@ -54,7 +51,7 @@
         p:maxAge="%{sp.agent.authn.cached.maxAge:3600}" />
 
     <bean id="ValidateAgentCredentials"
-        class="net.shibboleth.so.authn.impl.ValidateAgentCredentials" scope="prototype"
+        class="net.shibboleth.sp.authn.impl.ValidateAgentCredentials" scope="prototype"
         p:requireAll="%{sp.agent.authn.requireAll:false}"
         p:validators="#{getObject('shibboleth.AgentValidators') ?: getObject('DefaultAgentValidators')}"
         p:cleanupHook-ref="DefaultCleanupHook" />
@@ -69,7 +66,9 @@
         p:httpServletResponseSupplier-ref="shibboleth.HttpServletResponseSupplier" />
 
     <bean id="RecordResponseComplete" class="net.shibboleth.idp.profile.impl.RecordResponseComplete" scope="prototype" />
-        
+    
+    <bean id="PrepareAgentErrorResponse"
+        class="net.shibboleth.sp.profile.impl.PrepareAgentErrorResponse" scope="prototype" />
 
     <!-- ======== Beans for agent authentication ======== -->
 
@@ -163,5 +162,7 @@
         p:accountStateWarningPeriod="%{sp.agent.authn.LDAP.accountStateWarningPeriod:#{null}}"
         p:accountStateLoginFailures="%{sp.agent.authn.LDAP.accountStateLoginFailures:0}" />
 
+    <!-- This is just a mocked descriptor to stuff into the context for compatibility with the IdP code. -->
+    <bean id="AgentAuthnFlowDescriptor" parent="shibboleth.AuthenticationFlow" />
 
 </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
index 97610d2..dae358b 100644
--- 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
@@ -6,8 +6,9 @@
     <action-state id="AuthenticationSetup">
         <on-entry>
             <!-- Needed to allow reuse of various classes. -->
-            <evaluate expression="opensamlProfileRequestContext.ensureSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext))" />
+            <evaluate expression="opensamlProfileRequestContext.ensureSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext)).setAttemptedFlow(flowRequestContext.getActiveFlow().getApplicationContext().getBean('AgentAuthnFlowDescriptor'))" />
         </on-entry>
+        <evaluate expression="PopulateMetricContext" />
         <evaluate expression="ExtractUsernamePasswordFromBasicAuth" />
         <evaluate expression="InitializeAgentRequestContext" />
         <evaluate expression="ValidateAgentAddress" />
@@ -54,7 +55,7 @@
         
         <transition to="end" />
         
-        <exception-handler bean="RethrowingFlowExecutionExceptionHandler"/>
+        <exception-handler bean="RethrowingFlowExecutionExceptionHandler"/> 
     </action-state>
 
     <action-state id="HandleError">
@@ -62,13 +63,19 @@
             <evaluate expression="opensamlProfileRequestContext.ensureSubcontext(T(net.shibboleth.idp.profile.context.SpringRequestContext)).setRequestContext(flowRequestContext)" />
             <evaluate expression="LogEvent" />
         </on-entry>
+        <evaluate expression="PrepareAgentErrorResponse" />
         <evaluate expression="'proceed'" />
         
-        <transition on="proceed" to="HandleOutboundMessage" />
+        <transition on="proceed" to="EncodeAgentResponse" />
+        <transition to="HandleErrorWithView" />
         
         <exception-handler bean="RethrowingFlowExecutionExceptionHandler"/>
     </action-state>
 
+    <end-state id="HandleErrorWithView" view="sp/error">
+        <exception-handler bean="RethrowingFlowExecutionExceptionHandler"/>
+    </end-state>
+
     <!-- Passthrough state if an exception is thrown. -->
     <action-state id="LogRuntimeException">
         <on-entry>
@@ -79,6 +86,10 @@
         <transition to="HandleError" />
     </action-state>
 
+    <!-- Successful terminal state (success meaning outbound agent response encoded). -->
+
+    <end-state id="end" />
+
     <!-- All unhandled non-proceed results are turned into errors. -->
     <global-transitions>
         <transition on-exception="java.lang.RuntimeException" to="LogRuntimeException" />
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
new file mode 100644
index 0000000..b70be65
--- /dev/null
+++ b/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/ping/ping-beans.xml
@@ -0,0 +1,15 @@
+<?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/ping" />
+    <bean id="shibboleth.sp.loggingId" class="java.lang.String" c:_0="%{idp.service.logging.sp:SPAgent}" />
+
+    <bean id="DoPing" class="net.shibboleth.sp.profile.impl.DoPing" 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
new file mode 100644
index 0000000..010da3f
--- /dev/null
+++ b/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/ping/ping-flow.xml
@@ -0,0 +1,26 @@
+<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/abstract">
+
+    <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="DoOperation">
+        <evaluate expression="DoPing" />
+        <evaluate expression="'proceed'" />
+        
+        <!-- Branch to parent flow to send response. -->
+        <transition on="proceed" to="EncodeAgentResponse" />
+    </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/ping/ping-beans.xml" />
+
+</flow>
diff --git a/sp-conf-impl/src/main/resources/net/shibboleth/idp/module/conf/sp/agents.xml b/sp-conf-impl/src/main/resources/net/shibboleth/idp/module/conf/sp/agents.xml
index 6f5f117..91fc6c5 100644
--- a/sp-conf-impl/src/main/resources/net/shibboleth/idp/module/conf/sp/agents.xml
+++ b/sp-conf-impl/src/main/resources/net/shibboleth/idp/module/conf/sp/agents.xml
@@ -80,7 +80,7 @@
 
     <!-- ============ Agents ============ -->
 
-    <bean id="sp.example.org" parent="shibboleth.Agent">
+    <bean id="sp.example.org" parent="shibboleth.Agent" p:sharedSecrets="foo">
         <property name="applications">
             <set>
                 <bean p:id="default" parent="shibboleth.Application" p:issuer="https://sp.example.org"/>
diff --git a/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/DecodeAgentRequest.java b/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/DecodeAgentRequest.java
index 4c6410a..f999262 100644
--- a/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/DecodeAgentRequest.java
+++ b/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/DecodeAgentRequest.java
@@ -114,22 +114,30 @@ public class DecodeAgentRequest extends AbstractProfileAction {
             return;
         }
 
-        final String contentType = request.getContentType();
-        if (contentType == null || !contentType.equals("text/plain")) {
-            log.warn("{} Invalid HTTP content type, only text/plain supported: ", getLogPrefix(), contentType);
-            ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MESSAGE);
-            return;
-        }
+        final int contentLength = request.getContentLength();
+        if (contentLength != 0) {
+            final String contentType = request.getContentType();
+            if (contentType != null && !"text/plain".equals(contentType)) {
+                log.warn("{} Invalid HTTP content type ({}), only text/plain supported", getLogPrefix(),
+                        contentType != null ? contentType : "none");
+                ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MESSAGE);
+                return;
+            }
 
-        try (final InputStream in = request.getInputStream()) {
-            assert in != null;
-            final DDF msg = DDF.deserialize(in);
-            agentRequestContext.setInput(msg);
-            agentRequestContext.setOutput(new DDF());
-        } catch (final IOException e) {
-            log.warn("{} Unable to parse input message from HttpServletRequest", getLogPrefix(), e);
-            ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MESSAGE);
+            try (final InputStream in = request.getInputStream()) {
+                assert in != null;
+                final DDF msg = DDF.deserialize(in);
+                agentRequestContext.setInput(msg);
+            } catch (final IOException e) {
+                log.warn("{} Unable to parse input message from HttpServletRequest", getLogPrefix(), e);
+                ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MESSAGE);
+                return;
+            }
+        } else {
+            log.debug("{} No input message found in HttpServletRequest", getLogPrefix());
         }
+        
+        agentRequestContext.setOutput(new DDF());
     }
 
 }
\ No newline at end of file
diff --git a/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/DoPing.java b/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/DoPing.java
index f29fa01..731dad2 100644
--- a/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/DoPing.java
+++ b/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/DoPing.java
@@ -14,16 +14,17 @@
 
 package net.shibboleth.sp.profile.impl;
 
-import java.io.IOException;
 import java.time.Instant;
 
 import javax.annotation.Nonnull;
 
+import org.opensaml.profile.action.ActionSupport;
 import org.opensaml.profile.action.EventIds;
 import org.opensaml.profile.context.ProfileRequestContext;
 
 import net.shibboleth.idp.profile.AbstractProfileAction;
-import net.shibboleth.idp.profile.ActionSupport;
+import net.shibboleth.sp.context.AgentRequestContext;
+import net.shibboleth.sp.ddf.DDF;
 
 /**
  * Simple action for testing.
@@ -33,11 +34,12 @@ public class DoPing extends AbstractProfileAction {
     /** {@inheritDoc} */
     @Override
     protected void doExecute(@Nonnull ProfileRequestContext profileRequestContext) {
-        
-        try {
-            ensureHttpServletResponse().getWriter().println(Instant.now().getEpochSecond());
-        } catch (final IOException e) {
-            ActionSupport.buildEvent(profileRequestContext, EventIds.IO_ERROR);
+
+        final DDF out = profileRequestContext.ensureSubcontext(AgentRequestContext.class).getOutput();
+        if (out != null) {
+            out.longinteger(Instant.now().getEpochSecond());
+        } else {
+            ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MESSAGE);
         }
     }
     
diff --git a/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/PrepareAgentErrorResponse.java b/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/PrepareAgentErrorResponse.java
new file mode 100644
index 0000000..f9f765a
--- /dev/null
+++ b/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/PrepareAgentErrorResponse.java
@@ -0,0 +1,121 @@
+/*
+ * 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.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.EventContext;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.profile.context.navigate.CurrentOrPreviousEventLookup;
+import org.slf4j.Logger;
+
+import net.shibboleth.idp.profile.AbstractProfileAction;
+import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.primitive.LoggerFactory;
+import net.shibboleth.sp.context.AgentRequestContext;
+import net.shibboleth.sp.ddf.DDF;
+
+/**
+ * A profile action to encode the current event (typically an error) into an agent response in a
+ * {@link AgentRequestContext}.
+ * 
+ * @event {@link EventIds#PROCEED_EVENT_ID}
+ * @event {@link EventIds#INVALID_PROFILE_CTX}
+ * @pre <pre>AgentRequestContext.getOutput() != null</pre>
+ */
+public class PrepareAgentErrorResponse extends AbstractProfileAction {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(PrepareAgentErrorResponse.class);
+    
+    /** Lookup or creation strategy for {@link AgentRequestContext}. */
+    @Nonnull private Function<ProfileRequestContext,AgentRequestContext> agentRequestContextCreationStrategy;
+
+    /** Strategy function for access to {@link EventContext} to check. */
+    @Nonnull private Function<ProfileRequestContext,EventContext> eventContextLookupStrategy;
+    
+    /** Cached context containing the output message. */
+    @NonnullBeforeExec private DDF outputMessage;
+    
+    /** Constructor. */
+    public PrepareAgentErrorResponse() {
+        agentRequestContextCreationStrategy = new ChildContextLookup<>(AgentRequestContext.class, true);
+        eventContextLookupStrategy = new CurrentOrPreviousEventLookup();
+    }
+    
+    /**
+     * Sets the lookup or creation strategy for the {@link AgentRequestContext}.
+     * 
+     * @param strategy lookup strategy
+     */
+    public void setAgentRequestContextCreationStrategy(
+            @Nonnull final Function<ProfileRequestContext,AgentRequestContext> strategy) {
+        checkSetterPreconditions();
+        
+        agentRequestContextCreationStrategy = Constraint.isNotNull(strategy,
+                "AgentRequestContext lookup/creation strategy cannot be null");
+    }
+
+    /**
+     * Set lookup strategy for {@link EventContext} to check.
+     * 
+     * @param strategy  lookup strategy
+     */
+    public void setEventContextLookupStrategy(@Nonnull final Function<ProfileRequestContext,EventContext> strategy) {
+        checkSetterPreconditions();
+        
+        eventContextLookupStrategy = Constraint.isNotNull(strategy, "EventContext lookup strategy cannot be null");
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+        
+        if (!super.doPreExecute(profileRequestContext)) {
+            return false;
+        }
+        
+        final AgentRequestContext agentRequestContext = agentRequestContextCreationStrategy.apply(profileRequestContext);
+        if (agentRequestContext == null) {
+            log.error("{} No AgentRequestContext created", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
+            return false;
+        }
+        
+        outputMessage = new DDF();
+        agentRequestContext.setOutput(outputMessage);
+        
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+        final EventContext eventCtx = eventContextLookupStrategy.apply(profileRequestContext);
+        final Object event = eventCtx != null ? eventCtx.getEvent() : null;
+        if (event != null) {
+            final String eventString = event.toString();
+            outputMessage.structure().addmember("result").string(eventString);
+        }
+        
+    }
+
+}
\ 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