[java-identity-provider] branch main updated: IDP-1296 - Non-standard extension to discriminate logout endpoints
Scott Cantor
cantor.2 at osu.edu
Fri Jun 4 18:55:04 UTC 2021
This is an automated email from the git hooks/post-receive script.
scantor pushed a commit to branch main
in repository java-identity-provider.
View the commit online:
http://git.shibboleth.net/view/?p=java-identity-provider.git;a=commit;h=88652f33c62c015a9db08b835d8ca01e4ff812d7
The following commit(s) were added to refs/heads/main by this push:
new 88652f33c IDP-1296 - Non-standard extension to discriminate logout endpoints
88652f33c is described below
commit 88652f33c62c015a9db08b835d8ca01e4ff812d7
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Fri Jun 4 14:55:00 2021 -0400
IDP-1296 - Non-standard extension to discriminate logout endpoints
https://issues.shibboleth.net/jira/browse/IDP-1296
Store off ACS in tracked SPSessions.
---
.../idp/saml/session/SAML2SPSession.java | 24 +++++++++++++++-------
.../idp/saml/session/SAML2SPSessionTest.java | 17 +++++++--------
.../impl/SAML2SPSessionCreationStrategy.java | 16 ++++++++++++++-
.../session/impl/SAML2SPSessionSerializer.java | 24 +++++++++++++++++-----
.../impl/PrepareInboundMessageContextTest.java | 2 +-
.../session/impl/SAML2SPSessionSerializerTest.java | 2 +-
.../idp/saml/impl/session/saml2SPSession.json | 2 +-
7 files changed, 63 insertions(+), 24 deletions(-)
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/session/SAML2SPSession.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/session/SAML2SPSession.java
index af7730ab1..ac96d3385 100644
--- a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/session/SAML2SPSession.java
+++ b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/session/SAML2SPSession.java
@@ -51,6 +51,9 @@ public class SAML2SPSession extends BasicSPSession implements SPSessionEx {
/** The SessionIndex asserted to the SP. */
@Nonnull @NotEmpty private final String sessionIndex;
+ /** The ACS location used for the associated response. */
+ @Nullable @NotEmpty private final String acsLocation;
+
/** Whether logout propagation is possible. */
private final boolean supportsLogoutPropagation;
@@ -70,12 +73,7 @@ public class SAML2SPSession extends BasicSPSession implements SPSessionEx {
public SAML2SPSession(@Nonnull @NotEmpty final String id, @Nonnull final Instant creation,
@Nonnull final Instant expiration, @Nonnull final NameID assertedNameID,
@Nonnull @NotEmpty final String assertedIndex) {
- super(id, creation, expiration);
-
- nameID = Constraint.isNotNull(assertedNameID, "NameID cannot be null");
- sessionIndex = Constraint.isNotNull(StringSupport.trimOrNull(assertedIndex),
- "SessionIndex cannot be null or empty");
- supportsLogoutPropagation = true;
+ this(id, creation, expiration, assertedNameID, assertedIndex, null, true);
}
/**
@@ -86,16 +84,19 @@ public class SAML2SPSession extends BasicSPSession implements SPSessionEx {
* @param expiration expiration time of session
* @param assertedNameID the NameID asserted to the SP
* @param assertedIndex the SessionIndex asserted to the SP
+ * @param acsLoc the response endpoint used
* @param supportsLogoutProp whether the session supports logout propagation
*/
public SAML2SPSession(@Nonnull @NotEmpty final String id, @Nonnull final Instant creation,
@Nonnull final Instant expiration, @Nonnull final NameID assertedNameID,
- @Nonnull @NotEmpty final String assertedIndex, final boolean supportsLogoutProp) {
+ @Nonnull @NotEmpty final String assertedIndex, @Nullable @NotEmpty final String acsLoc,
+ final boolean supportsLogoutProp) {
super(id, creation, expiration);
nameID = Constraint.isNotNull(assertedNameID, "NameID cannot be null");
sessionIndex = Constraint.isNotNull(StringSupport.trimOrNull(assertedIndex),
"SessionIndex cannot be null or empty");
+ acsLocation = StringSupport.trimOrNull(acsLoc);
supportsLogoutPropagation = supportsLogoutProp;
}
// Checkstyle: ParameterNumber ON
@@ -128,6 +129,15 @@ public class SAML2SPSession extends BasicSPSession implements SPSessionEx {
@Nullable @NotEmpty public String getProtocol() {
return SAMLConstants.SAML20P_NS;
}
+
+ /**
+ * Get the ACS location used for the response that produced this session.
+ *
+ * @return ACS location
+ */
+ @Nullable @NotEmpty public String getACSLocation() {
+ return acsLocation;
+ }
/** {@inheritDoc} */
@Override
diff --git a/idp-saml-api/src/test/java/net/shibboleth/idp/saml/session/SAML2SPSessionTest.java b/idp-saml-api/src/test/java/net/shibboleth/idp/saml/session/SAML2SPSessionTest.java
index 43ef13a73..4e76b3c39 100644
--- a/idp-saml-api/src/test/java/net/shibboleth/idp/saml/session/SAML2SPSessionTest.java
+++ b/idp-saml-api/src/test/java/net/shibboleth/idp/saml/session/SAML2SPSessionTest.java
@@ -46,59 +46,60 @@ public class SAML2SPSessionTest extends OpenSAMLInitBaseTestCase {
Thread.sleep(50);
SAML2SPSession session = new SAML2SPSession("test", Instant.now(),
- Instant.now().plusSeconds(60), nameID, "1234567890", false);
+ Instant.now().plusSeconds(60), nameID, "1234567890", "https://sp.example.org/acs", false);
Assert.assertEquals(session.getId(), "test");
Assert.assertTrue(session.getCreationInstant().isAfter(start));
Assert.assertTrue(session.getExpirationInstant().isAfter(session.getCreationInstant()));
Assert.assertSame(session.getNameID(), nameID);
Assert.assertEquals(session.getSessionIndex(), "1234567890");
Assert.assertEquals(session.getSPSessionKey(), "joe at example.org");
+ Assert.assertEquals(session.getACSLocation(), "https://sp.example.org/acs");
Assert.assertFalse(session.supportsLogoutPropagation());
try {
- new SAML2SPSession(null, Instant.ofEpochMilli(0), Instant.ofEpochMilli(0), null, null, true);
+ new SAML2SPSession(null, Instant.ofEpochMilli(0), Instant.ofEpochMilli(0), null, null, null, true);
Assert.fail();
} catch (ConstraintViolationException e) {
}
try {
- new SAML2SPSession("", Instant.ofEpochMilli(0), Instant.ofEpochMilli(0), null, null, true);
+ new SAML2SPSession("", Instant.ofEpochMilli(0), Instant.ofEpochMilli(0), null, null, null, true);
Assert.fail();
} catch (ConstraintViolationException e) {
}
try {
- new SAML2SPSession(" ", Instant.ofEpochMilli(0), Instant.ofEpochMilli(0), null, null, true);
+ new SAML2SPSession(" ", Instant.ofEpochMilli(0), Instant.ofEpochMilli(0), null, null, null, true);
Assert.fail();
} catch (ConstraintViolationException e) {
}
try {
- new SAML2SPSession("foo", Instant.ofEpochMilli(0), Instant.ofEpochMilli(0), null, null, true);
+ new SAML2SPSession("foo", Instant.ofEpochMilli(0), Instant.ofEpochMilli(0), null, null, null, true);
Assert.fail();
} catch (ConstraintViolationException e) {
}
try {
- new SAML2SPSession("foo", start, Instant.ofEpochMilli(0), null, null, true);
+ new SAML2SPSession("foo", start, Instant.ofEpochMilli(0), null, null, null, true);
Assert.fail();
} catch (ConstraintViolationException e) {
}
try {
- new SAML2SPSession("foo", start, start, null, null, true);
+ new SAML2SPSession("foo", start, start, null, null, null, true);
Assert.fail();
} catch (ConstraintViolationException e) {
}
try {
- new SAML2SPSession("foo", start, start, nameID, null, true);
+ new SAML2SPSession("foo", start, start, nameID, null, null, true);
Assert.fail();
} catch (ConstraintViolationException e) {
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/session/impl/SAML2SPSessionCreationStrategy.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/session/impl/SAML2SPSessionCreationStrategy.java
index c38fb2438..800121b7c 100644
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/session/impl/SAML2SPSessionCreationStrategy.java
+++ b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/session/impl/SAML2SPSessionCreationStrategy.java
@@ -19,6 +19,7 @@ package net.shibboleth.idp.saml.session.impl;
import java.time.Duration;
import java.time.Instant;
+import java.util.List;
import java.util.function.Function;
import javax.annotation.Nonnull;
@@ -33,6 +34,8 @@ import org.opensaml.saml.common.messaging.context.SAMLPeerEntityContext;
import org.opensaml.saml.saml2.core.Assertion;
import org.opensaml.saml.saml2.core.AuthnStatement;
import org.opensaml.saml.saml2.core.Response;
+import org.opensaml.saml.saml2.core.SubjectConfirmation;
+import org.opensaml.saml.saml2.core.SubjectConfirmationData;
import org.opensaml.saml.saml2.metadata.SPSSODescriptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -103,6 +106,7 @@ public class SAML2SPSessionCreationStrategy implements Function<ProfileRequestCo
responseLookupStrategy = Constraint.isNotNull(strategy, "Response lookup strategy cannot be null");
}
+// Checkstyle: CyclomaticComplexity OFF
/** {@inheritDoc} */
@Nullable public SPSession apply(@Nullable final ProfileRequestContext input) {
@@ -134,6 +138,15 @@ public class SAML2SPSessionCreationStrategy implements Function<ProfileRequestCo
expiration = now.plus(sessionLifetime);
}
+ String acsLocation = null;
+ final List<SubjectConfirmation> sc = result.getFirst().getSubject().getSubjectConfirmations();
+ if (sc != null && !sc.isEmpty()) {
+ final SubjectConfirmationData scData = sc.get(0).getSubjectConfirmationData();
+ if (scData != null) {
+ acsLocation = scData.getRecipient();
+ }
+ }
+
// Do a basic check for outbound logout capability to the SP based on metadata.
// This may optimize out subsequent need to process the session for propagation.
boolean supportLogoutPropagation = false;
@@ -147,8 +160,9 @@ public class SAML2SPSessionCreationStrategy implements Function<ProfileRequestCo
}
return new SAML2SPSession(issuer, now, expiration, result.getFirst().getSubject().getNameID(),
- result.getSecond().getSessionIndex(), supportLogoutPropagation);
+ result.getSecond().getSessionIndex(), acsLocation, supportLogoutPropagation);
}
+// Checkstyle: CyclomaticComplexity ON
/**
* Locate the first assertion and authentication statement, such that the assertion subject
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/session/impl/SAML2SPSessionSerializer.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/session/impl/SAML2SPSessionSerializer.java
index b175ed819..5891241cf 100644
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/session/impl/SAML2SPSessionSerializer.java
+++ b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/session/impl/SAML2SPSessionSerializer.java
@@ -26,6 +26,7 @@ import java.util.Map;
import javax.annotation.Nonnull;
import javax.json.JsonObject;
+import javax.json.JsonString;
import javax.json.stream.JsonGenerator;
import org.opensaml.core.xml.XMLObject;
@@ -59,7 +60,10 @@ public class SAML2SPSessionSerializer extends AbstractSPSessionSerializer {
/** Field name of SessionIndex. */
@Nonnull @NotEmpty private static final String SESSION_INDEX_FIELD = "ix";
-
+
+ /** Field name of ACS location. */
+ @Nonnull @NotEmpty private static final String ACS_LOC_FIELD = "acs";
+
/** Field name of Single Logout indicator. */
@Nonnull @NotEmpty private static final String LOGOUT_PROP_FIELD = "slo";
@@ -101,6 +105,9 @@ public class SAML2SPSessionSerializer extends AbstractSPSessionSerializer {
generator.write(NAMEID_FIELD, SerializeSupport.nodeToString(
XMLObjectSupport.marshall(saml2Session.getNameID()), NO_XML_DECL_PARAMS));
generator.write(SESSION_INDEX_FIELD, saml2Session.getSessionIndex());
+ if (saml2Session.getACSLocation() != null) {
+ generator.write(ACS_LOC_FIELD, saml2Session.getACSLocation());
+ }
generator.write(LOGOUT_PROP_FIELD, saml2Session.supportsLogoutPropagation());
} catch (final MarshallingException e) {
throw new XMLRuntimeException("Error marshalling and serializing NameID", e);
@@ -112,14 +119,21 @@ public class SAML2SPSessionSerializer extends AbstractSPSessionSerializer {
@Nonnull protected SPSession doDeserialize(@Nonnull final JsonObject obj, @Nonnull @NotEmpty final String id,
@Nonnull final Instant creation, @Nonnull final Instant expiration) throws IOException {
- final String rawNameID = obj.getString(NAMEID_FIELD);
- final String sessionIndex = obj.getString(SESSION_INDEX_FIELD);
+ final JsonString rawNameID = obj.getJsonString(NAMEID_FIELD);
+ final JsonString sessionIndex = obj.getJsonString(SESSION_INDEX_FIELD);
+ final JsonString acsLocation = obj.getJsonString(ACS_LOC_FIELD);
final boolean supportsLogoutProp = obj.getBoolean(LOGOUT_PROP_FIELD, true);
+ if (rawNameID == null && sessionIndex == null) {
+ throw new IOException("Serialized SAML2SPSession missing required fields");
+ }
+
try {
- final XMLObject nameID = XMLObjectSupport.unmarshallFromReader(parserPool, new StringReader(rawNameID));
+ final XMLObject nameID =
+ XMLObjectSupport.unmarshallFromReader(parserPool, new StringReader(rawNameID.getString()));
if (nameID instanceof NameID) {
- return new SAML2SPSession(id, creation, expiration, (NameID) nameID, sessionIndex, supportsLogoutProp);
+ return new SAML2SPSession(id, creation, expiration, (NameID) nameID, sessionIndex.getString(),
+ acsLocation != null ? acsLocation.getString() : null, supportsLogoutProp);
}
throw new IOException("XMLObject stored in NameID field was not a NameID");
} catch (final XMLParserException | UnmarshallingException e) {
diff --git a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/session/impl/PrepareInboundMessageContextTest.java b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/session/impl/PrepareInboundMessageContextTest.java
index 1b86677fb..6fa9a4699 100644
--- a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/session/impl/PrepareInboundMessageContextTest.java
+++ b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/session/impl/PrepareInboundMessageContextTest.java
@@ -52,7 +52,7 @@ public class PrepareInboundMessageContextTest extends OpenSAMLInitBaseTestCase {
prc.setInboundMessageContext(null);
final SAML2SPSession session = new SAML2SPSession("https://sp.example.org", Instant.now(),
- Instant.now().plusSeconds(1800), SAML2ActionTestingSupport.buildNameID("jdoe"), "foo", true);
+ Instant.now().plusSeconds(1800), SAML2ActionTestingSupport.buildNameID("jdoe"), "foo", null, true);
prc.getSubcontext(LogoutPropagationContext.class, true).setSession(session);
action = new PrepareInboundMessageContext();
diff --git a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/session/impl/SAML2SPSessionSerializerTest.java b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/session/impl/SAML2SPSessionSerializerTest.java
index 4be5e07e2..1351b3fd6 100644
--- a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/session/impl/SAML2SPSessionSerializerTest.java
+++ b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/session/impl/SAML2SPSessionSerializerTest.java
@@ -88,7 +88,7 @@ public class SAML2SPSessionSerializerTest extends OpenSAMLInitBaseTestCase {
NameID nameID = (NameID) XMLObjectSupport.buildXMLObject(NameID.DEFAULT_ELEMENT_NAME);
nameID.setValue("joe at example.org");
- SAML2SPSession session = new SAML2SPSession("test", INSTANT, exp, nameID, SESSION_INDEX, false);
+ SAML2SPSession session = new SAML2SPSession("test", INSTANT, exp, nameID, SESSION_INDEX, "https://sp.example.org/acs", false);
String s = serializer.serialize(session);
String s2 = fileToString(DATAPATH + "saml2SPSession.json");
diff --git a/idp-saml-impl/src/test/resources/net/shibboleth/idp/saml/impl/session/saml2SPSession.json b/idp-saml-impl/src/test/resources/net/shibboleth/idp/saml/impl/session/saml2SPSession.json
index ac2ebee03..1bd55b9f4 100644
--- a/idp-saml-impl/src/test/resources/net/shibboleth/idp/saml/impl/session/saml2SPSession.json
+++ b/idp-saml-impl/src/test/resources/net/shibboleth/idp/saml/impl/session/saml2SPSession.json
@@ -1 +1 @@
-{"id":"test","ts":1378827849463,"nam":"<saml2:NameID xmlns:saml2=\"urn:oasis:names:tc:SAML:2.0:assertion\">joe at example.org</saml2:NameID>","ix":"1234567890","slo":false}
\ No newline at end of file
+{"id":"test","ts":1378827849463,"nam":"<saml2:NameID xmlns:saml2=\"urn:oasis:names:tc:SAML:2.0:assertion\">joe at example.org</saml2:NameID>","ix":"1234567890","acs":"https://sp.example.org/acs","slo":false}
\ 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