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.
This commit is contained in:
Luke Taylor 2008-04-07 22:17:09 +00:00
parent f57ba43780
commit 243b5f4a2a
4 changed files with 74 additions and 79 deletions

View File

@ -17,6 +17,7 @@ package org.springframework.security.util;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import java.lang.reflect.Field; import java.lang.reflect.Field;
@ -56,8 +57,7 @@ public final class FieldUtils {
* *
* @throws IllegalStateException if field could not be found * @throws IllegalStateException if field could not be found
*/ */
public static Field getField(Class clazz, String fieldName) public static Field getField(Class clazz, String fieldName) throws IllegalStateException {
throws IllegalStateException {
Assert.notNull(clazz, "Class required"); Assert.notNull(clazz, "Class required");
Assert.hasText(fieldName, "Field name required"); Assert.hasText(fieldName, "Field name required");
@ -73,6 +73,31 @@ public final class FieldUtils {
} }
} }
/**
* 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) { public static String getMutatorName(String fieldName) {
Assert.hasText(fieldName, "FieldName required"); Assert.hasText(fieldName, "FieldName required");

View File

@ -1,12 +1,9 @@
namespace beans = "http://www.springframework.org/schema/beans"
namespace a = "http://relaxng.org/ns/compatibility/annotations/1.0" 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" datatypes xsd = "http://www.w3.org/2001/XMLSchema-datatypes"
default namespace = "http://www.springframework.org/schema/security" 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 = hash =
## Defines the hashing algorithm used on user passwords. We recommend strongly against using MD4, as it is a very weak hashing algorithm. ## Defines the hashing algorithm used on user passwords. We recommend strongly against using MD4, as it is a very weak hashing algorithm.
@ -53,6 +50,8 @@ system-wide =
## A single value that will be used as the salt for a password encoder. ## A single value that will be used as the salt for a password encoder.
attribute system-wide {xsd:string} attribute system-wide {xsd:string}
boolean = "true" | "false"
ldap-server = ldap-server =
## Defines an LDAP server location or starts an embedded server. The url indicates the location of a remote server. If no url is given, an embedded server will be started, listening on the supplied port number. The port is optional and defaults to 33389. A Spring LDAP ContextSource bean will be registered for the server with the id supplied. ## Defines an LDAP server location or starts an embedded server. The url indicates the location of a remote server. If no url is given, an embedded server will be started, listening on the supplied port number. The port is optional and defaults to 33389. A Spring LDAP ContextSource bean will be registered for the server with the id supplied.
@ -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) } element http {http.attlist, (intercept-url+ & form-login? & openid-login & x509? & http-basic? & logout? & concurrent-session-control? & remember-me? & anonymous? & port-mappings) }
http.attlist &= 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". ## 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 &= http.attlist &=
## Controls the eagerness with which an HTTP session is created. If not set, defaults to "ifRequired". ## Controls the eagerness with which an HTTP session is created. If not set, defaults to "ifRequired".
attribute create-session {"ifRequired" | "always" | "never" }? attribute create-session {"ifRequired" | "always" | "never" }?
@ -198,10 +197,10 @@ http.attlist &=
path-type? path-type?
http.attlist &= http.attlist &=
## Whether test URLs should be converted to lower case prior to comparing with defined path patterns. If unspecified, defaults to "true". ## 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 &= http.attlist &=
## Provides versions of HttpServletRequest security methods such as isUserInRole() and getPrincipal() which are implemented by accessing the Spring SecurityContext. 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".
attribute servlet-api-provision {"true" | "false"}? attribute servlet-api-provision {boolean}?
http.attlist &= http.attlist &=
## Optional attribute specifying the ID of the AccessDecisionManager implementation which should be used for authorizing HTTP requests. ## Optional attribute specifying the ID of the AccessDecisionManager implementation which should be used for authorizing HTTP requests.
attribute access-decision-manager-ref {xsd:string}? attribute access-decision-manager-ref {xsd:string}?
@ -216,8 +215,10 @@ http.attlist &=
attribute entry-point-ref {xsd:string}? attribute entry-point-ref {xsd:string}?
http.attlist &= http.attlist &=
## Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults to "false" ## 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 = intercept-url =
## Specifies the access attributes and/or filter list for a particular set of URLs. ## 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}? attribute logout-success-url {xsd:string}?
logout.attlist &= logout.attlist &=
## Specifies whether a logout also causes HttpSession invalidation, which is generally desirable. If unspecified, defaults to true. ## 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 = form-login =
## Sets up a form login configuration for authentication with a username and password ## Sets up a form login configuration for authentication with a username and password
@ -294,7 +295,7 @@ fids.attlist &=
id? id?
fids.attlist &= fids.attlist &=
## as for http element ## as for http element
attribute lowercase-comparisons {"true" | "false"}? attribute lowercase-comparisons {boolean}?
fids.attlist &= fids.attlist &=
## as for http element ## as for http element
path-type? path-type?
@ -312,7 +313,7 @@ concurrent-sessions.attlist &=
concurrent-sessions.attlist &= concurrent-sessions.attlist &=
attribute expired-url {xsd:string}? attribute expired-url {xsd:string}?
concurrent-sessions.attlist &= concurrent-sessions.attlist &=
attribute exception-if-maximum-exceeded {"true" | "false"}? attribute exception-if-maximum-exceeded {boolean}?
concurrent-sessions.attlist &= concurrent-sessions.attlist &=
## Allows you to define an alias for the SessionRegistry bean in order to access it in your own configuration ## 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}? attribute session-registry-alias {xsd:string}?
@ -400,7 +401,7 @@ user.attlist &=
attribute authorities {xsd:string} attribute authorities {xsd:string}
user.attlist &= user.attlist &=
## Can be set to "true" to mark an account as locked and unusable. ## Can be set to "true" to mark an account as locked and unusable.
attribute locked {"true" | "false"}? attribute locked {boolean}?
jdbc-user-service = jdbc-user-service =

View File

@ -144,6 +144,12 @@
</xs:annotation> </xs:annotation>
</xs:attribute> </xs:attribute>
</xs:attributeGroup> </xs:attributeGroup>
<xs:simpleType name="boolean">
<xs:restriction base="xs:token">
<xs:enumeration value="true"/>
<xs:enumeration value="false"/>
</xs:restriction>
</xs:simpleType>
<xs:element name="ldap-server"> <xs:element name="ldap-server">
<xs:annotation> <xs:annotation>
<xs:documentation>Defines an LDAP server location or starts an embedded server. The url <xs:documentation>Defines an LDAP server location or starts an embedded server. The url
@ -609,7 +615,7 @@
</xs:complexType> </xs:complexType>
</xs:element> </xs:element>
<xs:attributeGroup name="http.attlist"> <xs:attributeGroup name="http.attlist">
<xs:attribute name="auto-config"> <xs:attribute name="auto-config" type="security:boolean">
<xs:annotation> <xs:annotation>
<xs:documentation>Automatically registers a login form, BASIC authentication, anonymous <xs:documentation>Automatically registers a login form, BASIC authentication, anonymous
authentication, logout services, remember-me and servlet-api-integration. If set to 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 configuration of each by providing the respective element). If unspecified, defaults to
"false".</xs:documentation> "false".</xs:documentation>
</xs:annotation> </xs:annotation>
<xs:simpleType>
<xs:restriction base="xs:token">
<xs:enumeration value="true"/>
<xs:enumeration value="false"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute> </xs:attribute>
<xs:attribute name="create-session"> <xs:attribute name="create-session">
<xs:annotation> <xs:annotation>
@ -650,30 +650,18 @@
</xs:restriction> </xs:restriction>
</xs:simpleType> </xs:simpleType>
</xs:attribute> </xs:attribute>
<xs:attribute name="lowercase-comparisons"> <xs:attribute name="lowercase-comparisons" type="security:boolean">
<xs:annotation> <xs:annotation>
<xs:documentation>Whether test URLs should be converted to lower case prior to comparing <xs:documentation>Whether test URLs should be converted to lower case prior to comparing
with defined path patterns. If unspecified, defaults to "true".</xs:documentation> with defined path patterns. If unspecified, defaults to "true".</xs:documentation>
</xs:annotation> </xs:annotation>
<xs:simpleType>
<xs:restriction base="xs:token">
<xs:enumeration value="true"/>
<xs:enumeration value="false"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute> </xs:attribute>
<xs:attribute name="servlet-api-provision"> <xs:attribute name="servlet-api-provision" type="security:boolean">
<xs:annotation> <xs:annotation>
<xs:documentation>Provides versions of HttpServletRequest security methods such as <xs:documentation>Provides versions of HttpServletRequest security methods such as
isUserInRole() and getPrincipal() which are implemented by accessing the Spring isUserInRole() and getPrincipal() which are implemented by accessing the Spring
SecurityContext. Defaults to "true".</xs:documentation> SecurityContext. Defaults to "true".</xs:documentation>
</xs:annotation> </xs:annotation>
<xs:simpleType>
<xs:restriction base="xs:token">
<xs:enumeration value="true"/>
<xs:enumeration value="false"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute> </xs:attribute>
<xs:attribute name="access-decision-manager-ref" type="xs:string"> <xs:attribute name="access-decision-manager-ref" type="xs:string">
<xs:annotation> <xs:annotation>
@ -710,17 +698,17 @@
used.</xs:documentation> used.</xs:documentation>
</xs:annotation> </xs:annotation>
</xs:attribute> </xs:attribute>
<xs:attribute name="once-per-request"> <xs:attribute name="once-per-request" type="security:boolean">
<xs:annotation> <xs:annotation>
<xs:documentation>Corresponds to the observeOncePerRequest property of <xs:documentation>Corresponds to the observeOncePerRequest property of
FilterSecurityInterceptor. Defaults to "false"</xs:documentation> FilterSecurityInterceptor. Defaults to "false"</xs:documentation>
</xs:annotation> </xs:annotation>
<xs:simpleType> </xs:attribute>
<xs:restriction base="xs:token"> <xs:attribute name="access-denied-page" type="xs:string">
<xs:enumeration value="true"/> <xs:annotation>
<xs:enumeration value="false"/> <xs:documentation>Allows the access denied page to be set (the user will be redirected here
</xs:restriction> if an AccessDeniedException is raised).</xs:documentation>
</xs:simpleType> </xs:annotation>
</xs:attribute> </xs:attribute>
</xs:attributeGroup> </xs:attributeGroup>
<xs:attributeGroup name="intercept-url.attlist"> <xs:attributeGroup name="intercept-url.attlist">
@ -794,17 +782,11 @@
specified, defaults to /.</xs:documentation> specified, defaults to /.</xs:documentation>
</xs:annotation> </xs:annotation>
</xs:attribute> </xs:attribute>
<xs:attribute name="invalidate-session"> <xs:attribute name="invalidate-session" type="security:boolean">
<xs:annotation> <xs:annotation>
<xs:documentation>Specifies whether a logout also causes HttpSession invalidation, which is <xs:documentation>Specifies whether a logout also causes HttpSession invalidation, which is
generally desirable. If unspecified, defaults to true.</xs:documentation> generally desirable. If unspecified, defaults to true.</xs:documentation>
</xs:annotation> </xs:annotation>
<xs:simpleType>
<xs:restriction base="xs:token">
<xs:enumeration value="true"/>
<xs:enumeration value="false"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute> </xs:attribute>
</xs:attributeGroup> </xs:attributeGroup>
<xs:attributeGroup name="form-login.attlist"> <xs:attributeGroup name="form-login.attlist">
@ -914,16 +896,10 @@
context.</xs:documentation> context.</xs:documentation>
</xs:annotation> </xs:annotation>
</xs:attribute> </xs:attribute>
<xs:attribute name="lowercase-comparisons"> <xs:attribute name="lowercase-comparisons" type="security:boolean">
<xs:annotation> <xs:annotation>
<xs:documentation>as for http element</xs:documentation> <xs:documentation>as for http element</xs:documentation>
</xs:annotation> </xs:annotation>
<xs:simpleType>
<xs:restriction base="xs:token">
<xs:enumeration value="true"/>
<xs:enumeration value="false"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute> </xs:attribute>
<xs:attribute name="path-type"> <xs:attribute name="path-type">
<xs:annotation> <xs:annotation>
@ -942,14 +918,7 @@
<xs:attributeGroup name="concurrent-sessions.attlist"> <xs:attributeGroup name="concurrent-sessions.attlist">
<xs:attribute name="max-sessions" type="xs:positiveInteger"/> <xs:attribute name="max-sessions" type="xs:positiveInteger"/>
<xs:attribute name="expired-url" type="xs:string"/> <xs:attribute name="expired-url" type="xs:string"/>
<xs:attribute name="exception-if-maximum-exceeded"> <xs:attribute name="exception-if-maximum-exceeded" type="security:boolean"/>
<xs:simpleType>
<xs:restriction base="xs:token">
<xs:enumeration value="true"/>
<xs:enumeration value="false"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="session-registry-alias" type="xs:string"> <xs:attribute name="session-registry-alias" type="xs:string">
<xs:annotation> <xs:annotation>
<xs:documentation>Allows you to define an alias for the SessionRegistry bean in order to <xs:documentation>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"</xs:documentation> comma (but no space). For example, "ROLE_USER,ROLE_ADMINISTRATOR"</xs:documentation>
</xs:annotation> </xs:annotation>
</xs:attribute> </xs:attribute>
<xs:attribute name="locked"> <xs:attribute name="locked" type="security:boolean">
<xs:annotation> <xs:annotation>
<xs:documentation>Can be set to "true" to mark an account as locked and <xs:documentation>Can be set to "true" to mark an account as locked and
unusable.</xs:documentation> unusable.</xs:documentation>
</xs:annotation> </xs:annotation>
<xs:simpleType>
<xs:restriction base="xs:token">
<xs:enumeration value="true"/>
<xs:enumeration value="false"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute> </xs:attribute>
</xs:attributeGroup> </xs:attributeGroup>
<xs:element name="jdbc-user-service" substitutionGroup="security:any-user-service"> <xs:element name="jdbc-user-service" substitutionGroup="security:any-user-service">

View File

@ -1,10 +1,6 @@
package org.springframework.security.config; package org.springframework.security.config;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.*;
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 java.util.Iterator; import java.util.Iterator;
import java.util.List; 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.basicauth.BasicProcessingFilter;
import org.springframework.security.ui.logout.LogoutFilter; import org.springframework.security.ui.logout.LogoutFilter;
import org.springframework.security.ui.preauth.x509.X509PreAuthenticatedProcessingFilter; 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.PersistentTokenBasedRememberMeServices;
import org.springframework.security.ui.rememberme.RememberMeProcessingFilter; import org.springframework.security.ui.rememberme.RememberMeProcessingFilter;
import org.springframework.security.ui.webapp.AuthenticationProcessingFilter; import org.springframework.security.ui.webapp.AuthenticationProcessingFilter;
import org.springframework.security.ui.webapp.DefaultLoginPageGeneratingFilter; import org.springframework.security.ui.webapp.DefaultLoginPageGeneratingFilter;
import org.springframework.security.util.FieldUtils;
import org.springframework.security.util.FilterChainProxy; import org.springframework.security.util.FilterChainProxy;
import org.springframework.security.util.InMemoryXmlApplicationContext; import org.springframework.security.util.InMemoryXmlApplicationContext;
import org.springframework.security.util.PortMapperImpl; import org.springframework.security.util.PortMapperImpl;
@ -200,6 +196,17 @@ public class HttpSecurityBeanDefinitionParserTests {
assertTrue(fsi.isObserveOncePerRequest()); assertTrue(fsi.isObserveOncePerRequest());
} }
@Test
public void accessDeniedPageAttributeIsSupported() throws Exception {
setContext("<http access-denied-page='/access-denied'><http-basic /></http>" + 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 @Test
public void interceptUrlWithRequiresChannelAddsChannelFilterToStack() { public void interceptUrlWithRequiresChannelAddsChannelFilterToStack() {
setContext( setContext(
@ -376,7 +383,6 @@ public class HttpSecurityBeanDefinitionParserTests {
assertEquals("Hello from the post processor!", service.getPostProcessorWasHere()); assertEquals("Hello from the post processor!", service.getPostProcessorWasHere());
} }
private void setContext(String context) { private void setContext(String context) {
appContext = new InMemoryXmlApplicationContext(context); appContext = new InMemoryXmlApplicationContext(context);
} }