Fix SwitchUserFilter matchers

Fixes: gh-4249
This commit is contained in:
Rob Winch 2018-09-14 09:06:04 -05:00
parent 5b26158f4d
commit 566fb939ca
2 changed files with 85 additions and 29 deletions

View File

@ -57,8 +57,11 @@ import org.springframework.security.web.authentication.SimpleUrlAuthenticationFa
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.security.web.util.UrlUtils; import org.springframework.security.web.util.UrlUtils;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.web.filter.GenericFilterBean; import org.springframework.web.filter.GenericFilterBean;
import org.springframework.web.util.UrlPathHelper;
/** /**
* Switch User processing filter responsible for user context switching. * Switch User processing filter responsible for user context switching.
@ -118,8 +121,8 @@ public class SwitchUserFilter extends GenericFilterBean
private ApplicationEventPublisher eventPublisher; private ApplicationEventPublisher eventPublisher;
private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource(); private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor(); protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
private String exitUserUrl = "/logout/impersonate"; private RequestMatcher exitUserMatcher = createMatcher("/logout/impersonate");
private String switchUserUrl = "/login/impersonate"; private RequestMatcher switchUserMatcher = createMatcher("/login/impersonate");
private String targetUrl; private String targetUrl;
private String switchFailureUrl; private String switchFailureUrl;
private String usernameParameter = SPRING_SECURITY_SWITCH_USERNAME_KEY; private String usernameParameter = SPRING_SECURITY_SWITCH_USERNAME_KEY;
@ -386,12 +389,10 @@ public class SwitchUserFilter extends GenericFilterBean
* @return <code>true</code> if the request requires a exit user, <code>false</code> * @return <code>true</code> if the request requires a exit user, <code>false</code>
* otherwise. * otherwise.
* *
* @see SwitchUserFilter#exitUserUrl * @see SwitchUserFilter#setExitUserUrl(String)
*/ */
protected boolean requiresExitUser(HttpServletRequest request) { protected boolean requiresExitUser(HttpServletRequest request) {
String uri = stripUri(request); return this.exitUserMatcher.matches(request);
return uri.endsWith(request.getContextPath() + this.exitUserUrl);
} }
/** /**
@ -402,12 +403,10 @@ public class SwitchUserFilter extends GenericFilterBean
* @return <code>true</code> if the request requires a switch, <code>false</code> * @return <code>true</code> if the request requires a switch, <code>false</code>
* otherwise. * otherwise.
* *
* @see SwitchUserFilter#switchUserUrl * @see SwitchUserFilter#setSwitchUserUrl(String)
*/ */
protected boolean requiresSwitchUser(HttpServletRequest request) { protected boolean requiresSwitchUser(HttpServletRequest request) {
String uri = stripUri(request); return this.switchUserMatcher.matches(request);
return uri.endsWith(request.getContextPath() + this.switchUserUrl);
} }
public void setApplicationEventPublisher(ApplicationEventPublisher eventPublisher) public void setApplicationEventPublisher(ApplicationEventPublisher eventPublisher)
@ -445,18 +444,40 @@ public class SwitchUserFilter extends GenericFilterBean
public void setExitUserUrl(String exitUserUrl) { public void setExitUserUrl(String exitUserUrl) {
Assert.isTrue(UrlUtils.isValidRedirectUrl(exitUserUrl), Assert.isTrue(UrlUtils.isValidRedirectUrl(exitUserUrl),
"exitUserUrl cannot be empty and must be a valid redirect URL"); "exitUserUrl cannot be empty and must be a valid redirect URL");
this.exitUserUrl = exitUserUrl; this.exitUserMatcher = createMatcher(exitUserUrl);
} }
/** /**
* Set the URL to respond to switch user processing. * Set the matcher to respond to exit user processing. This is a shortcut for
* {@link #setExitUserMatcher(RequestMatcher)}
*
* @param exitUserMatcher The exit matcher to use
*/
public void setExitUserMatcher(RequestMatcher exitUserMatcher) {
Assert.notNull(exitUserMatcher, "exitUserMatcher cannot be null");
this.exitUserMatcher = exitUserMatcher;
}
/**
* Set the URL to respond to switch user processing. This is a shortcut for
* {@link #setSwitchUserMatcher(RequestMatcher)}
* *
* @param switchUserUrl The switch user URL. * @param switchUserUrl The switch user URL.
*/ */
public void setSwitchUserUrl(String switchUserUrl) { public void setSwitchUserUrl(String switchUserUrl) {
Assert.isTrue(UrlUtils.isValidRedirectUrl(switchUserUrl), Assert.isTrue(UrlUtils.isValidRedirectUrl(switchUserUrl),
"switchUserUrl cannot be empty and must be a valid redirect URL"); "switchUserUrl cannot be empty and must be a valid redirect URL");
this.switchUserUrl = switchUserUrl; this.switchUserMatcher = createMatcher(switchUserUrl);
}
/**
* Set the matcher to respond to switch user processing.
*
* @param switchUserMatcher The switch user matcher.
*/
public void setSwitchUserMatcher(RequestMatcher switchUserMatcher) {
Assert.notNull(switchUserMatcher, "switchUserMatcher cannot be null");
this.switchUserMatcher = switchUserMatcher;
} }
/** /**
@ -541,21 +562,7 @@ public class SwitchUserFilter extends GenericFilterBean
this.switchAuthorityRole = switchAuthorityRole; this.switchAuthorityRole = switchAuthorityRole;
} }
/** private static RequestMatcher createMatcher(String pattern) {
* Strips any content after the ';' in the request URI return new AntPathRequestMatcher(pattern, null, true, new UrlPathHelper());
*
* @param request The http request
*
* @return The stripped uri
*/
private String stripUri(HttpServletRequest request) {
String uri = request.getRequestURI();
int idx = uri.indexOf(';');
if (idx > 0) {
uri = uri.substring(0, idx);
}
return uri;
} }
} }

