[java-plugin-shibd-saml] 02/02: Add subflow-specific prefix to allow subflows to detect their tokens.
Codeberg
noreply at shibboleth.net
Tue May 26 18:50:58 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-saml.
View the commit online:
https://codeberg.org/Shibboleth/java-plugin-shibd-saml/commit/acba4e30521a4d6cc3b9277373a4434e7d92f426
commit acba4e30521a4d6cc3b9277373a4434e7d92f426
Author: Scott Cantor <scott at restingparrotsoftware.com>
AuthorDate: Tue May 26 14:41:37 2026 -0400
Add subflow-specific prefix to allow subflows to detect their tokens.
---
.../META-INF/net.shibboleth.idp/postconfig.xml | 150 +++++++++++++--------
.../flows/saml2/SAML2LogoutConsumerFlowTest.java | 36 ++++-
.../saml2/profile/impl/ProcessLogoutRequest.java | 5 +-
.../profile/impl/ProcessLogoutRequestTest.java | 8 +-
4 files changed, 137 insertions(+), 62 deletions(-)
diff --git a/sp-saml-conf-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml b/sp-saml-conf-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
index 6a77b33..0ad97fa 100644
--- a/sp-saml-conf-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
+++ b/sp-saml-conf-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
@@ -80,86 +80,118 @@
</bean>
<!-- Logout consumer descriptors for each SAML binding. -->
+
+ <bean id="shibboletg.sp.TokenParameterPrefixCondition"
+ class="net.shibboleth.sp.profile.context.logic.InputStartsWithPredicate"
+ c:_0="token" c:_1="SAML:" />
<bean p:id="sp/logout/consumer/saml2/redirect" class="net.shibboleth.sp.profile.BasicLogoutConsumerFlowDescriptor">
<property name="activationCondition">
- <bean class="net.shibboleth.sp.profile.context.logic.HttpServletRequestPredicate"
- p:httpServletRequestSupplier-ref="shibboleth.RemotedHttpServletRequestSupplier">
- <property name="httpServletRequestValidator">
- <bean class="net.shibboleth.shared.servlet.impl.ChainingHttpServletRequestValidator">
- <property name="validators">
- <list>
- <bean class="net.shibboleth.shared.servlet.impl.BasicHttpServletRequestMethodValidator"
- p:allowedMethods="GET" />
- <ref bean="shibboleth.HttpServletRequestValidator.BasicParams.SAML2.Redirect.RequestOrResponse" />
- </list>
- </property>
- </bean>
- </property>
+ <bean parent="shibboleth.Conditions.OR">
+ <constructor-arg>
+ <list>
+ <ref bean="shibboletg.sp.TokenParameterPrefixCondition" />
+ <bean class="net.shibboleth.sp.profile.context.logic.HttpServletRequestPredicate"
+ p:httpServletRequestSupplier-ref="shibboleth.RemotedHttpServletRequestSupplier">
+ <property name="httpServletRequestValidator">
+ <bean class="net.shibboleth.shared.servlet.impl.ChainingHttpServletRequestValidator">
+ <property name="validators">
+ <list>
+ <bean class="net.shibboleth.shared.servlet.impl.BasicHttpServletRequestMethodValidator"
+ p:allowedMethods="GET" />
+ <ref bean="shibboleth.HttpServletRequestValidator.BasicParams.SAML2.Redirect.RequestOrResponse" />
+ </list>
+ </property>
+ </bean>
+ </property>
+ </bean>
+ </list>
+ </constructor-arg>
</bean>
</property>
</bean>
<bean p:id="sp/logout/consumer/saml2/post" class="net.shibboleth.sp.profile.BasicLogoutConsumerFlowDescriptor">
<property name="activationCondition">
- <bean class="net.shibboleth.sp.profile.context.logic.HttpServletRequestPredicate"
- p:httpServletRequestSupplier-ref="shibboleth.RemotedHttpServletRequestSupplier">
- <property name="httpServletRequestValidator">
- <bean class="net.shibboleth.shared.servlet.impl.ChainingHttpServletRequestValidator">
- <property name="validators">
- <list>
- <bean class="net.shibboleth.shared.servlet.impl.BasicHttpServletRequestMethodValidator"
- p:allowedMethods="POST" />
- <bean class="net.shibboleth.shared.servlet.impl.BasicHttpServletRequestContentTypeValidator"
- p:allowedContentTypes="application/x-www-form-urlencoded" />
- <ref bean="shibboleth.HttpServletRequestValidator.BasicParams.SAML2.POST.RequestOrResponse" />
- </list>
- </property>
- </bean>
- </property>
+ <bean parent="shibboleth.Conditions.OR">
+ <constructor-arg>
+ <list>
+ <ref bean="shibboletg.sp.TokenParameterPrefixCondition" />
+ <bean class="net.shibboleth.sp.profile.context.logic.HttpServletRequestPredicate"
+ p:httpServletRequestSupplier-ref="shibboleth.RemotedHttpServletRequestSupplier">
+ <property name="httpServletRequestValidator">
+ <bean class="net.shibboleth.shared.servlet.impl.ChainingHttpServletRequestValidator">
+ <property name="validators">
+ <list>
+ <bean class="net.shibboleth.shared.servlet.impl.BasicHttpServletRequestMethodValidator"
+ p:allowedMethods="POST" />
+ <bean class="net.shibboleth.shared.servlet.impl.BasicHttpServletRequestContentTypeValidator"
+ p:allowedContentTypes="application/x-www-form-urlencoded" />
+ <ref bean="shibboleth.HttpServletRequestValidator.BasicParams.SAML2.POST.RequestOrResponse" />
+ </list>
+ </property>
+ </bean>
+ </property>
+ </bean>
+ </list>
+ </constructor-arg>
</bean>
</property>
</bean>
<bean p:id="sp/logout/consumer/saml2/post-simplesign" class="net.shibboleth.sp.profile.BasicLogoutConsumerFlowDescriptor">
<property name="activationCondition">
- <bean class="net.shibboleth.sp.profile.context.logic.HttpServletRequestPredicate"
- p:httpServletRequestSupplier-ref="shibboleth.RemotedHttpServletRequestSupplier">
- <property name="httpServletRequestValidator">
- <bean class="net.shibboleth.shared.servlet.impl.ChainingHttpServletRequestValidator">
- <property name="validators">
- <list>
- <bean class="net.shibboleth.shared.servlet.impl.BasicHttpServletRequestMethodValidator"
- p:allowedMethods="POST" />
- <bean class="net.shibboleth.shared.servlet.impl.BasicHttpServletRequestContentTypeValidator"
- p:allowedContentTypes="application/x-www-form-urlencoded" />
- <ref bean="shibboleth.HttpServletRequestValidator.BasicParams.SAML2.POSTSimpleSign.RequestOrResponse" />
- </list>
- </property>
- </bean>
- </property>
+ <bean parent="shibboleth.Conditions.OR">
+ <constructor-arg>
+ <list>
+ <ref bean="shibboletg.sp.TokenParameterPrefixCondition" />
+ <bean class="net.shibboleth.sp.profile.context.logic.HttpServletRequestPredicate"
+ p:httpServletRequestSupplier-ref="shibboleth.RemotedHttpServletRequestSupplier">
+ <property name="httpServletRequestValidator">
+ <bean class="net.shibboleth.shared.servlet.impl.ChainingHttpServletRequestValidator">
+ <property name="validators">
+ <list>
+ <bean class="net.shibboleth.shared.servlet.impl.BasicHttpServletRequestMethodValidator"
+ p:allowedMethods="POST" />
+ <bean class="net.shibboleth.shared.servlet.impl.BasicHttpServletRequestContentTypeValidator"
+ p:allowedContentTypes="application/x-www-form-urlencoded" />
+ <ref bean="shibboleth.HttpServletRequestValidator.BasicParams.SAML2.POSTSimpleSign.RequestOrResponse" />
+ </list>
+ </property>
+ </bean>
+ </property>
+ </bean>
+ </list>
+ </constructor-arg>
</bean>
</property>
</bean>
<bean p:id="sp/logout/consumer/saml2/artifact" class="net.shibboleth.sp.profile.BasicLogoutConsumerFlowDescriptor">
<property name="activationCondition">
- <bean class="net.shibboleth.sp.profile.context.logic.HttpServletRequestPredicate"
- p:httpServletRequestSupplier-ref="shibboleth.RemotedHttpServletRequestSupplier">
- <property name="httpServletRequestValidator">
- <bean class="net.shibboleth.shared.servlet.impl.ChainingHttpServletRequestValidator">
- <property name="validators">
- <list>
- <bean class="net.shibboleth.shared.servlet.impl.BasicHttpServletRequestMethodValidator"
- p:allowedMethods="#{{ 'GET', 'POST' }}" />
- <bean class="net.shibboleth.shared.servlet.impl.BasicHttpServletRequestContentTypeValidator"
- p:allowedContentTypes="application/x-www-form-urlencoded"
- p:allowNullContentType="true" />
- <ref bean="shibboleth.HttpServletRequestValidator.BasicParams.SAML2.Artifact" />
- </list>
- </property>
- </bean>
- </property>
+ <bean parent="shibboleth.Conditions.OR">
+ <constructor-arg>
+ <list>
+ <ref bean="shibboletg.sp.TokenParameterPrefixCondition" />
+ <bean class="net.shibboleth.sp.profile.context.logic.HttpServletRequestPredicate"
+ p:httpServletRequestSupplier-ref="shibboleth.RemotedHttpServletRequestSupplier">
+ <property name="httpServletRequestValidator">
+ <bean class="net.shibboleth.shared.servlet.impl.ChainingHttpServletRequestValidator">
+ <property name="validators">
+ <list>
+ <bean class="net.shibboleth.shared.servlet.impl.BasicHttpServletRequestMethodValidator"
+ p:allowedMethods="#{{ 'GET', 'POST' }}" />
+ <bean class="net.shibboleth.shared.servlet.impl.BasicHttpServletRequestContentTypeValidator"
+ p:allowedContentTypes="application/x-www-form-urlencoded"
+ p:allowNullContentType="true" />
+ <ref bean="shibboleth.HttpServletRequestValidator.BasicParams.SAML2.Artifact" />
+ </list>
+ </property>
+ </bean>
+ </property>
+ </bean>
+ </list>
+ </constructor-arg>
</bean>
</property>
</bean>
diff --git a/sp-saml-conf-impl/src/test/java/net/shibboleth/sp/saml/flows/saml2/SAML2LogoutConsumerFlowTest.java b/sp-saml-conf-impl/src/test/java/net/shibboleth/sp/saml/flows/saml2/SAML2LogoutConsumerFlowTest.java
index 31f106e..21a125c 100644
--- a/sp-saml-conf-impl/src/test/java/net/shibboleth/sp/saml/flows/saml2/SAML2LogoutConsumerFlowTest.java
+++ b/sp-saml-conf-impl/src/test/java/net/shibboleth/sp/saml/flows/saml2/SAML2LogoutConsumerFlowTest.java
@@ -26,6 +26,7 @@ import javax.annotation.Nullable;
import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
import org.opensaml.core.xml.io.MarshallingException;
import org.opensaml.core.xml.util.XMLObjectSupport;
+import org.opensaml.profile.action.EventIds;
import org.opensaml.profile.context.ProfileRequestContext;
import org.opensaml.saml.common.SAMLObject;
import org.opensaml.saml.common.SAMLObjectBuilder;
@@ -291,6 +292,23 @@ public class SAML2LogoutConsumerFlowTest extends AbstractSPFlowTest {
validateLogoutRequestResult(false);
}
+ /**
+ * Test LogoutRequest flow when unsigned.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testRequestUnsigned() throws Exception {
+ final LogoutRequest request = buildLogoutRequest(ISSUER);
+ final DDF input = buildRemotedPOSTMessage(request, RELAY_STATE, null);
+ input.addmember(ConsumerConstants.SESSION_OPAQUE).addmember(PrepareAgentResponse.NAMEID_PARAM).string(
+ "<NameID xmlns='urn:oasis:names:tc:SAML:2.0:assertion' Format='"
+ + NameIDType.EMAIL + "' SPProvidedID='" + ISSUER + "'>jdoe at example.org</NameID>");
+ setApplicationRequest(APPLICATION_ID, input);
+
+ validateError(EventIds.INVALID_MESSAGE);
+ }
+
/**
* Test LogoutRequest flow with a match.
*
@@ -322,6 +340,20 @@ public class SAML2LogoutConsumerFlowTest extends AbstractSPFlowTest {
validateError(IdPEventIds.INVALID_PROFILE_CONFIG);
}
+ /**
+ * Test flow with unsigned response.
+ *
+ * @throws IOException
+ */
+ @Test
+ public void testResponseUnsigned() throws IOException {
+ final DDF input = buildRemotedPOSTMessage(buildLogoutResponse(ISSUER, StatusCode.SUCCESS, null), null, null);
+ setApplicationRequest(APPLICATION_ID, input);
+
+ validateError(EventIds.INVALID_MESSAGE);
+ }
+
+
/**
* Test LogoutResponse flow with a successful status.
*
@@ -385,6 +417,7 @@ public class SAML2LogoutConsumerFlowTest extends AbstractSPFlowTest {
final String token = output.getmember(ProcessLogoutRequest.TOKEN_PARAM).string();
assert token != null;
+ Assert.assertTrue(token.startsWith(ProcessLogoutRequest.TOKEN_PREFIX));
final SAMLStateData stateData;
try (final ServiceableComponent<AgentResolver> resolver = agentResolver.getServiceableComponent()) {
@@ -393,7 +426,8 @@ public class SAML2LogoutConsumerFlowTest extends AbstractSPFlowTest {
assert agent != null;
final Application app = agent.getApplication(APPLICATION_ID);
assert app != null;
- stateData = dummyStateManager.recoverFromStateToken(agent, app, token, SAMLStateData.class);
+ stateData = dummyStateManager.recoverFromStateToken(agent, app,
+ token.substring(ProcessLogoutRequest.TOKEN_PREFIX.length()), SAMLStateData.class);
}
assert stateData != null;
diff --git a/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/impl/ProcessLogoutRequest.java b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/impl/ProcessLogoutRequest.java
index 8f40c6e..7b5d30f 100644
--- a/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/impl/ProcessLogoutRequest.java
+++ b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/impl/ProcessLogoutRequest.java
@@ -80,6 +80,9 @@ public class ProcessLogoutRequest extends AbstractApplicationAction {
/** "token" parameter name. */
@Nonnull @NotEmpty public static final String TOKEN_PARAM = "token";
+
+ /** "token" value prefix. */
+ @Nonnull @NotEmpty public static final String TOKEN_PREFIX = "SAML:";
/** Class logger. */
@Nonnull private final Logger log = LoggerFactory.getLogger(ProcessLogoutRequest.class);
@@ -387,7 +390,7 @@ public class ProcessLogoutRequest extends AbstractApplicationAction {
try {
final String token = stateManager.preserveToStateToken(ensureAgent(), ensureApplication(), state);
- output.addmember(TOKEN_PARAM).string(token);
+ output.addmember(TOKEN_PARAM).string(TOKEN_PREFIX + token);
} catch (final IOException e) {
log.error("{} Exception producing state token", getLogPrefix(), e);
}
diff --git a/sp-saml-impl/src/test/java/net/shibboleth/sp/saml/saml2/profile/impl/ProcessLogoutRequestTest.java b/sp-saml-impl/src/test/java/net/shibboleth/sp/saml/saml2/profile/impl/ProcessLogoutRequestTest.java
index e70a2bb..bf3a40e 100644
--- a/sp-saml-impl/src/test/java/net/shibboleth/sp/saml/saml2/profile/impl/ProcessLogoutRequestTest.java
+++ b/sp-saml-impl/src/test/java/net/shibboleth/sp/saml/saml2/profile/impl/ProcessLogoutRequestTest.java
@@ -30,6 +30,7 @@ import org.opensaml.saml.saml2.core.Extensions;
import org.opensaml.saml.saml2.core.Issuer;
import org.opensaml.saml.saml2.core.LogoutRequest;
import org.opensaml.saml.saml2.core.LogoutResponse;
+import org.opensaml.saml.saml2.core.NameIDType;
import org.opensaml.saml.saml2.core.RequestAbstractType;
import org.opensaml.saml.saml2.core.SessionIndex;
import org.opensaml.saml.saml2.testing.SAML2ActionTestingSupport;
@@ -366,6 +367,9 @@ public class ProcessLogoutRequestTest extends BaseApplicationActionTest {
private void buildLogoutRequest() {
final LogoutRequest request = SAML2ActionTestingSupport.buildLogoutRequest(
SAML2ActionTestingSupport.buildNameID("jdoe"));
+ final Issuer issuer = request.getIssuer();
+ assert issuer != null;
+ issuer.setFormat(NameIDType.ENTITY);
prc.ensureInboundMessageContext().setMessage(request);
SAMLBindingSupport.setRelayState(prc.ensureInboundMessageContext(), RELAY_STATE);
}
@@ -386,7 +390,9 @@ public class ProcessLogoutRequestTest extends BaseApplicationActionTest {
*/
private void validateToken(@Nullable final String token) throws IOException {
assert token != null;
- final SAMLStateData data = stateManager.recoverFromStateToken(agent, application, token, SAMLStateData.class);
+ Assert.assertTrue(token.startsWith(ProcessLogoutRequest.TOKEN_PREFIX));
+ final SAMLStateData data = stateManager.recoverFromStateToken(agent, application,
+ token.substring(ProcessLogoutRequest.TOKEN_PREFIX.length()), SAMLStateData.class);
assert data != null;
final LogoutRequest request = (LogoutRequest) prc.ensureInboundMessageContext().getMessage();
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list