diff --git a/web/src/main/java/org/springframework/security/web/authentication/switchuser/SwitchUserFilter.java b/web/src/main/java/org/springframework/security/web/authentication/switchuser/SwitchUserFilter.java index d324bf7d83..9cdf90e9e8 100644 --- a/web/src/main/java/org/springframework/security/web/authentication/switchuser/SwitchUserFilter.java +++ b/web/src/main/java/org/springframework/security/web/authentication/switchuser/SwitchUserFilter.java @@ -75,11 +75,13 @@ import org.springframework.web.filter.GenericFilterBean; * Note: This URL will be required to have appropriate security constraints configured so that only users of that * role can access it (e.g. ROLE_ADMIN). *
- * On a successful switch, the user's SecurityContextHolder will be updated to reflect the
+ * On a successful switch, the user's SecurityContext will be updated to reflect the
* specified user and will also contain an additional
* {@link org.springframework.security.web.authentication.switchuser.SwitchUserGrantedAuthority} which contains the original user.
+ * Before switching, a check will be made on whether the user is already currently switched, and any current switch will
+ * be exited to prevent "nested" switches.
*
- * To 'exit' from a user context, the user will then need to access a URL (see exitUserUrl) that
+ * To 'exit' from a user context, the user needs to access a URL (see exitUserUrl) that
* will switch back to the original user as identified by the ROLE_PREVIOUS_ADMINISTRATOR.
*
* To configure the Switch User Processing Filter, create a bean definition for the Switch User processing @@ -288,7 +290,16 @@ public class SwitchUserFilter extends GenericFilterBean implements ApplicationEv // grant an additional authority that contains the original Authentication object // which will be used to 'exit' from the current switched user. - Authentication currentAuth = SecurityContextHolder.getContext().getAuthentication(); + + Authentication currentAuth; + + try { + // SEC-1763. Check first if we are already switched. + currentAuth = attemptExitUser(request); + } catch (AuthenticationCredentialsNotFoundException e) { + currentAuth = SecurityContextHolder.getContext().getAuthentication(); + } + GrantedAuthority switchAuthority = new SwitchUserGrantedAuthority(ROLE_PREVIOUS_ADMINISTRATOR, currentAuth); // get the original authorities diff --git a/web/src/test/java/org/springframework/security/web/authentication/switchuser/SwitchUserFilterTests.java b/web/src/test/java/org/springframework/security/web/authentication/switchuser/SwitchUserFilterTests.java index 4c1d246f76..3ac33958e3 100644 --- a/web/src/test/java/org/springframework/security/web/authentication/switchuser/SwitchUserFilterTests.java +++ b/web/src/test/java/org/springframework/security/web/authentication/switchuser/SwitchUserFilterTests.java @@ -374,6 +374,26 @@ public class SwitchUserFilterTests { assertTrue(AuthorityUtils.authorityListToSet(result.getAuthorities()).contains("ROLE_NEW")); } + // SEC-1763 + @Test + public void nestedSwitchesAreNotAllowed() throws Exception { + // original user + UsernamePasswordAuthenticationToken source = new UsernamePasswordAuthenticationToken("orig", "hawaii50", ROLES_12); + SecurityContextHolder.getContext().setAuthentication(source); + SecurityContextHolder.getContext().setAuthentication(switchToUser("jacklord")); + Authentication switched = switchToUser("dano"); + + SwitchUserGrantedAuthority switchedFrom = null; + + for (GrantedAuthority ga: switched.getAuthorities()) { + if (ga instanceof SwitchUserGrantedAuthority) { + switchedFrom = (SwitchUserGrantedAuthority)ga; + break; + } + } + + assertSame(source, switchedFrom.getSource()); + } //~ Inner Classes ==================================================================================================