[java-plugin-shibd] branch main updated: Add action to create state tokens for protocols.

Scott Cantor cantor.2 at osu.edu
Wed Aug 21 16:08:07 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=435d848fb237492c79f0974ae49ccdfce7438438

The following commit(s) were added to refs/heads/main by this push:
     new 435d848  Add action to create state tokens for protocols.
435d848 is described below

commit 435d848fb237492c79f0974ae49ccdfce7438438
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Wed Aug 21 12:08:04 2024 -0400

    Add action to create state tokens for protocols.
---
 .../session-initiator/session-initiator-beans.xml  |   4 +
 .../session-initiator/session-initiator-flow.xml   |   1 +
 .../sp/messaging/RemotedHttpServletRequest.java    |  19 ++-
 .../sp/messaging/RemotedHttpServletResponse.java   |   3 +
 .../shibboleth/sp/profile/impl/EncodeMessage.java  |   3 +-
 .../sp/profile/impl/MapResourceToStateToken.java   | 149 +++++++++++++++++++++
 6 files changed, 175 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
index 5a13abe..90b2971 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
@@ -10,4 +10,8 @@
     <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" />
+    
 </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
index de61b5a..bd2022c 100644
--- 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
@@ -16,6 +16,7 @@
         <on-entry>
             <evaluate expression="opensamlProfileRequestContext.ensureSubcontext(T(net.shibboleth.sp.context.AgentRequestContext)).getApplication().getSessionInitiators(opensamlProfileRequestContext).iterator()" result="flowScope.SessionInitiatorIterator" />
         </on-entry>
+        <evaluate expression="MapResourceToStateToken" />
         <evaluate expression="'proceed'" />
         
         <!-- Branch to child flow for actual work. -->
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/messaging/RemotedHttpServletRequest.java b/sp-server-api/src/main/java/net/shibboleth/sp/messaging/RemotedHttpServletRequest.java
index 4714f0c..831c81f 100644
--- a/sp-server-api/src/main/java/net/shibboleth/sp/messaging/RemotedHttpServletRequest.java
+++ b/sp-server-api/src/main/java/net/shibboleth/sp/messaging/RemotedHttpServletRequest.java
@@ -58,6 +58,7 @@ import jakarta.servlet.http.HttpSession;
 import jakarta.servlet.http.HttpUpgradeHandler;
 import jakarta.servlet.http.Part;
 import net.shibboleth.shared.annotation.constraint.NonnullElements;
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
 import net.shibboleth.shared.collection.CollectionSupport;
 import net.shibboleth.shared.collection.Pair;
 import net.shibboleth.shared.logic.Constraint;
