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

@ -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");