Explorar el Código

Add Session Index Support

Closes gh-10613
Josh Cummings hace 3 años
padre
commit
b1a905befe

+ 16 - 2
saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/DefaultSaml2AuthenticatedPrincipal.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2020 the original author or authors.
+ * Copyright 2002-2022 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@
 package org.springframework.security.saml2.provider.service.authentication;
 
 import java.io.Serializable;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
@@ -34,14 +35,22 @@ public class DefaultSaml2AuthenticatedPrincipal implements Saml2AuthenticatedPri
 
 	private final Map<String, List<Object>> attributes;
 
+	private final List<String> sessionIndexes;
+
 	private String registrationId;
 
 	public DefaultSaml2AuthenticatedPrincipal(String name, Map<String, List<Object>> attributes) {
+		this(name, attributes, Collections.emptyList());
+	}
+
+	public DefaultSaml2AuthenticatedPrincipal(String name, Map<String, List<Object>> attributes,
+			List<String> sessionIndexes) {
 		Assert.notNull(name, "name cannot be null");
 		Assert.notNull(attributes, "attributes cannot be null");
+		Assert.notNull(sessionIndexes, "sessionIndexes cannot be null");
 		this.name = name;
 		this.attributes = attributes;
-		this.registrationId = null;
+		this.sessionIndexes = sessionIndexes;
 	}
 
 	@Override
@@ -54,6 +63,11 @@ public class DefaultSaml2AuthenticatedPrincipal implements Saml2AuthenticatedPri
 		return this.attributes;
 	}
 
+	@Override
+	public List<String> getSessionIndexes() {
+		return this.sessionIndexes;
+	}
+
 	@Override
 	public String getRelyingPartyRegistrationId() {
 		return this.registrationId;

+ 5 - 1
saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticatedPrincipal.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2020 the original author or authors.
+ * Copyright 2002-2022 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -76,4 +76,8 @@ public interface Saml2AuthenticatedPrincipal extends AuthenticatedPrincipal {
 		return null;
 	}
 
+	default List<String> getSessionIndexes() {
+		return Collections.emptyList();
+	}
+
 }

+ 16 - 1
saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSamlLogoutRequestResolver.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2021 the original author or authors.
+ * Copyright 2002-2022 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -31,10 +31,12 @@ import org.opensaml.core.xml.io.MarshallingException;
 import org.opensaml.saml.saml2.core.Issuer;
 import org.opensaml.saml.saml2.core.LogoutRequest;
 import org.opensaml.saml.saml2.core.NameID;
+import org.opensaml.saml.saml2.core.SessionIndex;
 import org.opensaml.saml.saml2.core.impl.IssuerBuilder;
 import org.opensaml.saml.saml2.core.impl.LogoutRequestBuilder;
 import org.opensaml.saml.saml2.core.impl.LogoutRequestMarshaller;
 import org.opensaml.saml.saml2.core.impl.NameIDBuilder;
+import org.opensaml.saml.saml2.core.impl.SessionIndexBuilder;
 import org.w3c.dom.Element;
 
 import org.springframework.security.core.Authentication;
