Skip to content

Commit 44d99f4

Browse files
committed
Use SecurityContextHolderStrategy for Switch User
Issue gh-11060
1 parent 83b3bb3 commit 44d99f4

File tree

2 files changed

+72
-7
lines changed

2 files changed

+72
-7
lines changed

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

+22-7
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
import org.springframework.security.core.SpringSecurityMessageSource;
5050
import org.springframework.security.core.context.SecurityContext;
5151
import org.springframework.security.core.context.SecurityContextHolder;
52+
import org.springframework.security.core.context.SecurityContextHolderStrategy;
5253
import org.springframework.security.core.userdetails.UserDetails;
5354
import org.springframework.security.core.userdetails.UserDetailsChecker;
5455
import org.springframework.security.core.userdetails.UserDetailsService;
@@ -114,6 +115,9 @@ public class SwitchUserFilter extends GenericFilterBean implements ApplicationEv
114115

115116
public static final String ROLE_PREVIOUS_ADMINISTRATOR = "ROLE_PREVIOUS_ADMINISTRATOR";
116117

118+
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
119+
.getContextHolderStrategy();
120+
117121
private ApplicationEventPublisher eventPublisher;
118122

119123
private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();
@@ -175,9 +179,9 @@ private void doFilter(HttpServletRequest request, HttpServletResponse response,
175179
try {
176180
Authentication targetUser = attemptSwitchUser(request);
177181
// update the current context to the new target user
178-
SecurityContext context = SecurityContextHolder.createEmptyContext();
182+
SecurityContext context = this.securityContextHolderStrategy.createEmptyContext();
179183
context.setAuthentication(targetUser);
180-
SecurityContextHolder.setContext(context);
184+
this.securityContextHolderStrategy.setContext(context);
181185
this.logger.debug(LogMessage.format("Set SecurityContextHolder to %s", targetUser));
182186
// redirect to target url
183187
this.successHandler.onAuthenticationSuccess(request, response, targetUser);
@@ -192,9 +196,9 @@ private void doFilter(HttpServletRequest request, HttpServletResponse response,
192196
// get the original authentication object (if exists)
193197
Authentication originalUser = attemptExitUser(request);
194198
// update the current context back to the original user
195-
SecurityContext context = SecurityContextHolder.createEmptyContext();
199+
SecurityContext context = this.securityContextHolderStrategy.createEmptyContext();
196200
context.setAuthentication(originalUser);
197-
SecurityContextHolder.setContext(context);
201+
this.securityContextHolderStrategy.setContext(context);
198202
this.logger.debug(LogMessage.format("Set SecurityContextHolder to %s", originalUser));
199203
// redirect to target url
200204
this.successHandler.onAuthenticationSuccess(request, response, originalUser);
@@ -228,7 +232,7 @@ protected Authentication attemptSwitchUser(HttpServletRequest request) throws Au
228232
// publish event
229233
if (this.eventPublisher != null) {
230234
this.eventPublisher.publishEvent(new AuthenticationSwitchUserEvent(
231-
SecurityContextHolder.getContext().getAuthentication(), targetUser));
235+
this.securityContextHolderStrategy.getContext().getAuthentication(), targetUser));
232236
}
233237
return targetUserRequest;
234238
}
@@ -244,7 +248,7 @@ protected Authentication attemptSwitchUser(HttpServletRequest request) throws Au
244248
protected Authentication attemptExitUser(HttpServletRequest request)
245249
throws AuthenticationCredentialsNotFoundException {
246250
// need to check to see if the current user has a SwitchUserGrantedAuthority
247-
Authentication current = SecurityContextHolder.getContext().getAuthentication();
251+
Authentication current = this.securityContextHolderStrategy.getContext().getAuthentication();
248252
if (current == null) {
249253
throw new AuthenticationCredentialsNotFoundException(this.messages
250254
.getMessage("SwitchUserFilter.noCurrentUser", "No current user associated with this request"));
@@ -310,7 +314,7 @@ private Authentication getCurrentAuthentication(HttpServletRequest request) {
310314
return attemptExitUser(request);
311315
}
312316
catch (AuthenticationCredentialsNotFoundException ex) {
313-
return SecurityContextHolder.getContext().getAuthentication();
317+
return this.securityContextHolderStrategy.getContext().getAuthentication();
314318
}
315319
}
316320

@@ -510,6 +514,17 @@ public void setSwitchAuthorityRole(String switchAuthorityRole) {
510514
this.switchAuthorityRole = switchAuthorityRole;
511515
}
512516

517+
/**
518+
* Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use
519+
* the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.
520+
*
521+
* @since 5.8
522+
*/
523+
public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
524+
Assert.notNull(securityContextHolderStrategy, "securityContextHolderStrategy cannot be null");
525+
this.securityContextHolderStrategy = securityContextHolderStrategy;
526+
}
527+
513528
private static RequestMatcher createMatcher(String pattern) {
514529
return new AntPathRequestMatcher(pattern, "POST", true, new UrlPathHelper());
515530
}

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

