[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