SEC-1452: Added namespace support for custom expression handler for use with web access expressions.

This commit is contained in:
Luke Taylor 2011-05-19 15:26:44 +01:00
parent 63f160dc72
commit 27caecd53f
5 changed files with 48 additions and 7 deletions

View File

@ -8,6 +8,7 @@ import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
@ -59,7 +60,7 @@ public class FilterInvocationSecurityMetadataSourceParser implements BeanDefinit
return mds;
}
static BeanDefinition createSecurityMetadataSource(List<Element> interceptUrls, Element elt, ParserContext pc) {
static RootBeanDefinition createSecurityMetadataSource(List<Element> interceptUrls, Element elt, ParserContext pc) {
MatcherType matcherType = MatcherType.fromElement(elt);
boolean useExpressions = isUseExpressions(elt);
@ -87,7 +88,7 @@ public class FilterInvocationSecurityMetadataSourceParser implements BeanDefinit
fidsBuilder.getRawBeanDefinition().setSource(pc.extractSource(elt));
return fidsBuilder.getBeanDefinition();
return (RootBeanDefinition) fidsBuilder.getBeanDefinition();
}
static String registerDefaultExpressionHandler(ParserContext pc) {

View File

@ -451,15 +451,17 @@ class HttpConfigurationBuilder {
private void createFilterSecurityInterceptor(BeanReference authManager) {
boolean useExpressions = FilterInvocationSecurityMetadataSourceParser.isUseExpressions(httpElt);
BeanDefinition securityMds = FilterInvocationSecurityMetadataSourceParser.createSecurityMetadataSource(interceptUrls, httpElt, pc);
RootBeanDefinition securityMds = FilterInvocationSecurityMetadataSourceParser.createSecurityMetadataSource(interceptUrls, httpElt, pc);
RootBeanDefinition accessDecisionMgr;
ManagedList<BeanDefinition> voters = new ManagedList<BeanDefinition>(2);
if (useExpressions) {
BeanDefinitionBuilder expressionVoter = BeanDefinitionBuilder.rootBeanDefinition(WebExpressionVoter.class);
RuntimeBeanReference expressionHandler = new RuntimeBeanReference(
FilterInvocationSecurityMetadataSourceParser.registerDefaultExpressionHandler(pc));
// Read the expression handler from the FISMS
RuntimeBeanReference expressionHandler = (RuntimeBeanReference)
securityMds.getConstructorArgumentValues().getArgumentValue(1, RuntimeBeanReference.class).getValue();
expressionVoter.addPropertyValue("expressionHandler", expressionHandler);
voters.add(expressionVoter.getBeanDefinition());

View File

@ -202,7 +202,7 @@ msmds.attlist &= use-expressions?
global-method-security =
## Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with the ordered list of "protect-pointcut" sub-elements, Spring Security annotations and/or. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. If you use and enable all four sources of method security metadata (ie "protect-pointcut" declarations, expression annotations, @Secured and also JSR250 security annotations), the metadata sources will be queried in that order. In practical terms, this enables you to use XML to override method security metadata expressed in annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize etc.), @Secured and finally JSR-250.
element global-method-security {global-method-security.attlist, (pre-post-annotation-handling | expression-handler)?, protect-pointcut*, after-invocation-provider*}
element global-method-security {global-method-security.attlist, (pre-post-annotation-handling | expression-handler)?, protect-pointcut*, after-invocation-provider*}
global-method-security.attlist &=
## Specifies whether the use of Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this application context. Defaults to "disabled".
attribute pre-post-annotations {"disabled" | "enabled" }?
@ -268,7 +268,7 @@ http-firewall =
http =
## Container element for HTTP security configuration. Multiple elements can now be defined, each with a specific pattern to which the enclosed security configuration applies. A pattern can also be configured to bypass Spring Security's filters completely by setting the "secured" attribute to "false".
element http {http.attlist, (intercept-url* & access-denied-handler? & form-login? & openid-login? & x509? & jee? & http-basic? & logout? & session-management & remember-me? & anonymous? & port-mappings & custom-filter* & request-cache?) }
element http {http.attlist, (intercept-url* & access-denied-handler? & form-login? & openid-login? & x509? & jee? & http-basic? & logout? & session-management & remember-me? & anonymous? & port-mappings & custom-filter* & request-cache? & expression-handler?) }
http.attlist &=
## The request URL pattern which will be mapped to the filter chain created by this <http> element. If omitted, the filter chain will match all requests.
attribute pattern {xsd:token}?

View File

@ -682,6 +682,11 @@
</xs:complexType></xs:element>
<xs:element ref="security:custom-filter"/>
<xs:element ref="security:request-cache"/>
<xs:element name="expression-handler"><xs:annotation>
<xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based access-control is enabled. A default implementation (with no ACL support) will be used if not supplied.</xs:documentation>
</xs:annotation><xs:complexType>
<xs:attributeGroup ref="security:ref"/>
</xs:complexType></xs:element>
</xs:choice>
<xs:attributeGroup ref="security:http.attlist"/>
</xs:complexType></xs:element>

View File

@ -50,6 +50,9 @@ 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
class MiscHttpConfigTests extends AbstractHttpConfigTests {
def 'Minimal configuration parses'() {
@ -497,6 +500,25 @@ class MiscHttpConfigTests extends AbstractHttpConfigTests {
thrown(AccessDeniedException)
}
def expressionBasedAccessSupportsExternalExpressionHandler() {
setup:
xml.http('auto-config': 'true', 'use-expressions': 'true') {
interceptUrl('/**', "hasPermission('AnyObject','R')")
'expression-handler'(ref: 'expressionHandler')
}
bean('expressionHandler', DefaultWebSecurityExpressionHandler.class.name, [:], [permissionEvaluator: 'pe'])
bean('pe', MockPermissionEvaluator)
createAppContext()
def fis = getFilter(FilterSecurityInterceptor)
when: "Invoking allowed URL protected by hasPermission() expression succeeds"
SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken("joe", "", "ANY"));
fis.invoke(createFilterinvocation("/secure", null));
then:
notThrown(AccessDeniedException)
}
def protectedLoginPageReportsWarning() {
when:
xml.http('use-expressions': 'true') {
@ -647,6 +669,17 @@ class MiscHttpConfigTests extends AbstractHttpConfigTests {
}
}
class MockPermissionEvaluator implements PermissionEvaluator {
boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
return true
}
boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
return true
}
}
class MockEntryPoint extends LoginUrlAuthenticationEntryPoint {
public MockEntryPoint() {
super.setLoginFormUrl("/notused");