View File

@ -40,6 +40,7 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.util.FieldUtils; import org.springframework.security.util.FieldUtils;
import org.springframework.security.web.DefaultRedirectStrategy; import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
import javax.servlet.FilterChain; import javax.servlet.FilterChain;
import java.util.*; import java.util.*;
@ -112,6 +113,29 @@ public class SwitchUserFilterTests {
assertThat(filter.requiresExitUser(request)).isTrue(); assertThat(filter.requiresExitUser(request)).isTrue();
} }
@Test
// gh-4249
public void requiresExitUserWhenEndsWithThenDoesNotMatch() {
SwitchUserFilter filter = new SwitchUserFilter();
filter.setExitUserUrl("/j_spring_security_my_exit_user");
MockHttpServletRequest request = new MockHttpServletRequest();
request.setRequestURI("/foo/bar/j_spring_security_my_exit_user");
assertThat(filter.requiresExitUser(request)).isFalse();
}
@Test
public void requiresExitUserWhenMatcherThenWorks() {
SwitchUserFilter filter = new SwitchUserFilter();
filter.setExitUserMatcher(AnyRequestMatcher.INSTANCE);
MockHttpServletRequest request = new MockHttpServletRequest();
request.setRequestURI("/foo/bar/j_spring_security_my_exit_user");
assertThat(filter.requiresExitUser(request)).isTrue();
}
@Test @Test
public void requiresSwitchMatchesCorrectly() { public void requiresSwitchMatchesCorrectly() {
SwitchUserFilter filter = new SwitchUserFilter(); SwitchUserFilter filter = new SwitchUserFilter();
@ -123,6 +147,29 @@ public class SwitchUserFilterTests {
assertThat(filter.requiresSwitchUser(request)).isTrue(); assertThat(filter.requiresSwitchUser(request)).isTrue();
} }
@Test
// gh-4249
public void requiresSwitchUserWhenEndsWithThenDoesNotMatch() {
SwitchUserFilter filter = new SwitchUserFilter();
filter.setSwitchUserUrl("/j_spring_security_my_exit_user");
MockHttpServletRequest request = new MockHttpServletRequest();
request.setRequestURI("/foo/bar/j_spring_security_my_exit_user");
assertThat(filter.requiresSwitchUser(request)).isFalse();
}
@Test
public void requiresSwitchUserWhenMatcherThenWorks() {
SwitchUserFilter filter = new SwitchUserFilter();
filter.setSwitchUserMatcher(AnyRequestMatcher.INSTANCE);
MockHttpServletRequest request = new MockHttpServletRequest();
request.setRequestURI("/foo/bar/j_spring_security_my_exit_user");
assertThat(filter.requiresSwitchUser(request)).isTrue();
}
@Test(expected = UsernameNotFoundException.class) @Test(expected = UsernameNotFoundException.class)
public void attemptSwitchToUnknownUserFails() throws Exception { public void attemptSwitchToUnknownUserFails() throws Exception {
@ -218,6 +265,7 @@ public class SwitchUserFilterTests {
@Test @Test
public void defaultProcessesFilterUrlMatchesUrlWithPathParameter() { public void defaultProcessesFilterUrlMatchesUrlWithPathParameter() {
MockHttpServletRequest request = createMockSwitchRequest(); MockHttpServletRequest request = createMockSwitchRequest();
request.setContextPath("/webapp");
SwitchUserFilter filter = new SwitchUserFilter(); SwitchUserFilter filter = new SwitchUserFilter();
filter.setSwitchUserUrl("/login/impersonate"); filter.setSwitchUserUrl("/login/impersonate");
@ -351,6 +399,7 @@ public class SwitchUserFilterTests {
// http request // http request
MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletRequest request = new MockHttpServletRequest();
request.setRequestURI("/webapp/login/impersonate"); request.setRequestURI("/webapp/login/impersonate");
request.setContextPath("/webapp");
request.addParameter(SwitchUserFilter.SPRING_SECURITY_SWITCH_USERNAME_KEY, request.addParameter(SwitchUserFilter.SPRING_SECURITY_SWITCH_USERNAME_KEY,
"jacklord"); "jacklord");