Skip to content

Commit ae46032

Browse files
Merge branch '5.7.x' into 5.8.x
Closes gh-12510
2 parents 88a8ef6 + ffdb397 commit ae46032

File tree

2 files changed

+79
-0
lines changed

2 files changed

+79
-0
lines changed

web/src/main/java/org/springframework/security/web/authentication/switchuser/SwitchUserFilter.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@
5959
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
6060
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
6161
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
62+
import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
63+
import org.springframework.security.web.context.SecurityContextRepository;
6264
import org.springframework.security.web.util.UrlUtils;
6365
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
6466
import org.springframework.security.web.util.matcher.RequestMatcher;
@@ -146,6 +148,8 @@ public class SwitchUserFilter extends GenericFilterBean implements ApplicationEv
146148

147149
private AuthenticationFailureHandler failureHandler;
148150

151+
private SecurityContextRepository securityContextRepository = new RequestAttributeSecurityContextRepository();
152+
149153
@Override
150154
public void afterPropertiesSet() {
151155
Assert.notNull(this.userDetailsService, "userDetailsService must be specified");
@@ -183,6 +187,7 @@ private void doFilter(HttpServletRequest request, HttpServletResponse response,
183187
context.setAuthentication(targetUser);
184188
this.securityContextHolderStrategy.setContext(context);
185189
this.logger.debug(LogMessage.format("Set SecurityContextHolder to %s", targetUser));
190+
this.securityContextRepository.saveContext(context, request, response);
186191
// redirect to target url
187192
this.successHandler.onAuthenticationSuccess(request, response, targetUser);
188193
}
@@ -200,6 +205,7 @@ private void doFilter(HttpServletRequest request, HttpServletResponse response,
200205
context.setAuthentication(originalUser);
201206
this.securityContextHolderStrategy.setContext(context);
202207
this.logger.debug(LogMessage.format("Set SecurityContextHolder to %s", originalUser));
208+
this.securityContextRepository.saveContext(context, request, response);
203209
// redirect to target url
204210
this.successHandler.onAuthenticationSuccess(request, response, originalUser);
205211
return;
@@ -525,6 +531,19 @@ public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy secur
525531
this.securityContextHolderStrategy = securityContextHolderStrategy;
526532
}
527533

534+
/**
535+
* Sets the {@link SecurityContextRepository} to save the {@link SecurityContext} on
536+
* switch user success. The default is
537+
* {@link RequestAttributeSecurityContextRepository}.
538+
* @param securityContextRepository the {@link SecurityContextRepository} to use.
539+
* Cannot be null.
540+
* @since 5.7.7
541+
*/
542+
public void setSecurityContextRepository(SecurityContextRepository securityContextRepository) {
543+
Assert.notNull(securityContextRepository, "securityContextRepository cannot be null");
544+
this.securityContextRepository = securityContextRepository;
545+
}
546+
528547
private static RequestMatcher createMatcher(String pattern) {
529548
return new AntPathRequestMatcher(pattern, "POST", true, new UrlPathHelper());
530549
}

web/src/test/java/org/springframework/security/web/authentication/switchuser/SwitchUserFilterTests.java

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,18 @@
1616

1717
package org.springframework.security.web.authentication.switchuser;
1818

19+
import java.io.IOException;
1920
import java.util.ArrayList;
2021
import java.util.List;
2122

2223
import javax.servlet.FilterChain;
24+
import javax.servlet.ServletException;
2325

2426
import org.junit.jupiter.api.AfterEach;
2527
import org.junit.jupiter.api.BeforeEach;
2628
import org.junit.jupiter.api.Test;
2729

30+
import org.springframework.mock.web.MockFilterChain;
2831
import org.springframework.mock.web.MockHttpServletRequest;
2932
import org.springframework.mock.web.MockHttpServletResponse;
3033
import org.springframework.security.authentication.AccountExpiredException;
@@ -47,11 +50,15 @@
4750
import org.springframework.security.util.FieldUtils;
4851
import org.springframework.security.web.DefaultRedirectStrategy;
4952
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
53+
import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
54+
import org.springframework.security.web.context.SecurityContextRepository;
5055
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
56+
import org.springframework.test.util.ReflectionTestUtils;
5157

5258
import static org.assertj.core.api.Assertions.assertThat;
5359
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
5460
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
61+
import static org.mockito.ArgumentMatchers.any;
5562
import static org.mockito.Mockito.atLeastOnce;
5663
import static org.mockito.Mockito.mock;
5764
import static org.mockito.Mockito.never;
@@ -503,6 +510,59 @@ public void setSwitchFailureUrlWhenValidThenNoException() {
503510
filter.setSwitchFailureUrl("/foo");
504511
}
505512

513+
@Test
514+
void filterWhenDefaultSecurityContextRepositoryThenRequestAttributeRepository() {
515+
SwitchUserFilter switchUserFilter = new SwitchUserFilter();
516+
assertThat(ReflectionTestUtils.getField(switchUserFilter, "securityContextRepository"))
517+
.isInstanceOf(RequestAttributeSecurityContextRepository.class);
518+
}
519+
520+
@Test
521+
void doFilterWhenSwitchUserThenSaveSecurityContext() throws ServletException, IOException {
522+
SecurityContextRepository securityContextRepository = mock(SecurityContextRepository.class);
523+
MockHttpServletRequest request = new MockHttpServletRequest();
524+
MockHttpServletResponse response = new MockHttpServletResponse();
525+
MockFilterChain filterChain = new MockFilterChain();
526+
request.setParameter(SwitchUserFilter.SPRING_SECURITY_SWITCH_USERNAME_KEY, "jacklord");
527+
request.setRequestURI("/login/impersonate");
528+
SwitchUserFilter filter = new SwitchUserFilter();
529+
filter.setSecurityContextRepository(securityContextRepository);
530+
filter.setUserDetailsService(new MockUserDetailsService());
531+
filter.setTargetUrl("/target");
532+
filter.afterPropertiesSet();
533+
534+
filter.doFilter(request, response, filterChain);
535+
536+
verify(securityContextRepository).saveContext(any(), any(), any());
537+
}
538+
539+
@Test
540+
void doFilterWhenExitUserThenSaveSecurityContext() throws ServletException, IOException {
541+
UsernamePasswordAuthenticationToken source = UsernamePasswordAuthenticationToken.authenticated("dano",
542+
"hawaii50", ROLES_12);
543+
// set current user (Admin)
544+
List<GrantedAuthority> adminAuths = new ArrayList<>(ROLES_12);
545+
adminAuths.add(new SwitchUserGrantedAuthority("PREVIOUS_ADMINISTRATOR", source));
546+
UsernamePasswordAuthenticationToken admin = UsernamePasswordAuthenticationToken.authenticated("jacklord",
547+
"hawaii50", adminAuths);
548+
SecurityContextHolder.getContext().setAuthentication(admin);
549+
SecurityContextRepository securityContextRepository = mock(SecurityContextRepository.class);
550+
MockHttpServletRequest request = new MockHttpServletRequest();
551+
MockHttpServletResponse response = new MockHttpServletResponse();
552+
MockFilterChain filterChain = new MockFilterChain();
553+
request.setParameter(SwitchUserFilter.SPRING_SECURITY_SWITCH_USERNAME_KEY, "jacklord");
554+
request.setRequestURI("/logout/impersonate");
555+
SwitchUserFilter filter = new SwitchUserFilter();
556+
filter.setSecurityContextRepository(securityContextRepository);
557+
filter.setUserDetailsService(new MockUserDetailsService());
558+
filter.setTargetUrl("/target");
559+
filter.afterPropertiesSet();
560+
561+
filter.doFilter(request, response, filterChain);
562+
563+
verify(securityContextRepository).saveContext(any(), any(), any());
564+
}
565+
506566
private class MockUserDetailsService implements UserDetailsService {
507567

508568
private String password = "hawaii50";

0 commit comments

Comments
 (0)