[java-plugin-shibd-saml] branch main updated: Add asynch detection and preliminary NotOnOrAfter support.
Codeberg
noreply at shibboleth.net
Tue May 26 13:36:38 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/8f38f73d686a948b180a40446f4708ced951803e
The following commit(s) were added to refs/heads/main by this push:
new 8f38f73 Add asynch detection and preliminary NotOnOrAfter support.
8f38f73 is described below
commit 8f38f73d686a948b180a40446f4708ced951803e
Author: Scott Cantor <scott at restingparrotsoftware.com>
AuthorDate: Tue May 26 09:36:18 2026 -0400
Add asynch detection and preliminary NotOnOrAfter support.
---
.../saml2/profile/impl/ProcessLogoutRequest.java | 36 ++++++++++++++-
.../profile/impl/ProcessLogoutRequestTest.java | 52 ++++++++++++++++++++++
2 files changed, 86 insertions(+), 2 deletions(-)
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 58fef83..b18eff6 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
@@ -16,6 +16,7 @@ package net.shibboleth.sp.saml.saml2.profile.impl;
import java.io.IOException;
import java.io.StringReader;
+import java.time.Instant;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
@@ -31,6 +32,8 @@ import org.opensaml.profile.action.ActionSupport;
import org.opensaml.profile.action.EventIds;
import org.opensaml.profile.context.ProfileRequestContext;
import org.opensaml.saml.common.binding.SAMLBindingSupport;
+import org.opensaml.saml.ext.saml2aslo.Asynchronous;
+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.NameID;
@@ -230,6 +233,19 @@ public class ProcessLogoutRequest extends AbstractApplicationAction {
return;
}
+ // TODO: in principle we're supposed to store the logout message for as long as it lasts
+ // and reject assertions that match for that period of time, so we would need storage
+ // for that.
+
+ // Check expiration.
+ final Instant expires = logoutRequest.getNotOnOrAfter();
+ if (expires != null && !expires.isAfter(Instant.now())) {
+ log.info("{} Logout request has already expired", getLogPrefix());
+ addToken(profileRequestContext, issuer, false);
+ return;
+ }
+
+ // Unpack our session data for comparison.
final DDF input = agentRequestContext.getInput();
final String pickled = input != null ?
input.getmember(ConsumerConstants.SESSION_OPAQUE).getmember(PrepareAgentResponse.NAMEID_PARAM).string()
@@ -239,7 +255,7 @@ public class ProcessLogoutRequest extends AbstractApplicationAction {
addToken(profileRequestContext, issuer, false);
return;
}
-
+
// Decode NameID from session data from Agent and unpack the buried information.
NameID sessionNameID = null;
try {
@@ -332,6 +348,16 @@ public class ProcessLogoutRequest extends AbstractApplicationAction {
return false;
}
+ /**
+ * Gets whether the request contains the {@link Asynchronous} extension.
+ *
+ * @return true iff the request contains the extension
+ */
+ private boolean isAsynchronous() {
+ final Extensions exts = logoutRequest.getExtensions();
+ return exts != null && !exts.getUnknownXMLObjects(Asynchronous.DEFAULT_ELEMENT_NAME).isEmpty();
+ }
+
/**
* Generate token for Agent based on logout request and add to output.
*
@@ -346,11 +372,17 @@ public class ProcessLogoutRequest extends AbstractApplicationAction {
output.addmember(MATCHED_PARAM).integer(matched ? 1 : 0);
+ if (isAsynchronous()) {
+ log.debug("{} Logout request contained Asynchronous extension, skipping token generation for Agent",
+ getLogPrefix());
+ return;
+ }
+
final SAMLStateData state = new SAMLStateData();
state.setRequestID(logoutRequest.getID());
state.setAuthenticationAuthority(issuer != null ? issuer.getValue() : null);
- // We have no need for this field's usual purpose so this is a simple way to store off RelayState.
+ // We have no need for this field's usual purpose so this is a simple way to save RelayState from the IdP.
state.setResource(SAMLBindingSupport.getRelayState(profileRequestContext.ensureInboundMessageContext()));
try {
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 be836dc..3c124ba 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
@@ -16,6 +16,7 @@ package net.shibboleth.sp.saml.saml2.profile.impl;
import java.io.IOException;
+import java.time.Instant;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -24,9 +25,12 @@ import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
import org.opensaml.profile.action.EventIds;
import org.opensaml.saml.common.SAMLObjectBuilder;
import org.opensaml.saml.common.binding.SAMLBindingSupport;
+import org.opensaml.saml.ext.saml2aslo.Asynchronous;
+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.RequestAbstractType;
import org.opensaml.saml.saml2.core.SessionIndex;
import org.opensaml.saml.saml2.testing.SAML2ActionTestingSupport;
import org.springframework.core.io.ClassPathResource;
@@ -182,6 +186,25 @@ public class ProcessLogoutRequestTest extends BaseApplicationActionTest {
validateToken(output.getmember(ProcessLogoutRequest.TOKEN_PARAM).string());
}
+ @Test
+ public void testExpired() throws ComponentInitializationException, IOException {
+ ((LogoutRequest) prc.ensureInboundMessageContext().ensureMessage()).setNotOnOrAfter(Instant.now().minusSeconds(300));
+
+ final DDF input = new DDF(null).structure();
+ input.addmember(ConsumerConstants.SESSION_OPAQUE).addmember(PrepareAgentResponse.NAMEID_PARAM).string(
+ "<NameID xmlns='urn:oasis:names:tc:SAML:2.0:assertion' SPProvidedID='"
+ + ActionTestingSupport.INBOUND_MSG_ISSUER + "'>jdoe</NameID>");
+ arc.setInput(input);
+
+ final Event event = action.execute(src);
+ ActionTestingSupport.assertProceedEvent(event);
+
+ final DDF output = arc.getOutput();
+ assert output != null;
+ Assert.assertEquals(output.getmember(ProcessLogoutRequest.MATCHED_PARAM).integer(), 0);
+ validateToken(output.getmember(ProcessLogoutRequest.TOKEN_PARAM).string());
+ }
+
@Test
public void testNoSessionData() throws ComponentInitializationException, IOException {
final Event event = action.execute(src);
@@ -307,6 +330,35 @@ public class ProcessLogoutRequestTest extends BaseApplicationActionTest {
Assert.assertEquals(output.getmember(ProcessLogoutRequest.MATCHED_PARAM).integer(), 1);
validateToken(output.getmember(ProcessLogoutRequest.TOKEN_PARAM).string());
}
+
+ @Test
+ public void testMatchAsynch() throws ComponentInitializationException, IOException {
+ final DDF input = new DDF(null).structure();
+ input.addmember(ConsumerConstants.SESSION_OPAQUE).addmember(PrepareAgentResponse.NAMEID_PARAM).string(
+ "<NameID xmlns='urn:oasis:names:tc:SAML:2.0:assertion' SPProvidedID='"
+ + ActionTestingSupport.INBOUND_MSG_ISSUER + "'>jdoe</NameID>");
+ arc.setInput(input);
+
+ final SAMLObjectBuilder<Extensions> extsBuilder = (SAMLObjectBuilder<Extensions>)
+ XMLObjectProviderRegistrySupport.getBuilderFactory().<Extensions>ensureBuilder(
+ Extensions.DEFAULT_ELEMENT_NAME);
+ final SAMLObjectBuilder<Asynchronous> asyncBuilder = (SAMLObjectBuilder<Asynchronous>)
+ XMLObjectProviderRegistrySupport.getBuilderFactory().<Asynchronous>ensureBuilder(
+ Asynchronous.DEFAULT_ELEMENT_NAME);
+
+ final Extensions exts = extsBuilder.buildObject();
+ exts.getUnknownXMLObjects().add(asyncBuilder.buildObject());
+ ((RequestAbstractType) prc.ensureInboundMessageContext().ensureMessage()).setExtensions(exts);
+
+ final Event event = action.execute(src);
+ ActionTestingSupport.assertProceedEvent(event);
+
+ final DDF output = arc.getOutput();
+ assert output != null;
+ Assert.assertEquals(output.getmember(ProcessLogoutRequest.MATCHED_PARAM).integer(), 1);
+ Assert.assertTrue(output.getmember(ProcessLogoutRequest.TOKEN_PARAM).isnull());
+ }
+
/**
* Adds mock request content to the inbound {@link MessageContext).
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list