[java-identity-provider] branch dev/logout-enh updated: IDP-1825 - Extend SAML2 Session storage with knowledge of logout support
Scott Cantor
cantor.2 at osu.edu
Fri Jun 4 13:26:45 UTC 2021
This is an automated email from the git hooks/post-receive script.
scantor pushed a commit to branch dev/logout-enh
in repository java-identity-provider.
View the commit online:
http://git.shibboleth.net/view/?p=java-identity-provider.git;a=commit;h=934499f497186926f403f0a2f0d9ca7b8d591769
The following commit(s) were added to refs/heads/dev/logout-enh by this push:
new 934499f49 IDP-1825 - Extend SAML2 Session storage with knowledge of logout support
934499f49 is described below
commit 934499f497186926f403f0a2f0d9ca7b8d591769
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Fri Jun 4 09:26:42 2021 -0400
IDP-1825 - Extend SAML2 Session storage with knowledge of logout support
https://issues.shibboleth.net/jira/browse/IDP-1825
---
.../idp/cas/session/impl/CASSPSession.java | 7 +++++
idp-conf/src/test/resources/conf/idp.properties | 2 +-
.../idp/saml/session/SAML2SPSession.java | 34 ++++++++++++++++++++++
.../idp/saml/session/SAML2SPSessionTest.java | 17 ++++++-----
.../impl/SAML2SPSessionCreationStrategy.java | 17 ++++++++++-
.../session/impl/SAML2SPSessionSerializer.java | 9 ++++--
.../impl/PrepareInboundMessageContextTest.java | 2 +-
.../session/impl/SAML2SPSessionSerializerTest.java | 3 +-
.../idp/saml/impl/session/saml2SPSession.json | 2 +-
.../session/LogoutPropagationFlowDescriptor.java | 20 +++++++++++++
.../net/shibboleth/idp/session/SPSessionEx.java | 12 ++++++++
.../LogoutPropagationFlowDescriptorSelector.java | 3 +-
12 files changed, 112 insertions(+), 16 deletions(-)
diff --git a/idp-cas-impl/src/main/java/net/shibboleth/idp/cas/session/impl/CASSPSession.java b/idp-cas-impl/src/main/java/net/shibboleth/idp/cas/session/impl/CASSPSession.java
index 4ef038554..30ce273f5 100644
--- a/idp-cas-impl/src/main/java/net/shibboleth/idp/cas/session/impl/CASSPSession.java
+++ b/idp-cas-impl/src/main/java/net/shibboleth/idp/cas/session/impl/CASSPSession.java
@@ -66,6 +66,7 @@ public class CASSPSession extends BasicSPSession implements SPSessionEx {
return ticket;
}
+ /** {@inheritDoc} */
@Override
public String getSPSessionKey() {
return ticket;
@@ -75,6 +76,12 @@ public class CASSPSession extends BasicSPSession implements SPSessionEx {
@Nullable @NotEmpty public String getProtocol() {
return AbstractProtocolConfiguration.PROTOCOL_URI;
}
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean supportsLogoutPropagation() {
+ return true;
+ }
@Override
public String toString() {
diff --git a/idp-conf/src/test/resources/conf/idp.properties b/idp-conf/src/test/resources/conf/idp.properties
index fb9507cdd..c6dd2a22b 100644
--- a/idp-conf/src/test/resources/conf/idp.properties
+++ b/idp-conf/src/test/resources/conf/idp.properties
@@ -164,7 +164,7 @@ idp.session.secondaryServiceIndex = true
#idp.logout.authenticated = true
# Whether to hide logout propagation status reporting.
-idp.logout.propagationHidden = true
+#idp.logout.propagationHidden = false
# Bean to determine whether user should be allowed to cancel logout
#idp.logout.promptUser=shibboleth.Conditions.FALSE
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 c6834c268..af7730ab1 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;
+ /** Whether logout propagation is possible. */
+ private final boolean supportsLogoutPropagation;
+
// Checkstyle: ParameterNumber OFF
/**
* Constructor.
@@ -60,7 +63,10 @@ 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
+ *
+ * @deprecated
*/
+ @Deprecated(since="4.2.0", forRemoval=true)
public SAML2SPSession(@Nonnull @NotEmpty final String id, @Nonnull final Instant creation,
@Nonnull final Instant expiration, @Nonnull final NameID assertedNameID,
@Nonnull @NotEmpty final String assertedIndex) {
@@ -69,6 +75,28 @@ public class SAML2SPSession extends BasicSPSession implements SPSessionEx {
nameID = Constraint.isNotNull(assertedNameID, "NameID cannot be null");
sessionIndex = Constraint.isNotNull(StringSupport.trimOrNull(assertedIndex),
"SessionIndex cannot be null or empty");
+ supportsLogoutPropagation = true;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param id the identifier of the service associated with this session
+ * @param creation creation time of session
+ * @param expiration expiration time of session
+ * @param assertedNameID the NameID asserted to the SP
+ * @param assertedIndex the SessionIndex asserted to the SP
+ * @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) {
+ 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 = supportsLogoutProp;
}
// Checkstyle: ParameterNumber ON
@@ -101,6 +129,12 @@ public class SAML2SPSession extends BasicSPSession implements SPSessionEx {
return SAMLConstants.SAML20P_NS;
}
+ /** {@inheritDoc} */
+ @Override
+ public boolean supportsLogoutPropagation() {
+ return supportsLogoutPropagation;
+ }
+
/** {@inheritDoc} */
@Override
public int hashCode() {
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 424961982..43ef13a73 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,58 +46,59 @@ public class SAML2SPSessionTest extends OpenSAMLInitBaseTestCase {
Thread.sleep(50);
SAML2SPSession session = new SAML2SPSession("test", Instant.now(),
- Instant.now().plusSeconds(60), nameID, "1234567890");
+ Instant.now().plusSeconds(60), nameID, "1234567890", 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.assertFalse(session.supportsLogoutPropagation());
try {
- new SAML2SPSession(null, Instant.ofEpochMilli(0), Instant.ofEpochMilli(0), null, null);
+ new SAML2SPSession(null, Instant.ofEpochMilli(0), Instant.ofEpochMilli(0), null, null, true);
Assert.fail();
} catch (ConstraintViolationException e) {
}
try {
- new SAML2SPSession("", Instant.ofEpochMilli(0), Instant.ofEpochMilli(0), null, null);
+ new SAML2SPSession("", Instant.ofEpochMilli(0), Instant.ofEpochMilli(0), null, null, true);
Assert.fail();
} catch (ConstraintViolationException e) {
}
try {
- new SAML2SPSession(" ", Instant.ofEpochMilli(0), Instant.ofEpochMilli(0), null, null);
+ new SAML2SPSession(" ", Instant.ofEpochMilli(0), Instant.ofEpochMilli(0), null, null, true);
Assert.fail();
} catch (ConstraintViolationException e) {
}
try {
- new SAML2SPSession("foo", Instant.ofEpochMilli(0), Instant.ofEpochMilli(0), null, null);
+ new SAML2SPSession("foo", Instant.ofEpochMilli(0), Instant.ofEpochMilli(0), null, null, true);
Assert.fail();
} catch (ConstraintViolationException e) {
}
try {
- new SAML2SPSession("foo", start, Instant.ofEpochMilli(0), null, null);
+ new SAML2SPSession("foo", start, Instant.ofEpochMilli(0), null, null, true);
Assert.fail();
} catch (ConstraintViolationException e) {
}
try {
- new SAML2SPSession("foo", start, start, null, null);
+ new SAML2SPSession("foo", start, start, null, null, true);
Assert.fail();
} catch (ConstraintViolationException e) {
}
try {
- new SAML2SPSession("foo", start, start, nameID, null);
+ new SAML2SPSession("foo", start, start, nameID, 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 2253766af..c38fb2438 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
@@ -28,9 +28,12 @@ import org.opensaml.messaging.context.navigate.ChildContextLookup;
import org.opensaml.messaging.context.navigate.MessageLookup;
import org.opensaml.profile.context.ProfileRequestContext;
import org.opensaml.profile.context.navigate.OutboundMessageContextLookup;
+import org.opensaml.saml.common.messaging.context.SAMLMetadataContext;
+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.metadata.SPSSODescriptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -131,8 +134,20 @@ public class SAML2SPSessionCreationStrategy implements Function<ProfileRequestCo
expiration = now.plus(sessionLifetime);
}
+ // 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;
+ if (rpCtx.getRelyingPartyIdContextTree() instanceof SAMLPeerEntityContext) {
+ final SAMLMetadataContext mdCtx =
+ rpCtx.getRelyingPartyIdContextTree().getSubcontext(SAMLMetadataContext.class);
+ if (mdCtx != null && mdCtx.getRoleDescriptor() instanceof SPSSODescriptor) {
+ supportLogoutPropagation =
+ !((SPSSODescriptor) mdCtx.getRoleDescriptor()).getSingleLogoutServices().isEmpty();
+ }
+ }
+
return new SAML2SPSession(issuer, now, expiration, result.getFirst().getSubject().getNameID(),
- result.getSecond().getSessionIndex());
+ result.getSecond().getSessionIndex(), supportLogoutPropagation);
}
/**
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 f3a7374d0..b175ed819 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
@@ -59,7 +59,10 @@ public class SAML2SPSessionSerializer extends AbstractSPSessionSerializer {
/** Field name of SessionIndex. */
@Nonnull @NotEmpty private static final String SESSION_INDEX_FIELD = "ix";
-
+
+ /** Field name of Single Logout indicator. */
+ @Nonnull @NotEmpty private static final String LOGOUT_PROP_FIELD = "slo";
+
/** DOM configuration parameters used by LSSerializer to exclude XML declaration. */
@Nonnull private static final Map<String, Object> NO_XML_DECL_PARAMS;
@@ -98,6 +101,7 @@ 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());
+ generator.write(LOGOUT_PROP_FIELD, saml2Session.supportsLogoutPropagation());
} catch (final MarshallingException e) {
throw new XMLRuntimeException("Error marshalling and serializing NameID", e);
}
@@ -110,11 +114,12 @@ public class SAML2SPSessionSerializer extends AbstractSPSessionSerializer {
final String rawNameID = obj.getString(NAMEID_FIELD);
final String sessionIndex = obj.getString(SESSION_INDEX_FIELD);
+ final boolean supportsLogoutProp = obj.getBoolean(LOGOUT_PROP_FIELD, true);
try {
final XMLObject nameID = XMLObjectSupport.unmarshallFromReader(parserPool, new StringReader(rawNameID));
if (nameID instanceof NameID) {
- return new SAML2SPSession(id, creation, expiration, (NameID) nameID, sessionIndex);
+ return new SAML2SPSession(id, creation, expiration, (NameID) nameID, sessionIndex, 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 3580ce5ac..1b86677fb 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");
+ Instant.now().plusSeconds(1800), SAML2ActionTestingSupport.buildNameID("jdoe"), "foo", 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 7637d58c8..4be5e07e2 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);
+ SAML2SPSession session = new SAML2SPSession("test", INSTANT, exp, nameID, SESSION_INDEX, false);
String s = serializer.serialize(session);
String s2 = fileToString(DATAPATH + "saml2SPSession.json");
@@ -101,6 +101,7 @@ public class SAML2SPSessionSerializerTest extends OpenSAMLInitBaseTestCase {
Assert.assertEquals(session.getExpirationInstant(), session2.getExpirationInstant());
Assert.assertEquals(session.getNameID().getValue(), session2.getNameID().getValue());
Assert.assertEquals(session.getSessionIndex(), session2.getSessionIndex());
+ Assert.assertFalse(session2.supportsLogoutPropagation());
}
private String fileToString(String pathname) throws URISyntaxException, IOException {
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 47284787e..ac2ebee03 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"}
\ 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","slo":false}
\ No newline at end of file
diff --git a/idp-session-api/src/main/java/net/shibboleth/idp/session/LogoutPropagationFlowDescriptor.java b/idp-session-api/src/main/java/net/shibboleth/idp/session/LogoutPropagationFlowDescriptor.java
index c435b2f61..d5db0c615 100644
--- a/idp-session-api/src/main/java/net/shibboleth/idp/session/LogoutPropagationFlowDescriptor.java
+++ b/idp-session-api/src/main/java/net/shibboleth/idp/session/LogoutPropagationFlowDescriptor.java
@@ -56,6 +56,26 @@ public class LogoutPropagationFlowDescriptor extends AbstractIdentifiableInitial
@Nonnull public Class<? extends SPSession> getSessionType() {
return sessionType;
}
+
+ /**
+ * Test an input session to determine if this flow supports it.
+ *
+ * @param session input session
+ *
+ * @return true iff this flow should be used to propagate a logout to the corresponding SP
+ */
+ @SuppressWarnings("removal")
+ public boolean isSupported(@Nonnull final SPSession session) {
+ if (sessionType.isInstance(session)) {
+ if (session instanceof SPSessionEx) {
+ return ((SPSessionEx) session).supportsLogoutPropagation();
+ }
+
+ return true;
+ }
+
+ return false;
+ }
/** {@inheritDoc} */
@Override
diff --git a/idp-session-api/src/main/java/net/shibboleth/idp/session/SPSessionEx.java b/idp-session-api/src/main/java/net/shibboleth/idp/session/SPSessionEx.java
index 31dd3ef88..86c436620 100644
--- a/idp-session-api/src/main/java/net/shibboleth/idp/session/SPSessionEx.java
+++ b/idp-session-api/src/main/java/net/shibboleth/idp/session/SPSessionEx.java
@@ -38,4 +38,16 @@ public interface SPSessionEx extends SPSession {
* @return a protocol constant
*/
@Nullable @NotEmpty String getProtocol();
+
+ /**
+ * Gets whether the session allows for logout propagation.
+ *
+ * @return whether the session allows for logout propagation
+ *
+ * @since 4.2.0
+ */
+ default boolean supportsLogoutPropagation() {
+ return false;
+ }
+
}
\ No newline at end of file
diff --git a/idp-session-api/src/main/java/net/shibboleth/idp/session/logic/LogoutPropagationFlowDescriptorSelector.java b/idp-session-api/src/main/java/net/shibboleth/idp/session/logic/LogoutPropagationFlowDescriptorSelector.java
index f87257540..9276f1637 100644
--- a/idp-session-api/src/main/java/net/shibboleth/idp/session/logic/LogoutPropagationFlowDescriptorSelector.java
+++ b/idp-session-api/src/main/java/net/shibboleth/idp/session/logic/LogoutPropagationFlowDescriptorSelector.java
@@ -49,8 +49,9 @@ public class LogoutPropagationFlowDescriptorSelector implements Function<SPSessi
/** {@inheritDoc} */
@Nullable public LogoutPropagationFlowDescriptor apply(@Nullable final SPSession input) {
+
for (final LogoutPropagationFlowDescriptor flowDescriptor : availableFlows) {
- if (flowDescriptor.getSessionType().isInstance(input)) {
+ if (flowDescriptor.isSupported(input)) {
return flowDescriptor;
}
}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list