[java-plugin-shibd] branch main updated: Add a passthrough StateManager for "unusual" cases.

Codeberg noreply at shibboleth.net
Fri May 22 17:12:32 UTC 2026


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

codeberg pushed a commit to branch main
in repository java-plugin-shibd.

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

The following commit(s) were added to refs/heads/main by this push:
     new aed9a3d  Add a passthrough StateManager for "unusual" cases.
aed9a3d is described below

commit aed9a3da5fd0055145be7b96463d31a525a3b883
Author: Scott Cantor <scott at restingparrotsoftware.com>
AuthorDate: Fri May 22 13:12:15 2026 -0400

    Add a passthrough StateManager for "unusual" cases.
---
 .../sp/state/impl/PassthroughStateManager.java     |  73 +++++++++++
 .../sp/state/impl/PassthroughStateManagerTest.java | 142 +++++++++++++++++++++
 2 files changed, 215 insertions(+)

diff --git a/sp-server-impl/src/main/java/net/shibboleth/sp/state/impl/PassthroughStateManager.java b/sp-server-impl/src/main/java/net/shibboleth/sp/state/impl/PassthroughStateManager.java
new file mode 100644
index 0000000..9855ed6
--- /dev/null
+++ b/sp-server-impl/src/main/java/net/shibboleth/sp/state/impl/PassthroughStateManager.java
@@ -0,0 +1,73 @@
+/*
+ * 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.state.impl;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+import net.shibboleth.shared.codec.Base64Support;
+import net.shibboleth.shared.codec.DecodingException;
+import net.shibboleth.shared.codec.EncodingException;
+import net.shibboleth.sp.Agent;
+import net.shibboleth.sp.Application;
+import net.shibboleth.sp.state.AbstractStateManager;
+import net.shibboleth.sp.state.StateManager;
+
+/**
+ * {@link StateManager} implemented by returning data encoded as is.
+ */
+public class PassthroughStateManager extends AbstractStateManager {
+    
+    /** Constructor. */
+    public PassthroughStateManager() {
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    @Nonnull protected String doPreserve(@Nonnull final Agent agent, @Nonnull final Application application,
+            @Nonnull final String data, final boolean sealed) throws IOException {
+        
+        if (sealed) {
+            return data;
+        } else {
+            try {
+                return Base64Support.encode(data.getBytes(StandardCharsets.UTF_8), false);
+            } catch (final EncodingException e) {
+                throw new IOException(e);
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @Nullable protected String doRecover(@Nonnull final Agent agent, @Nonnull final Application application,
+            @Nonnull @NotEmpty final String stateToken, final boolean sealed) throws IOException {
+
+        if (sealed) {
+            return stateToken;
+        } else {
+            try {
+                return new String(Base64Support.decode(stateToken), StandardCharsets.UTF_8);
+            } catch (final DecodingException e) {
+                throw new IOException(e);
+            }
+        }
+    }
+    
+}
\ No newline at end of file
diff --git a/sp-server-impl/src/test/java/net/shibboleth/sp/state/impl/PassthroughStateManagerTest.java b/sp-server-impl/src/test/java/net/shibboleth/sp/state/impl/PassthroughStateManagerTest.java
new file mode 100644
index 0000000..3d7b792
--- /dev/null
+++ b/sp-server-impl/src/test/java/net/shibboleth/sp/state/impl/PassthroughStateManagerTest.java
@@ -0,0 +1,142 @@
+/*
+ * 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.state.impl;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.time.Instant;
+
+import javax.annotation.Nonnull;
+
+import org.springframework.core.io.ClassPathResource;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.shared.resource.Resource;
+import net.shibboleth.shared.security.DataSealer;
+import net.shibboleth.shared.security.impl.BasicKeystoreKeyStrategy;
+import net.shibboleth.sp.profile.impl.BaseApplicationActionTest;
+import net.shibboleth.sp.state.StateData;
+import net.shibboleth.sp.testing.TestResourceConverter;
+
+/**
+ * Unit tests for {@link PassthroughStateManager}.
+ */
+ at SuppressWarnings("javadoc")
+public class PassthroughStateManagerTest extends BaseApplicationActionTest {
+
+    @Nonnull @NotEmpty private static final String TEST_ISSUER = "https://sp.example.org";
+
+    @Nonnull @NotEmpty private static final String TEST_AUTHORITY = "https://idp.example.org";
+
+    // Unicode character at the end of that filename...
+    @Nonnull @NotEmpty private static final String TEST_RESOURCE = "https://sp.example.org/secure/foo☯.cgi";
+    
+    private Resource keystoreResource;
+    private Resource versionResource;
+    
+    private DataSealer sealer;
+    
+    @BeforeClass
+    public void setUp() throws ComponentInitializationException {
+        ClassPathResource resource =
+                new ClassPathResource("/net/shibboleth/sp/profile/impl/SealerKeyStore.jks");
+        Assert.assertTrue(resource.exists());
+        keystoreResource = TestResourceConverter.of(resource);
+
+        resource =
+                new ClassPathResource("/net/shibboleth/sp/profile/impl/SealerKeyStore.kver");
+        Assert.assertTrue(resource.exists());
+        versionResource = TestResourceConverter.of(resource);
+
+        final BasicKeystoreKeyStrategy strategy = new BasicKeystoreKeyStrategy();
+        strategy.setKeyAlias("secret");
+        strategy.setKeyPassword("kpassword");
+        strategy.setKeystorePassword("password");
+        strategy.setKeystoreResource(keystoreResource);
+        strategy.setKeyVersionResource(versionResource);
+        strategy.initialize();
+        
+        sealer = new DataSealer();
+        sealer.setKeyStrategy(strategy);
+        sealer.initialize();
+    }
+    
+    @AfterClass
+    public void tearDown() {
+        sealer.destroy();
+    }
+        
+    @Test
+    public void testSuccess() throws IOException, ComponentInitializationException {
+        final var stateManager = getStateManager(false);
+        
+        final StateData source = buildStateData();
+        
+        final String token = stateManager.preserveToStateToken(agent, application, source);
+        assert token != null;
+        
+        final StateData recovered = stateManager.recoverFromStateToken(agent, application, token, StateData.class);
+        Assert.assertEquals(source, recovered);
+    }
+
+    @Test
+    public void testSuccessSealed() throws IOException, ComponentInitializationException {
+        final var stateManager = getStateManager(true);
+        
+        final StateData source = buildStateData();
+        
+        final String token = stateManager.preserveToStateToken(agent, application, source);
+        assert token != null;
+        
+        final StateData recovered = stateManager.recoverFromStateToken(agent, application, token, StateData.class);
+        Assert.assertEquals(source, recovered);
+    }
+            
+    @Nonnull private PassthroughStateManager getStateManager(final boolean sealed)
+            throws ComponentInitializationException {
+        
+        final var stateManager = new PassthroughStateManager();
+        stateManager.setId("test");
+        
+        if (sealed) {
+            stateManager.setDataSealer(sealer);
+        }
+        
+        final ObjectMapper mapper = new ObjectMapper();
+        mapper.registerModule(new JavaTimeModule());
+        stateManager.setObjectMapper(mapper);
+        stateManager.initialize();
+        
+        return stateManager;
+    }
+    
+    @Nonnull private StateData buildStateData() {
+        final StateData data = new StateData();
+        data.setRequestTime(Instant.now());
+        data.setIssuer(TEST_ISSUER);
+        data.setAuthenticationAuthority(TEST_AUTHORITY);
+        data.setRawResource(TEST_RESOURCE.getBytes(StandardCharsets.UTF_8));
+        return data;
+    }
+    
+}
\ 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