diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/saml/TransportSamlInvalidateSessionAction.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/saml/TransportSamlInvalidateSessionAction.java index f151cf2c5afe7..0a5cee063183a 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/saml/TransportSamlInvalidateSessionAction.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/saml/TransportSamlInvalidateSessionAction.java @@ -120,7 +120,7 @@ private static String buildLogoutResponseUrl(SamlRealm realm, SamlLogoutRequestH } private void findAndInvalidateTokens(SamlRealm realm, SamlLogoutRequestHandler.Result result, ActionListener listener) { - final Map tokenMetadata = realm.createTokenMetadata(result.getNameId(), result.getSession()); + final Map tokenMetadata = realm.createTokenMetadata(result.getNameId(), result.getSession(), null); if (Strings.isNullOrEmpty((String) tokenMetadata.get(SamlRealm.TOKEN_METADATA_NAMEID_VALUE))) { // If we don't have a valid name-id to match against, don't do anything LOGGER.debug("Logout request [{}] has no NameID value, so cannot invalidate any sessions", result); diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlAttributes.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlAttributes.java index 3cecb6ae7c880..a8ccc5c91b488 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlAttributes.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlAttributes.java @@ -27,12 +27,20 @@ public class SamlAttributes implements Releasable { private final SamlNameId name; private final String session; + private final String inResponseTo; private final List attributes; private final List privateAttributes; - SamlAttributes(SamlNameId name, String session, List attributes, List privateAttributes) { + SamlAttributes( + SamlNameId name, + String session, + String inResponseTo, + List attributes, + List privateAttributes + ) { this.name = name; this.session = session; + this.inResponseTo = inResponseTo; this.attributes = attributes; this.privateAttributes = privateAttributes; } @@ -89,9 +97,24 @@ String session() { return session; } + String inResponseTo() { + return inResponseTo; + } + @Override public String toString() { - return getClass().getSimpleName() + "(" + name + ")[" + session + "]{" + attributes + "}{" + privateAttributes + "}"; + return getClass().getSimpleName() + + "(" + + name + + ")[" + + session + + "][" + + inResponseTo + + "]{" + + attributes + + "}{" + + privateAttributes + + "}"; } @Override diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthenticator.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthenticator.java index fa5b95bd2902f..2b68fb165e5bc 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthenticator.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthenticator.java @@ -117,7 +117,7 @@ private SamlAttributes authenticateResponse(Element element, Collection final Assertion assertion = details.v1(); final SamlNameId nameId = SamlNameId.forSubject(assertion.getSubject()); final String session = getSessionIndex(assertion); - final SamlAttributes samlAttributes = buildSamlAttributes(nameId, session, details.v2()); + final SamlAttributes samlAttributes = buildSamlAttributes(nameId, session, response.getInResponseTo(), details.v2()); if (logger.isTraceEnabled()) { StringBuilder sb = new StringBuilder(); sb.append("The SAML Assertion contained the following attributes: \n"); @@ -141,7 +141,7 @@ private SamlAttributes authenticateResponse(Element element, Collection return samlAttributes; } - private SamlAttributes buildSamlAttributes(SamlNameId nameId, String session, List attributes) { + private SamlAttributes buildSamlAttributes(SamlNameId nameId, String session, String inResponseTo, List attributes) { List samlAttributes = new ArrayList<>(); List samlPrivateAttributes = new ArrayList<>(); for (Attribute attribute : attributes) { @@ -151,7 +151,7 @@ private SamlAttributes buildSamlAttributes(SamlNameId nameId, String session, Li samlAttributes.add(new SamlAttributes.SamlAttribute(attribute)); } } - return new SamlAttributes(nameId, session, samlAttributes, samlPrivateAttributes); + return new SamlAttributes(nameId, session, inResponseTo, samlAttributes, samlPrivateAttributes); } private static String getSessionIndex(Assertion assertion) { diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlRealm.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlRealm.java index 5cc2fa5fa2f66..c56ea6d795835 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlRealm.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlRealm.java @@ -159,6 +159,7 @@ public final class SamlRealm extends Realm implements Releasable { public static final String TOKEN_METADATA_NAMEID_SP_QUALIFIER = "saml_nameid_sp_qual"; public static final String TOKEN_METADATA_NAMEID_SP_PROVIDED_ID = "saml_nameid_sp_id"; public static final String TOKEN_METADATA_SESSION = "saml_session"; + public static final String TOKEN_METADATA_IN_RESPONSE_TO = "saml_in_response_to"; public static final String TOKEN_METADATA_REALM = "saml_realm"; public static final String PRIVATE_ATTRIBUTES_METADATA = "saml_private_attributes"; @@ -588,7 +589,7 @@ private void buildUser(SamlAttributes attributes, ActionListener tokenMetadata = createTokenMetadata(attributes.name(), attributes.session()); + final Map tokenMetadata = createTokenMetadata(attributes.name(), attributes.session(), attributes.inResponseTo()); final Map> privateAttributesMetadata = attributes.privateAttributes() .stream() .collect(Collectors.toMap(SamlPrivateAttribute::name, SamlPrivateAttribute::values)); @@ -639,7 +640,7 @@ private void buildUser(SamlAttributes attributes, ActionListener createTokenMetadata(SamlNameId nameId, String session) { + public Map createTokenMetadata(SamlNameId nameId, String session, String inResponseTo) { final Map tokenMeta = new HashMap<>(); if (nameId != null) { tokenMeta.put(TOKEN_METADATA_NAMEID_VALUE, nameId.value); @@ -655,6 +656,9 @@ public Map createTokenMetadata(SamlNameId nameId, String session tokenMeta.put(TOKEN_METADATA_NAMEID_SP_PROVIDED_ID, null); } tokenMeta.put(TOKEN_METADATA_SESSION, session); + if (inResponseTo != null) { + tokenMeta.put(TOKEN_METADATA_IN_RESPONSE_TO, inResponseTo); + } tokenMeta.put(TOKEN_METADATA_REALM, name()); return tokenMeta; } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/saml/TransportSamlInvalidateSessionActionTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/saml/TransportSamlInvalidateSessionActionTests.java index 82696a3ef5c00..3f4a02d62546b 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/saml/TransportSamlInvalidateSessionActionTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/saml/TransportSamlInvalidateSessionActionTests.java @@ -463,7 +463,7 @@ private TokenService.CreateTokenResult storeToken(byte[] userTokenBytes, byte[] .user(new User("bob")) .realmRef(new RealmRef("native", NativeRealmSettings.TYPE, "node01")) .build(false); - final Map metadata = samlRealm.createTokenMetadata(nameId, session); + final Map metadata = samlRealm.createTokenMetadata(nameId, session, null); final PlainActionFuture future = new PlainActionFuture<>(); tokenService.createOAuth2Tokens(userTokenBytes, refreshTokenBytes, authentication, authentication, metadata, future); return future.actionGet(); diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/saml/TransportSamlLogoutActionTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/saml/TransportSamlLogoutActionTests.java index a8e4c13c8f4cc..8ad2cb4302060 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/saml/TransportSamlLogoutActionTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/saml/TransportSamlLogoutActionTests.java @@ -278,7 +278,8 @@ public void testLogoutInvalidatesToken() throws Exception { final Authentication.RealmRef realmRef = new Authentication.RealmRef(samlRealm.name(), SingleSpSamlRealmSettings.TYPE, "node01"); final Map tokenMetadata = samlRealm.createTokenMetadata( new SamlNameId(NameID.TRANSIENT, nameId, null, null, null), - session + session, + null ); final Authentication authentication = Authentication.newRealmAuthentication(user, realmRef); diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlAttributesTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlAttributesTests.java index 45d845f676c50..00f12edbc0c70 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlAttributesTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlAttributesTests.java @@ -19,9 +19,11 @@ public void testToString() { final String nameFormat = randomFrom(NameID.TRANSIENT, NameID.PERSISTENT, NameID.EMAIL); final String nameId = randomIdentifier(); final String session = randomAlphaOfLength(16); + final String inResponseTo = randomAlphanumericOfLength(16); final SamlAttributes attributes = new SamlAttributes( new SamlNameId(nameFormat, nameId, null, null, null), session, + inResponseTo, List.of( new SamlAttributes.SamlAttribute("urn:oid:0.9.2342.19200300.100.1.1", null, List.of("peter.ng")), new SamlAttributes.SamlAttribute("urn:oid:2.5.4.3", "name", List.of("Peter Ng")), @@ -46,6 +48,8 @@ public void testToString() { + ("NameId(" + nameFormat + ")=" + nameId) + ")[" + session + + "][" + + inResponseTo + "]{[" + "urn:oid:0.9.2342.19200300.100.1.1=[peter.ng](len=1)" + ", " diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlRealmTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlRealmTests.java index e5f37a5e9de0f..a1840473d2fd8 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlRealmTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlRealmTests.java @@ -85,7 +85,9 @@ import static org.elasticsearch.core.Strings.format; import static org.elasticsearch.test.ActionListenerUtils.anyActionListener; import static org.elasticsearch.test.TestMatchers.throwableWithMessage; +import static org.elasticsearch.xpack.security.authc.saml.SamlRealm.CONTEXT_TOKEN_DATA; import static org.elasticsearch.xpack.security.authc.saml.SamlRealm.PRIVATE_ATTRIBUTES_METADATA; +import static org.elasticsearch.xpack.security.authc.saml.SamlRealm.TOKEN_METADATA_IN_RESPONSE_TO; import static org.hamcrest.Matchers.arrayContainingInAnyOrder; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; @@ -101,6 +103,7 @@ import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; +import static org.hamcrest.Matchers.typeCompatibleWith; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -580,6 +583,7 @@ private AuthenticationResult performAuthentication( final String nameIdValue = principalIsEmailAddress ? "clint.barton@shield.gov" : "clint.barton"; final String uidValue = principalIsEmailAddress ? "cbarton@shield.gov" : "cbarton"; final String realmType = SingleSpSamlRealmSettings.TYPE; + final String inResponseTo = "_request_id_12345"; final RealmConfig.RealmIdentifier realmIdentifier = new RealmConfig.RealmIdentifier("mock", "mock_lookup"); final MockLookupRealm lookupRealm = new MockLookupRealm( @@ -669,6 +673,7 @@ private AuthenticationResult performAuthentication( final SamlAttributes attributes = new SamlAttributes( new SamlNameId(NameIDType.PERSISTENT, nameIdValue, idp.getEntityID(), sp.getEntityId(), null), randomAlphaOfLength(16), + inResponseTo, List.of( new SamlAttributes.SamlAttribute("urn:oid:0.9.2342.19200300.100.1.1", "uid", Collections.singletonList(uidValue)), new SamlAttributes.SamlAttribute("urn:oid:1.3.6.1.4.1.5923.1.5.1.1", "groups", groups), @@ -698,6 +703,11 @@ public void onResponse(AuthenticationResult result) { @SuppressWarnings("unchecked") var metadata = (Map>) result.getMetadata().get(PRIVATE_ATTRIBUTES_METADATA); secureAttributes.forEach((name, value) -> assertThat(metadata.get(name), equalTo(value))); + Object tokenContext = result.getMetadata().get(CONTEXT_TOKEN_DATA); + assertThat(tokenContext, notNullValue()); + assertThat(tokenContext.getClass(), typeCompatibleWith(Map.class)); + Object returnedInResponseTo = ((Map) tokenContext).get(TOKEN_METADATA_IN_RESPONSE_TO); + assertThat(returnedInResponseTo, equalTo(inResponseTo)); } super.onResponse(result); } @@ -778,6 +788,7 @@ private List performAttributeSelectionWithSplit(String delimiter, String final SamlAttributes attributes = new SamlAttributes( new SamlNameId(NameIDType.TRANSIENT, randomAlphaOfLength(24), null, null, null), randomAlphaOfLength(16), + randomAlphaOfLength(16), List.of( new SamlAttributes.SamlAttribute( "departments", @@ -850,6 +861,7 @@ public void testAttributeSelectionWithSplitAndListThrowsSecurityException() { final SamlAttributes attributes = new SamlAttributes( new SamlNameId(NameIDType.TRANSIENT, randomAlphaOfLength(24), null, null, null), randomAlphaOfLength(16), + randomAlphaOfLength(16), List.of( new SamlAttributes.SamlAttribute( "departments", @@ -883,6 +895,7 @@ public void testAttributeSelectionWithRegex() { final SamlAttributes attributes = new SamlAttributes( new SamlNameId(NameIDType.TRANSIENT, randomAlphaOfLength(24), null, null, null), randomAlphaOfLength(16), + randomAlphaOfLength(16), List.of( new SamlAttributes.SamlAttribute( "urn:oid:0.9.2342.19200300.100.1.3", @@ -1053,6 +1066,7 @@ public void testNonMatchingPrincipalPatternThrowsSamlException() throws Exceptio final SamlAttributes attributes = new SamlAttributes( new SamlNameId(NameIDType.TRANSIENT, randomAlphaOfLength(12), null, null, null), randomAlphaOfLength(16), + randomAlphaOfLength(16), List.of(new SamlAttributes.SamlAttribute("urn:oid:0.9.2342.19200300.100.1.3", "mail", Collections.singletonList(mail))), List.of() );