+50
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,10 @@
3535
import org.springframework.security.core.GrantedAuthority;
3636
import org.springframework.security.core.authority.AuthorityUtils;
3737
import org.springframework.security.core.authority.SimpleGrantedAuthority;
38+
import org.springframework.security.core.context.SecurityContext;
3839
import org.springframework.security.core.context.SecurityContextHolder;
40+
import org.springframework.security.core.context.SecurityContextHolderStrategy;
41+
import org.springframework.security.core.context.SecurityContextImpl;
3942
import org.springframework.security.core.userdetails.User;
4043
import org.springframework.security.core.userdetails.UserDetails;
4144
import org.springframework.security.core.userdetails.UserDetailsService;
@@ -48,8 +51,10 @@
4851
import static org.assertj.core.api.Assertions.assertThat;
4952
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
5053
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
54+
import static org.mockito.Mockito.atLeastOnce;
5155
import static org.mockito.Mockito.mock;
5256
import static org.mockito.Mockito.never;
57+
import static org.mockito.Mockito.spy;
5358
import static org.mockito.Mockito.verify;
5459

5560
/**
@@ -415,6 +420,21 @@ public void modificationOfAuthoritiesWorks() {
415420
assertThat(AuthorityUtils.authorityListToSet(result.getAuthorities())).contains("ROLE_NEW");
416421
}
417422

423+
@Test
424+
public void doFilterWhenCustomSecurityContextRepositoryThenUses() {
425+
SecurityContextHolderStrategy securityContextHolderStrategy = spy(new MockSecurityContextHolderStrategy(
426+
UsernamePasswordAuthenticationToken.unauthenticated("dano", "hawaii50")));
427+
MockHttpServletRequest request = new MockHttpServletRequest();
428+
request.addParameter(SwitchUserFilter.SPRING_SECURITY_SWITCH_USERNAME_KEY, "jacklord");
429+
SwitchUserFilter filter = new SwitchUserFilter();
430+
filter.setSecurityContextHolderStrategy(securityContextHolderStrategy);
431+
filter.setUserDetailsService(new MockUserDetailsService());
432+
Authentication result = filter.attemptSwitchUser(request);
433+
assertThat(result).isNotNull();
434+
assertThat(result.getName()).isEqualTo("jacklord");
435+
verify(securityContextHolderStrategy, atLeastOnce()).getContext();
436+
}
437+
418438
// SEC-1763
419439
@Test
420440
public void nestedSwitchesAreNotAllowed() {
@@ -511,4 +531,34 @@ else if ("steve".equals(username)) {
511531

512532
}
513533

534+
static final class MockSecurityContextHolderStrategy implements SecurityContextHolderStrategy {
535+
536+
private SecurityContext mock;
537+
538+
private MockSecurityContextHolderStrategy(Authentication authentication) {
539+
this.mock = new SecurityContextImpl(authentication);
540+
}
541+
542+
@Override
543+
public void clearContext() {
544+
this.mock = null;
545+
}
546+
547+
@Override
548+
public SecurityContext getContext() {
549+
return this.mock;
550+
}
551+
552+
@Override
553+
public void setContext(SecurityContext context) {
554+
this.mock = context;
555+
}
556+
557+
@Override
558+
public SecurityContext createEmptyContext() {
559+
return new SecurityContextImpl();
560+
}
561+
562+
}
563+
514564
}

0 commit comments

Comments
 (0)