Skip to content

Commit e1c211c

Browse files
committed
Use SecurityContextHolderStrategy for Switch User
Issue gh-11060
1 parent 98995f2 commit e1c211c

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
@@ -36,7 +36,10 @@
3636
import org.springframework.security.core.GrantedAuthority;
3737
import org.springframework.security.core.authority.AuthorityUtils;
3838
import org.springframework.security.core.authority.SimpleGrantedAuthority;
39+
import org.springframework.security.core.context.SecurityContext;
3940
import org.springframework.security.core.context.SecurityContextHolder;
41+
import org.springframework.security.core.context.SecurityContextHolderStrategy;
42+
import org.springframework.security.core.context.SecurityContextImpl;
4043
import org.springframework.security.core.userdetails.User;
4144
import org.springframework.security.core.userdetails.UserDetails;
4245
import org.springframework.security.core.userdetails.UserDetailsService;
@@ -49,8 +52,10 @@
4952
import static org.assertj.core.api.Assertions.assertThat;
5053
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
5154
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
55+
import static org.mockito.Mockito.atLeastOnce;
5256
import static org.mockito.Mockito.mock;
5357
import static org.mockito.Mockito.never;
58+
import static org.mockito.Mockito.spy;
5459
import static org.mockito.Mockito.verify;
5560

5661
/**
@@ -416,6 +421,21 @@ public void modificationOfAuthoritiesWorks() {
416421
assertThat(AuthorityUtils.authorityListToSet(result.getAuthorities())).contains("ROLE_NEW");
417422
}
418423

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

513533
}
514534

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

0 commit comments

Comments
 (0)