Quellcode durchsuchen

Saml2WebSsoAuthenticationFilter adds authentication details

Closes gh-7722
Daniel Garnier-Moiroux vor 3 Jahren
Ursprung
Commit
2fb8e66bc8

+ 9 - 0
saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/servlet/filter/Saml2WebSsoAuthenticationFilter.java

@@ -19,6 +19,7 @@ package org.springframework.security.saml2.provider.service.servlet.filter;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.springframework.security.authentication.AbstractAuthenticationToken;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.AuthenticationException;
 import org.springframework.security.saml2.core.Saml2Error;
@@ -109,6 +110,7 @@ public class Saml2WebSsoAuthenticationFilter extends AbstractAuthenticationProce
 					"No relying party registration found");
 			throw new Saml2AuthenticationException(saml2Error);
 		}
+		setDetails(request, authentication);
 		this.authenticationRequestRepository.removeAuthenticationRequest(request, response);
 		return getAuthenticationManager().authenticate(authentication);
 	}
@@ -138,4 +140,11 @@ public class Saml2WebSsoAuthenticationFilter extends AbstractAuthenticationProce
 		}
 	}
 
+	private void setDetails(HttpServletRequest request, Authentication authentication) {
+		if (AbstractAuthenticationToken.class.isAssignableFrom(authentication.getClass())) {
+			Object details = this.authenticationDetailsSource.buildDetails(request);
+			((AbstractAuthenticationToken) authentication).setDetails(details);
+		}
+	}
+
 }

+ 6 - 1
saml2/saml2-service-provider/src/opensaml4Main/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProvider.java

@@ -446,7 +446,12 @@ public final class OpenSaml4AuthenticationProvider implements AuthenticationProv
 			String serializedResponse = token.getSaml2Response();
 			Response response = parse(serializedResponse);
 			process(token, response);
-			return this.responseAuthenticationConverter.convert(new ResponseToken(response, token));
+			AbstractAuthenticationToken authenticationResponse = this.responseAuthenticationConverter
+					.convert(new ResponseToken(response, token));
+			if (authenticationResponse != null) {
+				authenticationResponse.setDetails(authentication.getDetails());
+			}
+			return authenticationResponse;
 		}
 		catch (Saml2AuthenticationException ex) {
 			throw ex;

+ 15 - 0
saml2/saml2-service-provider/src/opensaml4Test/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProviderTests.java

@@ -352,6 +352,21 @@ public class OpenSaml4AuthenticationProviderTests {
 				.satisfies(errorOf(Saml2ErrorCodes.DECRYPTION_ERROR, "Failed to decrypt EncryptedData"));
 	}
 
+	@Test
+	public void authenticateWhenAuthenticationHasDetailsThenSucceeds() {
+		Response response = response();
+		Assertion assertion = assertion();
+		assertion.getSubject().getSubjectConfirmations()
+				.forEach((sc) -> sc.getSubjectConfirmationData().setAddress("10.10.10.10"));
+		TestOpenSamlObjects.signed(assertion, TestSaml2X509Credentials.assertingPartySigningCredential(),
+				RELYING_PARTY_ENTITY_ID);
+		response.getAssertions().add(assertion);
+		Saml2AuthenticationToken token = token(response, verifying(registration()));
+		token.setDetails("some-details");
+		Authentication authentication = this.provider.authenticate(token);
+		assertThat(authentication.getDetails()).isEqualTo("some-details");
+	}
+
 	@Test
 	public void writeObjectWhenTypeIsSaml2AuthenticationThenNoException() throws IOException {
 		Response response = response();

+ 34 - 0
saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/servlet/filter/Saml2WebSsoAuthenticationFilterTests.java

@@ -25,12 +25,14 @@ import org.junit.jupiter.api.Test;
 import org.springframework.mock.web.MockFilterChain;
 import org.springframework.mock.web.MockHttpServletRequest;
 import org.springframework.mock.web.MockHttpServletResponse;
+import org.springframework.security.authentication.AuthenticationDetailsSource;
 import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.authentication.TestingAuthenticationToken;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.saml2.core.Saml2ParameterNames;
 import org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest;
 import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;
+import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationToken;
 import org.springframework.security.saml2.provider.service.authentication.TestSaml2AuthenticationTokens;
 import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
 import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
@@ -40,11 +42,13 @@ import org.springframework.security.saml2.provider.service.web.DefaultRelyingPar
 import org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver;
 import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationTokenConverter;
 import org.springframework.security.web.authentication.AuthenticationConverter;
+import org.springframework.security.web.authentication.WebAuthenticationDetails;
 import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
 import org.springframework.security.web.util.matcher.RequestMatcher;
 
 import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
 import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+import static org.assertj.core.api.Assertions.assertThatNoException;
 import static org.mockito.BDDMockito.given;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
@@ -119,6 +123,36 @@ public class Saml2WebSsoAuthenticationFilterTests {
 		verify(authenticationRequestRepository).removeAuthenticationRequest(this.request, this.response);
 	}
 
+	@Test
+	public void attemptAuthenticationAddsDetails() {
+		AuthenticationConverter authenticationConverter = mock(AuthenticationConverter.class);
+		final Saml2AuthenticationToken token = TestSaml2AuthenticationTokens.token();
+		given(authenticationConverter.convert(this.request)).willReturn(token);
+		final AuthenticationDetailsSource authenticationDetailsSource = mock(AuthenticationDetailsSource.class);
+		final WebAuthenticationDetails details = mock(WebAuthenticationDetails.class);
+		given(authenticationDetailsSource.buildDetails(this.request)).willReturn(details);
+		this.filter = new Saml2WebSsoAuthenticationFilter(authenticationConverter, "/some/other/path/{registrationId}");
+		this.filter.setAuthenticationManager((authentication) -> null);
+		this.filter.setAuthenticationDetailsSource(authenticationDetailsSource);
+		this.request.setPathInfo("/some/other/path/idp-registration-id");
+		this.filter.attemptAuthentication(this.request, this.response);
+		Assertions.assertEquals(details, token.getDetails());
+	}
+
+	@Test
+	public void attemptAuthenticationWhenAuthenticationNotAbstractAuthenticationTokenDoesNotAddDetails() {
+		AuthenticationConverter authenticationConverter = mock(AuthenticationConverter.class);
+		final Authentication authenticationWithoutDetails = mock(Authentication.class);
+		given(authenticationConverter.convert(this.request)).willReturn(authenticationWithoutDetails);
+		final AuthenticationDetailsSource authenticationDetailsSource = mock(AuthenticationDetailsSource.class);
+		this.filter = new Saml2WebSsoAuthenticationFilter(authenticationConverter, "/some/other/path/{registrationId}");
+		this.filter.setAuthenticationManager((authentication) -> null);
+		this.filter.setAuthenticationDetailsSource(authenticationDetailsSource);
+		this.request.setPathInfo("/some/other/path/idp-registration-id");
+		assertThatNoException().isThrownBy(() -> this.filter.attemptAuthentication(this.request, this.response));
+		verifyNoInteractions(authenticationDetailsSource);
+	}
+
 	@Test
 	public void setAuthenticationRequestRepositoryWhenNullThenThrowsIllegalArgument() {
 		assertThatIllegalArgumentException().isThrownBy(() -> this.filter.setAuthenticationRequestRepository(null))