SEC-1407: Use RequestMatcher instances as the FilterInvocationSecurityMetadataSource keys and in the FilterChainMap use by FilterChainProxy.
This greatly simplifies the code and opens up possibilities for other matching strategies (e.g. EL). This also means that matching is now completely strict - the order of the matchers is all that matters (not whether an HTTP method is included or not). The first matcher that returns true will be used.
This commit is contained in:
@@ -37,6 +37,9 @@ import org.springframework.security.web.FilterChainProxy;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
|
||||
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
|
||||
import org.springframework.security.web.util.AntPathRequestMatcher;
|
||||
import org.springframework.security.web.util.AnyRequestMatcher;
|
||||
import org.springframework.security.web.util.RequestMatcher;
|
||||
|
||||
/**
|
||||
* Tests {@link FilterChainProxy}.
|
||||
@@ -101,27 +104,15 @@ public class FilterChainProxyConfigTests {
|
||||
assertEquals(null, filterChainProxy.getFilters("/nomatch"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void urlStrippingPropertyIsRespected() throws Exception {
|
||||
FilterChainProxy filterChainProxy = (FilterChainProxy) appCtx.getBean("newFilterChainProxyNoDefaultPath", FilterChainProxy.class);
|
||||
|
||||
// Should only match if we are stripping the query string
|
||||
String url = "/blah.bar?x=something";
|
||||
assertNotNull(filterChainProxy.getFilters(url));
|
||||
assertEquals(2, filterChainProxy.getFilters(url).size());
|
||||
filterChainProxy.setStripQueryStringFromUrls(false);
|
||||
assertNull(filterChainProxy.getFilters(url));
|
||||
}
|
||||
|
||||
// SEC-1235
|
||||
@Test
|
||||
public void mixingPatternsAndPlaceholdersDoesntCauseOrderingIssues() throws Exception {
|
||||
FilterChainProxy filterChainProxy = (FilterChainProxy) appCtx.getBean("sec1235FilterChainProxy", FilterChainProxy.class);
|
||||
|
||||
String[] paths = filterChainProxy.getFilterChainMap().keySet().toArray(new String[0]);
|
||||
assertEquals("/login*", paths[0]);
|
||||
assertEquals("/logout", paths[1]);
|
||||
assertEquals("/**", paths[2]);
|
||||
RequestMatcher[] matchers = filterChainProxy.getFilterChainMap().keySet().toArray(new RequestMatcher[0]);
|
||||
assertEquals("/login*", ((AntPathRequestMatcher)matchers[0]).getPattern());
|
||||
assertEquals("/logout", ((AntPathRequestMatcher)matchers[1]).getPattern());
|
||||
assertTrue(matchers[2] instanceof AnyRequestMatcher);
|
||||
}
|
||||
|
||||
private void checkPathAndFilterOrder(FilterChainProxy filterChainProxy) throws Exception {
|
||||
|
||||
@@ -125,9 +125,6 @@ public class HttpSecurityBeanDefinitionParserTests {
|
||||
List<Filter> filterList = getFilters("/anyurl");
|
||||
|
||||
checkAutoConfigFilters(filterList);
|
||||
|
||||
assertEquals(true, FieldUtils.getFieldValue(appContext.getBean(BeanIds.FILTER_CHAIN_PROXY), "stripQueryStringFromUrls"));
|
||||
assertEquals(true, FieldUtils.getFieldValue(filterList.get(AUTO_CONFIG_FILTERS-1), "securityMetadataSource.stripQueryStringFromUrls"));
|
||||
}
|
||||
|
||||
@Test(expected=BeanDefinitionParsingException.class)
|
||||
@@ -136,8 +133,6 @@ public class HttpSecurityBeanDefinitionParserTests {
|
||||
}
|
||||
|
||||
private void checkAutoConfigFilters(List<Filter> filterList) throws Exception {
|
||||
// assertEquals("Expected " + AUTO_CONFIG_FILTERS + " filters in chain", AUTO_CONFIG_FILTERS, filterList.size());
|
||||
|
||||
Iterator<Filter> filters = filterList.iterator();
|
||||
|
||||
assertTrue(filters.next() instanceof SecurityContextPersistenceFilter);
|
||||
@@ -184,37 +179,34 @@ public class HttpSecurityBeanDefinitionParserTests {
|
||||
assertTrue(filters.size() == 0);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void regexPathsWorkCorrectly() throws Exception {
|
||||
setContext(
|
||||
" <http auto-config='true' path-type='regex'>" +
|
||||
" <http auto-config='true' request-matcher='regex'>" +
|
||||
" <intercept-url pattern='\\A\\/[a-z]+' filters='none' />" +
|
||||
" </http>" + AUTH_PROVIDER_XML);
|
||||
assertEquals(0, getFilters("/imlowercase").size());
|
||||
// This will be matched by the default pattern ".*"
|
||||
List<Filter> allFilters = getFilters("/ImCaughtByTheUniversalMatchPattern");
|
||||
List<Filter> allFilters = getFilters("/ImCaughtByTheAnyRequestMatcher");
|
||||
checkAutoConfigFilters(allFilters);
|
||||
assertEquals(false, FieldUtils.getFieldValue(appContext.getBean(BeanIds.FILTER_CHAIN_PROXY), "stripQueryStringFromUrls"));
|
||||
assertEquals(false, FieldUtils.getFieldValue(allFilters.get(AUTO_CONFIG_FILTERS-1), "securityMetadataSource.stripQueryStringFromUrls"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lowerCaseComparisonAttributeIsRespectedByFilterChainProxy() throws Exception {
|
||||
public void ciRegexPathsWorkCorrectly() throws Exception {
|
||||
setContext(
|
||||
" <http auto-config='true' path-type='ant' lowercase-comparisons='false'>" +
|
||||
" <intercept-url pattern='/Secure*' filters='none' />" +
|
||||
" <http auto-config='true' request-matcher='ciRegex'>" +
|
||||
" <intercept-url pattern='\\A\\/[a-z]+' filters='none' />" +
|
||||
" </http>" + AUTH_PROVIDER_XML);
|
||||
assertEquals(0, getFilters("/Secure").size());
|
||||
// These will be matched by the default pattern "/**"
|
||||
checkAutoConfigFilters(getFilters("/secure"));
|
||||
checkAutoConfigFilters(getFilters("/ImCaughtByTheUniversalMatchPattern"));
|
||||
assertEquals(0, getFilters("/imMixedCase").size());
|
||||
// This will be matched by the default pattern ".*"
|
||||
List<Filter> allFilters = getFilters("/Im_Caught_By_The_AnyRequestMatcher");
|
||||
assertTrue(allFilters.size() > 0);
|
||||
checkAutoConfigFilters(allFilters);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void formLoginWithNoLoginPageAddsDefaultLoginPageFilter() throws Exception {
|
||||
setContext(
|
||||
"<http auto-config='true' path-type='ant' lowercase-comparisons='false'>" +
|
||||
"<http auto-config='true' request-matcher='ant'>" +
|
||||
" <form-login />" +
|
||||
"</http>" + AUTH_PROVIDER_XML);
|
||||
// These will be matched by the default pattern "/**"
|
||||
@@ -315,26 +307,6 @@ public class HttpSecurityBeanDefinitionParserTests {
|
||||
assertSame(appContext.getBean("logoutHandler"), handler);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lowerCaseComparisonIsRespectedBySecurityFilterInvocationDefinitionSource() throws Exception {
|
||||
setContext(
|
||||
" <http auto-config='true' path-type='ant' lowercase-comparisons='false'>" +
|
||||
" <intercept-url pattern='/Secure*' access='ROLE_A, ROLE_B' />" +
|
||||
" <intercept-url pattern='/**' access='ROLE_C' />" +
|
||||
" </http>" + AUTH_PROVIDER_XML);
|
||||
|
||||
FilterSecurityInterceptor fis = getFilter(FilterSecurityInterceptor.class);
|
||||
|
||||
FilterInvocationSecurityMetadataSource fids = fis.getSecurityMetadataSource();
|
||||
Collection<ConfigAttribute> attrDef = fids.getAttributes(createFilterinvocation("/Secure", null));
|
||||
assertEquals(2, attrDef.size());
|
||||
assertTrue(attrDef.contains(new SecurityConfig("ROLE_A")));
|
||||
assertTrue(attrDef.contains(new SecurityConfig("ROLE_B")));
|
||||
attrDef = fids.getAttributes(createFilterinvocation("/secure", null));
|
||||
assertEquals(1, attrDef.size());
|
||||
assertTrue(attrDef.contains(new SecurityConfig("ROLE_C")));
|
||||
}
|
||||
|
||||
// SEC-1201
|
||||
@Test
|
||||
public void interceptUrlsAndFormLoginSupportPropertyPlaceholders() throws Exception {
|
||||
@@ -395,9 +367,9 @@ public class HttpSecurityBeanDefinitionParserTests {
|
||||
public void httpMethodMatchIsSupported() throws Exception {
|
||||
setContext(
|
||||
" <http auto-config='true'>" +
|
||||
" <intercept-url pattern='/**' access='ROLE_C' />" +
|
||||
" <intercept-url pattern='/secure*' method='DELETE' access='ROLE_SUPERVISOR' />" +
|
||||
" <intercept-url pattern='/secure*' method='POST' access='ROLE_A,ROLE_B' />" +
|
||||
" <intercept-url pattern='/**' access='ROLE_C' />" +
|
||||
" </http>" + AUTH_PROVIDER_XML);
|
||||
|
||||
FilterSecurityInterceptor fis = getFilter(FilterSecurityInterceptor.class);
|
||||
@@ -692,7 +664,6 @@ public class HttpSecurityBeanDefinitionParserTests {
|
||||
"</http>" +
|
||||
"<b:bean id='userService' class='org.springframework.security.core.userdetails.MockUserDetailsService'/> " +
|
||||
AUTH_PROVIDER_XML);
|
||||
// AbstractRememberMeServices rememberMeServices = (AbstractRememberMeServices) appContext.getBean(BeanIds.REMEMBER_ME_SERVICES);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -746,7 +717,6 @@ public class HttpSecurityBeanDefinitionParserTests {
|
||||
"</http>" +
|
||||
"<b:bean id='ss' class='org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy'/>"
|
||||
+ AUTH_PROVIDER_XML);
|
||||
//session-authentication-strategy-ref
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -768,12 +738,10 @@ public class HttpSecurityBeanDefinitionParserTests {
|
||||
getFilter(ConcurrentSessionFilter.class), "sessionRegistry");
|
||||
Object sessionRegistryFromFormLoginFilter = FieldUtils.getFieldValue(
|
||||
getFilter(UsernamePasswordAuthenticationFilter.class),"sessionStrategy.sessionRegistry");
|
||||
// Object sessionRegistryFromController = FieldUtils.getFieldValue(getConcurrentSessionController(),"sessionRegistry");
|
||||
Object sessionRegistryFromMgmtFilter = FieldUtils.getFieldValue(
|
||||
getFilter(SessionManagementFilter.class),"sessionStrategy.sessionRegistry");
|
||||
|
||||
assertSame(sessionRegistry, sessionRegistryFromConcurrencyFilter);
|
||||
// assertSame(sessionRegistry, sessionRegistryFromController);
|
||||
assertSame(sessionRegistry, sessionRegistryFromMgmtFilter);
|
||||
// SEC-1143
|
||||
assertSame(sessionRegistry, sessionRegistryFromFormLoginFilter);
|
||||
@@ -791,8 +759,6 @@ public class HttpSecurityBeanDefinitionParserTests {
|
||||
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken("bob", "pass");
|
||||
SecurityContextHolder.getContext().setAuthentication(auth);
|
||||
// Register 2 sessions and then check a third
|
||||
// req.setSession(new MockHttpSession());
|
||||
// auth.setDetails(new WebAuthenticationDetails(req));
|
||||
MockHttpServletResponse mockResponse = new MockHttpServletResponse();
|
||||
SaveContextOnUpdateOrErrorResponseWrapper response = new SaveContextOnUpdateOrErrorResponseWrapper(mockResponse, false) {
|
||||
protected void saveContext(SecurityContext context) {
|
||||
@@ -1240,7 +1206,6 @@ public class HttpSecurityBeanDefinitionParserTests {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.setMethod(method);
|
||||
request.setRequestURI(null);
|
||||
|
||||
request.setServletPath(path);
|
||||
|
||||
return new FilterInvocation(request, new MockHttpServletResponse(), new MockFilterChain());
|
||||
|
||||
Reference in New Issue
Block a user