@@ -67,6 +69,8 @@ final class OpenSamlLogoutRequestResolver {
 
 	private final NameIDBuilder nameIdBuilder;
 
+	private final SessionIndexBuilder sessionIndexBuilder;
+
 	private final LogoutRequestBuilder logoutRequestBuilder;
 
 	private final RelyingPartyRegistrationResolver relyingPartyRegistrationResolver;
@@ -87,6 +91,9 @@ final class OpenSamlLogoutRequestResolver {
 		Assert.notNull(this.issuerBuilder, "issuerBuilder must be configured in OpenSAML");
 		this.nameIdBuilder = (NameIDBuilder) registry.getBuilderFactory().getBuilder(NameID.DEFAULT_ELEMENT_NAME);
 		Assert.notNull(this.nameIdBuilder, "nameIdBuilder must be configured in OpenSAML");
+		this.sessionIndexBuilder = (SessionIndexBuilder) registry.getBuilderFactory()
+				.getBuilder(SessionIndex.DEFAULT_ELEMENT_NAME);
+		Assert.notNull(this.sessionIndexBuilder, "sessionIndexBuilder must be configured in OpenSAML");
 	}
 
 	/**
@@ -122,6 +129,14 @@ final class OpenSamlLogoutRequestResolver {
 		NameID nameId = this.nameIdBuilder.buildObject();
 		nameId.setValue(authentication.getName());
 		logoutRequest.setNameID(nameId);
+		if (authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal) {
+			Saml2AuthenticatedPrincipal principal = (Saml2AuthenticatedPrincipal) authentication.getPrincipal();
+			for (String index : principal.getSessionIndexes()) {
+				SessionIndex sessionIndex = this.sessionIndexBuilder.buildObject();
+				sessionIndex.setSessionIndex(index);
+				logoutRequest.getSessionIndexes().add(sessionIndex);
+			}
+		}
 		logoutRequestConsumer.accept(registration, logoutRequest);
 		if (logoutRequest.getID() == null) {
 			logoutRequest.setID("LR" + UUID.randomUUID());

+ 13 - 2
saml2/saml2-service-provider/src/opensaml4Main/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProvider.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2021 the original author or authors.
+ * Copyright 2002-2022 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -57,6 +57,7 @@ import org.opensaml.saml.saml2.assertion.impl.DelegationRestrictionConditionVali
 import org.opensaml.saml.saml2.core.Assertion;
 import org.opensaml.saml.saml2.core.Attribute;
 import org.opensaml.saml.saml2.core.AttributeStatement;
+import org.opensaml.saml.saml2.core.AuthnStatement;
 import org.opensaml.saml.saml2.core.Condition;
 import org.opensaml.saml.saml2.core.EncryptedAssertion;
 import org.opensaml.saml.saml2.core.OneTimeUse;
@@ -425,7 +426,9 @@ public final class OpenSaml4AuthenticationProvider implements AuthenticationProv
 			Assertion assertion = CollectionUtils.firstElement(response.getAssertions());
 			String username = assertion.getSubject().getNameID().getValue();
 			Map<String, List<Object>> attributes = getAssertionAttributes(assertion);
-			DefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(username, attributes);
+			List<String> sessionIndexes = getSessionIndexes(assertion);
+			DefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(username, attributes,
+					sessionIndexes);
 			String registrationId = responseToken.token.getRelyingPartyRegistration().getRegistrationId();
 			principal.setRelyingPartyRegistrationId(registrationId);
 			return new Saml2Authentication(principal, token.getSaml2Response(),
@@ -617,6 +620,14 @@ public final class OpenSaml4AuthenticationProvider implements AuthenticationProv
 		return attributeMap;
 	}
 
+	private static List<String> getSessionIndexes(Assertion assertion) {
+		List<String> sessionIndexes = new ArrayList<>();
+		for (AuthnStatement statement : assertion.getAuthnStatements()) {
+			sessionIndexes.add(statement.getSessionIndex());
+		}
+		return sessionIndexes;
+	}
+
 	private static Object getXmlObjectValue(XMLObject xmlObject) {
 		if (xmlObject instanceof XSAny) {
 			return ((XSAny) xmlObject).getTextContent();

+ 2 - 1
saml2/saml2-service-provider/src/opensaml4Test/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProviderTests.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2021 the original author or authors.
+ * Copyright 2002-2022 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -247,6 +247,7 @@ public class OpenSaml4AuthenticationProviderTests {
 		expected.put("registeredDate", Collections.singletonList(registeredDate));
 		assertThat((String) principal.getFirstAttribute("name")).isEqualTo("John Doe");
 		assertThat(principal.getAttributes()).isEqualTo(expected);
+		assertThat(principal.getSessionIndexes()).contains("session-index");
 	}
 
 	@Test

+ 5 - 1
saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/TestOpenSamlObjects.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2021 the original author or authors.
+ * Copyright 2002-2022 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -49,6 +49,7 @@ import org.opensaml.saml.saml2.core.Attribute;
 import org.opensaml.saml.saml2.core.AttributeStatement;
 import org.opensaml.saml.saml2.core.AttributeValue;
 import org.opensaml.saml.saml2.core.AuthnRequest;
+import org.opensaml.saml.saml2.core.AuthnStatement;
 import org.opensaml.saml.saml2.core.Conditions;
 import org.opensaml.saml.saml2.core.EncryptedAssertion;
 import org.opensaml.saml.saml2.core.EncryptedAttribute;
@@ -153,6 +154,9 @@ public final class TestOpenSamlObjects {
 		confirmationData.setRecipient(recipientUri);
 		subjectConfirmation.setSubjectConfirmationData(confirmationData);
 		assertion.getSubject().getSubjectConfirmations().add(subjectConfirmation);
+		AuthnStatement statement = build(AuthnStatement.DEFAULT_ELEMENT_NAME);
+		statement.setSessionIndex("session-index");
+		assertion.getAuthnStatements().add(statement);
 		return assertion;
 	}
 

+ 6 - 2
saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSamlLogoutRequestResolverTests.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2021 the original author or authors.
+ * Copyright 2002-2022 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@ package org.springframework.security.saml2.provider.service.web.authentication.l
 import java.io.ByteArrayInputStream;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 
 import javax.servlet.http.HttpServletRequest;
@@ -86,10 +87,13 @@ public class OpenSamlLogoutRequestResolverTests {
 		Saml2MessageBinding binding = registration.getAssertingPartyDetails().getSingleLogoutServiceBinding();
 		LogoutRequest logoutRequest = getLogoutRequest(saml2LogoutRequest.getSamlRequest(), binding);
 		assertThat(logoutRequest.getNameID().getValue()).isEqualTo(authentication.getName());
+		assertThat(logoutRequest.getSessionIndexes()).hasSize(1);
+		assertThat(logoutRequest.getSessionIndexes().get(0).getSessionIndex()).isEqualTo("session-index");
 	}
 
 	private Saml2Authentication authentication(RelyingPartyRegistration registration) {
-		DefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal("user", new HashMap<>());
+		DefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal("user", new HashMap<>(),
+				Arrays.asList("session-index"));
 		principal.setRelyingPartyRegistrationId(registration.getRegistrationId());
 		return new Saml2Authentication(principal, "response", new ArrayList<>());
 	}