parent
cf66487d3a
commit
7bf014f678
|
@ -15,67 +15,15 @@
|
||||||
*/
|
*/
|
||||||
package org.springframework.security.config.http
|
package org.springframework.security.config.http
|
||||||
|
|
||||||
import org.springframework.security.crypto.codec.Base64;
|
|
||||||
|
|
||||||
import java.security.Principal
|
|
||||||
|
|
||||||
import javax.servlet.Filter
|
import javax.servlet.Filter
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse
|
||||||
|
|
||||||
import org.springframework.beans.BeansException
|
|
||||||
import org.springframework.beans.factory.BeanCreationException
|
|
||||||
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer
|
|
||||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException
|
|
||||||
import org.springframework.mock.web.MockFilterChain
|
import org.springframework.mock.web.MockFilterChain
|
||||||
import org.springframework.mock.web.MockHttpServletRequest
|
import org.springframework.mock.web.MockHttpServletRequest
|
||||||
import org.springframework.mock.web.MockHttpServletResponse
|
import org.springframework.mock.web.MockHttpServletResponse
|
||||||
import org.springframework.security.access.AccessDeniedException
|
|
||||||
import org.springframework.security.access.SecurityConfig
|
import org.springframework.security.access.SecurityConfig
|
||||||
import org.springframework.security.authentication.AnonymousAuthenticationProvider;
|
import org.springframework.security.crypto.codec.Base64
|
||||||
import org.springframework.security.authentication.TestingAuthenticationToken
|
|
||||||
import org.springframework.security.config.BeanIds
|
|
||||||
import org.springframework.security.config.MockUserServiceBeanPostProcessor
|
|
||||||
import org.springframework.security.config.PostProcessedMockUserDetailsService
|
|
||||||
import org.springframework.security.config.util.InMemoryXmlApplicationContext
|
|
||||||
import org.springframework.security.core.authority.AuthorityUtils
|
|
||||||
import org.springframework.security.core.context.SecurityContext
|
|
||||||
import org.springframework.security.core.context.SecurityContextHolder
|
|
||||||
import org.springframework.security.openid.OpenIDAuthenticationFilter
|
|
||||||
import org.springframework.security.util.FieldUtils
|
|
||||||
import org.springframework.security.web.FilterChainProxy
|
|
||||||
import org.springframework.security.web.PortMapperImpl
|
|
||||||
import org.springframework.security.web.access.ExceptionTranslationFilter
|
|
||||||
import org.springframework.security.web.access.channel.ChannelProcessingFilter
|
|
||||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor
|
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor
|
||||||
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter
|
|
||||||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint
|
|
||||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
|
|
||||||
import org.springframework.security.web.authentication.logout.LogoutFilter
|
|
||||||
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler
|
|
||||||
import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter
|
|
||||||
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter
|
|
||||||
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint
|
|
||||||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter
|
|
||||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository
|
|
||||||
import org.springframework.security.web.context.SecurityContextPersistenceFilter
|
|
||||||
import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;
|
|
||||||
import org.springframework.security.web.jaasapi.JaasApiIntegrationFilter
|
|
||||||
import org.springframework.security.web.savedrequest.HttpSessionRequestCache
|
|
||||||
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter
|
|
||||||
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter
|
|
||||||
import org.springframework.security.web.session.SessionManagementFilter
|
|
||||||
import org.springframework.security.web.authentication.logout.CookieClearingLogoutHandler
|
|
||||||
import org.springframework.security.web.firewall.DefaultHttpFirewall
|
|
||||||
import org.springframework.security.BeanNameCollectingPostProcessor
|
|
||||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider
|
|
||||||
import org.springframework.security.access.vote.RoleVoter
|
|
||||||
import org.springframework.security.web.access.expression.WebExpressionVoter
|
|
||||||
import org.springframework.security.access.vote.AffirmativeBased
|
|
||||||
import org.springframework.security.access.PermissionEvaluator
|
|
||||||
import org.springframework.security.core.Authentication
|
|
||||||
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler
|
|
||||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher
|
|
||||||
import org.springframework.security.authentication.AuthenticationManager
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -86,128 +34,134 @@ class InterceptUrlConfigTests extends AbstractHttpConfigTests {
|
||||||
|
|
||||||
def "SEC-2256: intercept-url method is not given priority"() {
|
def "SEC-2256: intercept-url method is not given priority"() {
|
||||||
when:
|
when:
|
||||||
httpAutoConfig {
|
httpAutoConfig {
|
||||||
'intercept-url'(pattern: '/anyurl', access: "ROLE_USER")
|
'intercept-url'(pattern: '/anyurl', access: "ROLE_USER")
|
||||||
'intercept-url'(pattern: '/anyurl', 'method':'GET',access: 'ROLE_ADMIN')
|
'intercept-url'(pattern: '/anyurl', 'method':'GET',access: 'ROLE_ADMIN')
|
||||||
}
|
}
|
||||||
createAppContext()
|
createAppContext()
|
||||||
|
|
||||||
def fids = getFilter(FilterSecurityInterceptor).securityMetadataSource
|
def fids = getFilter(FilterSecurityInterceptor).securityMetadataSource
|
||||||
def attrs = fids.getAttributes(createFilterinvocation("/anyurl", "GET"))
|
def attrs = fids.getAttributes(createFilterinvocation("/anyurl", "GET"))
|
||||||
def attrsPost = fids.getAttributes(createFilterinvocation("/anyurl", "POST"))
|
def attrsPost = fids.getAttributes(createFilterinvocation("/anyurl", "POST"))
|
||||||
|
|
||||||
then:
|
then:
|
||||||
attrs.size() == 1
|
attrs.size() == 1
|
||||||
attrs.contains(new SecurityConfig("ROLE_USER"))
|
attrs.contains(new SecurityConfig("ROLE_USER"))
|
||||||
attrsPost.size() == 1
|
attrsPost.size() == 1
|
||||||
attrsPost.contains(new SecurityConfig("ROLE_USER"))
|
attrsPost.contains(new SecurityConfig("ROLE_USER"))
|
||||||
}
|
}
|
||||||
|
|
||||||
def "SEC-2355: intercept-url support patch"() {
|
def "SEC-2355: intercept-url support patch"() {
|
||||||
setup:
|
setup:
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest(method:'GET')
|
MockHttpServletRequest request = new MockHttpServletRequest(method:'GET')
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse()
|
MockHttpServletResponse response = new MockHttpServletResponse()
|
||||||
MockFilterChain chain = new MockFilterChain()
|
MockFilterChain chain = new MockFilterChain()
|
||||||
xml.http('use-expressions':false) {
|
xml.http('use-expressions':false) {
|
||||||
'http-basic'()
|
'http-basic'()
|
||||||
'intercept-url'(pattern: '/**', 'method':'PATCH',access: 'ROLE_ADMIN')
|
'intercept-url'(pattern: '/**', 'method':'PATCH',access: 'ROLE_ADMIN')
|
||||||
csrf(disabled:true)
|
csrf(disabled:true)
|
||||||
}
|
}
|
||||||
createAppContext()
|
createAppContext()
|
||||||
when: 'Method other than PATCH is used'
|
when: 'Method other than PATCH is used'
|
||||||
springSecurityFilterChain.doFilter(request,response,chain)
|
springSecurityFilterChain.doFilter(request,response,chain)
|
||||||
then: 'The response is OK'
|
then: 'The response is OK'
|
||||||
response.status == HttpServletResponse.SC_OK
|
response.status == HttpServletResponse.SC_OK
|
||||||
when: 'Method of PATCH is used'
|
when: 'Method of PATCH is used'
|
||||||
request = new MockHttpServletRequest(method:'PATCH')
|
request = new MockHttpServletRequest(method:'PATCH')
|
||||||
response = new MockHttpServletResponse()
|
response = new MockHttpServletResponse()
|
||||||
chain = new MockFilterChain()
|
chain = new MockFilterChain()
|
||||||
springSecurityFilterChain.doFilter(request, response, chain)
|
springSecurityFilterChain.doFilter(request, response, chain)
|
||||||
then: 'The response is unauthorized'
|
then: 'The response is unauthorized'
|
||||||
response.status == HttpServletResponse.SC_UNAUTHORIZED
|
response.status == HttpServletResponse.SC_UNAUTHORIZED
|
||||||
}
|
}
|
||||||
|
|
||||||
def "intercept-url supports hasAnyRoles"() {
|
def "intercept-url supports hasAnyRoles"() {
|
||||||
setup:
|
setup:
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest(method:'GET')
|
MockHttpServletRequest request = new MockHttpServletRequest(method:'GET')
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse()
|
MockHttpServletResponse response = new MockHttpServletResponse()
|
||||||
MockFilterChain chain = new MockFilterChain()
|
MockFilterChain chain = new MockFilterChain()
|
||||||
xml.http('use-expressions':true) {
|
xml.http('use-expressions':true) {
|
||||||
'http-basic'()
|
'http-basic'()
|
||||||
'intercept-url'(pattern: '/**', access: "hasAnyRole('ROLE_DEVELOPER','ROLE_USER')")
|
'intercept-url'(pattern: '/**', access: "hasAnyRole('ROLE_DEVELOPER','ROLE_USER')")
|
||||||
csrf(disabled:true)
|
csrf(disabled:true)
|
||||||
}
|
}
|
||||||
when:
|
when:
|
||||||
createAppContext()
|
createAppContext()
|
||||||
then: 'no error'
|
then: 'no error'
|
||||||
noExceptionThrown()
|
noExceptionThrown()
|
||||||
when: 'ROLE_USER can access'
|
when: 'ROLE_USER can access'
|
||||||
login(request, 'user', 'password')
|
login(request, 'user', 'password')
|
||||||
springSecurityFilterChain.doFilter(request,response,chain)
|
springSecurityFilterChain.doFilter(request,response,chain)
|
||||||
then: 'The response is OK'
|
then: 'The response is OK'
|
||||||
response.status == HttpServletResponse.SC_OK
|
response.status == HttpServletResponse.SC_OK
|
||||||
when: 'ROLE_A cannot access'
|
when: 'ROLE_A cannot access'
|
||||||
request = new MockHttpServletRequest(method:'GET')
|
request = new MockHttpServletRequest(method:'GET')
|
||||||
response = new MockHttpServletResponse()
|
response = new MockHttpServletResponse()
|
||||||
chain = new MockFilterChain()
|
chain = new MockFilterChain()
|
||||||
login(request, 'bob', 'bobspassword')
|
login(request, 'bob', 'bobspassword')
|
||||||
springSecurityFilterChain.doFilter(request,response,chain)
|
springSecurityFilterChain.doFilter(request,response,chain)
|
||||||
then: 'The response is Forbidden'
|
then: 'The response is Forbidden'
|
||||||
response.status == HttpServletResponse.SC_FORBIDDEN
|
response.status == HttpServletResponse.SC_FORBIDDEN
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def "SEC-2256: intercept-url supports path variables"() {
|
def "SEC-2256: intercept-url supports path variables"() {
|
||||||
setup:
|
setup:
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest(method:'GET')
|
MockHttpServletRequest request = new MockHttpServletRequest(method:'GET')
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse()
|
MockHttpServletResponse response = new MockHttpServletResponse()
|
||||||
MockFilterChain chain = new MockFilterChain()
|
MockFilterChain chain = new MockFilterChain()
|
||||||
xml.http('use-expressions':true) {
|
xml.http('use-expressions':true) {
|
||||||
'http-basic'()
|
'http-basic'()
|
||||||
'intercept-url'(pattern: '/user/{un}/**', access: "#un == authentication.name")
|
'intercept-url'(pattern: '/user/{un}/**', access: "#un == authentication.name")
|
||||||
'intercept-url'(pattern: '/**', access: "denyAll")
|
'intercept-url'(pattern: '/**', access: "denyAll")
|
||||||
}
|
}
|
||||||
createAppContext()
|
createAppContext()
|
||||||
login(request, 'user', 'password')
|
login(request, 'user', 'password')
|
||||||
when: 'user can access'
|
when: 'user can access'
|
||||||
request.servletPath = '/user/user/abc'
|
request.servletPath = '/user/user/abc'
|
||||||
springSecurityFilterChain.doFilter(request,response,chain)
|
springSecurityFilterChain.doFilter(request,response,chain)
|
||||||
then: 'The response is OK'
|
then: 'The response is OK'
|
||||||
response.status == HttpServletResponse.SC_OK
|
response.status == HttpServletResponse.SC_OK
|
||||||
when: 'user cannot access otheruser'
|
when: 'user cannot access otheruser'
|
||||||
request = new MockHttpServletRequest(method:'GET', servletPath : '/user/otheruser/abc')
|
request = new MockHttpServletRequest(method:'GET', servletPath : '/user/otheruser/abc')
|
||||||
login(request, 'user', 'password')
|
login(request, 'user', 'password')
|
||||||
chain.reset()
|
chain.reset()
|
||||||
springSecurityFilterChain.doFilter(request,response,chain)
|
springSecurityFilterChain.doFilter(request,response,chain)
|
||||||
then: 'The response is OK'
|
then: 'The response is OK'
|
||||||
response.status == HttpServletResponse.SC_FORBIDDEN
|
response.status == HttpServletResponse.SC_FORBIDDEN
|
||||||
|
when: 'user can access case insensitive URL'
|
||||||
|
request = new MockHttpServletRequest(method:'GET', servletPath : '/USER/user/abc')
|
||||||
|
login(request, 'user', 'password')
|
||||||
|
chain.reset()
|
||||||
|
springSecurityFilterChain.doFilter(request,response,chain)
|
||||||
|
then: 'The response is OK'
|
||||||
|
response.status == HttpServletResponse.SC_FORBIDDEN
|
||||||
}
|
}
|
||||||
|
|
||||||
def "SEC-2256: intercept-url supports path variable type conversion"() {
|
def "SEC-2256: intercept-url supports path variable type conversion"() {
|
||||||
setup:
|
setup:
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest(method:'GET')
|
MockHttpServletRequest request = new MockHttpServletRequest(method:'GET')
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse()
|
MockHttpServletResponse response = new MockHttpServletResponse()
|
||||||
MockFilterChain chain = new MockFilterChain()
|
MockFilterChain chain = new MockFilterChain()
|
||||||
xml.http('use-expressions':true) {
|
xml.http('use-expressions':true) {
|
||||||
'http-basic'()
|
'http-basic'()
|
||||||
'intercept-url'(pattern: '/user/{un}/**', access: "@id.isOne(#un)")
|
'intercept-url'(pattern: '/user/{un}/**', access: "@id.isOne(#un)")
|
||||||
'intercept-url'(pattern: '/**', access: "denyAll")
|
'intercept-url'(pattern: '/**', access: "denyAll")
|
||||||
}
|
}
|
||||||
bean('id', Id)
|
bean('id', Id)
|
||||||
createAppContext()
|
createAppContext()
|
||||||
login(request, 'user', 'password')
|
login(request, 'user', 'password')
|
||||||
when: 'can access id == 1'
|
when: 'can access id == 1'
|
||||||
request.servletPath = '/user/1/abc'
|
request.servletPath = '/user/1/abc'
|
||||||
springSecurityFilterChain.doFilter(request,response,chain)
|
springSecurityFilterChain.doFilter(request,response,chain)
|
||||||
then: 'The response is OK'
|
then: 'The response is OK'
|
||||||
response.status == HttpServletResponse.SC_OK
|
response.status == HttpServletResponse.SC_OK
|
||||||
when: 'user cannot access 2'
|
when: 'user cannot access 2'
|
||||||
request = new MockHttpServletRequest(method:'GET', servletPath : '/user/2/abc')
|
request = new MockHttpServletRequest(method:'GET', servletPath : '/user/2/abc')
|
||||||
login(request, 'user', 'password')
|
login(request, 'user', 'password')
|
||||||
chain.reset()
|
chain.reset()
|
||||||
springSecurityFilterChain.doFilter(request,response,chain)
|
springSecurityFilterChain.doFilter(request,response,chain)
|
||||||
then: 'The response is OK'
|
then: 'The response is OK'
|
||||||
response.status == HttpServletResponse.SC_FORBIDDEN
|
response.status == HttpServletResponse.SC_FORBIDDEN
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Id {
|
public static class Id {
|
||||||
|
|
|
@ -116,6 +116,25 @@ public class AuthorizeRequestsTests {
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_FORBIDDEN);
|
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_FORBIDDEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SEC-2256
|
||||||
|
@Test
|
||||||
|
public void antMatchersPathVariablesCaseInsensitive() throws Exception {
|
||||||
|
loadConfig(AntPatchersPathVariables.class);
|
||||||
|
|
||||||
|
this.request.setServletPath("/USER/user");
|
||||||
|
|
||||||
|
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
||||||
|
|
||||||
|
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
|
||||||
|
|
||||||
|
this.setup();
|
||||||
|
this.request.setServletPath("/USER/deny");
|
||||||
|
|
||||||
|
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
||||||
|
|
||||||
|
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_FORBIDDEN);
|
||||||
|
}
|
||||||
|
|
||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
@Configuration
|
@Configuration
|
||||||
static class AntPatchersPathVariables extends WebSecurityConfigurerAdapter {
|
static class AntPatchersPathVariables extends WebSecurityConfigurerAdapter {
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2015 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.springframework.security.web.access.expression;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import org.springframework.expression.EvaluationContext;
|
||||||
|
import org.springframework.security.web.FilterInvocation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exposes URI template variables as variables on the {@link EvaluationContext}. For
|
||||||
|
* example, the pattern "/user/{username}/**" would expose a variable named username based
|
||||||
|
* on the current URI.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* NOTE: This API is intentionally kept package scope as it may change in the future. It
|
||||||
|
* may be nice to allow users to augment expressions and queries
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author Rob Winch
|
||||||
|
* @since 4.1
|
||||||
|
*/
|
||||||
|
abstract class AbstractVariableEvaluationContextPostProcessor
|
||||||
|
implements EvaluationContextPostProcessor<FilterInvocation> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final EvaluationContext postProcess(EvaluationContext context,
|
||||||
|
FilterInvocation invocation) {
|
||||||
|
HttpServletRequest request = invocation.getHttpRequest();
|
||||||
|
Map<String, String> variables = extractVariables(request);
|
||||||
|
for (Map.Entry<String, String> entry : variables.entrySet()) {
|
||||||
|
context.setVariable(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract Map<String, String> extractVariables(
|
||||||
|
HttpServletRequest request);
|
||||||
|
|
||||||
|
}
|
|
@ -29,7 +29,7 @@ import org.springframework.expression.EvaluationContext;
|
||||||
* @since 4.1
|
* @since 4.1
|
||||||
* @param <I> the invocation to use for post processing
|
* @param <I> the invocation to use for post processing
|
||||||
*/
|
*/
|
||||||
interface SecurityEvaluationContextPostProcessor<I> {
|
interface EvaluationContextPostProcessor<I> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows post processing of the {@link EvaluationContext}. Implementations
|
* Allows post processing of the {@link EvaluationContext}. Implementations
|
|
@ -20,8 +20,11 @@ import java.util.Collection;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
import org.springframework.expression.ExpressionParser;
|
import org.springframework.expression.ExpressionParser;
|
||||||
import org.springframework.expression.ParseException;
|
import org.springframework.expression.ParseException;
|
||||||
import org.springframework.security.access.ConfigAttribute;
|
import org.springframework.security.access.ConfigAttribute;
|
||||||
|
@ -38,8 +41,8 @@ import org.springframework.util.Assert;
|
||||||
* @author Luke Taylor
|
* @author Luke Taylor
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
public final class ExpressionBasedFilterInvocationSecurityMetadataSource extends
|
public final class ExpressionBasedFilterInvocationSecurityMetadataSource
|
||||||
DefaultFilterInvocationSecurityMetadataSource {
|
extends DefaultFilterInvocationSecurityMetadataSource {
|
||||||
private final static Log logger = LogFactory
|
private final static Log logger = LogFactory
|
||||||
.getLog(ExpressionBasedFilterInvocationSecurityMetadataSource.class);
|
.getLog(ExpressionBasedFilterInvocationSecurityMetadataSource.class);
|
||||||
|
|
||||||
|
@ -67,20 +70,18 @@ public final class ExpressionBasedFilterInvocationSecurityMetadataSource extends
|
||||||
ArrayList<ConfigAttribute> attributes = new ArrayList<ConfigAttribute>(1);
|
ArrayList<ConfigAttribute> attributes = new ArrayList<ConfigAttribute>(1);
|
||||||
String expression = entry.getValue().toArray(new ConfigAttribute[1])[0]
|
String expression = entry.getValue().toArray(new ConfigAttribute[1])[0]
|
||||||
.getAttribute();
|
.getAttribute();
|
||||||
logger.debug("Adding web access control expression '" + expression
|
logger.debug("Adding web access control expression '" + expression + "', for "
|
||||||
+ "', for " + request);
|
+ request);
|
||||||
|
|
||||||
String pattern = null;
|
AbstractVariableEvaluationContextPostProcessor postProcessor = createPostProcessor(
|
||||||
if(request instanceof AntPathRequestMatcher) {
|
request);
|
||||||
pattern = ((AntPathRequestMatcher)request).getPattern();
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
attributes.add(new WebExpressionConfigAttribute(parser
|
attributes.add(new WebExpressionConfigAttribute(
|
||||||
.parseExpression(expression), new PathVariableSecurityEvaluationContextPostProcessor(pattern)));
|
parser.parseExpression(expression), postProcessor));
|
||||||
}
|
}
|
||||||
catch (ParseException e) {
|
catch (ParseException e) {
|
||||||
throw new IllegalArgumentException("Failed to parse expression '"
|
throw new IllegalArgumentException(
|
||||||
+ expression + "'");
|
"Failed to parse expression '" + expression + "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
requestToExpressionAttributesMap.put(request, attributes);
|
requestToExpressionAttributesMap.put(request, attributes);
|
||||||
|
@ -89,4 +90,29 @@ public final class ExpressionBasedFilterInvocationSecurityMetadataSource extends
|
||||||
return requestToExpressionAttributesMap;
|
return requestToExpressionAttributesMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static AbstractVariableEvaluationContextPostProcessor createPostProcessor(
|
||||||
|
Object request) {
|
||||||
|
if (request instanceof AntPathRequestMatcher) {
|
||||||
|
return new AntPathMatcherEvaluationContextPostProcessor(
|
||||||
|
(AntPathRequestMatcher) request);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class AntPathMatcherEvaluationContextPostProcessor
|
||||||
|
extends AbstractVariableEvaluationContextPostProcessor {
|
||||||
|
private final AntPathRequestMatcher matcher;
|
||||||
|
|
||||||
|
public AntPathMatcherEvaluationContextPostProcessor(
|
||||||
|
AntPathRequestMatcher matcher) {
|
||||||
|
this.matcher = matcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Map<String, String> extractVariables(
|
||||||
|
HttpServletRequest request) {
|
||||||
|
return this.matcher.extractUriTemplateVariables(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,74 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2015 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.springframework.security.web.access.expression;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
|
|
||||||
import org.springframework.expression.EvaluationContext;
|
|
||||||
import org.springframework.security.web.FilterInvocation;
|
|
||||||
import org.springframework.util.AntPathMatcher;
|
|
||||||
import org.springframework.util.PathMatcher;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exposes URI template variables as variables on the {@link EvaluationContext}.
|
|
||||||
* For example, the pattern "/user/{username}/**" would expose a variable named
|
|
||||||
* username based on the current URI.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* NOTE: This API is intentionally kept package scope as it may change in the future. It may be nice to allow users to augment expressions and queries
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @author Rob Winch
|
|
||||||
* @since 4.1
|
|
||||||
*/
|
|
||||||
class PathVariableSecurityEvaluationContextPostProcessor implements SecurityEvaluationContextPostProcessor<FilterInvocation> {
|
|
||||||
private final PathMatcher matcher = new AntPathMatcher();
|
|
||||||
private final String antPattern;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new instance.
|
|
||||||
*
|
|
||||||
* @param antPattern the ant pattern that may have template variables (i.e. "/user/{username}/**)
|
|
||||||
*/
|
|
||||||
public PathVariableSecurityEvaluationContextPostProcessor(String antPattern) {
|
|
||||||
this.antPattern = antPattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
public EvaluationContext postProcess(EvaluationContext context, FilterInvocation invocation) {
|
|
||||||
if(antPattern == null) {
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
String path = getRequestPath(invocation.getHttpRequest());
|
|
||||||
Map<String, String> variables = matcher.extractUriTemplateVariables(antPattern, path);
|
|
||||||
for(Map.Entry<String, String> entry : variables.entrySet()) {
|
|
||||||
context.setVariable(entry.getKey(), entry.getValue());
|
|
||||||
}
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getRequestPath(HttpServletRequest request) {
|
|
||||||
String url = request.getServletPath();
|
|
||||||
|
|
||||||
if (request.getPathInfo() != null) {
|
|
||||||
url += request.getPathInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -26,29 +26,34 @@ import org.springframework.security.web.FilterInvocation;
|
||||||
* @author Luke Taylor
|
* @author Luke Taylor
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
class WebExpressionConfigAttribute implements ConfigAttribute, SecurityEvaluationContextPostProcessor<FilterInvocation> {
|
class WebExpressionConfigAttribute implements ConfigAttribute,
|
||||||
|
EvaluationContextPostProcessor<FilterInvocation> {
|
||||||
private final Expression authorizeExpression;
|
private final Expression authorizeExpression;
|
||||||
private final SecurityEvaluationContextPostProcessor<FilterInvocation> postProcessor;
|
private final EvaluationContextPostProcessor<FilterInvocation> postProcessor;
|
||||||
|
|
||||||
public WebExpressionConfigAttribute(Expression authorizeExpression, SecurityEvaluationContextPostProcessor<FilterInvocation> postProcessor) {
|
public WebExpressionConfigAttribute(Expression authorizeExpression,
|
||||||
|
EvaluationContextPostProcessor<FilterInvocation> postProcessor) {
|
||||||
this.authorizeExpression = authorizeExpression;
|
this.authorizeExpression = authorizeExpression;
|
||||||
this.postProcessor = postProcessor;
|
this.postProcessor = postProcessor;
|
||||||
}
|
}
|
||||||
|
|
||||||
Expression getAuthorizeExpression() {
|
Expression getAuthorizeExpression() {
|
||||||
return authorizeExpression;
|
return this.authorizeExpression;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public EvaluationContext postProcess(EvaluationContext context, FilterInvocation fi) {
|
public EvaluationContext postProcess(EvaluationContext context, FilterInvocation fi) {
|
||||||
return postProcessor.postProcess(context, fi);
|
return this.postProcessor == null ? context
|
||||||
|
: this.postProcessor.postProcess(context, fi);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getAttribute() {
|
public String getAttribute() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return authorizeExpression.getExpressionString();
|
return this.authorizeExpression.getExpressionString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,12 +15,15 @@
|
||||||
*/
|
*/
|
||||||
package org.springframework.security.web.util.matcher;
|
package org.springframework.security.web.util.matcher;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
|
||||||
import org.springframework.util.AntPathMatcher;
|
import org.springframework.util.AntPathMatcher;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
@ -89,13 +92,14 @@ public final class AntPathRequestMatcher implements RequestMatcher {
|
||||||
* the incoming request doesn't doesn't have the same method.
|
* the incoming request doesn't doesn't have the same method.
|
||||||
* @param caseSensitive true if the matcher should consider case, else false
|
* @param caseSensitive true if the matcher should consider case, else false
|
||||||
*/
|
*/
|
||||||
public AntPathRequestMatcher(String pattern, String httpMethod, boolean caseSensitive) {
|
public AntPathRequestMatcher(String pattern, String httpMethod,
|
||||||
|
boolean caseSensitive) {
|
||||||
Assert.hasText(pattern, "Pattern cannot be null or empty");
|
Assert.hasText(pattern, "Pattern cannot be null or empty");
|
||||||
this.caseSensitive = caseSensitive;
|
this.caseSensitive = caseSensitive;
|
||||||
|
|
||||||
if (pattern.equals(MATCH_ALL) || pattern.equals("**")) {
|
if (pattern.equals(MATCH_ALL) || pattern.equals("**")) {
|
||||||
pattern = MATCH_ALL;
|
pattern = MATCH_ALL;
|
||||||
matcher = null;
|
this.matcher = null;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (!caseSensitive) {
|
if (!caseSensitive) {
|
||||||
|
@ -105,19 +109,20 @@ public final class AntPathRequestMatcher implements RequestMatcher {
|
||||||
// If the pattern ends with {@code /**} and has no other wildcards or path
|
// If the pattern ends with {@code /**} and has no other wildcards or path
|
||||||
// variables, then optimize to a sub-path match
|
// variables, then optimize to a sub-path match
|
||||||
if (pattern.endsWith(MATCH_ALL)
|
if (pattern.endsWith(MATCH_ALL)
|
||||||
&& (pattern.indexOf('?') == -1 && pattern.indexOf('{') == -1 && pattern
|
&& (pattern.indexOf('?') == -1 && pattern.indexOf('{') == -1
|
||||||
.indexOf('}') == -1)
|
&& pattern.indexOf('}') == -1)
|
||||||
&& pattern.indexOf("*") == pattern.length() - 2) {
|
&& pattern.indexOf("*") == pattern.length() - 2) {
|
||||||
matcher = new SubpathMatcher(pattern.substring(0, pattern.length() - 3));
|
this.matcher = new SubpathMatcher(
|
||||||
|
pattern.substring(0, pattern.length() - 3));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
matcher = new SpringAntMatcher(pattern);
|
this.matcher = new SpringAntMatcher(pattern);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.pattern = pattern;
|
this.pattern = pattern;
|
||||||
this.httpMethod = StringUtils.hasText(httpMethod) ? HttpMethod
|
this.httpMethod = StringUtils.hasText(httpMethod) ? HttpMethod.valueOf(httpMethod)
|
||||||
.valueOf(httpMethod) : null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -127,19 +132,20 @@ public final class AntPathRequestMatcher implements RequestMatcher {
|
||||||
* @param request the request to match against. The ant pattern will be matched
|
* @param request the request to match against. The ant pattern will be matched
|
||||||
* against the {@code servletPath} + {@code pathInfo} of the request.
|
* against the {@code servletPath} + {@code pathInfo} of the request.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public boolean matches(HttpServletRequest request) {
|
public boolean matches(HttpServletRequest request) {
|
||||||
if (httpMethod != null && StringUtils.hasText(request.getMethod())
|
if (this.httpMethod != null && StringUtils.hasText(request.getMethod())
|
||||||
&& httpMethod != valueOf(request.getMethod())) {
|
&& this.httpMethod != valueOf(request.getMethod())) {
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("Request '" + request.getMethod() + " "
|
logger.debug("Request '" + request.getMethod() + " "
|
||||||
+ getRequestPath(request) + "'" + " doesn't match '" + httpMethod
|
+ getRequestPath(request) + "'" + " doesn't match '"
|
||||||
+ " " + pattern);
|
+ this.httpMethod + " " + this.pattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pattern.equals(MATCH_ALL)) {
|
if (this.pattern.equals(MATCH_ALL)) {
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("Request '" + getRequestPath(request)
|
logger.debug("Request '" + getRequestPath(request)
|
||||||
+ "' matched by universal pattern '/**'");
|
+ "' matched by universal pattern '/**'");
|
||||||
|
@ -151,11 +157,19 @@ public final class AntPathRequestMatcher implements RequestMatcher {
|
||||||
String url = getRequestPath(request);
|
String url = getRequestPath(request);
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("Checking match of request : '" + url + "'; against '" + pattern
|
logger.debug("Checking match of request : '" + url + "'; against '"
|
||||||
+ "'");
|
+ this.pattern + "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
return matcher.matches(url);
|
return this.matcher.matches(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> extractUriTemplateVariables(HttpServletRequest request) {
|
||||||
|
if (this.matcher == null || !matches(request)) {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
String url = getRequestPath(request);
|
||||||
|
return this.matcher.extractUriTemplateVariables(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getRequestPath(HttpServletRequest request) {
|
private String getRequestPath(HttpServletRequest request) {
|
||||||
|
@ -165,7 +179,7 @@ public final class AntPathRequestMatcher implements RequestMatcher {
|
||||||
url += request.getPathInfo();
|
url += request.getPathInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!caseSensitive) {
|
if (!this.caseSensitive) {
|
||||||
url = url.toLowerCase();
|
url = url.toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,7 +187,7 @@ public final class AntPathRequestMatcher implements RequestMatcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPattern() {
|
public String getPattern() {
|
||||||
return pattern;
|
return this.pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -189,9 +203,9 @@ public final class AntPathRequestMatcher implements RequestMatcher {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
int code = 31 ^ pattern.hashCode();
|
int code = 31 ^ this.pattern.hashCode();
|
||||||
if (httpMethod != null) {
|
if (this.httpMethod != null) {
|
||||||
code ^= httpMethod.hashCode();
|
code ^= this.httpMethod.hashCode();
|
||||||
}
|
}
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
|
@ -199,10 +213,10 @@ public final class AntPathRequestMatcher implements RequestMatcher {
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append("Ant [pattern='").append(pattern).append("'");
|
sb.append("Ant [pattern='").append(this.pattern).append("'");
|
||||||
|
|
||||||
if (httpMethod != null) {
|
if (this.httpMethod != null) {
|
||||||
sb.append(", ").append(httpMethod);
|
sb.append(", ").append(this.httpMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.append("]");
|
sb.append("]");
|
||||||
|
@ -230,6 +244,8 @@ public final class AntPathRequestMatcher implements RequestMatcher {
|
||||||
|
|
||||||
private static interface Matcher {
|
private static interface Matcher {
|
||||||
boolean matches(String path);
|
boolean matches(String path);
|
||||||
|
|
||||||
|
Map<String, String> extractUriTemplateVariables(String path);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SpringAntMatcher implements Matcher {
|
private static class SpringAntMatcher implements Matcher {
|
||||||
|
@ -241,8 +257,14 @@ public final class AntPathRequestMatcher implements RequestMatcher {
|
||||||
this.pattern = pattern;
|
this.pattern = pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean matches(String path) {
|
public boolean matches(String path) {
|
||||||
return antMatcher.match(pattern, path);
|
return antMatcher.match(this.pattern, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> extractUriTemplateVariables(String path) {
|
||||||
|
return antMatcher.extractUriTemplateVariables(this.pattern, path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,14 +276,20 @@ public final class AntPathRequestMatcher implements RequestMatcher {
|
||||||
private final int length;
|
private final int length;
|
||||||
|
|
||||||
private SubpathMatcher(String subpath) {
|
private SubpathMatcher(String subpath) {
|
||||||
assert !subpath.contains("*");
|
assert!subpath.contains("*");
|
||||||
this.subpath = subpath;
|
this.subpath = subpath;
|
||||||
this.length = subpath.length();
|
this.length = subpath.length();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean matches(String path) {
|
public boolean matches(String path) {
|
||||||
return path.startsWith(subpath)
|
return path.startsWith(this.subpath)
|
||||||
&& (path.length() == length || path.charAt(length) == '/');
|
&& (path.length() == this.length || path.charAt(this.length) == '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> extractUriTemplateVariables(String path) {
|
||||||
|
return Collections.emptyMap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,20 +15,28 @@
|
||||||
*/
|
*/
|
||||||
package org.springframework.security.web.access.expression;
|
package org.springframework.security.web.access.expression;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||||
import org.springframework.mock.web.MockFilterChain;
|
import org.springframework.mock.web.MockFilterChain;
|
||||||
import org.springframework.mock.web.MockHttpServletRequest;
|
import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
import org.springframework.mock.web.MockHttpServletResponse;
|
import org.springframework.mock.web.MockHttpServletResponse;
|
||||||
import org.springframework.security.web.FilterInvocation;
|
import org.springframework.security.web.FilterInvocation;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Rob Winch
|
* @author Rob Winch
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class PathVariableSecurityEvaluationContextPostProcessorTests {
|
public class AbstractVariableEvaluationContextPostProcessorTests {
|
||||||
PathVariableSecurityEvaluationContextPostProcessor processor;
|
AbstractVariableEvaluationContextPostProcessor processor;
|
||||||
|
|
||||||
FilterInvocation invocation;
|
FilterInvocation invocation;
|
||||||
|
|
||||||
|
@ -38,19 +46,34 @@ public class PathVariableSecurityEvaluationContextPostProcessorTests {
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup() {
|
public void setup() {
|
||||||
processor = new PathVariableSecurityEvaluationContextPostProcessor("/");
|
this.processor = new VariableEvaluationContextPostProcessor();
|
||||||
|
|
||||||
request = new MockHttpServletRequest();
|
this.request = new MockHttpServletRequest();
|
||||||
request.setServletPath("/");
|
this.request.setServletPath("/");
|
||||||
response = new MockHttpServletResponse();
|
this.response = new MockHttpServletResponse();
|
||||||
invocation = new FilterInvocation(request,response, new MockFilterChain());
|
this.invocation = new FilterInvocation(this.request, this.response,
|
||||||
context = new StandardEvaluationContext();
|
new MockFilterChain());
|
||||||
|
this.context = new StandardEvaluationContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void queryIgnored() {
|
public void postProcess() {
|
||||||
request.setQueryString("logout");
|
this.processor.postProcess(this.context, this.invocation);
|
||||||
processor.postProcess(context, invocation);
|
|
||||||
|
for (String key : VariableEvaluationContextPostProcessor.RESULTS.keySet()) {
|
||||||
|
assertThat(this.context.lookupVariable(key))
|
||||||
|
.isEqualTo(VariableEvaluationContextPostProcessor.RESULTS.get(key));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class VariableEvaluationContextPostProcessor
|
||||||
|
extends AbstractVariableEvaluationContextPostProcessor {
|
||||||
|
static final Map<String, String> RESULTS = Collections.singletonMap("a", "b");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Map<String, String> extractVariables(HttpServletRequest request) {
|
||||||
|
return RESULTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -51,7 +51,7 @@ public class WebExpressionVoterTests {
|
||||||
public void supportsWebConfigAttributeAndFilterInvocation() throws Exception {
|
public void supportsWebConfigAttributeAndFilterInvocation() throws Exception {
|
||||||
WebExpressionVoter voter = new WebExpressionVoter();
|
WebExpressionVoter voter = new WebExpressionVoter();
|
||||||
assertThat(voter.supports(new WebExpressionConfigAttribute(mock(Expression.class),
|
assertThat(voter.supports(new WebExpressionConfigAttribute(mock(Expression.class),
|
||||||
mock(SecurityEvaluationContextPostProcessor.class)))).isTrue();
|
mock(EvaluationContextPostProcessor.class)))).isTrue();
|
||||||
assertThat(voter.supports(FilterInvocation.class)).isTrue();
|
assertThat(voter.supports(FilterInvocation.class)).isTrue();
|
||||||
assertThat(voter.supports(MethodInvocation.class)).isFalse();
|
assertThat(voter.supports(MethodInvocation.class)).isFalse();
|
||||||
|
|
||||||
|
@ -69,8 +69,8 @@ public class WebExpressionVoterTests {
|
||||||
public void grantsAccessIfExpressionIsTrueDeniesIfFalse() {
|
public void grantsAccessIfExpressionIsTrueDeniesIfFalse() {
|
||||||
WebExpressionVoter voter = new WebExpressionVoter();
|
WebExpressionVoter voter = new WebExpressionVoter();
|
||||||
Expression ex = mock(Expression.class);
|
Expression ex = mock(Expression.class);
|
||||||
SecurityEvaluationContextPostProcessor postProcessor = mock(
|
EvaluationContextPostProcessor postProcessor = mock(
|
||||||
SecurityEvaluationContextPostProcessor.class);
|
EvaluationContextPostProcessor.class);
|
||||||
when(postProcessor.postProcess(any(EvaluationContext.class),
|
when(postProcessor.postProcess(any(EvaluationContext.class),
|
||||||
any(FilterInvocation.class))).thenAnswer(new Answer<EvaluationContext>() {
|
any(FilterInvocation.class))).thenAnswer(new Answer<EvaluationContext>() {
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue