Add SecurityContextHolderStrategy Java Configuration for Saml2

Issue gh-11061
This commit is contained in:
Josh Cummings 2022-06-21 17:12:11 -06:00
parent e90a11b1c0
commit 19181a5afd
No known key found for this signature in database
GPG Key ID: A306A51F43B8E5A5
4 changed files with 57 additions and 3 deletions

View File

@ -243,6 +243,7 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
relyingPartyRegistrationRepository(http);
this.saml2WebSsoAuthenticationFilter = new Saml2WebSsoAuthenticationFilter(getAuthenticationConverter(http),
this.loginProcessingUrl);
this.saml2WebSsoAuthenticationFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
setAuthenticationRequestRepository(http, this.saml2WebSsoAuthenticationFilter);
setAuthenticationFilter(this.saml2WebSsoAuthenticationFilter);
super.loginProcessingUrl(this.loginProcessingUrl);

View File

@ -31,7 +31,7 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.LogoutConfigurer;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;
import org.springframework.security.saml2.provider.service.authentication.logout.OpenSamlLogoutRequestValidator;
import org.springframework.security.saml2.provider.service.authentication.logout.OpenSamlLogoutResponseValidator;
@ -248,6 +248,7 @@ public final class Saml2LogoutConfigurer<H extends HttpSecurityBuilder<H>>
Saml2LogoutRequestFilter filter = new Saml2LogoutRequestFilter(registrations,
this.logoutRequestConfigurer.logoutRequestValidator(), logoutResponseResolver, logoutHandlers);
filter.setLogoutRequestMatcher(createLogoutRequestMatcher());
filter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
return postProcess(filter);
}
@ -271,7 +272,7 @@ public final class Saml2LogoutConfigurer<H extends HttpSecurityBuilder<H>>
private RequestMatcher createLogoutMatcher() {
RequestMatcher logout = new AntPathRequestMatcher(this.logoutUrl, "POST");
RequestMatcher saml2 = new Saml2RequestMatcher();
RequestMatcher saml2 = new Saml2RequestMatcher(getSecurityContextHolderStrategy());
return new AndRequestMatcher(logout, saml2);
}
@ -464,9 +465,15 @@ public final class Saml2LogoutConfigurer<H extends HttpSecurityBuilder<H>>
private static class Saml2RequestMatcher implements RequestMatcher {
private final SecurityContextHolderStrategy securityContextHolderStrategy;
Saml2RequestMatcher(SecurityContextHolderStrategy securityContextHolderStrategy) {
this.securityContextHolderStrategy = securityContextHolderStrategy;
}
@Override
public boolean matches(HttpServletRequest request) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Authentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();
if (authentication == null) {
return false;
}

View File

@ -45,6 +45,7 @@ import org.springframework.mock.web.MockHttpSession;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.SecurityContextChangedListenerConfig;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.test.SpringTestContext;
@ -53,6 +54,8 @@ import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextChangedListener;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.saml2.core.Saml2ErrorCodes;
import org.springframework.security.saml2.core.Saml2Utils;
import org.springframework.security.saml2.core.TestSaml2X509Credentials;
@ -91,10 +94,13 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.springframework.security.config.Customizer.withDefaults;
import static org.springframework.security.config.annotation.SecurityContextChangedListenerArgumentMatchers.setAuthentication;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
@ -162,6 +168,26 @@ public class Saml2LoginConfigurerTests {
// @formatter:on
}
@Test
public void saml2LoginWhenCustomSecurityContextHolderStrategyThenUses() throws Exception {
this.spring
.register(Saml2LoginConfig.class, SecurityContextChangedListenerConfig.class, ResourceController.class)
.autowire();
// @formatter:off
MockHttpSession session = (MockHttpSession) this.mvc
.perform(post("/login/saml2/sso/registration-id")
.param("SAMLResponse", SIGNED_RESPONSE))
.andExpect(redirectedUrl("/")).andReturn().getRequest().getSession(false);
this.mvc.perform(get("/").session(session))
.andExpect(content().string("test@saml.user"));
// @formatter:on
SecurityContextHolderStrategy strategy = this.spring.getContext().getBean(SecurityContextHolderStrategy.class);
verify(strategy, atLeastOnce()).getContext();
SecurityContextChangedListener listener = this.spring.getContext()
.getBean(SecurityContextChangedListener.class);
verify(listener, times(2)).securityContextChanged(setAuthentication(Saml2Authentication.class));
}
@Test
public void saml2LoginWhenConfiguringAuthenticationManagerThenTheManagerIsUsed() throws Exception {
// setup application context

View File

@ -43,11 +43,13 @@ import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.SecurityContextChangedListenerConfig;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.saml2.core.Saml2Utils;
import org.springframework.security.saml2.core.Saml2X509Credential;
import org.springframework.security.saml2.core.TestSaml2X509Credentials;
@ -271,6 +273,24 @@ public class Saml2LogoutConfigurerTests {
verify(getBean(LogoutHandler.class)).logout(any(), any(), any());
}
@Test
public void saml2LogoutRequestWhenCustomSecurityContextHolderStrategyThenUses() throws Exception {
this.spring.register(Saml2LogoutDefaultsConfig.class, SecurityContextChangedListenerConfig.class).autowire();
DefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal("user",
Collections.emptyMap());
principal.setRelyingPartyRegistrationId("get");
Saml2Authentication user = new Saml2Authentication(principal, "response",
AuthorityUtils.createAuthorityList("ROLE_USER"));
MvcResult result = this.mvc.perform(get("/logout/saml2/slo").param("SAMLRequest", this.apLogoutRequest)
.param("RelayState", this.apLogoutRequestRelayState).param("SigAlg", this.apLogoutRequestSigAlg)
.param("Signature", this.apLogoutRequestSignature).with(samlQueryString()).with(authentication(user)))
.andExpect(status().isFound()).andReturn();
String location = result.getResponse().getHeader("Location");
assertThat(location).startsWith("https://ap.example.org/logout/saml2/response");
verify(getBean(LogoutHandler.class)).logout(any(), any(), any());
verify(getBean(SecurityContextHolderStrategy.class), atLeastOnce()).getContext();
}
// gh-11235
@Test
public void saml2LogoutRequestWhenLowercaseEncodingThenLogsOutAndSendsLogoutResponse() throws Exception {