@@ -79,6 +80,18 @@ import com.google.common.collect.Multimap;
 @NotThreadSafe
 public class RemotedHttpServletRequest implements HttpServletRequest {
 
+    /** Field holding name of {@link DDF} structure containing request. */
+    @Nonnull @NotEmpty public static final String STRUCTURE_NAME = "http";
+
+    /** Field holding request URL (full URL without query string). */
+    @Nonnull @NotEmpty public static final String REQUEST_URL = "url";
+
+    /** Field holding request URI (path without query string). */
+    @Nonnull @NotEmpty public static final String REQUEST_URI = "uri";
+
+    /** Field holding query string. */
+    @Nonnull @NotEmpty public static final String QUERY_STRING = "query";
+
     /** Empty byte array for empty bodies. */
     @Nonnull private static final byte[] EMPTY_BODY = new byte[0];
     
@@ -487,7 +500,7 @@ public class RemotedHttpServletRequest implements HttpServletRequest {
 
     /** {@inheritDoc} */
     public String getQueryString() {
-        return obj.getmember("query").string();
+        return obj.getmember(QUERY_STRING).string();
     }
 
     /** {@inheritDoc} */
@@ -512,12 +525,12 @@ public class RemotedHttpServletRequest implements HttpServletRequest {
 
     /** {@inheritDoc} */
     public String getRequestURI() {
-        return decodeUnsafeString(obj.getmember("uri").unsafe_string());
+        return decodeUnsafeString(obj.getmember(REQUEST_URI).unsafe_string());
     }
 
     /** {@inheritDoc} */
     public StringBuffer getRequestURL() {
-        final String url = decodeUnsafeString(obj.getmember("url").unsafe_string());
+        final String url = decodeUnsafeString(obj.getmember(REQUEST_URL).unsafe_string());
         return new StringBuffer(url != null ? url : "");
     }
 
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/messaging/RemotedHttpServletResponse.java b/sp-server-api/src/main/java/net/shibboleth/sp/messaging/RemotedHttpServletResponse.java
index a383f21..bada40d 100644
--- a/sp-server-api/src/main/java/net/shibboleth/sp/messaging/RemotedHttpServletResponse.java
+++ b/sp-server-api/src/main/java/net/shibboleth/sp/messaging/RemotedHttpServletResponse.java
@@ -46,6 +46,9 @@ import net.shibboleth.sp.ddf.DDF;
 @NotThreadSafe
 public class RemotedHttpServletResponse implements HttpServletResponse {
 
+    /** Field holding name of {@link DDF} structure containing response. */
+    @Nonnull @NotEmpty public static final String STRUCTURE_NAME = "http";
+    
     /** Underlying object for remoted data. */
     @Nonnull private final DDF obj;
     
diff --git a/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/EncodeMessage.java b/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/EncodeMessage.java
index 7b84503..2186a81 100644
--- a/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/EncodeMessage.java
+++ b/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/EncodeMessage.java
@@ -54,7 +54,8 @@ public class EncodeMessage extends org.opensaml.profile.action.impl.EncodeMessag
     protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
         final AgentRequestContext agentRequestContext =
                 profileRequestContext.ensureSubcontext(AgentRequestContext.class);
-        if (createOutputObjects) {
+        
+        if (createOutputObjects && agentRequestContext.getOutput() == null) {
             final DDF output = new DDF(null);
             agentRequestContext.setOutput(output);
             agentRequestContext.setRemotedHttpServletResponse(
diff --git a/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/MapResourceToStateToken.java b/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/MapResourceToStateToken.java
new file mode 100644
index 0000000..71948a2
--- /dev/null
+++ b/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/MapResourceToStateToken.java
@@ -0,0 +1,149 @@
+/*
+ * 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.io.IOException;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+
+import com.google.common.primitives.Bytes;
+
+import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+import net.shibboleth.shared.primitive.LoggerFactory;
+import net.shibboleth.sp.context.AgentRequestContext;
+import net.shibboleth.sp.ddf.DDF;
+import net.shibboleth.sp.messaging.RemotedHttpServletRequest;
+import net.shibboleth.sp.messaging.RemotedHttpServletResponse;
+import net.shibboleth.sp.messaging.impl.RemotedHttpServletRequestResponseContext;
+import net.shibboleth.sp.profile.AbstractApplicationAction;
+import net.shibboleth.sp.profile.InitiatorConstants;
+
+/**
+ * Action that maps a requested resource URL into a state token to be consumed
+ * by protocol request generation logic.
+ */
+public class MapResourceToStateToken extends AbstractApplicationAction {
+
+    /** Static byte array with query string separator. */
+    @Nonnull @NotEmpty private static byte[] QUERY_SEPERATOR = {'?'};
+    
+    /** Class logger. */
+    @Nonnull private Logger log = LoggerFactory.getLogger(MapResourceToStateToken.class);
+    
+    /** Whether to create the output objects into which the message will be encoded. */
+    private boolean createOutputObjects;
+    
+    /** Cached request context. */
+    @NonnullBeforeExec private AgentRequestContext agentRequestContext;
+
+    /** Agent input. */
+    @NonnullBeforeExec private DDF input;
+
+    /** Remoted request structure. */
+    @NonnullBeforeExec private DDF httpRequest;
+
+    /**
+     * Sets whether to create the output {@link DDF} and {@link RemotedHttpServletResponse}.
+     * 
+     * <p>Defaults to false.</p>
+     * 
+     * @param flag flag to set
+     */
+    public void setCreateOutputObjects(final boolean flag) {
+        checkSetterPreconditions();
+        
+        createOutputObjects = flag;
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+        if (!super.doPreExecute(profileRequestContext)) {
+            return false;
+        }
+        
+        agentRequestContext = profileRequestContext.ensureSubcontext(AgentRequestContext.class);
+        
+        input = agentRequestContext.getInput();
+        if (input == null) {
+            log.debug("{} Input message was absent", getLogPrefix());
+            return false;
+        }
+        
+        if (input.getmember(InitiatorConstants.STATE).isstring()) {
+            log.debug("{} Input message already contains {} parameter", getLogPrefix(), InitiatorConstants.STATE);
+            return false;
+        }
+        
+        httpRequest = input.getmember(RemotedHttpServletRequest.STRUCTURE_NAME);
+        if (!httpRequest.isstruct()) {
+            log.debug("{} Input message did not contain remoted HTTP request", getLogPrefix());
+            return false;
+        }
+        
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+        
+        if (createOutputObjects && agentRequestContext.getOutput() == null) {
+            final DDF output = new DDF(null);
+            agentRequestContext.setOutput(output);
+            agentRequestContext.setRemotedHttpServletResponse(new RemotedHttpServletResponse(
+                    output.structure().addmember(RemotedHttpServletResponse.STRUCTURE_NAME)));
+        }
+
+        // We have to preserve the full URL, so we don't get to just approximate it and assume
+        // a particular String encoding. So this requires operating on the underlying byte arrays from
+        // the agent, to preserve them exactly.
+        
+        byte[] requestURL = httpRequest.getmember(RemotedHttpServletRequest.REQUEST_URL).unsafe_string();
+        if (requestURL == null || requestURL.length == 0) {
+            log.warn("{} Remoted HTTP request did not contain request URL", getLogPrefix());
+            return;
+        }
+        
+        // Java method to get URL doesn't include query!?!
+        final byte[] query = httpRequest.getmember(RemotedHttpServletRequest.QUERY_STRING).unsafe_string();
+        if (query != null && query.length > 0) {
+            requestURL = Bytes.concat(requestURL, QUERY_SEPERATOR, query);
+            assert requestURL != null;
+        }
+        
+        // We do the crazy stuff to accomodate cookie-backed state management.
+        try {
+            RemotedHttpServletRequestResponseContext.loadCurrent(agentRequestContext.getRemotedHttpServletRequest(),
+                    agentRequestContext.getRemotedHttpServletResponse());
+            
+            try {
+                final String token = ensureApplication().getStateTokenManager().preserveToStateToken(
+                        ensureAgent(), ensureApplication(), requestURL);
+                input.addmember(InitiatorConstants.STATE).string(token);
+                log.debug("{} Requested resource preserved to state token: {}", getLogPrefix(), token);
+            } catch (final IOException e) {
+                log.warn("{} Exception preserving requested resource to state token", getLogPrefix(), e);
+            }
+        } finally {
+            RemotedHttpServletRequestResponseContext.clearCurrent();
+        }
+    }
+
+}
\ 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