[java-plugin-shibd] branch dev/StateMgmtWIP updated: Fixes, adjust unit tests.

Codeberg noreply at shibboleth.net
Mon Apr 27 16:46:23 UTC 2026


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

codeberg pushed a commit to branch dev/StateMgmtWIP
in repository java-plugin-shibd.

View the commit online:
https://codeberg.org/Shibboleth/java-plugin-shibd/commit/4fc1ca116d8c6712d171cff1d55aadd560a4c2b2

The following commit(s) were added to refs/heads/dev/StateMgmtWIP by this push:
     new 4fc1ca1  Fixes, adjust unit tests.
4fc1ca1 is described below

commit 4fc1ca116d8c6712d171cff1d55aadd560a4c2b2
Author: Scott Cantor <scott at restingparrotsoftware.com>
AuthorDate: Mon Apr 27 12:44:11 2026 -0400

    Fixes, adjust unit tests.
---
 .../sp/profile/impl/PreservePostData.java          | 12 +++-
 .../sp/profile/impl/IssueDiscoveryRequestTest.java | 11 ++--
 .../sp/profile/impl/PreservePostDataTest.java      | 68 ++++++++++++++++++++--
 3 files changed, 79 insertions(+), 12 deletions(-)

diff --git a/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/PreservePostData.java b/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/PreservePostData.java
index ad37101..5a523d1 100644
--- a/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/PreservePostData.java
+++ b/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/PreservePostData.java
@@ -57,6 +57,12 @@ import net.shibboleth.sp.profile.SPConstants;
  * Action that detects submitted form data, and when permitted, stores it in a {@link StorageService} and
  * issues a cookie associated with the active state token to preserve a pointer to the data for recovery.
  * 
