From 243b5f4a2a61fc9886c81ab139d43ee7ad909b56 Mon Sep 17 00:00:00 2001 From: Luke Taylor Date: Mon, 7 Apr 2008 22:17:09 +0000 Subject: [PATCH] SEC-746: impossible to specify errorPage for the AccessDeniedHandlerImp when using namespace based configuration http://jira.springframework.org/browse/SEC-746. Added access-denied-page to http element. --- .../security/util/FieldUtils.java | 29 ++++++- .../security/config/spring-security-2.0.rnc | 27 +++---- .../security/config/spring-security-2.0.xsd | 77 +++++-------------- ...HttpSecurityBeanDefinitionParserTests.java | 20 +++-- 4 files changed, 74 insertions(+), 79 deletions(-) diff --git a/core/src/main/java/org/springframework/security/util/FieldUtils.java b/core/src/main/java/org/springframework/security/util/FieldUtils.java index c76b40e2b7..81433a1040 100644 --- a/core/src/main/java/org/springframework/security/util/FieldUtils.java +++ b/core/src/main/java/org/springframework/security/util/FieldUtils.java @@ -17,6 +17,7 @@ package org.springframework.security.util; import org.springframework.util.Assert; import org.springframework.util.ReflectionUtils; +import org.springframework.util.StringUtils; import java.lang.reflect.Field; @@ -56,8 +57,7 @@ public final class FieldUtils { * * @throws IllegalStateException if field could not be found */ - public static Field getField(Class clazz, String fieldName) - throws IllegalStateException { + public static Field getField(Class clazz, String fieldName) throws IllegalStateException { Assert.notNull(clazz, "Class required"); Assert.hasText(fieldName, "Field name required"); @@ -72,6 +72,31 @@ public final class FieldUtils { throw new IllegalStateException("Could not locate field '" + fieldName + "' on class " + clazz); } } + + /** + * Returns the value of a (nested) field on a bean. Intended for testing. + * @param bean the object + * @param fieldName the field name, with "." separating nested properties + * @return the value of the nested field + */ + public static Object getFieldValue(Object bean, String fieldName) throws IllegalAccessException { + Assert.notNull(bean, "Bean cannot be null"); + Assert.hasText(fieldName, "Field name required"); + String[] nestedFields = StringUtils.tokenizeToStringArray(fieldName, "."); + Class componentClass = bean.getClass(); + Field field = null; + Object value = bean; + + for (int i=0; i < nestedFields.length; i++) { + field = getField(componentClass, nestedFields[i]); + field.setAccessible(true); + value = field.get(value); + componentClass = value.getClass(); + } + + return value; + + } public static String getMutatorName(String fieldName) { Assert.hasText(fieldName, "FieldName required"); diff --git a/core/src/main/resources/org/springframework/security/config/spring-security-2.0.rnc b/core/src/main/resources/org/springframework/security/config/spring-security-2.0.rnc index 846d3514c1..1bbfb1fa46 100644 --- a/core/src/main/resources/org/springframework/security/config/spring-security-2.0.rnc +++ b/core/src/main/resources/org/springframework/security/config/spring-security-2.0.rnc @@ -1,12 +1,9 @@ - -namespace beans = "http://www.springframework.org/schema/beans" namespace a = "http://relaxng.org/ns/compatibility/annotations/1.0" -namespace security = "http://www.springframework.org/schema/security" datatypes xsd = "http://www.w3.org/2001/XMLSchema-datatypes" default namespace = "http://www.springframework.org/schema/security" -start = http | ldap-server | authentication-provider | ldap-authentication-provider | user-service +start = http | ldap-server | authentication-provider | ldap-authentication-provider | any-user-service | ldap-server | ldap-authentication-provider hash = ## Defines the hashing algorithm used on user passwords. We recommend strongly against using MD4, as it is a very weak hashing algorithm. @@ -52,6 +49,8 @@ user-property = system-wide = ## A single value that will be used as the salt for a password encoder. attribute system-wide {xsd:string} + +boolean = "true" | "false" ldap-server = @@ -189,7 +188,7 @@ http = element http {http.attlist, (intercept-url+ & form-login? & openid-login & x509? & http-basic? & logout? & concurrent-session-control? & remember-me? & anonymous? & port-mappings) } http.attlist &= ## Automatically registers a login form, BASIC authentication, anonymous authentication, logout services, remember-me and servlet-api-integration. If set to "true", all of these capabilities are added (although you can still customize the configuration of each by providing the respective element). If unspecified, defaults to "false". - attribute auto-config {"true" | "false" }? + attribute auto-config {boolean}? http.attlist &= ## Controls the eagerness with which an HTTP session is created. If not set, defaults to "ifRequired". attribute create-session {"ifRequired" | "always" | "never" }? @@ -198,10 +197,10 @@ http.attlist &= path-type? http.attlist &= ## Whether test URLs should be converted to lower case prior to comparing with defined path patterns. If unspecified, defaults to "true". - attribute lowercase-comparisons {"true" | "false"}? + attribute lowercase-comparisons {boolean}? http.attlist &= ## Provides versions of HttpServletRequest security methods such as isUserInRole() and getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to "true". - attribute servlet-api-provision {"true" | "false"}? + attribute servlet-api-provision {boolean}? http.attlist &= ## Optional attribute specifying the ID of the AccessDecisionManager implementation which should be used for authorizing HTTP requests. attribute access-decision-manager-ref {xsd:string}? @@ -216,8 +215,10 @@ http.attlist &= attribute entry-point-ref {xsd:string}? http.attlist &= ## Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults to "false" - attribute once-per-request {"true" | "false"}? - + attribute once-per-request {boolean}? +http.attlist &= + ## Allows the access denied page to be set (the user will be redirected here if an AccessDeniedException is raised). + attribute access-denied-page {xsd:string}? intercept-url = ## Specifies the access attributes and/or filter list for a particular set of URLs. @@ -250,7 +251,7 @@ logout.attlist &= attribute logout-success-url {xsd:string}? logout.attlist &= ## Specifies whether a logout also causes HttpSession invalidation, which is generally desirable. If unspecified, defaults to true. - attribute invalidate-session {"true" | "false"}? + attribute invalidate-session {boolean}? form-login = ## Sets up a form login configuration for authentication with a username and password @@ -294,7 +295,7 @@ fids.attlist &= id? fids.attlist &= ## as for http element - attribute lowercase-comparisons {"true" | "false"}? + attribute lowercase-comparisons {boolean}? fids.attlist &= ## as for http element path-type? @@ -312,7 +313,7 @@ concurrent-sessions.attlist &= concurrent-sessions.attlist &= attribute expired-url {xsd:string}? concurrent-sessions.attlist &= - attribute exception-if-maximum-exceeded {"true" | "false"}? + attribute exception-if-maximum-exceeded {boolean}? concurrent-sessions.attlist &= ## Allows you to define an alias for the SessionRegistry bean in order to access it in your own configuration attribute session-registry-alias {xsd:string}? @@ -400,7 +401,7 @@ user.attlist &= attribute authorities {xsd:string} user.attlist &= ## Can be set to "true" to mark an account as locked and unusable. - attribute locked {"true" | "false"}? + attribute locked {boolean}? jdbc-user-service = diff --git a/core/src/main/resources/org/springframework/security/config/spring-security-2.0.xsd b/core/src/main/resources/org/springframework/security/config/spring-security-2.0.xsd index 457e1f1fe4..b5cce75f31 100644 --- a/core/src/main/resources/org/springframework/security/config/spring-security-2.0.xsd +++ b/core/src/main/resources/org/springframework/security/config/spring-security-2.0.xsd @@ -144,6 +144,12 @@ + + + + + + Defines an LDAP server location or starts an embedded server. The url @@ -609,7 +615,7 @@ - + Automatically registers a login form, BASIC authentication, anonymous authentication, logout services, remember-me and servlet-api-integration. If set to @@ -617,12 +623,6 @@ configuration of each by providing the respective element). If unspecified, defaults to "false". - - - - - - @@ -650,30 +650,18 @@ - + Whether test URLs should be converted to lower case prior to comparing with defined path patterns. If unspecified, defaults to "true". - - - - - - - + Provides versions of HttpServletRequest security methods such as isUserInRole() and getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to "true". - - - - - - @@ -710,17 +698,17 @@ used. - + Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults to "false" - - - - - - + + + + Allows the access denied page to be set (the user will be redirected here + if an AccessDeniedException is raised). + @@ -794,17 +782,11 @@ specified, defaults to /. - + Specifies whether a logout also causes HttpSession invalidation, which is generally desirable. If unspecified, defaults to true. - - - - - - @@ -914,16 +896,10 @@ context. - + as for http element - - - - - - @@ -942,14 +918,7 @@ - - - - - - - - + Allows you to define an alias for the SessionRegistry bean in order to @@ -1136,17 +1105,11 @@ comma (but no space). For example, "ROLE_USER,ROLE_ADMINISTRATOR" - + Can be set to "true" to mark an account as locked and unusable. - - - - - - diff --git a/core/src/test/java/org/springframework/security/config/HttpSecurityBeanDefinitionParserTests.java b/core/src/test/java/org/springframework/security/config/HttpSecurityBeanDefinitionParserTests.java index 63aae7bc47..336141076a 100644 --- a/core/src/test/java/org/springframework/security/config/HttpSecurityBeanDefinitionParserTests.java +++ b/core/src/test/java/org/springframework/security/config/HttpSecurityBeanDefinitionParserTests.java @@ -1,10 +1,6 @@ package org.springframework.security.config; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; import java.util.Iterator; import java.util.List; @@ -35,11 +31,11 @@ import org.springframework.security.ui.WebAuthenticationDetails; import org.springframework.security.ui.basicauth.BasicProcessingFilter; import org.springframework.security.ui.logout.LogoutFilter; import org.springframework.security.ui.preauth.x509.X509PreAuthenticatedProcessingFilter; -import org.springframework.security.ui.rememberme.AbstractRememberMeServices; import org.springframework.security.ui.rememberme.PersistentTokenBasedRememberMeServices; import org.springframework.security.ui.rememberme.RememberMeProcessingFilter; import org.springframework.security.ui.webapp.AuthenticationProcessingFilter; import org.springframework.security.ui.webapp.DefaultLoginPageGeneratingFilter; +import org.springframework.security.util.FieldUtils; import org.springframework.security.util.FilterChainProxy; import org.springframework.security.util.InMemoryXmlApplicationContext; import org.springframework.security.util.PortMapperImpl; @@ -198,6 +194,17 @@ public class HttpSecurityBeanDefinitionParserTests { FilterSecurityInterceptor fsi = (FilterSecurityInterceptor) filters.get(filters.size() - 1); assertTrue(fsi.isObserveOncePerRequest()); + } + + @Test + public void accessDeniedPageAttributeIsSupported() throws Exception { + setContext("" + AUTH_PROVIDER_XML); + FilterChainProxy filterChainProxy = getFilterChainProxy(); + List filters = filterChainProxy.getFilters("/someurl"); + + ExceptionTranslationFilter etf = (ExceptionTranslationFilter) filters.get(filters.size() - 2); + + assertEquals("/access-denied", FieldUtils.getFieldValue(etf, "accessDeniedHandler.errorPage")); } @Test @@ -376,7 +383,6 @@ public class HttpSecurityBeanDefinitionParserTests { assertEquals("Hello from the post processor!", service.getPostProcessorWasHere()); } - private void setContext(String context) { appContext = new InMemoryXmlApplicationContext(context); }