[java-plugin-shibd] branch main updated: Add prefixing capability to state manager.
Codeberg
noreply at shibboleth.net
Mon Jun 1 18:42:10 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/37cc5fb7fab4e29ab1d4810c4cc5dc84be3038a5
The following commit(s) were added to refs/heads/main by this push:
new 37cc5fb Add prefixing capability to state manager.
37cc5fb is described below
commit 37cc5fb7fab4e29ab1d4810c4cc5dc84be3038a5
Author: Scott Cantor <scott at restingparrotsoftware.com>
AuthorDate: Mon Jun 1 14:41:34 2026 -0400
Add prefixing capability to state manager.
---
.../shibboleth/sp/state/AbstractStateManager.java | 28 +++++++++--
.../sp/state/impl/PassthroughStateManagerTest.java | 55 ++++++++++++++++++++--
2 files changed, 76 insertions(+), 7 deletions(-)
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/state/AbstractStateManager.java b/sp-server-api/src/main/java/net/shibboleth/sp/state/AbstractStateManager.java
index 26ccc79..8d9c102 100644
--- a/sp-server-api/src/main/java/net/shibboleth/sp/state/AbstractStateManager.java
+++ b/sp-server-api/src/main/java/net/shibboleth/sp/state/AbstractStateManager.java
@@ -35,6 +35,7 @@ import net.shibboleth.shared.component.ComponentInitializationException;
import net.shibboleth.shared.logic.Constraint;
import net.shibboleth.shared.primitive.LoggerFactory;
import net.shibboleth.shared.primitive.NonnullSupplier;
+import net.shibboleth.shared.primitive.StringSupport;
import net.shibboleth.shared.security.DataSealer;
import net.shibboleth.shared.security.DataSealerException;
import net.shibboleth.shared.security.IdentifierGenerationStrategy;
@@ -56,7 +57,10 @@ public abstract class AbstractStateManager extends AbstractIdentifiableInitializ
/** Identifier generation. */
@NonnullAfterInit private IdentifierGenerationStrategy identifierStrategy;
-
+
+ /** Optional prefix to attach to token. */
+ @Nonnull private String prefix;
+
/** JSON object mapper. */
@NonnullAfterInit private ObjectMapper objectMapper;
@@ -74,6 +78,7 @@ public abstract class AbstractStateManager extends AbstractIdentifiableInitializ
*/
@SuppressWarnings("null")
public AbstractStateManager() {
+ prefix = "";
expiration = Duration.ofMinutes(30);
}
@@ -108,6 +113,16 @@ public abstract class AbstractStateManager extends AbstractIdentifiableInitializ
identifierStrategy = Constraint.isNotNull(strategy, "IdentifierGenerationStrategy cannot be null");
}
+
+ /**
+ * Sets optional prefix to apply to the token.
+ *
+ * @param s prefix string
+ */
+ public void setPrefix(@Nullable final String s) {
+ final String trimmed = StringSupport.trimOrNull(s);
+ prefix = trimmed != null ? trimmed : "";
+ }
/**
* Set the JSON {@link ObjectMapper} to use for serialization.
@@ -213,10 +228,10 @@ public abstract class AbstractStateManager extends AbstractIdentifiableInitializ
final DataSealer localDataSealer = dataSealer;
if (localDataSealer != null) {
log.debug("State data will be sealed before preservation");
- return doPreserve(agent, application, localDataSealer.wrap(serializedState,
+ return prefix + doPreserve(agent, application, localDataSealer.wrap(serializedState,
Instant.now().plus(expiration)), true);
} else {
- return doPreserve(agent, application, serializedState, false);
+ return prefix + doPreserve(agent, application, serializedState, false);
}
} catch (final JsonProcessingException | DataSealerException e) {
throw new IOException("Error preserving state", e);
@@ -227,6 +242,11 @@ public abstract class AbstractStateManager extends AbstractIdentifiableInitializ
@Nullable public <T extends StateData> T recoverFromStateToken(@Nonnull final Agent agent,
@Nonnull final Application application, @Nonnull @NotEmpty final String token,
@Nonnull final Class<T> type) throws IOException {
+
+ if (prefix.length() > 0 && !token.startsWith(prefix)) {
+ log.warn("State token '{}' did not start with expected prefix ({})", token, prefix);
+ return null;
+ }
final ReplayCache localCache = replayCache;
if (localCache != null) {
@@ -238,7 +258,7 @@ public abstract class AbstractStateManager extends AbstractIdentifiableInitializ
}
}
- String data = doRecover(agent, application, token, dataSealer != null);
+ String data = doRecover(agent, application, token.substring(prefix.length()), dataSealer != null);
if (data == null) {
return null;
}
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
index 3d7b792..4aeea7b 100644
--- 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
@@ -19,6 +19,7 @@ import java.nio.charset.StandardCharsets;
import java.time.Instant;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import org.springframework.core.io.ClassPathResource;
import org.testng.Assert;
@@ -88,7 +89,7 @@ public class PassthroughStateManagerTest extends BaseApplicationActionTest {
@Test
public void testSuccess() throws IOException, ComponentInitializationException {
- final var stateManager = getStateManager(false);
+ final var stateManager = getStateManager(false, null);
final StateData source = buildStateData();
@@ -101,7 +102,7 @@ public class PassthroughStateManagerTest extends BaseApplicationActionTest {
@Test
public void testSuccessSealed() throws IOException, ComponentInitializationException {
- final var stateManager = getStateManager(true);
+ final var stateManager = getStateManager(true, null);
final StateData source = buildStateData();
@@ -111,12 +112,60 @@ public class PassthroughStateManagerTest extends BaseApplicationActionTest {
final StateData recovered = stateManager.recoverFromStateToken(agent, application, token, StateData.class);
Assert.assertEquals(source, recovered);
}
+
+ @Test
+ public void testPrefixSuccess() throws IOException, ComponentInitializationException {
+ final var stateManager = getStateManager(false, "FOO:");
+
+ 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 testPrefixSuccessSealed() throws IOException, ComponentInitializationException {
+ final var stateManager = getStateManager(true, "FOO:");
+
+ 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(expectedExceptions=IOException.class)
+ public void testBadSealed() throws IOException, ComponentInitializationException {
+ final var stateManager = getStateManager(true, null);
+
+ stateManager.recoverFromStateToken(agent, application, "bad", StateData.class);
+ }
+
+ @Test
+ public void testBadPrefix() throws IOException, ComponentInitializationException {
+ final var stateManager = getStateManager(false, "FOO:");
+
+ final StateData source = buildStateData();
+
+ final String token = stateManager.preserveToStateToken(agent, application, source);
+ assert token != null;
+
+ final StateData recovered = stateManager.recoverFromStateToken(agent, application, token.substring(1), StateData.class);
+ Assert.assertNull(recovered);
+ }
+
- @Nonnull private PassthroughStateManager getStateManager(final boolean sealed)
+ @Nonnull private PassthroughStateManager getStateManager(final boolean sealed, @Nullable final String prefix)
throws ComponentInitializationException {
final var stateManager = new PassthroughStateManager();
stateManager.setId("test");
+ stateManager.setPrefix(prefix);
if (sealed) {
stateManager.setDataSealer(sealer);
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list