+ * <p>The state token to bind to is collected from a {@link StateDataContext} accessed via lookup strategy.</p>
+ * 
+ * <p>The action also has support for detecting data previously preserved by this action by checking for
+ * a {@link SPConstants#STATE} member in the input message and using that to recover the data and clean up
+ * that record so it can be re-associated with the new state token found in the context.</p>
+ * 
  * @event {@link EventIds#PROCEED_EVENT_ID}
  * @event {@link EventIds#INVALID_MESSAGE}
  * @event {@link EventIds#INVALID_PROFILE_CTX}
@@ -369,15 +375,15 @@ public class PreservePostData extends AbstractApplicationAction {
                     agentRequestContext.getRemotedHttpServletResponse());
          
             // Check for cookie to get storage key.
-            final String key = cookieManager.getCookieValue(cookiePrefix + stateToken, null);
+            final String key = cookieManager.getCookieValue(cookiePrefix + previousStateToken, null);
             if (key == null) {
                 log.debug("{} No recovery cookie for state token {}, skipping POST recovery", getLogPrefix(),
-                        stateToken);
+                        previousStateToken);
                 return null;
             }
             
             // Unset the cookie.
-            cookieManager.unsetCookie(cookiePrefix + stateToken);
+            cookieManager.unsetCookie(cookiePrefix + previousStateToken);
             
             // Try and read/delete the storage record.
             
diff --git a/sp-server-impl/src/test/java/net/shibboleth/sp/profile/impl/IssueDiscoveryRequestTest.java b/sp-server-impl/src/test/java/net/shibboleth/sp/profile/impl/IssueDiscoveryRequestTest.java
index 089ed29..e809910 100644
--- a/sp-server-impl/src/test/java/net/shibboleth/sp/profile/impl/IssueDiscoveryRequestTest.java
+++ b/sp-server-impl/src/test/java/net/shibboleth/sp/profile/impl/IssueDiscoveryRequestTest.java
@@ -33,11 +33,11 @@ import net.shibboleth.idp.profile.testing.ActionTestingSupport;
 import net.shibboleth.profile.relyingparty.BasicRelyingPartyConfiguration;
 import net.shibboleth.shared.annotation.constraint.NotEmpty;
 import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.sp.context.StateDataContext;
 import net.shibboleth.sp.ddf.DDF;
 import net.shibboleth.sp.messaging.RemotedHttpServletResponse;
 import net.shibboleth.sp.messaging.impl.RemotedlHttpServletResponseSupplier;
 import net.shibboleth.sp.profile.InitiatorConstants;
-import net.shibboleth.sp.profile.SPConstants;
 
 /**
  * Unit test for {@link IssueDiscoveryRequest} action.
@@ -155,10 +155,12 @@ public class IssueDiscoveryRequestTest extends BaseApplicationActionTest {
     @Test
     public void testState() throws ComponentInitializationException {
         final DDF input = new DDF(null).structure();
-        input.addmember(SPConstants.STATE).string("foo bar");
         input.addmember(InitiatorConstants.DISCOVERY_RETURN_URL).string(escaper.escape("https://sp.example.org/handler?DS=1"));
         arc.setInput(input);
 
+        // Populate state for action.
+        prc.ensureSubcontext(StateDataContext.class).setStateToken("foo bar");
+        
         final Event event = action.execute(src);
         ActionTestingSupport.assertProceedEvent(event);
         
@@ -170,15 +172,16 @@ public class IssueDiscoveryRequestTest extends BaseApplicationActionTest {
         Assert.assertEquals(redirect, buildDiscoveryURL(TEST_DISCOVERY_URL, TEST_RESPONSE_URL, "foo bar", false));
     }
 
-
     @Test
     public void testPassive() throws ComponentInitializationException {
         final DDF input = new DDF(null).structure();
-        input.addmember(SPConstants.STATE).string("foo bar");
         input.addmember(InitiatorConstants.DISCOVERY_RETURN_URL).string(escaper.escape("https://sp.example.org/handler?DS=1"));
         input.addmember("prompt").string("none");
         arc.setInput(input);
 
+        // Populate state for action.
+        prc.ensureSubcontext(StateDataContext.class).setStateToken("foo bar");
+        
         final Event event = action.execute(src);
         ActionTestingSupport.assertProceedEvent(event);
         
diff --git a/sp-server-impl/src/test/java/net/shibboleth/sp/profile/impl/PreservePostDataTest.java b/sp-server-impl/src/test/java/net/shibboleth/sp/profile/impl/PreservePostDataTest.java
index 2fd2828..32fb36c 100644
--- a/sp-server-impl/src/test/java/net/shibboleth/sp/profile/impl/PreservePostDataTest.java
+++ b/sp-server-impl/src/test/java/net/shibboleth/sp/profile/impl/PreservePostDataTest.java
@@ -33,12 +33,16 @@ import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import jakarta.servlet.http.Cookie;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
 import net.shibboleth.idp.profile.testing.ActionTestingSupport;
 import net.shibboleth.shared.annotation.constraint.NotEmpty;
 import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.shared.logic.Constraint;
 import net.shibboleth.shared.net.CookieManager;
 import net.shibboleth.shared.net.CookieManager.SameSiteValue;
 import net.shibboleth.shared.primitive.NonnullSupplier;
+import net.shibboleth.sp.context.StateDataContext;
 import net.shibboleth.sp.ddf.DDF;
 import net.shibboleth.sp.messaging.RemotedHttpServletRequest;
 import net.shibboleth.sp.profile.SPConstants;
@@ -75,8 +79,16 @@ public class PreservePostDataTest extends BaseApplicationActionTest {
         response = new MockHttpServletResponse();
         
         cookieManager = new CookieManager();
-        cookieManager.setHttpServletRequestSupplier(NonnullSupplier.of(request));
-        cookieManager.setHttpServletResponseSupplier(NonnullSupplier.of(response));
+        cookieManager.setHttpServletRequestSupplier(new NonnullSupplier<HttpServletRequest>() {
+            @Nonnull public HttpServletRequest get() {
+                return request;
+            }
+        });
+        cookieManager.setHttpServletResponseSupplier(new NonnullSupplier<HttpServletResponse>() {
+            @Nonnull public HttpServletResponse get() {
+                return response;
+            }
+        });
         cookieManager.setCookieLimit(10);
         cookieManager.setSameSite(SameSiteValue.None);
         cookieManager.setMaxAge(-1);
@@ -94,7 +106,6 @@ public class PreservePostDataTest extends BaseApplicationActionTest {
         action.initialize();
 
         input = new DDF(null).structure();
-        input.addmember(SPConstants.STATE).string(TEST_STATE);
         final DDF http = input.addmember(RemotedHttpServletRequest.STRUCTURE_NAME);
         http.addmember(RemotedHttpServletRequest.CONTENT_TYPE).string("application/x-www-form-urlencoded");
         http.addmember(RemotedHttpServletRequest.BODY).unsafe_string(TEST_DATA.getBytes());
@@ -146,13 +157,15 @@ public class PreservePostDataTest extends BaseApplicationActionTest {
     
     @Test
     public void testNoStateToken() {
-        input.addmember(SPConstants.STATE).remove();
         final Event event = action.execute(src);
         ActionTestingSupport.assertEvent(event, EventIds.INVALID_MESSAGE);
     }
     
     @Test
     public void testSuccess() throws IOException {
+        
+        prc.ensureSubcontext(StateDataContext.class).setStateToken(TEST_STATE);
+        
         final Event event = action.execute(src);
         ActionTestingSupport.assertProceedEvent(event);
         Assert.assertEquals(response.getCookies().length, 1);
@@ -172,7 +185,7 @@ public class PreservePostDataTest extends BaseApplicationActionTest {
         
     @Test
     public void testPurge() throws ComponentInitializationException, InterruptedException {
-        
+
         final List<Cookie> cookies = new ArrayList<>(12);
         for (int i = 0; i < 12; ++i) {
             cookies.add(new Cookie(PreservePostData.DEFAULT_COOKIE_PREFIX + i, "foo" + i));
@@ -180,10 +193,55 @@ public class PreservePostDataTest extends BaseApplicationActionTest {
         }
         request.setCookies(cookies.toArray(new Cookie[12]));
         
+        prc.ensureSubcontext(StateDataContext.class).setStateToken(TEST_STATE);
+        
         final Event event = action.execute(src);
         ActionTestingSupport.assertProceedEvent(event);
         
         Assert.assertEquals(response.getCookies().length, 3);
     }
 
+    @Test
+    public void testPreviousRecovery() throws IOException {
+        
+        // First run with inline state to preserve data.
+        testSuccess();
+        
+        Cookie cookie = response.getCookie(PreservePostData.DEFAULT_COOKIE_PREFIX + TEST_STATE);
+        assert cookie != null;
+        Assert.assertEquals(cookie.getMaxAge(), -1);
+        
+        final String originalKey = cookie.getValue();
+
+        // Now re-run with that state supplied via input message and mutate new state.
+
+        Constraint.isNotNull(arc.getInput(), "Input was null").addmember(SPConstants.STATE).string(TEST_STATE);
+        Constraint.isNotNull(arc.getInput(), "Input was null").getmember(RemotedHttpServletRequest.STRUCTURE_NAME).destroy();
+        prc.ensureSubcontext(StateDataContext.class).setStateToken(TEST_STATE + "2");
+        
+        // Transfer cookies from response to new request.
+        request = new MockHttpServletRequest();
+        request.setCookies(response.getCookies());
+        response = new MockHttpServletResponse();
+        
+        final Event event = action.execute(src);
+        ActionTestingSupport.assertProceedEvent(event);
+        Assert.assertEquals(response.getCookies().length, 2);
+        
+        cookie = response.getCookie(PreservePostData.DEFAULT_COOKIE_PREFIX + TEST_STATE + "2");
+        assert cookie != null;
+        Assert.assertEquals(cookie.getMaxAge(), -1);
+        Assert.assertEquals(cookie.getAttribute("SameSite"), SameSiteValue.None.getValue());
+        
+        final String key = cookie.getValue();
+        
+        final StorageRecord<String> record = storageService.read(agent.getId() + ".PostData", key);
+        assert record != null;
+        Assert.assertEquals(record.getVersion(), 1);
+        Assert.assertEquals(record.getValue(), TEST_DATA);
+        
+        // Make sure original record is gone.
+        Assert.assertNull(storageService.read(agent.getId() + ".PostData", originalKey));
+    }
+    
 }
\ 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