1
0
mirror of synced 2026-07-13 14:45:10 +00:00

Compare commits

..

89 Commits

Author SHA1 Message Date
Luke Taylor 45253ca148 Updated sandbox pom versions to match release 2008-06-04 09:56:08 +00:00
Luke Taylor 807ae835b8 [maven-release-plugin] copy for tag spring-security-parent-2.0.2 2008-06-03 16:06:44 +00:00
Luke Taylor 122e1c47ed Changed rnc filename prior to 2.0.2 release 2008-06-01 19:34:50 +00:00
Luke Taylor 64ab7e534c Spelling corrections in Javadoc. 2008-06-01 17:26:27 +00:00
Luke Taylor ab6d29d927 SEC-862: Make logoutSuccessUrl accessible to sub-classes. 2008-06-01 16:15:09 +00:00
Luke Taylor 2a510f3539 Added question on login with multiple fields to faq 2008-06-01 15:25:39 +00:00
Luke Taylor 7c0f8b9756 Corrected typo in docbook. 2008-05-30 20:48:05 +00:00
Luke Taylor 1d9d7eb9a7 Removed accidental commit of SavedRequest clearing code in TargetUrlResolverImpl 2008-05-30 17:53:09 +00:00
Luke Taylor 0cfcc0e9f1 Tidying Javadoc 2008-05-30 17:51:23 +00:00
Luke Taylor 373f7b648b Corrected outdated filter name in Javadoc 2008-05-30 17:49:03 +00:00
Luke Taylor a5cae70949 SEC-800: Removed references to outdated method configuration classes. 2008-05-30 16:35:09 +00:00
Luke Taylor ecd2cc6da7 Added some Assert calls to setters and improved comments. 2008-05-30 15:29:51 +00:00
Luke Taylor f228d013d8 SEC-861: Change default value of justUseSavedRequestOnGet to false 2008-05-30 15:09:51 +00:00
Luke Taylor 4de4bb8e87 SEC-860: Added setter for authenticationDetailsSource to AbstractRememberMeServices 2008-05-30 14:29:32 +00:00
Luke Taylor f8cded10ee Typo. 2008-05-30 11:20:16 +00:00
Luke Taylor c031588975 SEC-606: Added support for customizable credentials character set. 2008-05-29 18:00:15 +00:00
Luke Taylor 36a192b70f SEC-858: Replaced integer properties in schema with strings to allow use of placeholders. 2008-05-29 16:13:14 +00:00
Luke Taylor 980a72f9a0 Removed TODO (done). 2008-05-29 15:54:50 +00:00
Luke Taylor 517a7f117a SEC-857: Make request wrapper getParameterValues() consistent with getParameterMap() etc. 2008-05-29 15:49:43 +00:00
Luke Taylor 244579faf4 OPEN - issue SEC-856: GroupManager JdbcUserDetailsManager implementation: addGroupAuthority() method doesn't work.
http://jira.springframework.org/browse/SEC-856. Refactored class to remove the JDBC-related inner classes.
2008-05-28 16:25:28 +00:00
Luke Taylor 87ba871605 Minor doc updates 2008-05-28 13:38:33 +00:00
Luke Taylor d63536cc0d SEC-821: Added support for eternal session registry and concurrent session controller to the 2.0.2 namespace. 2008-05-27 13:14:21 +00:00
Luke Taylor 8b5bbe3800 SEC-830: Changed SavedRequestAwareWrapper to make wrapped request parameters take precedence over saved request ones. 2008-05-25 22:57:03 +00:00
Luke Taylor cf4072c517 Context file improvements (based on sts suggestions) 2008-05-25 20:57:07 +00:00
Luke Taylor 4b45e5d7c2 Fixed OSGi version numbers in ranges [x,y] by adding a property pom.version.osgi 2008-05-25 20:55:17 +00:00
Luke Taylor 45c3084502 SEC-836: Made LDAP namespace elements use subtree group searching by default. 2008-05-23 23:57:01 +00:00
Luke Taylor 871e529840 SEC-850: custom-authentication-provider Registering Separate Bean Definitions in App Context and Providers List
http://jira.springframework.org/browse/SEC-850. Added extra test.
2008-05-23 23:32:57 +00:00
Luke Taylor d1005e4cfb SEC-850: custom-authentication-provider Registering Separate Bean Definitions in App Context and Providers List
http://jira.springframework.org/browse/SEC-850. Changed bean decorator to add a bean reference to the ProviderManager rather than a bean definition.
2008-05-23 23:25:09 +00:00
Luke Taylor 9ce0270226 Fixed typo in test name 2008-05-23 22:57:30 +00:00
Luke Taylor 7603ce2f97 SEC-848: Remove all Spring LDAP dependecy loading from namespace parsers
http://jira.springframework.org/browse/SEC-848. Replaced class references with class names.
2008-05-23 21:30:57 +00:00
Luke Taylor 859e99edf4 SEC-851: Fix port number in LDAP sample. 2008-05-23 21:24:48 +00:00
Luke Taylor 25ba269db0 SEC-835: use setContentType on response for J2EE 1.3 compatibility. 2008-05-23 20:55:10 +00:00
Luke Taylor 11b448c0e0 SEC-847: Updated the xsl file to inline openid-login and other elements 2008-05-23 16:29:44 +00:00
Luke Taylor 08c5fe8925 Fixed autoboxing issue 2008-05-22 12:19:00 +00:00
Luke Taylor fbe3ca48f4 SEC-823, SEC-843: Allow setting of custom RememberMeServices and token validity periodon remember-me namespace element 2008-05-21 16:03:05 +00:00
Luke Taylor 3e33b8a880 Update InMemoryXmlApplicationContext to use 2.0.2 schema 2008-05-20 22:46:37 +00:00
Luke Taylor b60c578b25 SEC-844: Support for SHA-256 hashing. 2008-05-20 22:45:02 +00:00
Luke Taylor 03981ab6a0 SEC-844: Added sec-256 to namespace schema 2008-05-20 22:32:03 +00:00
Luke Taylor e9adbd4d62 SEC-844, SEC-843, SEC-823: Added support for sha-256, custom remember-me services and setting of remember me token validity period to namespace schema. Also added 2.0.2 XSD file 2008-05-20 19:48:32 +00:00
Luke Taylor 29d31b72d0 SEC-837: Add special character filtering to LDAP search filters 2008-05-20 19:25:37 +00:00
Luke Taylor 3fb1f59fde SEC-837: Add special character filtering to LDAP search filterscore/src/test/java/org/springframework/security/ldap 2008-05-20 19:22:49 +00:00
Luke Taylor 219d2e8962 Corrected link 2008-05-20 10:57:17 +00:00
Luke Taylor ff215e6750 Minor doc fixes 2008-05-20 10:54:29 +00:00
Luke Taylor 6ae81d553b SEC-842: Minor doc fixes 2008-05-20 10:48:59 +00:00
Luke Taylor 5af53da106 Improved doc for'filters' attribute 2008-05-18 11:09:50 +00:00
Luke Taylor 2329dadf48 Removed jalopy parameter comments 2008-05-15 17:58:15 +00:00
Luke Taylor fb5eefeea5 SEC-740: Finished preauth chapter 2008-05-15 17:00:45 +00:00
Luke Taylor f269373442 IDE-791: Remove explicit Spring LDAP class dependencies from LdapServerBDP. 2008-05-15 14:33:42 +00:00
Luke Taylor 4f6b4e4bfd Make sample login pages use c:out for data output 2008-05-15 12:48:13 +00:00
Luke Taylor 8b2c0468ff OPEN - issue SEC-834: Session fixation attack protection will cause problems with URL rewriting
http://jira.springframework.org/browse/SEC-834. Modified HttpSecurityBDP to add session-fixation parameters to openId and form-login filters. Also added sessionRegistry property to AbstractProcessingFilter so that it doesn't conflict with concurrent session control.
2008-05-15 01:34:14 +00:00
Luke Taylor d17a2da9e0 SEC-834: Session fixation attack protection will cause problems with URL rewriting
http://jira.springframework.org/browse/SEC-834. Changed position of SessionFixationProtectionFilter and modified it to make a decision about whether authentication has taken place prior to calling doFilter(). Previously it did this on the return through the filter chain, which caused the problem described in this issue.
2008-05-15 00:26:27 +00:00
Luke Taylor 7f38c656ca SEC-820: Expand regular expression used in hierarchical roles. 2008-05-14 22:59:33 +00:00
Luke Taylor 6493df13f8 SEC-803: Removed use of websphere SubjectHelper class. 2008-05-14 22:51:39 +00:00
Luke Taylor d4defb10fe SEC-833: Fixed login-failure-url in contacts sample app. 2008-05-14 22:41:13 +00:00
Luke Taylor 59543af4fb SEC-826: Support for JPA PersistenceContext annotation broken
http://jira.springframework.org/browse/SEC-826 Moved all injection post-processing to BeanPostProcessors (and deleted bean factory post-processor) to prevent early instantiation problems. Beas should now all be instantiated before the injection takes place.
2008-05-14 16:41:52 +00:00
Luke Taylor 332f8fe5a1 SEC-624: Minor updates to docs 2008-05-13 17:16:19 +00:00
Luke Taylor 7a8eec11da SEC-765: Brief outline of preauth sample 2008-05-13 17:14:45 +00:00
Luke Taylor ff61644219 SEC-740: More on preauth 2008-05-13 17:13:47 +00:00
Luke Taylor 1fee538c7e Fixed typo in setter method (uses of). 2008-05-13 15:32:30 +00:00
Luke Taylor ae2470127c Fixed typo in setter method "seAttributePrefix" 2008-05-13 13:51:49 +00:00
Luke Taylor 2a4d859812 SEC-829: Minor doc fix 2008-05-13 10:18:09 +00:00
Luke Taylor 15b893f9ae SEC-809: OpenIDProcessingFilter updated to set authentication details (to make compatible with concurrent session control). 2008-05-12 20:05:24 +00:00
Luke Taylor de886e36fa SEC-624: Expanded general info on obtaining samples and added pointers to ldap and cas versions 2008-05-10 17:21:18 +00:00
Luke Taylor ad9a667b75 SEC-624: Inserted sub-sections for key class definitions so they appear in toc 2008-05-10 17:19:46 +00:00
Luke Taylor f70701d55a SEC-624: Added section on 'getting the source' for reference from samples chapter 2008-05-10 17:18:29 +00:00
Luke Taylor e1b226ee57 Added 2.0.2 namespace file 2008-05-10 17:16:46 +00:00
Luke Taylor af0153d833 Extended intro to Authentication part to include pointers to tech overview and namespace config 2008-05-10 17:10:49 +00:00
Luke Taylor d78a021fe1 Added basic intro to preauth 2008-05-10 16:07:39 +00:00
Luke Taylor e1c17450b3 Updated faqs to add infinite loop and access denied debug message 2008-05-10 12:31:14 +00:00
Luke Taylor add2649397 Javadoc typo. 2008-05-09 18:09:56 +00:00
Luke Taylor 781d88bd30 OPEN - issue SEC-825: Query string isn't beig stripped from URLs when ant matcher is in use (regression issue)
http://jira.springframework.org/browse/SEC-825. Make sure the property is set on DefaultFilterInvocationDefinitionSource when ant paths are in use.
2008-05-09 18:08:32 +00:00
Luke Taylor 1030dca353 SEC-786: Added information on the need ofor a UserDetailsService if using auto-config/remember-me 2008-05-09 15:01:39 +00:00
Luke Taylor b99f9d343d SEC-624: Added some info on use of role-prefix 2008-05-09 14:25:42 +00:00
Luke Taylor c0e829a41d SEC-700: Added info on new remember-me imlementation and namespace config examples 2008-05-08 15:59:01 +00:00
Luke Taylor 5a1258a4ca Corrected references to parts and reading order 2008-05-08 15:54:27 +00:00
Luke Taylor 883b92e7bd SEC-822: Converted to long arithmetic to prevent integer overflowing with long token validity periods 2008-05-08 15:07:40 +00:00
Luke Taylor 301d021bf5 SEC-817: NPE in org.springframework.security.config.FilterChainProxyPostProcessor
Reversed order of beanName.equals() call as suggested.
2008-05-07 13:58:53 +00:00
Luke Taylor 8ad2d681ab SEC-818: Changed redirect URL validation to ignore potential property placeholders at parsing time and report a warning through the parser context rather than an error. Also validated the URLs in the beans themselves using Asserts, so an exception will occur later when the beans have been created rather than while assembling the bean definitions. 2008-05-07 13:49:20 +00:00
Luke Taylor 5cf0c84e2f SEC-814: Added standard bean config to ldap example and updated doc to provide some pointers to DefaultLdapAuthoritiesPopulator 2008-05-06 14:50:14 +00:00
Luke Taylor afc757e618 Removed reference to LdapDataAccessException since it isn't actually mentioned except in javadoc 2008-05-06 14:43:52 +00:00
Luke Taylor c333070fe3 Javadoc tidying 2008-05-06 13:59:46 +00:00
Luke Taylor fca3a2a709 SEC-812: Added missing TextUtils file 2008-05-05 19:09:09 +00:00
Luke Taylor fa44c74993 SEC-812: Added entity-escaping of username stored under last username key, to prevent problems if it is rendered in a page without escaping the text. 2008-05-05 18:37:02 +00:00
Luke Taylor 06719053f1 Removed commons lang dependency. 2008-05-05 17:18:47 +00:00
Luke Taylor e7b6fe09e1 Corrected css for 'poweredBy' 2008-05-03 16:18:19 +00:00
Ben Alex 9961c7f867 Moved to correct build location. 2008-05-02 10:52:57 +00:00
Ben Alex 7a2e1e13d3 SEC-811: Provide a mechanism to allocate and rebuild cryptographically strong, randomised tokens. 2008-05-02 10:38:56 +00:00
Luke Taylor a599ef5398 [maven-release-plugin] prepare for next development iteration 2008-05-01 20:09:03 +00:00
Luke Taylor 3e808335a4 [maven-release-plugin] prepare release spring-security-parent-2.0.1 2008-05-01 20:07:46 +00:00
142 changed files with 3880 additions and 1749 deletions
+4 -4
View File
@@ -3,13 +3,13 @@
<parent>
<artifactId>spring-security-parent</artifactId>
<groupId>org.springframework.security</groupId>
<version>2.0.1</version>
<version>2.0.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-acl</artifactId>
<name>Spring Security - ACL module</name>
<version>2.0.1</version>
<version>2.0.2</version>
<packaging>bundle</packaging>
<dependencies>
@@ -48,12 +48,12 @@
<properties>
<spring.osgi.export>
org.springframework.security.*;version=${pom.version}
org.springframework.security.*;version=${pom.version.osgi}
</spring.osgi.export>
<spring.osgi.import>
net.sf.ehcache.*;version="[1.4.1, 2.0.0)";resolution:=optional,
org.springframework.security.*;version="[${pom.version},${pom.version}]",
org.springframework.security.*;version="[${pom.version.osgi},${pom.version.osgi}]",
org.springframework.context.*;version="${spring.version.osgi}",
org.springframework.dao.*;version="${spring.version.osgi}";resolution:=optional,
org.springframework.jdbc.*;version="${spring.version.osgi}";resolution:=optional,
+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-adapters</artifactId>
<version>2.0.1</version>
<version>2.0.2</version>
</parent>
<artifactId>spring-security-catalina</artifactId>
<name>Spring Security - Catalina adapter</name>
+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-adapters</artifactId>
<version>2.0.1</version>
<version>2.0.2</version>
</parent>
<artifactId>spring-security-jboss</artifactId>
<name>Spring Security - JBoss adapter</name>
+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-adapters</artifactId>
<version>2.0.1</version>
<version>2.0.2</version>
</parent>
<artifactId>spring-security-jetty</artifactId>
<name>Spring Security - Jetty adapter</name>
+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-parent</artifactId>
<version>2.0.1</version>
<version>2.0.2</version>
</parent>
<artifactId>spring-security-adapters</artifactId>
<name>Spring Security - Adapters</name>
+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-adapters</artifactId>
<version>2.0.1</version>
<version>2.0.2</version>
</parent>
<artifactId>spring-security-resin</artifactId>
<name>Spring Security - Resin adapter</name>
+3 -3
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-parent</artifactId>
<version>2.0.1</version>
<version>2.0.2</version>
</parent>
<artifactId>spring-security-cas-client</artifactId>
<name>Spring Security - CAS support</name>
@@ -42,11 +42,11 @@
<properties>
<spring.osgi.export>
org.springframework.security.*;version=${pom.version}
org.springframework.security.*;version=${pom.version.osgi}
</spring.osgi.export>
<spring.osgi.import>
org.springframework.security.*;version="[${pom.version},${pom.version}]",
org.springframework.security.*;version="[${pom.version.osgi},${pom.version.osgi}]",
org.springframework.beans.*;version="${spring.version.osgi}",
org.springframework.context.*;version="${spring.version.osgi}",
org.springframework.dao.*;version="${spring.version.osgi}";resolution:=optional,
@@ -30,12 +30,14 @@ import org.springframework.util.Assert;
/**
* Used by the <code>SecurityEnforcementFilter</code> to commence authentication via the JA-SIG Central
* Authentication Service (CAS).<P>The user's browser will be redirected to the JA-SIG CAS enterprise-wide login
* page. This page is specified by the <code>loginUrl</code> property. Once login is complete, the CAS login page will
* Used by the <code>ExceptionTranslationFilter</code> to commence authentication via the JA-SIG Central
* Authentication Service (CAS).
* <p>
* The user's browser will be redirected to the JA-SIG CAS enterprise-wide login page.
* This page is specified by the <code>loginUrl</code> property. Once login is complete, the CAS login page will
* redirect to the page indicated by the <code>service</code> property. The <code>service</code> is a HTTP URL
* belonging to the current application. The <code>service</code> URL is monitored by the {@link CasProcessingFilter},
* which will validate the CAS login was successful.</p>
* which will validate the CAS login was successful.
*
* @author Ben Alex
* @author Scott Battaglia
@@ -65,8 +67,8 @@ public class CasProcessingFilterEntryPoint implements AuthenticationEntryPoint,
}
public void commence(final ServletRequest servletRequest, final ServletResponse servletResponse,
final AuthenticationException authenticationException)
throws IOException, ServletException {
final AuthenticationException authenticationException) throws IOException, ServletException {
final HttpServletResponse response = (HttpServletResponse) servletResponse;
final String urlEncodedService = CommonUtils.constructServiceUrl(null, response, this.serviceProperties.getService(), null, "ticket", this.encodeServiceUrlWithSessionId);
final String redirectUrl = CommonUtils.constructRedirectUrl(this.loginUrl, "service", urlEncodedService, this.serviceProperties.isSendRenew(), false);
@@ -21,9 +21,10 @@ import org.springframework.util.Assert;
/**
* Stores properties related to this CAS service.
* <p>Each web application capable of processing CAS tickets is known as a service.
* <p>
* Each web application capable of processing CAS tickets is known as a service.
* This class stores the properties that are relevant to the local CAS service, being the application
* that is being secured by Spring Security.</p>
* that is being secured by Spring Security.
*
* @author Ben Alex
* @version $Id$
@@ -42,7 +43,8 @@ public class ServiceProperties implements InitializingBean {
/**
* Represents the service the user is authenticating to.
* <p>This service is the callback URL belonging to the local Spring Security System for Spring secured application.
* <p>
* This service is the callback URL belonging to the local Spring Security System for Spring secured application.
* For example,
* <pre>
* https://www.mycompany.com/application/j_spring_cas_security_check
@@ -56,10 +58,12 @@ public class ServiceProperties implements InitializingBean {
/**
* Indicates whether the <code>renew</code> parameter should be sent to the CAS login URL and CAS
* validation URL.<p>If <code>true</code>, it will force CAS to authenticate the user again (even if the
* validation URL.
* <p>
* If <code>true</code>, it will force CAS to authenticate the user again (even if the
* user has previously authenticated). During ticket validation it will require the ticket was generated as a
* consequence of an explicit login. High security applications would probably set this to <code>true</code>.
* Defaults to <code>false</code>, providing automated single sign on.</p>
* Defaults to <code>false</code>, providing automated single sign on.
*
* @return whether to send the <code>renew</code> parameter to CAS
*/
+2 -2
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-parent</artifactId>
<version>2.0.1</version>
<version>2.0.2</version>
</parent>
<packaging>bundle</packaging>
<artifactId>spring-security-core-tiger</artifactId>
@@ -89,7 +89,7 @@
<spring.osgi.import>
javax.annotation.*;version="[1.0.0, 2.0.0)",
org.springframework.security.*;version="[${pom.version},${pom.version}]",
org.springframework.security.*;version="[${pom.version.osgi},${pom.version.osgi}]",
org.springframework.core.*;version="${spring.version.osgi}"
</spring.osgi.import>
@@ -0,0 +1,12 @@
# Logging
#
# $Id: log4j.properties 2385 2007-12-20 20:53:26Z luke_t $
log4j.rootCategory=DEBUG, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p %c - %m%n
log4j.category.org.springframework.security=DEBUG
+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-parent</artifactId>
<version>2.0.1</version>
<version>2.0.2</version>
</parent>
<packaging>bundle</packaging>
<artifactId>spring-security-core</artifactId>
@@ -90,7 +90,7 @@ public class SimpleAttributes2GrantedAuthoritiesMapper implements Attributes2Gra
return attributePrefix == null ? "" : attributePrefix;
}
public void seAttributePrefix(String string) {
public void setAttributePrefix(String string) {
attributePrefix = string;
}
@@ -21,6 +21,7 @@ import org.springframework.security.ui.FilterChainOrder;
import org.springframework.security.ui.SpringSecurityFilter;
import org.springframework.security.ui.logout.LogoutHandler;
import org.springframework.security.ui.logout.SecurityContextLogoutHandler;
import org.springframework.security.util.UrlUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
@@ -60,6 +61,7 @@ public class ConcurrentSessionFilter extends SpringSecurityFilter implements Ini
public void afterPropertiesSet() throws Exception {
Assert.notNull(sessionRegistry, "SessionRegistry required");
Assert.isTrue(UrlUtils.isValidRedirectUrl(expiredUrl), expiredUrl + " isn't a valid redirect URL");
}
public void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
@@ -23,12 +23,12 @@ public abstract class AbstractUserDetailsServiceBeanDefinitionParser implements
/** UserDetailsService bean Id. For use in a stateful context (i.e. in AuthenticationProviderBDP) */
private String id;
protected abstract Class getBeanClass(Element element);
protected abstract String getBeanClassName(Element element);
protected abstract void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder);
public BeanDefinition parse(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(getBeanClass(element));
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(getBeanClassName(element));
doParse(element, parserContext, builder);
@@ -3,27 +3,40 @@ package org.springframework.security.config;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* Just registers an alias name for the default ProviderManager used by the namespace
* Registers an alias name for the default ProviderManager used by the namespace
* configuration, allowing users to reference it in their beans and clearly see where the name is
* coming from.
* coming from. Also allows the ConcurrentSessionController to be set on the ProviderManager.
*
* @author Luke Taylor
* @version $Id$
*/
public class AuthenticationManagerBeanDefinitionParser implements BeanDefinitionParser {
private static final String ATT_ALIAS = "alias";
private static final String ATT_SESSION_CONTROLLER_REF = "session-controller-ref";
private static final String ATT_ALIAS = "alias";
public BeanDefinition parse(Element element, ParserContext parserContext) {
BeanDefinition authManager = ConfigUtils.registerProviderManagerIfNecessary(parserContext);
String alias = element.getAttribute(ATT_ALIAS);
if (!StringUtils.hasText(alias)) {
parserContext.getReaderContext().error(ATT_ALIAS + " is required.", element );
}
String sessionControllerRef = element.getAttribute(ATT_SESSION_CONTROLLER_REF);
if (StringUtils.hasText(sessionControllerRef)) {
ConfigUtils.setSessionControllerOnAuthenticationManager(parserContext,
BeanIds.CONCURRENT_SESSION_CONTROLLER, element);
authManager.getPropertyValues().addPropertyValue("sessionController",
new RuntimeBeanReference(sessionControllerRef));
}
parserContext.getRegistry().registerAlias(BeanIds.AUTHENTICATION_MANAGER, alias);
@@ -1,7 +1,5 @@
package org.springframework.security.config;
import org.springframework.beans.factory.config.BeanDefinition;
/**
* Contains all the default Bean IDs created by the namespace support in Spring Security 2.
* <p>
@@ -18,7 +16,8 @@ public abstract class BeanIds {
/** Package protected as end users shouldn't really be using this BFPP directly */
static final String INTERCEPT_METHODS_BEAN_FACTORY_POST_PROCESSOR = "_interceptMethodsBeanfactoryPP";
static final String CONTEXT_SOURCE_SETTING_POST_PROCESSOR = "_contextSettingPostProcessor";
static final String HTTP_POST_PROCESSOR = "_httpConfigBeanFactoryPostProcessor";
static final String ENTRY_POINT_INJECTION_POST_PROCESSOR = "_entryPointInjectionBeanPostProcessor";
static final String USER_DETAILS_SERVICE_INJECTION_POST_PROCESSOR = "_userServiceInjectionPostProcessor";
static final String FILTER_CHAIN_POST_PROCESSOR = "_filterChainProxyPostProcessor";
static final String FILTER_LIST = "_filterChainList";
@@ -63,7 +62,7 @@ public abstract class BeanIds {
public static final String CONTEXT_SOURCE = "_securityContextSource";
public static final String PORT_MAPPER = "_portMapper";
public static final String X509_FILTER = "_x509ProcessingFilter";
public static final String X509_AUTH_PROVIDER = "_x509AuthenitcationProvider";
public static final String X509_AUTH_PROVIDER = "_x509AuthenticationProvider";
public static final String PRE_AUTH_ENTRY_POINT = "_preAuthenticatedProcessingFilterEntryPoint";
public static final String REMEMBER_ME_SERVICES_INJECTION_POST_PROCESSOR = "_rememberMeServicesInjectionBeanPostProcessor";
@@ -29,7 +29,8 @@ public class ConcurrentSessionsBeanDefinitionParser implements BeanDefinitionPar
static final String ATT_EXPIRY_URL = "expired-url";
static final String ATT_MAX_SESSIONS = "max-sessions";
static final String ATT_EXCEPTION_IF_MAX_EXCEEDED = "exception-if-maximum-exceeded";
static final String ATT_SESSION_REGISTRY_ALIAS = "session-registry-alias";
static final String ATT_SESSION_REGISTRY_ALIAS = "session-registry-alias";
static final String ATT_SESSION_REGISTRY_REF = "session-registry-ref";
public BeanDefinition parse(Element element, ParserContext parserContext) {
CompositeComponentDefinition compositeDef =
@@ -38,26 +39,43 @@ public class ConcurrentSessionsBeanDefinitionParser implements BeanDefinitionPar
BeanDefinitionRegistry beanRegistry = parserContext.getRegistry();
RootBeanDefinition sessionRegistry = new RootBeanDefinition(SessionRegistryImpl.class);
String sessionRegistryId = element.getAttribute(ATT_SESSION_REGISTRY_REF);
if (!StringUtils.hasText(sessionRegistryId)) {
RootBeanDefinition sessionRegistry = new RootBeanDefinition(SessionRegistryImpl.class);
beanRegistry.registerBeanDefinition(BeanIds.SESSION_REGISTRY, sessionRegistry);
parserContext.registerComponent(new BeanComponentDefinition(sessionRegistry, BeanIds.SESSION_REGISTRY));
sessionRegistryId = BeanIds.SESSION_REGISTRY;
} else {
// Register the default ID as an alias so that things like session fixation filter can access it
beanRegistry.registerAlias(sessionRegistryId, BeanIds.SESSION_REGISTRY);
}
String registryAlias = element.getAttribute(ATT_SESSION_REGISTRY_ALIAS);
if (StringUtils.hasText(registryAlias)) {
beanRegistry.registerAlias(sessionRegistryId, registryAlias);
}
BeanDefinitionBuilder filterBuilder =
BeanDefinitionBuilder.rootBeanDefinition(ConcurrentSessionFilter.class);
BeanDefinitionBuilder controllerBuilder
= BeanDefinitionBuilder.rootBeanDefinition(ConcurrentSessionControllerImpl.class);
controllerBuilder.addPropertyValue("sessionRegistry", new RuntimeBeanReference(BeanIds.SESSION_REGISTRY));
filterBuilder.addPropertyValue("sessionRegistry", new RuntimeBeanReference(BeanIds.SESSION_REGISTRY));
filterBuilder.addPropertyValue("sessionRegistry", new RuntimeBeanReference(sessionRegistryId));
Object source = parserContext.extractSource(element);
filterBuilder.setSource(source);
filterBuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
controllerBuilder.setSource(source);
controllerBuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
String expiryUrl = element.getAttribute(ATT_EXPIRY_URL);
if (StringUtils.hasText(expiryUrl)) {
ConfigUtils.validateHttpRedirect(expiryUrl, parserContext, source);
filterBuilder.addPropertyValue("expiredUrl", expiryUrl);
}
}
BeanDefinitionBuilder controllerBuilder
= BeanDefinitionBuilder.rootBeanDefinition(ConcurrentSessionControllerImpl.class);
controllerBuilder.setSource(source);
controllerBuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
controllerBuilder.addPropertyValue("sessionRegistry", new RuntimeBeanReference(sessionRegistryId));
String maxSessions = element.getAttribute(ATT_MAX_SESSIONS);
@@ -72,23 +90,14 @@ public class ConcurrentSessionsBeanDefinitionParser implements BeanDefinitionPar
}
BeanDefinition controller = controllerBuilder.getBeanDefinition();
beanRegistry.registerBeanDefinition(BeanIds.SESSION_REGISTRY, sessionRegistry);
parserContext.registerComponent(new BeanComponentDefinition(sessionRegistry, BeanIds.SESSION_REGISTRY));
String registryAlias = element.getAttribute(ATT_SESSION_REGISTRY_ALIAS);
if (StringUtils.hasText(registryAlias)) {
beanRegistry.registerAlias(BeanIds.SESSION_REGISTRY, registryAlias);
}
beanRegistry.registerBeanDefinition(BeanIds.CONCURRENT_SESSION_CONTROLLER, controller);
parserContext.registerComponent(new BeanComponentDefinition(controller, BeanIds.CONCURRENT_SESSION_CONTROLLER));
beanRegistry.registerBeanDefinition(BeanIds.CONCURRENT_SESSION_FILTER, filterBuilder.getBeanDefinition());
parserContext.registerComponent(new BeanComponentDefinition(filterBuilder.getBeanDefinition(), BeanIds.CONCURRENT_SESSION_FILTER));
ConfigUtils.addHttpFilter(parserContext, new RuntimeBeanReference(BeanIds.CONCURRENT_SESSION_FILTER));
BeanDefinition providerManager = ConfigUtils.registerProviderManagerIfNecessary(parserContext);
providerManager.getPropertyValues().addPropertyValue("sessionController", controller);
ConfigUtils.setSessionControllerOnAuthenticationManager(parserContext, BeanIds.CONCURRENT_SESSION_CONTROLLER, element);
parserContext.popAndRegisterContainingComponent();
@@ -1,11 +1,13 @@
package org.springframework.security.config;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.RuntimeBeanReference;
@@ -16,10 +18,12 @@ import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.security.afterinvocation.AfterInvocationProviderManager;
import org.springframework.security.providers.ProviderManager;
import org.springframework.security.userdetails.UserDetailsService;
import org.springframework.security.util.UrlUtils;
import org.springframework.security.vote.AffirmativeBased;
import org.springframework.security.vote.AuthenticatedVoter;
import org.springframework.security.vote.RoleVoter;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* Utility methods used internally by the Spring Security namespace configuration code.
@@ -88,28 +92,6 @@ public abstract class ConfigUtils {
return authManager;
}
/**
* Obtains a user details service for use in RememberMeServices etc. Will return a caching version
* if available so should not be used for beans which need to separate the two.
*/
static RuntimeBeanReference getUserDetailsService(ConfigurableListableBeanFactory bf) {
String[] services = bf.getBeanNamesForType(CachingUserDetailsService.class, false, false);
if (services.length == 0) {
services = bf.getBeanNamesForType(UserDetailsService.class);
}
if (services.length == 0) {
throw new IllegalArgumentException("No UserDetailsService registered.");
} else if (services.length > 1) {
throw new IllegalArgumentException("More than one UserDetailsService registered. Please " +
"use a specific Id in your configuration");
}
return new RuntimeBeanReference(services[0]);
}
static ManagedList getRegisteredProviders(ParserContext parserContext) {
BeanDefinition authManager = registerProviderManagerIfNecessary(parserContext);
return (ManagedList) authManager.getPropertyValues().getPropertyValue("providers").getValue();
@@ -180,12 +162,25 @@ public abstract class ConfigUtils {
/**
* Checks the value of an XML attribute which represents a redirect URL.
* If not empty or starting with "/" or "http" it will raise an error.
* If not empty or starting with "$" (potential placeholder), "/" or "http" it will raise an error.
*/
static void validateHttpRedirect(String url, ParserContext pc, Object source) {
if (!StringUtils.hasText(url) || url.startsWith("/") || url.toLowerCase().startsWith("http")) {
if (UrlUtils.isValidRedirectUrl(url) || url.startsWith("$")) {
return;
}
pc.getReaderContext().error(url + " is not a valid redirect URL (must start with '/' or http(s))", source);
pc.getReaderContext().warning(url + " is not a valid redirect URL (must start with '/' or http(s))", source);
}
static void setSessionControllerOnAuthenticationManager(ParserContext pc, String beanName, Element sourceElt) {
BeanDefinition authManager = registerProviderManagerIfNecessary(pc);
PropertyValue pv = authManager.getPropertyValues().getPropertyValue("sessionController");
if (pv != null && pv.getValue() != null) {
pc.getReaderContext().error("A session controller has already been set on the authentication manager. " +
"The <concurrent-session-control> element isn't compatible with a custom session controller",
pc.extractSource(sourceElt));
}
authManager.getPropertyValues().addPropertyValue("sessionController", new RuntimeBeanReference(beanName));
}
}
@@ -3,6 +3,7 @@ package org.springframework.security.config;
import org.springframework.beans.factory.xml.BeanDefinitionDecorator;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.w3c.dom.Node;
@@ -16,7 +17,7 @@ import org.w3c.dom.Node;
*/
public class CustomAuthenticationProviderBeanDefinitionDecorator implements BeanDefinitionDecorator {
public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder holder, ParserContext parserContext) {
ConfigUtils.getRegisteredProviders(parserContext).add(holder.getBeanDefinition());
ConfigUtils.getRegisteredProviders(parserContext).add(new RuntimeBeanReference(holder.getBeanName()));
return holder;
}
@@ -0,0 +1,59 @@
package org.springframework.security.config;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.security.ui.AuthenticationEntryPoint;
import org.springframework.security.ui.ExceptionTranslationFilter;
import org.springframework.util.Assert;
/**
*
* @author Luke Taylor
* @since 2.0.2
*/
public class EntryPointInjectionBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
private final Log logger = LogFactory.getLog(getClass());
private ConfigurableListableBeanFactory beanFactory;
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (!BeanIds.EXCEPTION_TRANSLATION_FILTER.equals(beanName)) {
return bean;
}
logger.info("Selecting AuthenticationEntryPoint for use in ExceptionTranslationFilter");
ExceptionTranslationFilter etf = (ExceptionTranslationFilter) beanFactory.getBean(BeanIds.EXCEPTION_TRANSLATION_FILTER);
Object entryPoint = null;
if (beanFactory.containsBean(BeanIds.MAIN_ENTRY_POINT)) {
entryPoint = beanFactory.getBean(BeanIds.MAIN_ENTRY_POINT);
logger.info("Using main configured AuthenticationEntryPoint.");
} else {
Map entryPoints = beanFactory.getBeansOfType(AuthenticationEntryPoint.class);
Assert.isTrue(entryPoints.size() != 0, "No AuthenticationEntryPoint instances defined");
Assert.isTrue(entryPoints.size() == 1, "More than one AuthenticationEntryPoint defined in context");
entryPoint = entryPoints.values().toArray()[0];
}
logger.info("Using bean '" + entryPoint + "' as the entry point.");
etf.setAuthenticationEntryPoint((AuthenticationEntryPoint) entryPoint);
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
}
}
@@ -44,7 +44,7 @@ public class FilterChainProxyPostProcessor implements BeanPostProcessor, BeanFac
private ListableBeanFactory beanFactory;
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if(!beanName.equals(BeanIds.FILTER_CHAIN_PROXY)) {
if(!BeanIds.FILTER_CHAIN_PROXY.equals(beanName)) {
return bean;
}
@@ -1,5 +1,6 @@
package org.springframework.security.config;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
@@ -54,6 +55,18 @@ public class FormLoginBeanDefinitionParser implements BeanDefinitionParser {
Object source = null;
// Copy values from the session fixation protection filter
final Boolean sessionFixationProtectionEnabled =
new Boolean(pc.getRegistry().containsBeanDefinition(BeanIds.SESSION_FIXATION_PROTECTION_FILTER));
Boolean migrateSessionAttributes = Boolean.FALSE;
if (sessionFixationProtectionEnabled.booleanValue()) {
PropertyValue pv =
pc.getRegistry().getBeanDefinition(BeanIds.SESSION_FIXATION_PROTECTION_FILTER)
.getPropertyValues().getPropertyValue("migrateSessionAttributes");
migrateSessionAttributes = (Boolean)pv.getValue();
}
if (elt != null) {
source = pc.extractSource(elt);
loginUrl = elt.getAttribute(ATT_LOGIN_URL);
@@ -79,10 +92,20 @@ public class FormLoginBeanDefinitionParser implements BeanDefinitionParser {
filterBean.getPropertyValues().addPropertyValue("authenticationManager",
new RuntimeBeanReference(BeanIds.AUTHENTICATION_MANAGER));
filterBean.getPropertyValues().addPropertyValue("invalidateSessionOnSuccessfulAuthentication",
sessionFixationProtectionEnabled);
filterBean.getPropertyValues().addPropertyValue("migrateInvalidatedSessionAttributes",
migrateSessionAttributes);
if (pc.getRegistry().containsBeanDefinition(BeanIds.REMEMBER_ME_SERVICES)) {
filterBean.getPropertyValues().addPropertyValue("rememberMeServices",
new RuntimeBeanReference(BeanIds.REMEMBER_ME_SERVICES) );
}
if (pc.getRegistry().containsBeanDefinition(BeanIds.SESSION_REGISTRY)) {
filterBean.getPropertyValues().addPropertyValue("sessionRegistry",
new RuntimeBeanReference(BeanIds.SESSION_REGISTRY));
}
BeanDefinitionBuilder entryPointBuilder =
BeanDefinitionBuilder.rootBeanDefinition(AuthenticationProcessingFilterEntryPoint.class);
@@ -181,11 +181,14 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
new X509BeanDefinitionParser().parse(x509Elt, parserContext);
}
// Register the post processor which will tie up the loose ends in the configuration once the app context has been created and all beans are available.
RootBeanDefinition postProcessor = new RootBeanDefinition(HttpSecurityConfigPostProcessor.class);
// Register the post processors which will tie up the loose ends in the configuration once the app context has been created and all beans are available.
RootBeanDefinition postProcessor = new RootBeanDefinition(EntryPointInjectionBeanPostProcessor.class);
postProcessor.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(BeanIds.HTTP_POST_PROCESSOR, postProcessor);
registry.registerBeanDefinition(BeanIds.ENTRY_POINT_INJECTION_POST_PROCESSOR, postProcessor);
RootBeanDefinition postProcessor2 = new RootBeanDefinition(UserDetailsServiceInjectionBeanPostProcessor.class);
postProcessor2.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(BeanIds.USER_DETAILS_SERVICE_INJECTION_POST_PROCESSOR, postProcessor2);
return null;
}
@@ -276,8 +279,11 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
builder.addPropertyValue("observeOncePerRequest", Boolean.FALSE);
}
builder.addPropertyValue("objectDefinitionSource",
new DefaultFilterInvocationDefinitionSource(matcher, filterInvocationDefinitionMap));
DefaultFilterInvocationDefinitionSource fids =
new DefaultFilterInvocationDefinitionSource(matcher, filterInvocationDefinitionMap);
fids.setStripQueryStringFromUrls(matcher instanceof AntUrlPathMatcher);
builder.addPropertyValue("objectDefinitionSource", fids);
pc.getRegistry().registerBeanDefinition(BeanIds.FILTER_SECURITY_INTERCEPTOR, builder.getBeanDefinition());
ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.FILTER_SECURITY_INTERCEPTOR));
}
@@ -288,7 +294,7 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
new RuntimeBeanReference(BeanIds.CHANNEL_DECISION_MANAGER));
DefaultFilterInvocationDefinitionSource channelFilterInvDefSource =
new DefaultFilterInvocationDefinitionSource(matcher, channelRequestMap);
channelFilterInvDefSource.setStripQueryStringFromUrls(matcher instanceof AntUrlPathMatcher);
channelFilter.getPropertyValues().addPropertyValue("filterInvocationDefinitionSource",
channelFilterInvDefSource);
@@ -383,9 +389,8 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
}
BeanDefinition openIDProvider = openIDProviderBuilder.getBeanDefinition();
ConfigUtils.getRegisteredProviders(pc).add(openIDProvider);
pc.getRegistry().registerBeanDefinition(BeanIds.OPEN_ID_PROVIDER, openIDProvider);
ConfigUtils.getRegisteredProviders(pc).add(new RuntimeBeanReference(BeanIds.OPEN_ID_PROVIDER));
}
boolean needLoginPage = false;
@@ -1,150 +0,0 @@
package org.springframework.security.config;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.core.Ordered;
import org.springframework.security.ui.AuthenticationEntryPoint;
import org.springframework.security.userdetails.UserDetailsByNameServiceWrapper;
import org.springframework.util.Assert;
/**
* Responsible for tying up the HTTP security configuration once all the beans are registered.
* This class does not actually instantiate any beans (for example, it should not call {@link BeanFactory#getBean(String)}).
* All the wiring up should be done using bean definitions or bean references to avoid this. This approach should avoid any
* conflict with other processors.
*
* @author Luke Taylor
* @author Ben Alex
* @version $Id$
* @since 2.0
*/
public class HttpSecurityConfigPostProcessor implements BeanFactoryPostProcessor, Ordered {
private Log logger = LogFactory.getLog(getClass());
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
injectUserDetailsServiceIntoRememberMeServices(beanFactory);
injectUserDetailsServiceIntoX509Provider(beanFactory);
injectUserDetailsServiceIntoOpenIDProvider(beanFactory);
injectAuthenticationEntryPointIntoExceptionTranslationFilter(beanFactory);
}
private void injectUserDetailsServiceIntoRememberMeServices(ConfigurableListableBeanFactory bf) {
try {
BeanDefinition rememberMeServices = bf.getBeanDefinition(BeanIds.REMEMBER_ME_SERVICES);
PropertyValue pv = rememberMeServices.getPropertyValues().getPropertyValue("userDetailsService");
if (pv == null) {
rememberMeServices.getPropertyValues().addPropertyValue("userDetailsService",
ConfigUtils.getUserDetailsService(bf));
} else {
RuntimeBeanReference cachingUserService = getCachingUserService(bf, pv.getValue());
if (cachingUserService != null) {
rememberMeServices.getPropertyValues().addPropertyValue("userDetailsService", cachingUserService);
}
}
} catch (NoSuchBeanDefinitionException e) {
// ignore
}
}
private void injectUserDetailsServiceIntoX509Provider(ConfigurableListableBeanFactory bf) {
try {
BeanDefinition x509AuthProvider = bf.getBeanDefinition(BeanIds.X509_AUTH_PROVIDER);
PropertyValue pv = x509AuthProvider.getPropertyValues().getPropertyValue("preAuthenticatedUserDetailsService");
if (pv == null) {
BeanDefinitionBuilder preAuthUserService = BeanDefinitionBuilder.rootBeanDefinition(UserDetailsByNameServiceWrapper.class);
preAuthUserService.addPropertyValue("userDetailsService", ConfigUtils.getUserDetailsService(bf));
x509AuthProvider.getPropertyValues().addPropertyValue("preAuthenticatedUserDetailsService",
preAuthUserService.getBeanDefinition());
} else {
RootBeanDefinition preAuthUserService = (RootBeanDefinition) pv.getValue();
Object userService =
preAuthUserService.getPropertyValues().getPropertyValue("userDetailsService").getValue();
RuntimeBeanReference cachingUserService = getCachingUserService(bf, userService);
if (cachingUserService != null) {
preAuthUserService.getPropertyValues().addPropertyValue("userDetailsService", cachingUserService);
}
}
} catch (NoSuchBeanDefinitionException e) {
// ignore
}
}
private void injectUserDetailsServiceIntoOpenIDProvider(ConfigurableListableBeanFactory beanFactory) {
try {
BeanDefinition openIDProvider = beanFactory.getBeanDefinition(BeanIds.OPEN_ID_PROVIDER);
PropertyValue pv = openIDProvider.getPropertyValues().getPropertyValue("userDetailsService");
if (pv == null) {
openIDProvider.getPropertyValues().addPropertyValue("userDetailsService",
ConfigUtils.getUserDetailsService(beanFactory));
}
} catch (NoSuchBeanDefinitionException e) {
// ignore
}
}
private RuntimeBeanReference getCachingUserService(ConfigurableListableBeanFactory bf, Object userServiceRef) {
Assert.isInstanceOf(RuntimeBeanReference.class, userServiceRef,
"userDetailsService property value must be a RuntimeBeanReference");
String id = ((RuntimeBeanReference)userServiceRef).getBeanName();
// Overwrite with the caching version if available
String cachingId = id + AbstractUserDetailsServiceBeanDefinitionParser.CACHING_SUFFIX;
if (bf.containsBeanDefinition(cachingId)) {
return new RuntimeBeanReference(cachingId);
}
return null;
}
/**
* Selects the entry point that should be used in ExceptionTranslationFilter. If an entry point has been
* set during parsing of form, openID and basic authentication information, or via a custom reference
* (using <tt>custom-entry-point</tt>, then that will be used. Otherwise there
* must be a single entry point bean and that will be used.
*
* Todo: this could probably be more easily be done in a BeanPostProcessor for ExceptionTranslationFilter.
*
*/
private void injectAuthenticationEntryPointIntoExceptionTranslationFilter(ConfigurableListableBeanFactory beanFactory) {
logger.info("Selecting AuthenticationEntryPoint for use in ExceptionTranslationFilter");
BeanDefinition etf =
beanFactory.getBeanDefinition(BeanIds.EXCEPTION_TRANSLATION_FILTER);
String entryPoint = null;
if (beanFactory.containsBean(BeanIds.MAIN_ENTRY_POINT)) {
entryPoint = BeanIds.MAIN_ENTRY_POINT;
logger.info("Using main configured AuthenticationEntryPoint set to " + BeanIds.MAIN_ENTRY_POINT);
} else {
String[] entryPoints = beanFactory.getBeanNamesForType(AuthenticationEntryPoint.class);
Assert.isTrue(entryPoints.length != 0, "No AuthenticationEntryPoint instances defined");
Assert.isTrue(entryPoints.length == 1, "More than one AuthenticationEntryPoint defined in context");
entryPoint = entryPoints[0];
}
logger.info("Using bean '" + entryPoint + "' as the entry point.");
etf.getPropertyValues().addPropertyValue("authenticationEntryPoint", new RuntimeBeanReference(entryPoint));
}
public int getOrder() {
return HIGHEST_PRECEDENCE + 1;
}
}
@@ -1,6 +1,5 @@
package org.springframework.security.config;
import org.springframework.security.userdetails.jdbc.JdbcUserDetailsManager;
import org.springframework.util.StringUtils;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
@@ -18,8 +17,8 @@ public class JdbcUserServiceBeanDefinitionParser extends AbstractUserDetailsServ
static final String ATT_GROUP_AUTHORITIES_QUERY = "group-authorities-by-username-query";
static final String ATT_ROLE_PREFIX = "role-prefix";
protected Class getBeanClass(Element element) {
return JdbcUserDetailsManager.class;
protected String getBeanClassName(Element element) {
return "org.springframework.security.userdetails.jdbc.JdbcUserDetailsManager";
}
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
@@ -1,11 +1,8 @@
package org.springframework.security.config;
import org.springframework.security.ldap.search.FilterBasedLdapUserSearch;
import org.springframework.security.providers.ldap.LdapAuthenticationProvider;
import org.springframework.security.providers.ldap.authenticator.BindAuthenticator;
import org.springframework.security.providers.ldap.authenticator.PasswordComparisonAuthenticator;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
@@ -30,12 +27,16 @@ public class LdapProviderBeanDefinitionParser implements BeanDefinitionParser {
private static final String ATT_USER_PASSWORD = "password-attribute";
private static final String ATT_HASH = PasswordEncoderParser.ATT_HASH;
private static final String DEF_USER_SEARCH_FILTER="uid={0}";
private static final String DEF_USER_SEARCH_FILTER = "uid={0}";
private static final String PROVIDER_CLASS = "org.springframework.security.providers.ldap.LdapAuthenticationProvider";
private static final String BIND_AUTH_CLASS = "org.springframework.security.providers.ldap.authenticator.BindAuthenticator";
private static final String PASSWD_AUTH_CLASS = "org.springframework.security.providers.ldap.authenticator.PasswordComparisonAuthenticator";
public BeanDefinition parse(Element elt, ParserContext parserContext) {
RuntimeBeanReference contextSource = LdapUserServiceBeanDefinitionParser.parseServerReference(elt, parserContext);
RootBeanDefinition searchBean = LdapUserServiceBeanDefinitionParser.parseSearchBean(elt, parserContext);
BeanDefinition searchBean = LdapUserServiceBeanDefinitionParser.parseSearchBean(elt, parserContext);
String userDnPattern = elt.getAttribute(ATT_USER_DN_PATTERN);
String[] userDnPatternArray = new String[0];
@@ -45,22 +46,25 @@ public class LdapProviderBeanDefinitionParser implements BeanDefinitionParser {
// TODO: Validate the pattern and make sure it is a valid DN.
} else if (searchBean == null) {
logger.info("No search information or DN pattern specified. Using default search filter '" + DEF_USER_SEARCH_FILTER + "'");
searchBean = new RootBeanDefinition(FilterBasedLdapUserSearch.class);
searchBean.setSource(elt);
searchBean.getConstructorArgumentValues().addIndexedArgumentValue(0, "");
searchBean.getConstructorArgumentValues().addIndexedArgumentValue(1, DEF_USER_SEARCH_FILTER);
searchBean.getConstructorArgumentValues().addIndexedArgumentValue(2, contextSource);
BeanDefinitionBuilder searchBeanBuilder = BeanDefinitionBuilder.rootBeanDefinition(LdapUserServiceBeanDefinitionParser.LDAP_SEARCH_CLASS);
searchBeanBuilder.setSource(elt);
searchBeanBuilder.addConstructorArg("");
searchBeanBuilder.addConstructorArg(DEF_USER_SEARCH_FILTER);
searchBeanBuilder.addConstructorArg(contextSource);
searchBean = searchBeanBuilder.getBeanDefinition();
}
RootBeanDefinition authenticator = new RootBeanDefinition(BindAuthenticator.class);
BeanDefinitionBuilder authenticatorBuilder =
BeanDefinitionBuilder.rootBeanDefinition(BIND_AUTH_CLASS);
Element passwordCompareElt = DomUtils.getChildElementByTagName(elt, Elements.LDAP_PASSWORD_COMPARE);
if (passwordCompareElt != null) {
authenticator = new RootBeanDefinition(PasswordComparisonAuthenticator.class);
authenticatorBuilder =
BeanDefinitionBuilder.rootBeanDefinition(PASSWD_AUTH_CLASS);
String passwordAttribute = passwordCompareElt.getAttribute(ATT_USER_PASSWORD);
if (StringUtils.hasText(passwordAttribute)) {
authenticator.getPropertyValues().addPropertyValue("passwordAttributeName", passwordAttribute);
authenticatorBuilder.addPropertyValue("passwordAttributeName", passwordAttribute);
}
Element passwordEncoderElement = DomUtils.getChildElementByTagName(passwordCompareElt, Elements.PASSWORD_ENCODER);
@@ -70,33 +74,34 @@ public class LdapProviderBeanDefinitionParser implements BeanDefinitionParser {
if (StringUtils.hasText(hash)) {
parserContext.getReaderContext().warning("Attribute 'hash' cannot be used with 'password-encoder' and " +
"will be ignored.", parserContext.extractSource(elt));
}
}
PasswordEncoderParser pep = new PasswordEncoderParser(passwordEncoderElement, parserContext);
authenticator.getPropertyValues().addPropertyValue("passwordEncoder", pep.getPasswordEncoder());
authenticatorBuilder.addPropertyValue("passwordEncoder", pep.getPasswordEncoder());
if (pep.getSaltSource() != null) {
parserContext.getReaderContext().warning("Salt source information isn't valid when used with LDAP", passwordEncoderElement);
parserContext.getReaderContext().warning("Salt source information isn't valid when used with LDAP",
passwordEncoderElement);
}
} else if (StringUtils.hasText(hash)) {
Class encoderClass = (Class) PasswordEncoderParser.ENCODER_CLASSES.get(hash);
authenticator.getPropertyValues().addPropertyValue("passwordEncoder", new RootBeanDefinition(encoderClass));
authenticatorBuilder.addPropertyValue("passwordEncoder", new RootBeanDefinition(encoderClass));
}
}
}
authenticator.getConstructorArgumentValues().addGenericArgumentValue(contextSource);
authenticator.getPropertyValues().addPropertyValue("userDnPatterns", userDnPatternArray);
authenticatorBuilder.addConstructorArg(contextSource);
authenticatorBuilder.addPropertyValue("userDnPatterns", userDnPatternArray);
if (searchBean != null) {
authenticator.getPropertyValues().addPropertyValue("userSearch", searchBean);
authenticatorBuilder.addPropertyValue("userSearch", searchBean);
}
RootBeanDefinition ldapProvider = new RootBeanDefinition(LdapAuthenticationProvider.class);
ldapProvider.getConstructorArgumentValues().addGenericArgumentValue(authenticator);
ldapProvider.getConstructorArgumentValues().addGenericArgumentValue(LdapUserServiceBeanDefinitionParser.parseAuthoritiesPopulator(elt, parserContext));
ldapProvider.getPropertyValues().addPropertyValue("userDetailsContextMapper",
BeanDefinitionBuilder ldapProvider = BeanDefinitionBuilder.rootBeanDefinition(PROVIDER_CLASS);
ldapProvider.addConstructorArg(authenticatorBuilder.getBeanDefinition());
ldapProvider.addConstructorArg(LdapUserServiceBeanDefinitionParser.parseAuthoritiesPopulator(elt, parserContext));
ldapProvider.addPropertyValue("userDetailsContextMapper",
LdapUserServiceBeanDefinitionParser.parseUserDetailsClass(elt, parserContext));
ConfigUtils.getRegisteredProviders(parserContext).add(ldapProvider);
ConfigUtils.getRegisteredProviders(parserContext).add(ldapProvider.getBeanDefinition());
return null;
}
@@ -1,6 +1,10 @@
package org.springframework.security.config;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
@@ -8,7 +12,6 @@ import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedSet;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
@@ -20,7 +23,9 @@ import org.apache.commons.logging.LogFactory;
* @version $Id$
*/
public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
private Log logger = LogFactory.getLog(getClass());
private static final String CONTEXT_SOURCE_CLASS="org.springframework.security.ldap.DefaultSpringSecurityContextSource";
private final Log logger = LogFactory.getLog(getClass());
/** Defines the Url of the ldap server to use. If not specified, an embedded apache DS instance will be created */
private static final String ATT_URL = "url";
@@ -53,7 +58,8 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
if (!StringUtils.hasText(url)) {
contextSource = createEmbeddedServer(elt, parserContext);
} else {
contextSource = new RootBeanDefinition(DefaultSpringSecurityContextSource.class);
contextSource = new RootBeanDefinition();
contextSource.setBeanClassName(CONTEXT_SOURCE_CLASS);
contextSource.getConstructorArgumentValues().addIndexedArgumentValue(0, url);
}
@@ -92,17 +98,22 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
*/
private RootBeanDefinition createEmbeddedServer(Element element, ParserContext parserContext) {
Object source = parserContext.extractSource(element);
BeanDefinitionBuilder configuration = BeanDefinitionBuilder.rootBeanDefinition("org.apache.directory.server.configuration.MutableServerStartupConfiguration");
BeanDefinitionBuilder partition = BeanDefinitionBuilder.rootBeanDefinition("org.apache.directory.server.core.partition.impl.btree.MutableBTreePartitionConfiguration");
BeanDefinitionBuilder configuration =
BeanDefinitionBuilder.rootBeanDefinition("org.apache.directory.server.configuration.MutableServerStartupConfiguration");
BeanDefinitionBuilder partition =
BeanDefinitionBuilder.rootBeanDefinition("org.apache.directory.server.core.partition.impl.btree.MutableBTreePartitionConfiguration");
configuration.setSource(source);
partition.setSource(source);
DirContextAdapter rootContext = new DirContextAdapter();
rootContext.setAttributeValues("objectClass", new String[] {"top", "domain", "extensibleObject"});
rootContext.setAttributeValue("dc", "springsecurity");
Attributes rootAttributes = new BasicAttributes("dc", "springsecurity");
Attribute a = new BasicAttribute("objectClass");
a.add("top");
a.add("domain");
a.add("extensibleObject");
rootAttributes.put(a);
partition.addPropertyValue("name", "springsecurity");
partition.addPropertyValue("contextEntry", rootContext.getAttributes());
partition.addPropertyValue("contextEntry", rootAttributes);
String suffix = element.getAttribute(ATT_ROOT_SUFFIX);
@@ -131,15 +142,15 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
String url = "ldap://127.0.0.1:" + port + "/" + suffix;
RootBeanDefinition contextSource = new RootBeanDefinition(DefaultSpringSecurityContextSource.class);
contextSource.getConstructorArgumentValues().addIndexedArgumentValue(0, url);
contextSource.getPropertyValues().addPropertyValue("userDn", "uid=admin,ou=system");
contextSource.getPropertyValues().addPropertyValue("password", "secret");
BeanDefinitionBuilder contextSource = BeanDefinitionBuilder.rootBeanDefinition(CONTEXT_SOURCE_CLASS);
contextSource.addConstructorArg(url);
contextSource.addPropertyValue("userDn", "uid=admin,ou=system");
contextSource.addPropertyValue("password", "secret");
RootBeanDefinition apacheContainer = new RootBeanDefinition("org.springframework.security.config.ApacheDSContainer", null, null);
apacheContainer.setSource(source);
apacheContainer.getConstructorArgumentValues().addGenericArgumentValue(configuration.getBeanDefinition());
apacheContainer.getConstructorArgumentValues().addGenericArgumentValue(contextSource);
apacheContainer.getConstructorArgumentValues().addGenericArgumentValue(contextSource.getBeanDefinition());
String ldifs = element.getAttribute(ATT_LDIF_FILE);
if (!StringUtils.hasText(ldifs)) {
@@ -157,6 +168,6 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
parserContext.getRegistry().registerBeanDefinition(BeanIds.EMBEDDED_APACHE_DS, apacheContainer);
return contextSource;
return (RootBeanDefinition) contextSource.getBeanDefinition();
}
}
@@ -1,11 +1,5 @@
package org.springframework.security.config;
import org.springframework.security.userdetails.ldap.InetOrgPersonContextMapper;
import org.springframework.security.userdetails.ldap.LdapUserDetailsMapper;
import org.springframework.security.userdetails.ldap.LdapUserDetailsService;
import org.springframework.security.userdetails.ldap.PersonContextMapper;
import org.springframework.security.ldap.search.FilterBasedLdapUserSearch;
import org.springframework.security.ldap.populator.DefaultLdapAuthoritiesPopulator;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.RootBeanDefinition;
@@ -35,9 +29,15 @@ public class LdapUserServiceBeanDefinitionParser extends AbstractUserDetailsServ
static final String ATT_USER_CLASS = "user-details-class";
static final String OPT_PERSON = "person";
static final String OPT_INETORGPERSON = "inetOrgPerson";
public static final String LDAP_SEARCH_CLASS = "org.springframework.security.ldap.search.FilterBasedLdapUserSearch";
public static final String PERSON_MAPPER_CLASS = "org.springframework.security.userdetails.ldap.PersonContextMapper";
public static final String INET_ORG_PERSON_MAPPER_CLASS = "org.springframework.security.userdetails.ldap.InetOrgPersonContextMapper";
public static final String LDAP_USER_MAPPER_CLASS = "org.springframework.security.userdetails.ldap.LdapUserDetailsMapper";
public static final String LDAP_AUTHORITIES_POPULATOR_CLASS = "org.springframework.security.ldap.populator.DefaultLdapAuthoritiesPopulator";
protected Class getBeanClass(Element element) {
return LdapUserDetailsService.class;
protected String getBeanClassName(Element element) {
return "org.springframework.security.userdetails.ldap.LdapUserDetailsService";
}
protected void doParse(Element elt, ParserContext parserContext, BeanDefinitionBuilder builder) {
@@ -68,7 +68,7 @@ public class LdapUserServiceBeanDefinitionParser extends AbstractUserDetailsServ
return null;
}
BeanDefinitionBuilder searchBuilder = BeanDefinitionBuilder.rootBeanDefinition(FilterBasedLdapUserSearch.class);
BeanDefinitionBuilder searchBuilder = BeanDefinitionBuilder.rootBeanDefinition(LDAP_SEARCH_CLASS);
searchBuilder.setSource(source);
searchBuilder.addConstructorArg(userSearchBase);
searchBuilder.addConstructorArg(userSearchFilter);
@@ -96,12 +96,12 @@ public class LdapUserServiceBeanDefinitionParser extends AbstractUserDetailsServ
static RootBeanDefinition parseUserDetailsClass(Element elt, ParserContext parserContext) {
String userDetailsClass = elt.getAttribute(ATT_USER_CLASS);
if(OPT_PERSON.equals(userDetailsClass)) {
return new RootBeanDefinition(PersonContextMapper.class);
if (OPT_PERSON.equals(userDetailsClass)) {
return new RootBeanDefinition(PERSON_MAPPER_CLASS, null, null);
} else if (OPT_INETORGPERSON.equals(userDetailsClass)) {
return new RootBeanDefinition(InetOrgPersonContextMapper.class);
return new RootBeanDefinition(INET_ORG_PERSON_MAPPER_CLASS, null, null);
}
return new RootBeanDefinition(LdapUserDetailsMapper.class);
return new RootBeanDefinition(LDAP_USER_MAPPER_CLASS, null, null);
}
static RootBeanDefinition parseAuthoritiesPopulator(Element elt, ParserContext parserContext) {
@@ -118,18 +118,19 @@ public class LdapUserServiceBeanDefinitionParser extends AbstractUserDetailsServ
groupSearchBase = DEF_GROUP_SEARCH_BASE;
}
BeanDefinitionBuilder populator = BeanDefinitionBuilder.rootBeanDefinition(DefaultLdapAuthoritiesPopulator.class);
BeanDefinitionBuilder populator = BeanDefinitionBuilder.rootBeanDefinition(LDAP_AUTHORITIES_POPULATOR_CLASS);
populator.setSource(parserContext.extractSource(elt));
populator.addConstructorArg(parseServerReference(elt, parserContext));
populator.addConstructorArg(groupSearchBase);
populator.addPropertyValue("groupSearchFilter", groupSearchFilter);
populator.addPropertyValue("searchSubtree", Boolean.TRUE);
if (StringUtils.hasText(rolePrefix)) {
if ("none".equals(rolePrefix)) {
rolePrefix = "";
}
populator.addPropertyValue("rolePrefix", rolePrefix);
}
}
if (StringUtils.hasLength(groupRoleAttribute)) {
populator.addPropertyValue("groupRoleAttribute", groupRoleAttribute);
@@ -23,7 +23,7 @@ public class MethodSecurityInterceptorPostProcessor implements BeanPostProcessor
private BeanFactory beanFactory;
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if(!beanName.equals(BeanIds.METHOD_SECURITY_INTERCEPTOR)) {
if(!BeanIds.METHOD_SECURITY_INTERCEPTOR.equals(beanName)) {
return bean;
}
@@ -35,6 +35,7 @@ public class PasswordEncoderParser {
static final String ATT_BASE_64 = "base64";
static final String OPT_HASH_PLAINTEXT = "plaintext";
static final String OPT_HASH_SHA = "sha";
static final String OPT_HASH_SHA256 = "sha-256";
static final String OPT_HASH_MD4 = "md4";
static final String OPT_HASH_MD5 = "md5";
static final String OPT_HASH_LDAP_SHA = "{sha}";
@@ -45,6 +46,7 @@ public class PasswordEncoderParser {
ENCODER_CLASSES = new HashMap();
ENCODER_CLASSES.put(OPT_HASH_PLAINTEXT, PlaintextPasswordEncoder.class);
ENCODER_CLASSES.put(OPT_HASH_SHA, ShaPasswordEncoder.class);
ENCODER_CLASSES.put(OPT_HASH_SHA256, ShaPasswordEncoder.class);
ENCODER_CLASSES.put(OPT_HASH_MD4, Md4PasswordEncoder.class);
ENCODER_CLASSES.put(OPT_HASH_MD5, Md5PasswordEncoder.class);
ENCODER_CLASSES.put(OPT_HASH_LDAP_SHA, LdapShaPasswordEncoder.class);
@@ -74,6 +76,11 @@ public class PasswordEncoderParser {
} else {
Class beanClass = (Class) ENCODER_CLASSES.get(hash);
RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
if (OPT_HASH_SHA256.equals(hash)) {
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, new Integer(256));
}
beanDefinition.setSource(parserContext.extractSource(element));
if (useBase64) {
if (BaseDigestPasswordEncoder.class.isAssignableFrom(beanClass)) {
@@ -23,12 +23,14 @@ import org.w3c.dom.Element;
*/
public class RememberMeBeanDefinitionParser implements BeanDefinitionParser {
static final String ATT_KEY = "key";
static final String DEF_KEY = "doesNotMatter";
static final String DEF_KEY = "SpringSecured";
static final String ATT_DATA_SOURCE = "data-source";
static final String ATT_DATA_SOURCE = "data-source-ref";
static final String ATT_SERVICES_REF = "services-ref";
static final String ATT_TOKEN_REPOSITORY = "token-repository-ref";
static final String ATT_USER_SERVICE_REF = "user-service-ref";
static final String ATT_TOKEN_VALIDITY = "token-validity-seconds";
protected final Log logger = LogFactory.getLog(getClass());
public BeanDefinition parse(Element element, ParserContext parserContext) {
@@ -37,32 +39,46 @@ public class RememberMeBeanDefinitionParser implements BeanDefinitionParser {
String key = null;
Object source = null;
String userServiceRef = null;
String rememberMeServicesRef = null;
String tokenValiditySeconds = null;
if (element != null) {
tokenRepository = element.getAttribute(ATT_TOKEN_REPOSITORY);
dataSource = element.getAttribute(ATT_DATA_SOURCE);
key = element.getAttribute(ATT_KEY);
userServiceRef = element.getAttribute(ATT_USER_SERVICE_REF);
userServiceRef = element.getAttribute(ATT_USER_SERVICE_REF);
rememberMeServicesRef = element.getAttribute(ATT_SERVICES_REF);
tokenValiditySeconds = element.getAttribute(ATT_TOKEN_VALIDITY);
source = parserContext.extractSource(element);
}
RootBeanDefinition filter = new RootBeanDefinition(RememberMeProcessingFilter.class);
RootBeanDefinition services = new RootBeanDefinition(PersistentTokenBasedRememberMeServices.class);
filter.getPropertyValues().addPropertyValue("authenticationManager",
new RuntimeBeanReference(BeanIds.AUTHENTICATION_MANAGER));
if (!StringUtils.hasText(key)) {
key = DEF_KEY;
}
RootBeanDefinition services = null;
boolean dataSourceSet = StringUtils.hasText(dataSource);
boolean tokenRepoSet = StringUtils.hasText(tokenRepository);
boolean servicesRefSet = StringUtils.hasText(rememberMeServicesRef);
boolean userServiceSet = StringUtils.hasText(userServiceRef);
boolean tokenValiditySet = StringUtils.hasText(tokenValiditySeconds);
if (servicesRefSet && (dataSourceSet || tokenRepoSet || userServiceSet || tokenValiditySet)) {
parserContext.getReaderContext().error(ATT_SERVICES_REF + " can't be used in combination with attributes "
+ ATT_TOKEN_REPOSITORY + "," + ATT_DATA_SOURCE + ", " + ATT_USER_SERVICE_REF + " or " + ATT_TOKEN_VALIDITY, source);
}
if (dataSourceSet && tokenRepoSet) {
parserContext.getReaderContext().error("Specify tokenRepository or dataSource but not both", element);
parserContext.getReaderContext().error("Specify " + ATT_TOKEN_REPOSITORY + " or " +
ATT_DATA_SOURCE +" but not both", source);
}
boolean isPersistent = dataSourceSet | tokenRepoSet;
if (isPersistent) {
Object tokenRepo;
services = new RootBeanDefinition(PersistentTokenBasedRememberMeServices.class);
if (tokenRepoSet) {
tokenRepo = new RuntimeBeanReference(tokenRepository);
@@ -72,39 +88,51 @@ public class RememberMeBeanDefinitionParser implements BeanDefinitionParser {
new RuntimeBeanReference(dataSource));
}
services.getPropertyValues().addPropertyValue("tokenRepository", tokenRepo);
} else {
isPersistent = false;
} else if (!servicesRefSet) {
services = new RootBeanDefinition(TokenBasedRememberMeServices.class);
}
if (!StringUtils.hasText(key) && !isPersistent) {
key = DEF_KEY;
if (services != null) {
if (userServiceSet) {
services.getPropertyValues().addPropertyValue("userDetailsService", new RuntimeBeanReference(userServiceRef));
}
if (tokenValiditySet) {
services.getPropertyValues().addPropertyValue("tokenValiditySeconds", new Integer(tokenValiditySeconds));
}
services.setSource(source);
services.getPropertyValues().addPropertyValue(ATT_KEY, key);
parserContext.getRegistry().registerBeanDefinition(BeanIds.REMEMBER_ME_SERVICES, services);
} else {
parserContext.getRegistry().registerAlias(rememberMeServicesRef, BeanIds.REMEMBER_ME_SERVICES);
}
BeanDefinition authManager = ConfigUtils.registerProviderManagerIfNecessary(parserContext);
RootBeanDefinition provider = new RootBeanDefinition(RememberMeAuthenticationProvider.class);
filter.setSource(source);
services.setSource(source);
provider.setSource(source);
if (StringUtils.hasText(userServiceRef)) {
services.getPropertyValues().addPropertyValue("userDetailsService", new RuntimeBeanReference(userServiceRef));
}
provider.getPropertyValues().addPropertyValue(ATT_KEY, key);
services.getPropertyValues().addPropertyValue(ATT_KEY, key);
ManagedList providers = (ManagedList) authManager.getPropertyValues().getPropertyValue("providers").getValue();
providers.add(provider);
filter.getPropertyValues().addPropertyValue("rememberMeServices",
new RuntimeBeanReference(BeanIds.REMEMBER_ME_SERVICES));
parserContext.getRegistry().registerBeanDefinition(BeanIds.REMEMBER_ME_SERVICES, services);
parserContext.getRegistry().registerBeanDefinition(BeanIds.REMEMBER_ME_FILTER, filter);
ConfigUtils.addHttpFilter(parserContext, new RuntimeBeanReference(BeanIds.REMEMBER_ME_FILTER));
registerProvider(parserContext, source, key);
registerFilter(parserContext, source);
return null;
}
private void registerProvider(ParserContext pc, Object source, String key) {
BeanDefinition authManager = ConfigUtils.registerProviderManagerIfNecessary(pc);
RootBeanDefinition provider = new RootBeanDefinition(RememberMeAuthenticationProvider.class);
provider.setSource(source);
provider.getPropertyValues().addPropertyValue(ATT_KEY, key);
ManagedList providers = (ManagedList) authManager.getPropertyValues().getPropertyValue("providers").getValue();
providers.add(provider);
}
private void registerFilter(ParserContext pc, Object source) {
RootBeanDefinition filter = new RootBeanDefinition(RememberMeProcessingFilter.class);
filter.setSource(source);
filter.getPropertyValues().addPropertyValue("authenticationManager",
new RuntimeBeanReference(BeanIds.AUTHENTICATION_MANAGER));
filter.getPropertyValues().addPropertyValue("rememberMeServices",
new RuntimeBeanReference(BeanIds.REMEMBER_ME_SERVICES));
pc.getRegistry().registerBeanDefinition(BeanIds.REMEMBER_ME_FILTER, filter);
ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.REMEMBER_ME_FILTER));
}
}
@@ -33,7 +33,7 @@ public class RememberMeServicesInjectionBeanPostProcessor implements BeanPostPro
logger.info("Setting RememberMeServices on bean " + beanName);
pf.setRememberMeServices(getRememberMeServices());
}
} else if (beanName.equals(BeanIds.BASIC_AUTHENTICATION_FILTER)) {
} else if (BeanIds.BASIC_AUTHENTICATION_FILTER.equals(beanName)) {
// NB: For remember-me to be sent back, a user must submit a "_spring_security_remember_me" with their login request.
// Most of the time a user won't present such a parameter with their BASIC authentication request.
// In the future we might support setting the AbstractRememberMeServices.alwaysRemember = true, but I am reluctant to
@@ -0,0 +1,139 @@
package org.springframework.security.config;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.security.providers.preauth.PreAuthenticatedAuthenticationProvider;
import org.springframework.security.ui.rememberme.AbstractRememberMeServices;
import org.springframework.security.userdetails.UserDetailsByNameServiceWrapper;
import org.springframework.security.userdetails.UserDetailsService;
import org.springframework.util.Assert;
/**
*
* @author Luke Taylor
* @since 2.0.2
*/
public class UserDetailsServiceInjectionBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
private final Log logger = LogFactory.getLog(getClass());
private ConfigurableListableBeanFactory beanFactory;
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (BeanIds.X509_AUTH_PROVIDER.equals(beanName)) {
injectUserDetailsServiceIntoX509Provider((PreAuthenticatedAuthenticationProvider) bean);
} else if (BeanIds.REMEMBER_ME_SERVICES.equals(beanName)) {
injectUserDetailsServiceIntoRememberMeServices((AbstractRememberMeServices)bean);
} else if (BeanIds.OPEN_ID_PROVIDER.equals(beanName)) {
injectUserDetailsServiceIntoOpenIDProvider(bean);
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
private void injectUserDetailsServiceIntoRememberMeServices(AbstractRememberMeServices services) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(BeanIds.REMEMBER_ME_SERVICES);
PropertyValue pv = beanDefinition.getPropertyValues().getPropertyValue("userDetailsService");
if (pv == null) {
services.setUserDetailsService(getUserDetailsService());
} else {
UserDetailsService cachingUserService = getCachingUserService(pv.getValue());
if (cachingUserService != null) {
services.setUserDetailsService(cachingUserService);
}
}
}
private void injectUserDetailsServiceIntoX509Provider(PreAuthenticatedAuthenticationProvider provider) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(BeanIds.X509_AUTH_PROVIDER);
PropertyValue pv = beanDefinition.getPropertyValues().getPropertyValue("preAuthenticatedUserDetailsService");
UserDetailsByNameServiceWrapper wrapper = new UserDetailsByNameServiceWrapper();
if (pv == null) {
wrapper.setUserDetailsService(getUserDetailsService());
provider.setPreAuthenticatedUserDetailsService(wrapper);
} else {
RootBeanDefinition preAuthUserService = (RootBeanDefinition) pv.getValue();
Object userService =
preAuthUserService.getPropertyValues().getPropertyValue("userDetailsService").getValue();
UserDetailsService cachingUserService = getCachingUserService(userService);
if (cachingUserService != null) {
wrapper.setUserDetailsService(cachingUserService);
provider.setPreAuthenticatedUserDetailsService(wrapper);
}
}
}
private void injectUserDetailsServiceIntoOpenIDProvider(Object bean) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(BeanIds.OPEN_ID_PROVIDER);
PropertyValue pv = beanDefinition.getPropertyValues().getPropertyValue("userDetailsService");
if (pv == null) {
BeanWrapperImpl beanWrapper = new BeanWrapperImpl(bean);
beanWrapper.setPropertyValue("userDetailsService", getUserDetailsService());
}
}
/**
* Obtains a user details service for use in RememberMeServices etc. Will return a caching version
* if available so should not be used for beans which need to separate the two.
*/
UserDetailsService getUserDetailsService() {
Map beans = beanFactory.getBeansOfType(CachingUserDetailsService.class);
if (beans.size() == 0) {
beans = beanFactory.getBeansOfType(UserDetailsService.class);
}
if (beans.size() == 0) {
throw new SecurityConfigurationException("No UserDetailsService registered.");
} else if (beans.size() > 1) {
throw new SecurityConfigurationException("More than one UserDetailsService registered. Please " +
"use a specific Id in your configuration");
}
return (UserDetailsService) beans.values().toArray()[0];
}
private UserDetailsService getCachingUserService(Object userServiceRef) {
Assert.isInstanceOf(RuntimeBeanReference.class, userServiceRef,
"userDetailsService property value must be a RuntimeBeanReference");
String id = ((RuntimeBeanReference)userServiceRef).getBeanName();
// Overwrite with the caching version if available
String cachingId = id + AbstractUserDetailsServiceBeanDefinitionParser.CACHING_SUFFIX;
if (beanFactory.containsBeanDefinition(cachingId)) {
return (UserDetailsService) beanFactory.getBean(cachingId);
}
return null;
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
}
}
@@ -6,7 +6,6 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.security.userdetails.memory.InMemoryDaoImpl;
import org.springframework.security.userdetails.memory.UserMap;
import org.springframework.security.userdetails.User;
import org.springframework.security.util.AuthorityUtils;
@@ -33,8 +32,8 @@ public class UserServiceBeanDefinitionParser extends AbstractUserDetailsServiceB
static final String ATT_DISABLED = "disabled";
static final String ATT_LOCKED = "locked";
protected Class getBeanClass(Element element) {
return InMemoryDaoImpl.class;
protected String getBeanClassName(Element element) {
return "org.springframework.security.userdetails.memory.InMemoryDaoImpl";
}
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
@@ -45,8 +45,8 @@ public class X509BeanDefinitionParser implements BeanDefinitionParser {
}
BeanDefinition provider = new RootBeanDefinition(PreAuthenticatedAuthenticationProvider.class);
ConfigUtils.getRegisteredProviders(parserContext).add(provider);
parserContext.getRegistry().registerBeanDefinition(BeanIds.X509_AUTH_PROVIDER, provider);
ConfigUtils.getRegisteredProviders(parserContext).add(new RuntimeBeanReference(BeanIds.X509_AUTH_PROVIDER));
String userServiceRef = element.getAttribute(ATT_USER_SERVICE_REF);
@@ -61,7 +61,7 @@ import java.util.Collection;
* interceptor. It will also implement the proper handling of secure object invocations, namely:
* <ol>
* <li>Obtain the {@link Authentication} object from the {@link SecurityContextHolder}.</li>
* <li>Determine if the request relates to a secured or public invocation by ooking up the secure object request
* <li>Determine if the request relates to a secured or public invocation by looking up the secure object request
* against the {@link ObjectDefinitionSource}.</li>
* <li>For an invocation that is secured (there is a
* <code>ConfigAttributeDefinition</code> for the secure object invocation):
@@ -142,7 +142,7 @@ public final class ProtectPointcutPostProcessor implements BeanPostProcessor {
}
public void addPointcut(String pointcutExpression, ConfigAttributeDefinition definition) {
Assert.hasText(pointcutExpression, "An AspecTJ pointcut expression is required");
Assert.hasText(pointcutExpression, "An AspectJ pointcut expression is required");
Assert.notNull(definition, "ConfigAttributeDefinition required");
pointcutMap.put(pointcutExpression, definition);
@@ -78,7 +78,7 @@ public class DefaultFilterInvocationDefinitionSource implements FilterInvocation
DefaultFilterInvocationDefinitionSource(UrlMatcher urlMatcher) {
this.urlMatcher = urlMatcher;
}
/**
* Builds the internal request map from the supplied map. The key elements should be of type {@link RequestKey},
* which contains a URL path and an optional HTTP method (may be null). The path stored in the key will depend on
@@ -252,7 +252,7 @@ public class DefaultFilterInvocationDefinitionSource implements FilterInvocation
return urlMatcher.requiresLowerCaseUrl();
}
protected void setStripQueryStringFromUrls(boolean stripQueryStringFromUrls) {
public void setStripQueryStringFromUrls(boolean stripQueryStringFromUrls) {
this.stripQueryStringFromUrls = stripQueryStringFromUrls;
}
}
@@ -17,8 +17,6 @@ package org.springframework.security.ldap;
import org.springframework.security.GrantedAuthority;
import org.springframework.security.ldap.LdapDataAccessException;
import org.springframework.ldap.core.DirContextOperations;
@@ -42,7 +40,6 @@ public interface LdapAuthoritiesPopulator {
*
* @return the granted authorities for the given user.
*
* @throws LdapDataAccessException if there is a problem accessing the directory.
*/
GrantedAuthority[] getGrantedAuthorities(DirContextOperations userData, String username);
}
@@ -22,6 +22,7 @@ import org.springframework.ldap.core.ContextSource;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.ldap.core.DistinguishedName;
import org.springframework.ldap.core.LdapEncoder;
import org.springframework.util.Assert;
import org.apache.commons.logging.Log;
@@ -135,9 +136,16 @@ public class SpringSecurityLdapTemplate extends org.springframework.ldap.core.Ld
* @return the set of String values for the attribute as a union of the values found in all the matching entries.
*/
public Set searchForSingleAttributeValues(final String base, final String filter, final Object[] params,
final String attributeName) {
String formattedFilter = MessageFormat.format(filter, params);
final String attributeName) {
// Escape the params acording to RFC2254
Object[] encodedParams = new String[params.length];
for (int i=0; i < params.length; i++) {
encodedParams[i] = LdapEncoder.filterEncode(params[i].toString());
}
String formattedFilter = MessageFormat.format(filter, encodedParams);
logger.debug("Using filter: " + formattedFilter);
final HashSet set = new HashSet();
@@ -69,13 +69,13 @@ import java.util.Set;
* <pre>
* &lt;bean id="ldapAuthoritiesPopulator"
* class="org.springframework.security.providers.ldap.populator.DefaultLdapAuthoritiesPopulator">
* &lt;constructor-arg>&lt;ref local="contextSource"/>&lt;/constructor-arg>
* &lt;constructor-arg>&lt;value>ou=groups&lt;/value>&lt;/constructor-arg>
* &lt;property name="groupRoleAttribute">&lt;value>ou&lt;/value>&lt;/property>
* &lt;constructor-arg ref="contextSource"/>
* &lt;constructor-arg value="ou=groups"/>
* &lt;property name="groupRoleAttribute" value="ou"/>
* &lt;!-- the following properties are shown with their default values -->
* &lt;property name="searchSubTree">&lt;value>false&lt;/value>&lt;/property>
* &lt;property name="rolePrefix">&lt;value>ROLE_&lt;/value>&lt;/property>
* &lt;property name="convertToUpperCase">&lt;value>true&lt;/value>&lt;/property>
* &lt;property name="searchSubTree" value="false"/>
* &lt;property name="rolePrefix" value="ROLE_"/>
* &lt;property name="convertToUpperCase" value="true"/>
* &lt;/bean>
* </pre>
* A search for roles for user "uid=ben,ou=people,dc=springframework,dc=org" would return the single granted authority
@@ -284,6 +284,10 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
this.groupSearchFilter = groupSearchFilter;
}
/**
* Sets the prefix which will be prepended to the values loaded from the directory.
* Defaults to "ROLE_" for compatibility with <tt>RoleVoter/tt>.
*/
public void setRolePrefix(String rolePrefix) {
Assert.notNull(rolePrefix, "rolePrefix must not be null");
this.rolePrefix = rolePrefix;
@@ -21,7 +21,9 @@ import org.springframework.security.AuthenticationException;
import org.springframework.security.AuthenticationManager;
import org.springframework.security.util.RedirectUtils;
import org.springframework.security.util.SessionUtils;
import org.springframework.security.util.UrlUtils;
import org.springframework.security.concurrent.SessionRegistry;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.security.event.authentication.InteractiveAuthenticationSuccessEvent;
@@ -43,10 +45,6 @@ import org.springframework.util.Assert;
import java.io.IOException;
import java.util.Properties;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
@@ -210,13 +208,18 @@ public abstract class AbstractProcessingFilter extends SpringSecurityFilter impl
private boolean allowSessionCreation = true;
private boolean serverSideRedirect = false;
private SessionRegistry sessionRegistry;
//~ Methods ========================================================================================================
public void afterPropertiesSet() throws Exception {
Assert.hasLength(filterProcessesUrl, "filterProcessesUrl must be specified");
Assert.isTrue(UrlUtils.isValidRedirectUrl(filterProcessesUrl), filterProcessesUrl + " isn't a valid redirect URL");
Assert.hasLength(defaultTargetUrl, "defaultTargetUrl must be specified");
Assert.isTrue(UrlUtils.isValidRedirectUrl(defaultTargetUrl), defaultTargetUrl + " isn't a valid redirect URL");
// Assert.hasLength(authenticationFailureUrl, "authenticationFailureUrl must be specified");
Assert.isTrue(UrlUtils.isValidRedirectUrl(authenticationFailureUrl), authenticationFailureUrl + " isn't a valid redirect URL");
Assert.notNull(authenticationManager, "authenticationManager must be specified");
Assert.notNull(rememberMeServices, "rememberMeServices cannot be null");
Assert.notNull(targetUrlResolver, "targetUrlResolver cannot be null");
@@ -355,7 +358,7 @@ public abstract class AbstractProcessingFilter extends SpringSecurityFilter impl
}
if (invalidateSessionOnSuccessfulAuthentication) {
SessionUtils.startNewSessionIfRequired(request, migrateInvalidatedSessionAttributes, null);
SessionUtils.startNewSessionIfRequired(request, migrateInvalidatedSessionAttributes, sessionRegistry);
}
String targetUrl = determineTargetUrl(request);
@@ -567,5 +570,13 @@ public abstract class AbstractProcessingFilter extends SpringSecurityFilter impl
*/
public void setServerSideRedirect(boolean serverSideRedirect) {
this.serverSideRedirect = serverSideRedirect;
}
}
/**
* The session registry needs to be set if session fixation attack protection is in use (and concurrent
* session control is enabled).
*/
public void setSessionRegistry(SessionRegistry sessionRegistry) {
this.sessionRegistry = sessionRegistry;
}
}
@@ -54,7 +54,7 @@ public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
//~ Methods ========================================================================================================
public void handle(ServletRequest request, ServletResponse response, AccessDeniedException accessDeniedException)
throws IOException, ServletException {
throws IOException, ServletException {
if (errorPage != null) {
// Put exception into request scope (perhaps of use to a view)
((HttpServletRequest) request).setAttribute(SPRING_SECURITY_ACCESS_DENIED_EXCEPTION_KEY,
@@ -80,7 +80,7 @@ public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
*/
public void setErrorPage(String errorPage) {
if ((errorPage != null) && !errorPage.startsWith("/")) {
throw new IllegalArgumentException("ErrorPage must begin with '/'");
throw new IllegalArgumentException("errorPage must begin with '/'");
}
this.errorPage = errorPage;
@@ -22,8 +22,7 @@ public abstract class FilterChainOrder {
public static final int CHANNEL_FILTER = FILTER_CHAIN_FIRST;
public static final int CONCURRENT_SESSION_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
public static final int HTTP_SESSION_CONTEXT_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
public static final int SESSION_FIXATION_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
public static final int HTTP_SESSION_CONTEXT_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
public static final int LOGOUT_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
public static final int X509_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
public static final int PRE_AUTH_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
@@ -37,6 +36,7 @@ public abstract class FilterChainOrder {
public static final int ANONYMOUS_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
public static final int EXCEPTION_TRANSLATION_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
public static final int NTLM_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
public static final int SESSION_FIXATION_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
public static final int FILTER_SECURITY_INTERCEPTOR = FILTER_CHAIN_FIRST + INTERVAL * i++;
public static final int SWITCH_USER_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
@@ -46,7 +46,6 @@ public abstract class FilterChainOrder {
filterNameToOrder.put("FIRST", new Integer(Integer.MIN_VALUE));
filterNameToOrder.put("CHANNEL_FILTER", new Integer(CHANNEL_FILTER));
filterNameToOrder.put("CONCURRENT_SESSION_FILTER", new Integer(CONCURRENT_SESSION_FILTER));
filterNameToOrder.put("SESSION_CONTEXT_INTEGRATION_FILTER", new Integer(HTTP_SESSION_CONTEXT_FILTER));
filterNameToOrder.put("LOGOUT_FILTER", new Integer(LOGOUT_FILTER));
filterNameToOrder.put("X509_FILTER", new Integer(X509_FILTER));
filterNameToOrder.put("PRE_AUTH_FILTER", new Integer(PRE_AUTH_FILTER));
@@ -59,6 +58,7 @@ public abstract class FilterChainOrder {
filterNameToOrder.put("ANONYMOUS_FILTER", new Integer(ANONYMOUS_FILTER));
filterNameToOrder.put("EXCEPTION_TRANSLATION_FILTER", new Integer(EXCEPTION_TRANSLATION_FILTER));
filterNameToOrder.put("NTLM_FILTER", new Integer(NTLM_FILTER));
filterNameToOrder.put("SESSION_CONTEXT_INTEGRATION_FILTER", new Integer(HTTP_SESSION_CONTEXT_FILTER));
filterNameToOrder.put("FILTER_SECURITY_INTERCEPTOR", new Integer(FILTER_SECURITY_INTERCEPTOR));
filterNameToOrder.put("SWITCH_USER_FILTER", new Integer(SWITCH_USER_FILTER));
filterNameToOrder.put("LAST", new Integer(Integer.MAX_VALUE));
@@ -6,24 +6,23 @@ import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import javax.servlet.http.HttpSession;
import org.springframework.security.Authentication;
import org.springframework.security.AuthenticationTrustResolver;
import org.springframework.security.AuthenticationTrustResolverImpl;
import org.springframework.security.concurrent.SessionRegistry;
import org.springframework.security.context.HttpSessionContextIntegrationFilter;
import org.springframework.security.context.SecurityContext;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.security.util.SessionUtils;
/**
* Detects that a user has been authenticated since the start of the request and starts a new session.
* <p>
* This is essentially a generalization of the functionality that was implemented for SEC-399. Additionally, it will
* update the configured SessionRegistry if one is in use, thus preventing problems when used with Spring Security's
* concurrent session control.
* <p>
* If the response has already been committed when the filter checks the authentication state, then it isn't possible
* to create a new session and the filter will print a warning to that effect.
* This is essentially a generalization of the functionality that was implemented for SEC-399.
* Additionally, it will update the configured SessionRegistry if one is in use, thus preventing problems when used
* with Spring Security's concurrent session control.
*
* @author Martin Algesten
* @author Luke Taylor
@@ -55,22 +54,17 @@ public class SessionFixationProtectionFilter extends SpringSecurityFilter {
}
request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
if (isAuthenticated()) {
// We don't have to worry about session fixation attack if already authenticated
chain.doFilter(request, response);
return;
}
HttpSession session = request.getSession();
SecurityContext sessionSecurityContext =
(SecurityContext) session.getAttribute(HttpSessionContextIntegrationFilter.SPRING_SECURITY_CONTEXT_KEY);
SessionFixationProtectionResponseWrapper wrapper =
new SessionFixationProtectionResponseWrapper(response, request);
try {
chain.doFilter(request, wrapper);
} finally {
if (!wrapper.isNewSessionStarted()) {
startNewSessionIfRequired(request, response);
}
if (sessionSecurityContext == null && isAuthenticated()) {
// The user has been authenticated during the current request, so do the session migration
startNewSessionIfRequired(request, response);
}
chain.doFilter(request, response);
}
private boolean isAuthenticated() {
@@ -92,83 +86,12 @@ public class SessionFixationProtectionFilter extends SpringSecurityFilter {
}
/**
* Called when the an initially unauthenticated request completes or a redirect or sendError occurs.
* Called when the a user wasn't authenticated at the start of the request but has been during it
* <p>
* If the user is now authenticated, a new session will be created, the session attributes copied to it (if
* <tt>migrateSessionAttributes</tt> is set and the sessionRegistry updated with the new session information.
* A new session will be created, the session attributes copied to it (if
* <tt>migrateSessionAttributes</tt> is set) and the sessionRegistry updated with the new session information.
*/
protected void startNewSessionIfRequired(HttpServletRequest request, HttpServletResponse response) {
if (isAuthenticated()) {
if (request.getSession(false) != null && response.isCommitted()) {
logger.warn("Response is already committed. Unable to create new session.");
}
SessionUtils.startNewSessionIfRequired(request, migrateSessionAttributes, sessionRegistry);
}
}
/**
* Response wrapper to handle the situation where we need to migrate the session after a redirect or sendError.
* Similar in function to Martin Algesten's OnRedirectUpdateSessionResponseWrapper used in
* HttpSessionContextIntegrationFilter.
* <p>
* Only used to wrap the response if the conditions are right at the start of the request to potentially
* require starting a new session, i.e. that the user isn't authenticated and a session existed to begin with.
*/
class SessionFixationProtectionResponseWrapper extends HttpServletResponseWrapper {
private HttpServletRequest request;
private boolean newSessionStarted;
SessionFixationProtectionResponseWrapper(HttpServletResponse response, HttpServletRequest request) {
super(response);
this.request = request;
}
/**
* Makes sure a new session is created before calling the
* superclass <code>sendError()</code>
*/
public void sendError(int sc) throws IOException {
startNewSession();
super.sendError(sc);
}
/**
* Makes sure a new session is created before calling the
* superclass <code>sendError()</code>
*/
public void sendError(int sc, String msg) throws IOException {
startNewSession();
super.sendError(sc, msg);
}
/**
* Makes sure a new session is created before calling the
* superclass <code>sendRedirect()</code>
*/
public void sendRedirect(String location) throws IOException {
startNewSession();
super.sendRedirect(location);
}
public void flushBuffer() throws IOException {
startNewSession();
super.flushBuffer();
}
/**
* Calls <code>startNewSessionIfRequired()</code>
*/
private void startNewSession() {
if (newSessionStarted) {
return;
}
startNewSessionIfRequired(request, this);
newSessionStarted = true;
}
boolean isNewSessionStarted() {
return newSessionStarted;
}
protected void startNewSessionIfRequired(HttpServletRequest request, HttpServletResponse response) {
SessionUtils.startNewSessionIfRequired(request, migrateSessionAttributes, sessionRegistry);
}
}
@@ -25,6 +25,7 @@ import org.springframework.security.ui.savedrequest.SavedRequest;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Default implementation for {@link TargetUrlResolver}
* <p>
@@ -46,11 +47,10 @@ public class TargetUrlResolverImpl implements TargetUrlResolver {
/**
* If <code>true</code>, will only use <code>SavedRequest</code> to determine the target URL on successful
* authentication if the request that caused the authentication request was a GET.
* It will return null for a POST/PUT request.
* In most cases it's meaningless to redirect to a URL generated by a POST/PUT request.
* Defaults to true.
* It will then return null for a POST/PUT request.
* Defaults to false.
*/
private boolean justUseSavedRequestOnGet = true;
private boolean justUseSavedRequestOnGet = false;
/* (non-Javadoc)
* @see org.acegisecurity.ui.TargetUrlResolver#determineTargetUrl(org.acegisecurity.ui.savedrequest.SavedRequest, javax.servlet.http.HttpServletRequest, org.acegisecurity.Authentication)
@@ -93,6 +93,7 @@ public class BasicProcessingFilter extends SpringSecurityFilter implements Initi
private AuthenticationManager authenticationManager;
private RememberMeServices rememberMeServices;
private boolean ignoreFailure = false;
private String credentialsCharset = "UTF-8";
//~ Methods ========================================================================================================
@@ -114,8 +115,8 @@ public class BasicProcessingFilter extends SpringSecurityFilter implements Initi
}
if ((header != null) && header.startsWith("Basic ")) {
String base64Token = header.substring(6);
String token = new String(Base64.decodeBase64(base64Token.getBytes()));
byte[] base64Token = header.substring(6).getBytes("UTF-8");
String token = new String(Base64.decodeBase64(base64Token), getCredentialsCharset(httpRequest));
String username = "";
String password = "";
@@ -172,7 +173,7 @@ public class BasicProcessingFilter extends SpringSecurityFilter implements Initi
chain.doFilter(httpRequest, httpResponse);
}
private boolean authenticationIsRequired(String username) {
private boolean authenticationIsRequired(String username) {
// Only reauthenticate if username doesn't match SecurityContextHolder and user isn't authenticated
// (see SEC-53)
Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication();
@@ -235,7 +236,16 @@ public class BasicProcessingFilter extends SpringSecurityFilter implements Initi
this.rememberMeServices = rememberMeServices;
}
public int getOrder() {
public void setCredentialsCharset(String credentialsCharset) {
Assert.hasText(credentialsCharset, "credentialsCharset cannot be null or empty");
this.credentialsCharset = credentialsCharset;
}
protected String getCredentialsCharset(HttpServletRequest httpRequest) {
return credentialsCharset;
}
public int getOrder() {
return FilterChainOrder.BASIC_PROCESSING_FILTER;
}
}
@@ -26,6 +26,7 @@ import org.springframework.security.Authentication;
import org.springframework.security.ui.SpringSecurityFilter;
import org.springframework.security.ui.FilterChainOrder;
import org.springframework.security.util.RedirectUtils;
import org.springframework.security.util.UrlUtils;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -58,6 +59,7 @@ public class LogoutFilter extends SpringSecurityFilter {
public LogoutFilter(String logoutSuccessUrl, LogoutHandler[] handlers) {
Assert.notEmpty(handlers, "LogoutHandlers are required");
this.logoutSuccessUrl = logoutSuccessUrl;
Assert.isTrue(UrlUtils.isValidRedirectUrl(logoutSuccessUrl), logoutSuccessUrl + " isn't a valid redirect URL");
this.handlers = handlers;
}
@@ -130,7 +132,7 @@ public class LogoutFilter extends SpringSecurityFilter {
String targetUrl = request.getParameter("logoutSuccessUrl");
if(!StringUtils.hasLength(targetUrl)) {
targetUrl = logoutSuccessUrl;
targetUrl = getLogoutSuccessUrl();
}
if (!StringUtils.hasLength(targetUrl)) {
@@ -161,9 +163,14 @@ public class LogoutFilter extends SpringSecurityFilter {
public void setFilterProcessesUrl(String filterProcessesUrl) {
Assert.hasText(filterProcessesUrl, "FilterProcessesUrl required");
Assert.isTrue(UrlUtils.isValidRedirectUrl(filterProcessesUrl), filterProcessesUrl + " isn't a valid redirect URL");
this.filterProcessesUrl = filterProcessesUrl;
}
protected String getLogoutSuccessUrl() {
return logoutSuccessUrl;
}
protected String getFilterProcessesUrl() {
return filterProcessesUrl;
}
@@ -24,171 +24,182 @@ import org.apache.commons.logging.LogFactory;
* @since 2.0
*/
final class WASSecurityHelper {
private static final Log logger = LogFactory.getLog(WASSecurityHelper.class);
private static final Log logger = LogFactory.getLog(WASSecurityHelper.class);
private static final String USER_REGISTRY = "UserRegistry";
private static final String USER_REGISTRY = "UserRegistry";
private static Method getRunAsSubject = null;
private static Method getRunAsSubject = null;
private static Method getWSCredentialFromSubject = null;
private static Method getGroupsForUser = null;
private static Method getGroupsForUser = null;
private static Method getSecurityName = null;
private static Method getSecurityName = null;
// SEC-803
private static Class wsCredentialClass = null;
/**
* Get the security name for the given subject.
*
* @param subject
* The subject for which to retrieve the security name
* @return String the security name for the given subject
*/
private static final String getSecurityName(final Subject subject) {
if (logger.isDebugEnabled()) {
logger.debug("Determining Websphere security name for subject " + subject);
}
String userSecurityName = null;
if (subject != null) {
// SEC-803
Object credential = subject.getPublicCredentials(getWSCredentialClass()).iterator().next();
if (credential != null) {
userSecurityName = (String)invokeMethod(getSecurityNameMethod(),credential,null);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Websphere security name is " + userSecurityName + " for subject " + subject);
}
return userSecurityName;
}
/**
* Get the security name for the given subject.
*
* @param subject
* The subject for which to retrieve the security name
* @return String the security name for the given subject
*/
private static final String getSecurityName(final Subject subject) {
if (logger.isDebugEnabled()) {
logger.debug("Determining Websphere security name for subject " + subject);
}
String userSecurityName = null;
if (subject != null) {
Object credential = invokeMethod(getWSCredentialFromSubjectMethod(),null,new Object[]{subject});
if (credential != null) {
userSecurityName = (String)invokeMethod(getSecurityNameMethod(),credential,null);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Websphere security name is " + userSecurityName + " for subject " + subject);
}
return userSecurityName;
}
/**
* Get the current RunAs subject.
*
* @return Subject the current RunAs subject
*/
private static final Subject getRunAsSubject() {
logger.debug("Retrieving WebSphere RunAs subject");
// get Subject: WSSubject.getCallerSubject ();
return (Subject) invokeMethod(getRunAsSubjectMethod(), null, new Object[] {});
}
/**
* Get the current RunAs subject.
*
* @return Subject the current RunAs subject
*/
private static final Subject getRunAsSubject() {
logger.debug("Retrieving WebSphere RunAs subject");
// get Subject: WSSubject.getCallerSubject ();
return (Subject) invokeMethod(getRunAsSubjectMethod(), null, new Object[] {});
}
/**
* Get the WebSphere group names for the given subject.
*
* @param subject
* The subject for which to retrieve the WebSphere group names
* @return the WebSphere group names for the given subject
*/
private static final String[] getWebSphereGroups(final Subject subject) {
return getWebSphereGroups(getSecurityName(subject));
}
/**
* Get the WebSphere group names for the given subject.
*
* @param subject
* The subject for which to retrieve the WebSphere group names
* @return the WebSphere group names for the given subject
*/
private static final String[] getWebSphereGroups(final Subject subject) {
return getWebSphereGroups(getSecurityName(subject));
}
/**
* Get the WebSphere group names for the given security name.
*
* @param securityName
* The securityname for which to retrieve the WebSphere group names
* @return the WebSphere group names for the given security name
*/
private static final String[] getWebSphereGroups(final String securityName) {
Context ic = null;
try {
// TODO: Cache UserRegistry object
ic = new InitialContext();
Object objRef = ic.lookup(USER_REGISTRY);
Object userReg = PortableRemoteObject.narrow(objRef, Class.forName ("com.ibm.websphere.security.UserRegistry"));
if (logger.isDebugEnabled()) {
logger.debug("Determining WebSphere groups for user " + securityName + " using WebSphere UserRegistry " + userReg);
}
final Collection groups = (Collection) invokeMethod(getGroupsForUserMethod(), userReg, new Object[]{ securityName });
if (logger.isDebugEnabled()) {
logger.debug("Groups for user " + securityName + ": " + groups.toString());
}
String[] result = new String[groups.size()];
return (String[]) groups.toArray(result);
} catch (Exception e) {
logger.error("Exception occured while looking up groups for user", e);
throw new RuntimeException("Exception occured while looking up groups for user", e);
} finally {
try {
ic.close();
} catch (NamingException e) {
logger.debug("Exception occured while closing context", e);
}
}
}
/**
* Get the WebSphere group names for the given security name.
*
* @param securityName
* The securityname for which to retrieve the WebSphere group names
* @return the WebSphere group names for the given security name
*/
private static final String[] getWebSphereGroups(final String securityName) {
Context ic = null;
try {
// TODO: Cache UserRegistry object
ic = new InitialContext();
Object objRef = ic.lookup(USER_REGISTRY);
Object userReg = PortableRemoteObject.narrow(objRef, Class.forName ("com.ibm.websphere.security.UserRegistry"));
if (logger.isDebugEnabled()) {
logger.debug("Determining WebSphere groups for user " + securityName + " using WebSphere UserRegistry " + userReg);
}
final Collection groups = (Collection) invokeMethod(getGroupsForUserMethod(), userReg, new Object[]{ securityName });
if (logger.isDebugEnabled()) {
logger.debug("Groups for user " + securityName + ": " + groups.toString());
}
String[] result = new String[groups.size()];
return (String[]) groups.toArray(result);
} catch (Exception e) {
logger.error("Exception occured while looking up groups for user", e);
throw new RuntimeException("Exception occured while looking up groups for user", e);
} finally {
try {
ic.close();
} catch (NamingException e) {
logger.debug("Exception occured while closing context", e);
}
}
}
/**
* @return
*/
public static final String[] getGroupsForCurrentUser() {
return getWebSphereGroups(getRunAsSubject());
}
/**
* @return
*/
public static final String[] getGroupsForCurrentUser() {
return getWebSphereGroups(getRunAsSubject());
}
public static final String getCurrentUserName() {
return getSecurityName(getRunAsSubject());
}
private static final Object invokeMethod(Method method, Object instance, Object[] args)
{
try {
return method.invoke(instance,args);
} catch (IllegalArgumentException e) {
logger.error("Error while invoking method "+method.getClass().getName()+"."+method.getName()+"("+ Arrays.asList(args)+")",e);
throw new RuntimeException("Error while invoking method "+method.getClass().getName()+"."+method.getName()+"("+Arrays.asList(args)+")",e);
} catch (IllegalAccessException e) {
logger.error("Error while invoking method "+method.getClass().getName()+"."+method.getName()+"("+Arrays.asList(args)+")",e);
throw new RuntimeException("Error while invoking method "+method.getClass().getName()+"."+method.getName()+"("+Arrays.asList(args)+")",e);
} catch (InvocationTargetException e) {
logger.error("Error while invoking method "+method.getClass().getName()+"."+method.getName()+"("+Arrays.asList(args)+")",e);
throw new RuntimeException("Error while invoking method "+method.getClass().getName()+"."+method.getName()+"("+Arrays.asList(args)+")",e);
}
}
public static final String getCurrentUserName() {
return getSecurityName(getRunAsSubject());
}
private static final Object invokeMethod(Method method, Object instance, Object[] args)
{
try {
return method.invoke(instance,args);
} catch (IllegalArgumentException e) {
logger.error("Error while invoking method "+method.getClass().getName()+"."+method.getName()+"("+ Arrays.asList(args)+")",e);
throw new RuntimeException("Error while invoking method "+method.getClass().getName()+"."+method.getName()+"("+Arrays.asList(args)+")",e);
} catch (IllegalAccessException e) {
logger.error("Error while invoking method "+method.getClass().getName()+"."+method.getName()+"("+Arrays.asList(args)+")",e);
throw new RuntimeException("Error while invoking method "+method.getClass().getName()+"."+method.getName()+"("+Arrays.asList(args)+")",e);
} catch (InvocationTargetException e) {
logger.error("Error while invoking method "+method.getClass().getName()+"."+method.getName()+"("+Arrays.asList(args)+")",e);
throw new RuntimeException("Error while invoking method "+method.getClass().getName()+"."+method.getName()+"("+Arrays.asList(args)+")",e);
}
}
private static final Method getMethod(String className, String methodName, String[] parameterTypeNames) {
try {
Class c = Class.forName(className);
final int len = parameterTypeNames.length;
Class[] parameterTypes = new Class[len];
for (int i = 0; i < len; i++) {
parameterTypes[i] = Class.forName(parameterTypeNames[i]);
}
return c.getDeclaredMethod(methodName, parameterTypes);
} catch (ClassNotFoundException e) {
logger.error("Required class"+className+" not found");
throw new RuntimeException("Required class"+className+" not found",e);
} catch (NoSuchMethodException e) {
logger.error("Required method "+methodName+" with parameter types ("+ Arrays.asList(parameterTypeNames) +") not found on class "+className);
throw new RuntimeException("Required class"+className+" not found",e);
}
}
private static final Method getMethod(String className, String methodName, String[] parameterTypeNames) {
try {
Class c = Class.forName(className);
final int len = parameterTypeNames.length;
Class[] parameterTypes = new Class[len];
for (int i = 0; i < len; i++) {
parameterTypes[i] = Class.forName(parameterTypeNames[i]);
}
return c.getDeclaredMethod(methodName, parameterTypes);
} catch (ClassNotFoundException e) {
logger.error("Required class"+className+" not found");
throw new RuntimeException("Required class"+className+" not found",e);
} catch (NoSuchMethodException e) {
logger.error("Required method "+methodName+" with parameter types ("+ Arrays.asList(parameterTypeNames) +") not found on class "+className);
throw new RuntimeException("Required class"+className+" not found",e);
}
}
private static final Method getRunAsSubjectMethod() {
if (getRunAsSubject == null) {
getRunAsSubject = getMethod("com.ibm.websphere.security.auth.WSSubject", "getRunAsSubject", new String[] {});
}
return getRunAsSubject;
}
private static final Method getRunAsSubjectMethod() {
if (getRunAsSubject == null) {
getRunAsSubject = getMethod("com.ibm.websphere.security.auth.WSSubject", "getRunAsSubject", new String[] {});
}
return getRunAsSubject;
}
private static final Method getGroupsForUserMethod() {
if (getGroupsForUser == null) {
getGroupsForUser = getMethod("com.ibm.websphere.security.UserRegistry", "getGroupsForUser", new String[] { "java.lang.String" });
}
return getGroupsForUser;
}
private static final Method getWSCredentialFromSubjectMethod() {
if (getWSCredentialFromSubject == null) {
getWSCredentialFromSubject = getMethod("com.ibm.ws.security.auth.SubjectHelper", "getWSCredentialFromSubject",
new String[] { "javax.security.auth.Subject" });
}
return getWSCredentialFromSubject;
}
private static final Method getGroupsForUserMethod() {
if (getGroupsForUser == null) {
getGroupsForUser = getMethod("com.ibm.websphere.security.UserRegistry", "getGroupsForUser", new String[] { "java.lang.String" });
}
return getGroupsForUser;
}
private static final Method getSecurityNameMethod() {
if (getSecurityName == null) {
getSecurityName = getMethod("com.ibm.websphere.security.cred.WSCredential", "getSecurityName", new String[] {});
}
return getSecurityName;
}
private static final Method getSecurityNameMethod() {
if (getSecurityName == null) {
getSecurityName = getMethod("com.ibm.websphere.security.cred.WSCredential", "getSecurityName", new String[] {});
}
return getSecurityName;
}
// SEC-803
private static final Class getWSCredentialClass() {
if (wsCredentialClass == null) {
wsCredentialClass = getClass("com.ibm.websphere.security.cred.WSCredential");
}
return wsCredentialClass;
}
private static final Class getClass(String className) {
try {
return Class.forName(className);
} catch (ClassNotFoundException e) {
logger.error("Required class " + className + " not found");
throw new RuntimeException("Required class " + className + " not found",e);
}
}
}
@@ -1,6 +1,7 @@
package org.springframework.security.ui.preauth.websphere;
import org.apache.commons.lang.ArrayUtils;
import java.util.Arrays;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
@@ -68,8 +69,8 @@ public class WebSpherePreAuthenticatedAuthenticationDetailsSource extends Authen
String[] webSphereGroups = WASSecurityHelper.getGroupsForCurrentUser();
GrantedAuthority[] userGas = webSphereGroups2GrantedAuthoritiesMapper.getGrantedAuthorities(webSphereGroups);
if (LOG.isDebugEnabled()) {
LOG.debug("WebSphere groups [" + ArrayUtils.toString(webSphereGroups) + "] mapped to Granted Authorities: ["
+ ArrayUtils.toString(userGas) + "]");
LOG.debug("WebSphere groups: " + Arrays.asList(webSphereGroups) + " mapped to Granted Authorities: "
+ Arrays.asList(userGas));
}
return userGas;
}
@@ -313,7 +313,14 @@ public abstract class AbstractRememberMeServices implements RememberMeServices,
this.alwaysRemember = alwaysRemember;
}
/**
* Sets the name of the parameter which should be checked for to see if a remember-me has been requested
* during a login request. This should be the same name you assign to the checkbox in your login form.
*
* @param parameter the HTTP request parameter
*/
public void setParameter(String parameter) {
Assert.hasText(parameter, "Parameter name cannot be null");
this.parameter = parameter;
}
@@ -326,6 +333,7 @@ public abstract class AbstractRememberMeServices implements RememberMeServices,
}
public void setUserDetailsService(UserDetailsService userDetailsService) {
Assert.notNull(userDetailsService, "UserDetailsService canot be null");
this.userDetailsService = userDetailsService;
}
@@ -348,4 +356,9 @@ public abstract class AbstractRememberMeServices implements RememberMeServices,
protected AuthenticationDetailsSource getAuthenticationDetailsSource() {
return authenticationDetailsSource;
}
public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) {
Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource cannot be null");
this.authenticationDetailsSource = authenticationDetailsSource;
}
}
@@ -152,7 +152,7 @@ public class TokenBasedRememberMeServices extends AbstractRememberMeServices {
}
int tokenLifetime = calculateLoginLifetime(request, successfulAuthentication);
long expiryTime = System.currentTimeMillis() + 1000*tokenLifetime;
long expiryTime = System.currentTimeMillis() + 1000L*tokenLifetime;
String signatureValue = makeTokenSignature(expiryTime, username, password);
@@ -22,6 +22,7 @@ import org.springframework.security.providers.UsernamePasswordAuthenticationToke
import org.springframework.security.ui.AbstractProcessingFilter;
import org.springframework.security.ui.FilterChainOrder;
import org.springframework.security.util.TextUtils;
import org.springframework.util.Assert;
import javax.servlet.http.HttpServletRequest;
@@ -72,7 +73,7 @@ public class AuthenticationProcessingFilter extends AbstractProcessingFilter {
HttpSession session = request.getSession(false);
if (session != null || getAllowSessionCreation()) {
request.getSession().setAttribute(SPRING_SECURITY_LAST_USERNAME_KEY, username);
request.getSession().setAttribute(SPRING_SECURITY_LAST_USERNAME_KEY, TextUtils.escapeEntities(username));
}
// Allow subclasses to set the "details" property
@@ -76,8 +76,7 @@ public class DefaultLoginPageGeneratingFilter extends SpringSecurityFilter {
protected void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
if (isLoginUrlRequest(request)) {
String loginPageHtml = generateLoginPageHtml(request);
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html");
response.setContentType("text/html;charset=UTF-8");
response.setContentLength(loginPageHtml.length());
response.getOutputStream().print(loginPageHtml);
@@ -126,7 +126,7 @@ public class RoleHierarchyImpl implements RoleHierarchy {
* references a set of the reachable lower roles.
*/
private void buildRolesReachableInOneStepMap() {
String parsingRegex = "(\\s*(\\w+)\\s*\\>\\s*(\\w+))";
String parsingRegex = "(\\s*([^\\s>]+)\\s*\\>\\s*([^\\s>]+))";
Pattern pattern = Pattern.compile(parsingRegex);
Matcher roleHierarchyMatcher = pattern.matcher(roleHierarchyStringRepresentation);
@@ -15,19 +15,17 @@ import org.springframework.security.userdetails.UserDetails;
import org.springframework.security.userdetails.UserDetailsManager;
import org.springframework.security.userdetails.GroupManager;
import org.springframework.context.ApplicationContextException;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.object.MappingSqlQuery;
import org.springframework.jdbc.object.SqlQuery;
import org.springframework.jdbc.object.SqlUpdate;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.util.Assert;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.sql.DataSource;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.List;
/**
@@ -116,28 +114,6 @@ public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsMa
private String groupAuthoritiesSql = DEF_GROUP_AUTHORITIES_QUERY_SQL;
private String deleteGroupAuthoritySql = DEF_DELETE_GROUP_AUTHORITY_SQL;
private SqlUpdate insertUser;
private SqlUpdate deleteUser;
private SqlUpdate updateUser;
private SqlUpdate insertAuthority;
private SqlUpdate deleteUserAuthorities;
private SqlQuery userExistsQuery;
private SqlUpdate changePassword;
private SqlQuery findAllGroupsQuery;
private SqlQuery findUsersInGroupQuery;
private SqlUpdate insertGroup;
private SqlQuery findGroupIdQuery;
private SqlUpdate insertGroupAuthority;
private SqlUpdate deleteGroup;
private SqlUpdate deleteGroupMembers;
private SqlUpdate deleteGroupAuthorities;
private SqlUpdate renameGroup;
private SqlUpdate insertGroupMember;
private SqlUpdate deleteGroupMember;
private SqlQuery groupAuthoritiesQuery;
private SqlUpdate deleteGroupAuthority;
private AuthenticationManager authenticationManager;
private UserCache userCache = new NullUserCache();
@@ -150,43 +126,36 @@ public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsMa
"not be performed.");
}
insertUser = new InsertUser(getDataSource());
deleteUser = new DeleteUser(getDataSource());
updateUser = new UpdateUser(getDataSource());
insertAuthority = new InsertAuthority(getDataSource());
deleteUserAuthorities = new DeleteUserAuthorities(getDataSource());
userExistsQuery = new UserExistsQuery(getDataSource());
changePassword = new ChangePassword(getDataSource());
findAllGroupsQuery = new AllGroupsQuery(getDataSource());
findUsersInGroupQuery = new GroupMembersQuery(getDataSource());
insertGroup = new InsertGroup(getDataSource());
findGroupIdQuery = new FindGroupIdQuery(getDataSource());
insertGroupAuthority = new InsertGroupAuthority(getDataSource());
deleteGroup = new DeleteGroup(getDataSource());
deleteGroupAuthorities = new DeleteGroupAuthorities(getDataSource());
deleteGroupMembers = new DeleteGroupMembers(getDataSource());
renameGroup = new RenameGroup(getDataSource());
insertGroupMember = new InsertGroupMember(getDataSource());
deleteGroupMember = new DeleteGroupMember (getDataSource());
groupAuthoritiesQuery = new GroupAuthoritiesByGroupNameMapping(getDataSource());
deleteGroupAuthority = new DeleteGroupAuthority(getDataSource());
super.initDao();
}
//~ UserDetailsManager implementation ==============================================================================
public void createUser(UserDetails user) {
public void createUser(final UserDetails user) {
validateUserDetails(user);
insertUser.update(new Object[] {user.getUsername(), user.getPassword(), Boolean.valueOf(user.isEnabled())});
getJdbcTemplate().update(createUserSql, new PreparedStatementSetter() {
public void setValues(PreparedStatement ps) throws SQLException {
ps.setString(1, user.getUsername());
ps.setString(2, user.getPassword());
ps.setBoolean(3, user.isEnabled());
}
});
insertUserAuthorities(user);
}
public void updateUser(UserDetails user) {
public void updateUser(final UserDetails user) {
validateUserDetails(user);
updateUser.update(new Object[] {user.getPassword(), Boolean.valueOf(user.isEnabled()), user.getUsername()});
deleteUserAuthorities.update(user.getUsername());
getJdbcTemplate().update(updateUserSql, new PreparedStatementSetter() {
public void setValues(PreparedStatement ps) throws SQLException {
ps.setString(1, user.getPassword());
ps.setBoolean(2, user.isEnabled());
ps.setString(3, user.getUsername());
}
});
deleteUserAuthorities(user.getUsername());
insertUserAuthorities(user);
userCache.removeUserFromCache(user.getUsername());
@@ -194,15 +163,20 @@ public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsMa
private void insertUserAuthorities(UserDetails user) {
for (int i=0; i < user.getAuthorities().length; i++) {
insertAuthority.update(user.getUsername(), user.getAuthorities()[i].getAuthority());
getJdbcTemplate().update(createAuthoritySql,
new Object[] {user.getUsername(), user.getAuthorities()[i].getAuthority()});
}
}
public void deleteUser(String username) {
deleteUserAuthorities.update(username);
deleteUser.update(username);
deleteUserAuthorities(username);
getJdbcTemplate().update(deleteUserSql, new Object[] {username});
userCache.removeUserFromCache(username);
}
private void deleteUserAuthorities(String username) {
getJdbcTemplate().update(deleteUserAuthoritiesSql, new Object[] {username});
}
public void changePassword(String oldPassword, String newPassword) throws AuthenticationException {
Authentication currentUser = SecurityContextHolder.getContext().getAuthentication();
@@ -226,7 +200,7 @@ public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsMa
logger.debug("Changing password for user '"+ username + "'");
changePassword.update(new String[] {newPassword, username});
getJdbcTemplate().update(changePasswordSql, new String[] {newPassword, username});
SecurityContextHolder.getContext().setAuthentication(createNewAuthentication(currentUser, newPassword));
@@ -244,10 +218,10 @@ public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsMa
}
public boolean userExists(String username) {
List users = userExistsQuery.execute(username);
List users = getJdbcTemplate().queryForList(userExistsSql, new Object[] {username});
if (users.size() > 1) {
throw new IllegalStateException("More than one user found with name '" + username + "'");
throw new IncorrectResultSizeDataAccessException("More than one user found with name '" + username + "'", 1);
}
return users.size() == 1;
@@ -256,26 +230,33 @@ public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsMa
//~ GroupManager implementation ====================================================================================
public String[] findAllGroups() {
return (String[]) findAllGroupsQuery.execute().toArray(new String[0]);
return (String[]) getJdbcTemplate().queryForList(findAllGroupsSql, String.class).toArray(new String[0]);
}
public String[] findUsersInGroup(String groupName) {
Assert.hasText(groupName);
return (String[]) findUsersInGroupQuery.execute(groupName).toArray(new String[0]);
return (String[]) getJdbcTemplate().queryForList(findUsersInGroupSql, new String[] {groupName}, String.class).toArray(new String[0]);
}
public void createGroup(String groupName, GrantedAuthority[] authorities) {
public void createGroup(final String groupName, final GrantedAuthority[] authorities) {
Assert.hasText(groupName);
Assert.notNull(authorities);
logger.debug("Creating new group '" + groupName + "' with authorities " +
AuthorityUtils.authorityArrayToSet(authorities));
insertGroup.update(groupName);
Integer id = (Integer) findGroupIdQuery.findObject(groupName);
getJdbcTemplate().update(insertGroupSql, new String[] {groupName});
final int groupId = findGroupId(groupName);
for (int i=0; i < authorities.length; i++) {
insertGroupAuthority.update( new Object[] {id, authorities[i].getAuthority()});
final String authority = authorities[i].getAuthority();
getJdbcTemplate().update(insertGroupAuthoritySql, new PreparedStatementSetter() {
public void setValues(PreparedStatement ps) throws SQLException {
ps.setInt(1, groupId);
ps.setString(2, authority);
}
});
}
}
@@ -283,10 +264,15 @@ public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsMa
logger.debug("Deleting group '" + groupName + "'");
Assert.hasText(groupName);
int id = ((Integer) findGroupIdQuery.findObject(groupName)).intValue();
deleteGroupMembers.update(id);
deleteGroupAuthorities.update(id);
deleteGroup.update(id);
final int id = findGroupId(groupName);
PreparedStatementSetter groupIdPSS = new PreparedStatementSetter() {
public void setValues(PreparedStatement ps) throws SQLException {
ps.setInt(1, id);
}
};
getJdbcTemplate().update(deleteGroupMembersSql, groupIdPSS);
getJdbcTemplate().update(deleteGroupAuthoritiesSql, groupIdPSS);
getJdbcTemplate().update(deleteGroupSql, groupIdPSS);
}
public void renameGroup(String oldName, String newName) {
@@ -294,54 +280,90 @@ public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsMa
Assert.hasText(oldName);
Assert.hasText(newName);
renameGroup.update(newName, oldName);
getJdbcTemplate().update(renameGroupSql, new String[] {newName, oldName});
}
public void addUserToGroup(String username, String groupName) {
public void addUserToGroup(final String username, final String groupName) {
logger.debug("Adding user '" + username + "' to group '" + groupName + "'");
Assert.hasText(username);
Assert.hasText(groupName);
Integer id = (Integer) findGroupIdQuery.findObject(groupName);
final int id = findGroupId(groupName);
getJdbcTemplate().update(insertGroupMemberSql, new PreparedStatementSetter() {
public void setValues(PreparedStatement ps) throws SQLException {
ps.setInt(1, id);
ps.setString(2, username);
}
});
insertGroupMember.update(new Object[] {id, username});
userCache.removeUserFromCache(username);
}
public void removeUserFromGroup(String username, String groupName) {
public void removeUserFromGroup(final String username, final String groupName) {
logger.debug("Removing user '" + username + "' to group '" + groupName + "'");
Assert.hasText(username);
Assert.hasText(groupName);
Integer id = (Integer) findGroupIdQuery.findObject(groupName);
final int id = findGroupId(groupName);
getJdbcTemplate().update(deleteGroupMemberSql, new PreparedStatementSetter() {
public void setValues(PreparedStatement ps) throws SQLException {
ps.setInt(1, id);
ps.setString(2, username);
}
});
deleteGroupMember.update(new Object[] {id, username});
userCache.removeUserFromCache(username);
}
public GrantedAuthority[] findGroupAuthorities(String groupName) {
logger.debug("Loading authorities for group '" + groupName + "'");
Assert.hasText(groupName);
List authorities = getJdbcTemplate().query(groupAuthoritiesSql, new String[] {groupName}, new RowMapper() {
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
String roleName = getRolePrefix() + rs.getString(3);
GrantedAuthorityImpl authority = new GrantedAuthorityImpl(roleName);
return (GrantedAuthority[]) groupAuthoritiesQuery.execute(groupName).toArray(new GrantedAuthority[0]);
return authority;
}
});
return (GrantedAuthority[]) authorities.toArray(new GrantedAuthority[0]);
}
public void removeGroupAuthority(String groupName, GrantedAuthority authority) {
public void removeGroupAuthority(String groupName, final GrantedAuthority authority) {
logger.debug("Removing authority '" + authority + "' from group '" + groupName + "'");
Assert.hasText(groupName);
Assert.notNull(authority);
Integer id = (Integer) findGroupIdQuery.findObject(groupName);
deleteGroupAuthority.update(new Object[] {id, authority});
final int id = findGroupId(groupName);
getJdbcTemplate().update(deleteGroupAuthoritySql, new PreparedStatementSetter() {
public void setValues(PreparedStatement ps) throws SQLException {
ps.setInt(1, id);
ps.setString(2, authority.getAuthority());
}
});
}
public void addGroupAuthority(String groupName, GrantedAuthority authority) {
public void addGroupAuthority(final String groupName, final GrantedAuthority authority) {
logger.debug("Adding authority '" + authority + "' to group '" + groupName + "'");
Assert.hasText(groupName);
Assert.notNull(authority);
Integer id = (Integer) findGroupIdQuery.findObject(groupName);
insertGroupAuthority.update(new Object[] {id, authority.getAuthority()});
final int id = findGroupId(groupName);
getJdbcTemplate().update(insertGroupAuthoritySql, new PreparedStatementSetter() {
public void setValues(PreparedStatement ps) throws SQLException {
ps.setInt(1, id);
ps.setString(2, authority.getAuthority());
}
});
}
private int findGroupId(String group) {
return getJdbcTemplate().queryForInt(findGroupIdSql, new Object[] {group});
}
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
@@ -411,201 +433,4 @@ public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsMa
Assert.hasText(authorities[i].getAuthority(), "getAuthority() method must return a non-empty string");
}
}
//~ Inner Classes ==================================================================================================
private class InsertUser extends SqlUpdate {
public InsertUser(DataSource ds) {
super(ds, createUserSql);
declareParameter(new SqlParameter(Types.VARCHAR));
declareParameter(new SqlParameter(Types.VARCHAR));
declareParameter(new SqlParameter(Types.BOOLEAN));
compile();
}
}
private class DeleteUser extends SqlUpdate {
public DeleteUser(DataSource ds) {
super(ds, deleteUserSql);
declareParameter(new SqlParameter(Types.VARCHAR));
compile();
}
}
private class InsertAuthority extends SqlUpdate {
public InsertAuthority(DataSource ds) {
super(ds, createAuthoritySql);
declareParameter(new SqlParameter(Types.VARCHAR));
declareParameter(new SqlParameter(Types.VARCHAR));
compile();
}
}
private class DeleteUserAuthorities extends SqlUpdate {
public DeleteUserAuthorities(DataSource ds) {
super(ds, deleteUserAuthoritiesSql);
declareParameter(new SqlParameter(Types.VARCHAR));
compile();
}
}
private class UpdateUser extends SqlUpdate {
public UpdateUser(DataSource ds) {
super(ds, updateUserSql);
declareParameter(new SqlParameter(Types.VARCHAR));
declareParameter(new SqlParameter(Types.BOOLEAN));
declareParameter(new SqlParameter(Types.VARCHAR));
compile();
}
}
private class ChangePassword extends SqlUpdate {
public ChangePassword(DataSource ds) {
super(ds, changePasswordSql);
declareParameter(new SqlParameter(Types.VARCHAR));
declareParameter(new SqlParameter(Types.VARCHAR));
compile();
}
}
private class UserExistsQuery extends MappingSqlQuery {
public UserExistsQuery(DataSource ds) {
super(ds, userExistsSql);
declareParameter(new SqlParameter(Types.VARCHAR));
compile();
}
protected Object mapRow(ResultSet rs, int rowNum) throws SQLException {
return rs.getString(1);
}
}
private class AllGroupsQuery extends MappingSqlQuery {
public AllGroupsQuery(DataSource ds) {
super(ds, findAllGroupsSql);
compile();
}
protected Object mapRow(ResultSet rs, int rowNum) throws SQLException {
return rs.getString(1);
}
}
private class GroupMembersQuery extends MappingSqlQuery {
public GroupMembersQuery(DataSource ds) {
super(ds, findUsersInGroupSql);
declareParameter(new SqlParameter(Types.VARCHAR));
compile();
}
protected Object mapRow(ResultSet rs, int rowNum) throws SQLException {
return rs.getString(1);
}
}
private class InsertGroup extends SqlUpdate {
public InsertGroup(DataSource ds) {
super(ds, insertGroupSql);
declareParameter(new SqlParameter(Types.VARCHAR));
compile();
}
}
private class FindGroupIdQuery extends MappingSqlQuery {
public FindGroupIdQuery(DataSource ds) {
super(ds, findGroupIdSql);
declareParameter(new SqlParameter(Types.INTEGER));
compile();
}
protected Object mapRow(ResultSet rs, int rowNum) throws SQLException {
return new Integer(rs.getInt(1));
}
}
private class InsertGroupAuthority extends SqlUpdate {
public InsertGroupAuthority(DataSource ds) {
super(ds, insertGroupAuthoritySql);
declareParameter(new SqlParameter(Types.INTEGER));
declareParameter(new SqlParameter(Types.VARCHAR));
compile();
}
}
protected class DeleteGroup extends SqlUpdate {
public DeleteGroup(DataSource ds) {
super(ds, deleteGroupSql);
declareParameter(new SqlParameter(Types.INTEGER));
compile();
}
}
private class DeleteGroupMembers extends SqlUpdate {
public DeleteGroupMembers(DataSource ds) {
super(ds, deleteGroupMembersSql);
declareParameter(new SqlParameter(Types.INTEGER));
compile();
}
}
protected class DeleteGroupAuthorities extends SqlUpdate {
public DeleteGroupAuthorities(DataSource ds) {
super(ds, deleteGroupAuthoritiesSql);
declareParameter(new SqlParameter(Types.INTEGER));
compile();
}
}
private class RenameGroup extends SqlUpdate {
public RenameGroup(DataSource ds) {
super(ds, renameGroupSql);
declareParameter(new SqlParameter(Types.VARCHAR));
declareParameter(new SqlParameter(Types.VARCHAR));
compile();
}
}
protected class InsertGroupMember extends SqlUpdate {
public InsertGroupMember(DataSource ds) {
super(ds, insertGroupMemberSql);
declareParameter(new SqlParameter(Types.INTEGER));
declareParameter(new SqlParameter(Types.VARCHAR));
compile();
}
}
private class DeleteGroupMember extends SqlUpdate {
public DeleteGroupMember(DataSource ds) {
super(ds, deleteGroupMemberSql);
declareParameter(new SqlParameter(Types.INTEGER));
declareParameter(new SqlParameter(Types.VARCHAR));
compile();
}
}
private class GroupAuthoritiesByGroupNameMapping extends MappingSqlQuery {
protected GroupAuthoritiesByGroupNameMapping(DataSource ds) {
super(ds, groupAuthoritiesSql);
declareParameter(new SqlParameter(Types.VARCHAR));
compile();
}
protected Object mapRow(ResultSet rs, int rownum) throws SQLException {
String roleName = getRolePrefix() + rs.getString(3);
GrantedAuthorityImpl authority = new GrantedAuthorityImpl(roleName);
return authority;
}
}
private class DeleteGroupAuthority extends SqlUpdate {
public DeleteGroupAuthority(DataSource ds) {
super(ds, deleteGroupAuthoritySql);
declareParameter(new SqlParameter(Types.INTEGER));
declareParameter(new SqlParameter(Types.VARCHAR));
compile();
}
}
}
@@ -15,7 +15,7 @@ public class InMemoryXmlApplicationContext extends AbstractXmlApplicationContext
" xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'\n" +
" xsi:schemaLocation='http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\n" +
"http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd\n" +
"http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.1.xsd'>\n";
"http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.2.xsd'>\n";
private static final String BEANS_CLOSE = "</b:beans>\n";
Resource inMemoryXml;
@@ -0,0 +1,33 @@
package org.springframework.security.util;
/**
* Utilities for working with Strings and text.
*
* @author Luke Taylor
* @version $Id$
*/
public abstract class TextUtils {
public static String escapeEntities(String s) {
StringBuffer sb = new StringBuffer();
for (int i=0; i < s.length(); i++) {
char c = s.charAt(i);
if(c == '<') {
sb.append("&lt;");
} else if (c == '>') {
sb.append("&gt;");
} else if (c == '"') {
sb.append("&#034;");
} else if (c == '\'') {
sb.append("&#039;");
} else {
sb.append(c);
}
}
return sb.toString();
}
}
@@ -18,6 +18,7 @@ package org.springframework.security.util;
import org.springframework.security.intercept.web.FilterInvocation;
import org.springframework.security.ui.savedrequest.SavedRequest;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
@@ -41,16 +42,6 @@ public final class UrlUtils {
* Obtains the full URL the client used to make the request.<p>Note that the server port will not be shown
* if it is the default server port for HTTP or HTTPS (ie 80 and 443 respectively).</p>
*
* @param scheme DOCUMENT ME!
* @param serverName DOCUMENT ME!
* @param serverPort DOCUMENT ME!
* @param contextPath DOCUMENT ME!
* @param requestUrl DOCUMENT ME!
* @param servletPath DOCUMENT ME!
* @param requestURI DOCUMENT ME!
* @param pathInfo DOCUMENT ME!
* @param queryString DOCUMENT ME!
*
* @return the full URL
*/
private static String buildFullRequestUrl(String scheme, String serverName, int serverPort, String contextPath,
@@ -73,12 +64,6 @@ public final class UrlUtils {
/**
* Obtains the web application-specific fragment of the URL.
*
* @param servletPath DOCUMENT ME!
* @param requestURI DOCUMENT ME!
* @param contextPath DOCUMENT ME!
* @param pathInfo DOCUMENT ME!
* @param queryString DOCUMENT ME!
*
* @return the URL, excluding any server name, context path or servlet path
*/
private static String buildRequestUrl(String servletPath, String requestURI, String contextPath, String pathInfo,
@@ -117,4 +102,8 @@ public final class UrlUtils {
return buildRequestUrl(sr.getServletPath(), sr.getRequestURI(), sr.getContextPath(), sr.getPathInfo(),
sr.getQueryString());
}
public static boolean isValidRedirectUrl(String url) {
return !StringUtils.hasText(url) || url.startsWith("/") || url.toLowerCase().startsWith("http");
}
}
@@ -25,9 +25,9 @@ import org.springframework.security.GrantedAuthority;
/**
* Votes if any {@link ConfigAttribute#getAttribute()} starts with a prefix
* indicating that it is a role. The default prefix string is <Code>ROLE_</code>,
* but this may be overriden to any value. It may also be set to empty, which
* but this may be overridden to any value. It may also be set to empty, which
* means that essentially any attribute will be voted on. As described further
* below, the effect of an empty prefix may not be quite desireable.
* below, the effect of an empty prefix may not be quite desirable.
* <p>
* Abstains from voting if no configuration attribute commences with the role
* prefix. Votes to grant access if there is an exact matching
@@ -40,7 +40,7 @@ import org.springframework.security.GrantedAuthority;
* ConfigAttribute. When there are different categories of ConfigAttributes
* used, this will not be optimal since the voter will be voting for attributes
* which do not represent roles. However, this option may be of some use when
* using preexisting role names without a prefix, and no ability exists to
* using pre-existing role names without a prefix, and no ability exists to
* prefix them with a role prefix on reading them in, such as provided for
* example in {@link org.springframework.security.userdetails.jdbc.JdbcDaoImpl}.
* <p>
@@ -62,8 +62,8 @@ public class RoleVoter implements AccessDecisionVoter {
}
/**
* Allows the default role prefix of <code>ROLE_</code> to be overriden.
* May be set to an empty value, although this is usually not desireable.
* Allows the default role prefix of <code>ROLE_</code> to be overridden.
* May be set to an empty value, although this is usually not desirable.
*
* @param rolePrefix the new prefix
*/
@@ -28,12 +28,15 @@ import org.apache.commons.logging.LogFactory;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import java.util.Map.Entry;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
@@ -116,9 +119,6 @@ public class SavedRequestAwareWrapper extends SecurityContextHolderAwareRequestW
//~ Methods ========================================================================================================
/**
* The default behavior of this method is to return getCookies() on the wrapped request object.
*/
public Cookie[] getCookies() {
if (savedRequest == null) {
return super.getCookies();
@@ -129,10 +129,6 @@ public class SavedRequestAwareWrapper extends SecurityContextHolderAwareRequestW
}
}
/**
* The default behavior of this method is to return getDateHeader(String name) on the wrapped request
* object.
*/
public long getDateHeader(String name) {
if (savedRequest == null) {
return super.getDateHeader(name);
@@ -154,9 +150,6 @@ public class SavedRequestAwareWrapper extends SecurityContextHolderAwareRequestW
}
}
/**
* The default behavior of this method is to return getHeader(String name) on the wrapped request object.
*/
public String getHeader(String name) {
if (savedRequest == null) {
return super.getHeader(name);
@@ -174,9 +167,6 @@ public class SavedRequestAwareWrapper extends SecurityContextHolderAwareRequestW
}
}
/**
* The default behavior of this method is to return getHeaderNames() on the wrapped request object.
*/
public Enumeration getHeaderNames() {
if (savedRequest == null) {
return super.getHeaderNames();
@@ -185,9 +175,6 @@ public class SavedRequestAwareWrapper extends SecurityContextHolderAwareRequestW
}
}
/**
* The default behavior of this method is to return getHeaders(String name) on the wrapped request object.
*/
public Enumeration getHeaders(String name) {
if (savedRequest == null) {
return super.getHeaders(name);
@@ -196,10 +183,6 @@ public class SavedRequestAwareWrapper extends SecurityContextHolderAwareRequestW
}
}
/**
* The default behavior of this method is to return getIntHeader(String name) on the wrapped request
* object.
*/
public int getIntHeader(String name) {
if (savedRequest == null) {
return super.getIntHeader(name);
@@ -214,9 +197,6 @@ public class SavedRequestAwareWrapper extends SecurityContextHolderAwareRequestW
}
}
/**
* The default behavior of this method is to return getLocale() on the wrapped request object.
*/
public Locale getLocale() {
if (savedRequest == null) {
return super.getLocale();
@@ -238,10 +218,6 @@ public class SavedRequestAwareWrapper extends SecurityContextHolderAwareRequestW
}
}
/**
* The default behavior of this method is to return getLocales() on the wrapped request object.
*
*/
public Enumeration getLocales() {
if (savedRequest == null) {
return super.getLocales();
@@ -259,10 +235,6 @@ public class SavedRequestAwareWrapper extends SecurityContextHolderAwareRequestW
}
}
/**
* The default behavior of this method is to return getMethod() on the wrapped request object.
*
*/
public String getMethod() {
if (savedRequest == null) {
return super.getMethod();
@@ -272,80 +244,90 @@ public class SavedRequestAwareWrapper extends SecurityContextHolderAwareRequestW
}
/**
* The default behavior of this method is to return getParameter(String name) on the wrapped request
* object.
* If the parameter is available from the wrapped request then either
* <ol>
* <li>There is no saved request (it a normal request)</li>
* <li>There is a saved request, but the request has been forwarded/included to a URL with parameters, either
* supplementing or overriding the saved request values.</li>
* </ol>
* In both cases the value from the wrapped request should be used.
* <p>
* If the value from the wrapped request is null, an attempt will be made to retrieve the parameter
* from the SavedRequest, if available..
*/
public String getParameter(String name) {
/*
if (savedRequest == null) {
return super.getParameter(name);
} else {
String value = null;
String[] values = savedRequest.getParameterValues(name);
if (values == null)
return null;
for (int i = 0; i < values.length; i++) {
value = values[i];
break;
}
return value;
}
*/
//we do not get value from super.getParameter because there is a bug in Jetty servlet-container
String value = null;
String[] values = null;
if (savedRequest == null) {
values = super.getParameterValues(name);
} else {
values = savedRequest.getParameterValues(name);
String value = super.getParameter(name);
if (value != null || savedRequest == null) {
return value;
}
if (values == null) {
return null;
}
String[] values = savedRequest.getParameterValues(name);
if (values == null)
return null;
for (int i = 0; i < values.length; i++) {
value = values[i];
break;
}
for (int i = 0; i < values.length; i++) {
value = values[i];
break;
}
return value;
return value;
}
/**
* The default behavior of this method is to return getParameterMap() on the wrapped request object.
*/
public Map getParameterMap() {
if (savedRequest == null) {
return super.getParameterMap();
} else {
return savedRequest.getParameterMap();
Map parameters = super.getParameterMap();
if (savedRequest == null) {
return parameters;
}
// We have a saved request so merge the values, with the wrapped request taking precedence (see getParameter())
Map newParameters = new HashMap(savedRequest.getParameterMap().size() + parameters.size());
newParameters.putAll(parameters);
Iterator savedParams = savedRequest.getParameterMap().entrySet().iterator();
while (savedParams.hasNext()) {
Map.Entry entry = (Entry) savedParams.next();
String name = (String) entry.getKey();
String[] savedParamValues = (String[]) entry.getValue();
if (newParameters.containsKey(name)) {
// merge values
String[] existingValues = (String[]) newParameters.get(name);
String[] mergedValues = new String[savedParamValues.length + existingValues.length];
System.arraycopy(existingValues, 0, mergedValues, 0, existingValues.length);
System.arraycopy(savedParamValues, 0, mergedValues, existingValues.length, savedParamValues.length);
newParameters.put(name, mergedValues);
} else {
newParameters.put(name, savedParamValues);
}
}
return newParameters;
}
/**
* The default behavior of this method is to return getParameterNames() on the wrapped request object.
*/
public Enumeration getParameterNames() {
if (savedRequest == null) {
return super.getParameterNames();
} else {
return new Enumerator(savedRequest.getParameterNames());
}
return new Enumerator(getParameterMap().keySet());
}
/**
* The default behavior of this method is to return getParameterValues(String name) on the wrapped request
* object.
*/
public String[] getParameterValues(String name) {
if (savedRequest == null) {
return super.getParameterValues(name);
} else {
return savedRequest.getParameterValues(name);
}
String[] savedRequestParams = savedRequest == null ? null : savedRequest.getParameterValues(name);
String[] wrappedRequestParams = super.getParameterValues(name);
if (savedRequestParams == null && wrappedRequestParams == null) {
return null;
}
List combinedParams = new ArrayList();
if (wrappedRequestParams != null) {
combinedParams.addAll(Arrays.asList(wrappedRequestParams));
}
if (savedRequestParams != null) {
combinedParams.addAll(Arrays.asList(savedRequestParams));
}
return (String[]) combinedParams.toArray(new String[combinedParams.size()]);
}
}
@@ -1,3 +1,3 @@
http\://www.springframework.org/schema/security/spring-security-2.0.xsd=org/springframework/security/config/spring-security-2.0.xsd
http\://www.springframework.org/schema/security/spring-security-2.0.1.xsd=org/springframework/security/config/spring-security-2.0.1.xsd
http\://www.springframework.org/schema/security/spring-security-2.0.2.xsd=org/springframework/security/config/spring-security-2.0.2.xsd
@@ -7,7 +7,7 @@ start = http | ldap-server | authentication-provider | ldap-authentication-provi
hash =
## Defines the hashing algorithm used on user passwords. We recommend strongly against using MD4, as it is a very weak hashing algorithm.
attribute hash {"plaintext" | "sha" | "md5" | "md4" | "{sha}" | "{ssha}"}
attribute hash {"plaintext" | "sha" | "sha-256" | "md5" | "md4" | "{sha}" | "{ssha}"}
base64 =
## Whether a string should be base64 encoded
attribute base64 {"true" | "false"}
@@ -34,6 +34,10 @@ cache-ref =
user-service-ref =
## A reference to a user-service (or UserDetailsService bean) Id
attribute user-service-ref {xsd:string}
data-source-ref =
## A reference to a DataSource bean
attribute data-source-ref {xsd:string}
password-encoder =
## element which defines a password encoding strategy. Used by an authentication provider to convert submitted passwords to hashed versions, for example.
@@ -53,7 +57,7 @@ system-wide =
boolean = "true" | "false"
role-prefix =
## A non-empty string prefix that will be added to role strings loaded from persistent storage (e.g. "ROLE_").
## A non-empty string prefix that will be added to role strings loaded from persistent storage (e.g. "ROLE_"). Use the value "none" for no prefix in cases where the default is non-empty.
attribute role-prefix {xsd:string}
@@ -251,7 +255,7 @@ intercept-url.attlist &=
attribute method {"GET" | "DELETE" | "HEAD" | "OPTIONS" | "POST" | "PUT" | "TRACE"}?
intercept-url.attlist &=
## The filter list for the path. Currently can be set to "none" to remove a path from having any filters applied. The full filter stack (consisting of all defined filters, will be applied to any other paths).
## The filter list for the path. Currently can be set to "none" to remove a path from having any filters applied. The full filter stack (consisting of all filters created by the namespace configuration, and any added using 'custom-filter'), will be applied to any other paths.
attribute filters {"none"}?
intercept-url.attlist &=
## Used to specify that a URL must be accessed over http or https
@@ -331,20 +335,44 @@ concurrent-session-control =
concurrent-sessions.attlist &=
attribute max-sessions {xsd:positiveInteger}?
concurrent-sessions.attlist &=
## The URL a user will be redirected to if they attempt to use a session which has been "expired" by the concurrent session controller.
attribute expired-url {xsd:string}?
concurrent-sessions.attlist &=
## Specifies that an exception should be raised when a user attempts to login twice. The default behaviour is to expire the original session.
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}?
concurrent-sessions.attlist &=
## A reference to an external SessionRegistry implementation which will be used in place of the standard one.
attribute session-registry-ref {xsd:string}?
remember-me =
## Sets up remember-me authentication. If used with the "key" attribute (or no attributes) the cookie-only implementation will be used. Specifying "token-repository-ref" or "remember-me-data-source-ref" will use the more secure, persisten token approach.
element remember-me {remember-me.attlist}
remember-me.attlist &=
(attribute key {xsd:string} | (attribute token-repository-ref {xsd:string} | attribute data-source-ref {xsd:string}))
## The "key" used to identify cookies from a specific token-based remember-me application. You should set this to a unique value for your application.
attribute key {xsd:string}?
remember-me.attlist &=
(token-repository-ref | remember-me-data-source-ref | remember-me-services-ref)
remember-me.attlist &=
user-service-ref?
remember-me.attlist &=
## The period (in seconds) for which the remember-me cookie should be valid.
attribute token-validity-seconds {xsd:positiveInteger}?
token-repository-ref =
## Reference to a PersistentTokenRepository bean for use with the persistent token remember-me implementation.
attribute token-repository-ref {xsd:string}
remember-me-services-ref =
## Allows a custom implementation of RememberMeServices to be used. Note that this implementation should return RememberMeAuthenticationToken instances with the same "key" value as specified in the remember-me element. Alternatively it should register its own AuthenticationProvider.
attribute services-ref {xsd:string}?
remember-me-data-source-ref =
## DataSource bean for the database that contains the token
data-source-ref
anonymous =
## Adds support for automatically granting all anonymous web requests a particular principal identity and a corresponding granted authority.
@@ -384,11 +412,15 @@ x509.attlist &=
user-service-ref?
authentication-manager =
## If you are using namespace configuration with Spring Security, an AuthenticationManager will automatically be registered. This element simple allows you to define an alias to allow you to reference the authentication-manager in your own beans.
## If you are using namespace configuration with Spring Security, an AuthenticationManager will automatically be registered. This element allows you to define an alias to allow you to reference the authentication-manager in your own beans.
element authentication-manager {authman.attlist}
## The alias you wish to use for the AuthenticationManager bean
authman.attlist &=
## The alias you wish to use for the AuthenticationManager bean
attribute alias {xsd:ID}
authman.attlist &=
## Allows the session controller to be set on the internal AuthenticationManager. This should not be used with the <concurrent-session-control /> element
attribute session-controller-ref {xsd:string}?
authentication-provider =
## Indicates that the contained user-service should be used as an authentication source.
@@ -10,7 +10,7 @@
<xsl:output method="xml" indent="yes"/>
<xsl:variable name="elts-to-inline">
<xsl:text>,intercept-url,form-login,x509,password-encoder,http-basic,logout,concurrent-session-control,remember-me,anonymous,port-mappings,password-compare,salt-source,filter-chain,protect-pointcut,</xsl:text>
<xsl:text>,anonymous,concurrent-session-control,filter-chain,form-login,http-basic,intercept-url,logout,password-encoder,port-mappings,port-mapper,password-compare,protect,protect-pointcut,remember-me,salt-source,x509,</xsl:text>
</xsl:variable>
<xsl:template match="xs:element">
@@ -65,7 +65,7 @@ public class SimpleRoles2GrantedAuthoritiesMapperTests extends TestCase {
String[] expectedGas = { "ROLE_Role1", "ROLE_Role2", "ROLE_ROLE_Role3" };
SimpleAttributes2GrantedAuthoritiesMapper mapper = getDefaultMapper();
mapper.setAddPrefixIfAlreadyExisting(true);
mapper.seAttributePrefix("ROLE_");
mapper.setAttributePrefix("ROLE_");
testGetGrantedAuthorities(mapper, roles, expectedGas);
}
@@ -74,7 +74,7 @@ public class SimpleRoles2GrantedAuthoritiesMapperTests extends TestCase {
String[] expectedGas = { "ROLE_Role1", "ROLE_Role2", "ROLE_Role3" };
SimpleAttributes2GrantedAuthoritiesMapper mapper = getDefaultMapper();
mapper.setAddPrefixIfAlreadyExisting(false);
mapper.seAttributePrefix("ROLE_");
mapper.setAttributePrefix("ROLE_");
testGetGrantedAuthorities(mapper, roles, expectedGas);
}
@@ -83,7 +83,7 @@ public class SimpleRoles2GrantedAuthoritiesMapperTests extends TestCase {
String[] expectedGas = { "ROLE_Role1", "ROLE_Role2", "ROLE_role_Role3" };
SimpleAttributes2GrantedAuthoritiesMapper mapper = getDefaultMapper();
mapper.setAddPrefixIfAlreadyExisting(false);
mapper.seAttributePrefix("ROLE_");
mapper.setAttributePrefix("ROLE_");
testGetGrantedAuthorities(mapper, roles, expectedGas);
}
@@ -93,7 +93,7 @@ public class SimpleRoles2GrantedAuthoritiesMapperTests extends TestCase {
SimpleAttributes2GrantedAuthoritiesMapper mapper = getDefaultMapper();
mapper.setAddPrefixIfAlreadyExisting(false);
mapper.setConvertAttributeToUpperCase(true);
mapper.seAttributePrefix("ROLE_");
mapper.setAttributePrefix("ROLE_");
testGetGrantedAuthorities(mapper, roles, expectedGas);
}
@@ -111,7 +111,7 @@ public class SimpleRoles2GrantedAuthoritiesMapperTests extends TestCase {
private SimpleAttributes2GrantedAuthoritiesMapper getDefaultMapper() {
SimpleAttributes2GrantedAuthoritiesMapper mapper = new SimpleAttributes2GrantedAuthoritiesMapper();
mapper.seAttributePrefix("");
mapper.setAttributePrefix("");
mapper.setConvertAttributeToLowerCase(false);
mapper.setConvertAttributeToUpperCase(false);
mapper.setAddPrefixIfAlreadyExisting(false);
@@ -1,8 +1,12 @@
package org.springframework.security.config;
import static org.junit.Assert.*;
import org.springframework.security.providers.ProviderManager;
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
import org.springframework.security.providers.AuthenticationProvider;
import org.springframework.security.providers.encoding.ShaPasswordEncoder;
import org.springframework.security.util.FieldUtils;
import org.springframework.security.util.InMemoryXmlApplicationContext;
import org.springframework.context.support.AbstractXmlApplicationContext;
@@ -71,6 +75,19 @@ public class AuthenticationProviderBeanDefinitionParserTests {
getProvider().authenticate(bob);
}
@Test
public void providerWithSha256PasswordEncoderIsSupported() throws Exception {
setContext(" <authentication-provider>" +
" <password-encoder hash='sha-256'/>" +
" <user-service>" +
" <user name='bob' password='notused' authorities='ROLE_A' />" +
" </user-service>" +
" </authentication-provider>");
ShaPasswordEncoder encoder = (ShaPasswordEncoder) FieldUtils.getFieldValue(getProvider(), "passwordEncoder");
assertEquals("SHA-256", encoder.getAlgorithm());
}
@Test
public void passwordIsBase64EncodedWhenBase64IsEnabled() throws Exception {
setContext(" <authentication-provider>" +
@@ -81,7 +98,7 @@ public class AuthenticationProviderBeanDefinitionParserTests {
" </authentication-provider>");
getProvider().authenticate(bob);
}
}
@Test
public void externalUserServiceAndPasswordEncoderWork() throws Exception {
@@ -21,7 +21,7 @@ public class CustomAfterInvocationProviderBeanDefinitionDecoratorTests {
}
@Test
public void customAuthenticationProviderIsAddedToInterceptor() {
public void customAfterInvocationProviderIsAddedToInterceptor() {
setContext(
"<global-method-security />" +
"<b:bean id='aip' class='org.springframework.security.config.MockAfterInvocationProvider'>" +
@@ -1,21 +1,44 @@
package org.springframework.security.config;
import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.security.providers.ProviderManager;
import org.springframework.security.util.InMemoryXmlApplicationContext;
public class CustomAuthenticationProviderBeanDefinitionDecoratorTests {
@Test
public void decoratorParsesSuccessfully() {
public void decoratedProviderParsesSuccessfully() {
InMemoryXmlApplicationContext ctx = new InMemoryXmlApplicationContext(
"<b:bean id='someBean' class='org.springframework.security.config.TestBusinessBeanImpl'>" +
" <intercept-methods>" +
" <protect method='org.springframework.security.config.TestBusinessBean.*' access='ROLE_A' />" +
" </intercept-methods>" +
"</b:bean>" + HttpSecurityBeanDefinitionParserTests.AUTH_PROVIDER_XML
"<b:bean class='org.springframework.security.providers.dao.DaoAuthenticationProvider'>" +
" <custom-authentication-provider />" +
" <b:property name='userDetailsService' ref='us'/>" +
"</b:bean>" +
"<user-service id='us'>" +
" <user name='bob' password='bobspassword' authorities='ROLE_A,ROLE_B' />" +
"</user-service>"
);
ctx.getBean("someBean");
ProviderManager authMgr = (ProviderManager) ctx.getBean(BeanIds.AUTHENTICATION_MANAGER);
assertEquals(1, authMgr.getProviders().size());
}
@Test
public void decoratedBeanAndRegisteredProviderAreTheSameObject() {
InMemoryXmlApplicationContext ctx = new InMemoryXmlApplicationContext(
"<b:bean id='myProvider' class='org.springframework.security.providers.dao.DaoAuthenticationProvider'>" +
" <custom-authentication-provider />" +
" <b:property name='userDetailsService' ref='us'/>" +
"</b:bean>" +
"<user-service id='us'>" +
" <user name='bob' password='bobspassword' authorities='ROLE_A,ROLE_B' />" +
"</user-service>"
);
ProviderManager authMgr = (ProviderManager) ctx.getBean(BeanIds.AUTHENTICATION_MANAGER);
assertEquals(1, authMgr.getProviders().size());
assertSame(ctx.getBean("myProvider"), authMgr.getProviders().get(0));
}
}
@@ -9,6 +9,7 @@ import java.util.List;
import org.junit.After;
import org.junit.Test;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
import org.springframework.context.support.AbstractXmlApplicationContext;
import org.springframework.mock.web.MockHttpServletRequest;
@@ -56,7 +57,7 @@ public class HttpSecurityBeanDefinitionParserTests {
private AbstractXmlApplicationContext appContext;
static final String AUTH_PROVIDER_XML =
" <authentication-provider>" +
" <user-service>" +
" <user-service id='us'>" +
" <user name='bob' password='bobspassword' authorities='ROLE_A,ROLE_B' />" +
" <user name='bill' password='billspassword' authorities='ROLE_A,ROLE_B,AUTH_OTHER' />" +
" </user-service>" +
@@ -82,6 +83,7 @@ public class HttpSecurityBeanDefinitionParserTests {
List filterList = getFilters("/anyurl");
checkAutoConfigFilters(filterList);
assertEquals(true, FieldUtils.getFieldValue(filterList.get(10), "objectDefinitionSource.stripQueryStringFromUrls"));
}
@Test(expected=BeanDefinitionParsingException.class)
@@ -94,8 +96,7 @@ public class HttpSecurityBeanDefinitionParserTests {
Iterator filters = filterList.iterator();
assertTrue(filters.next() instanceof HttpSessionContextIntegrationFilter);
assertTrue(filters.next() instanceof SessionFixationProtectionFilter);
assertTrue(filters.next() instanceof HttpSessionContextIntegrationFilter);
assertTrue(filters.next() instanceof LogoutFilter);
Object authProcFilter = filters.next();
assertTrue(authProcFilter instanceof AuthenticationProcessingFilter);
@@ -110,6 +111,7 @@ public class HttpSecurityBeanDefinitionParserTests {
assertTrue(filters.next() instanceof RememberMeProcessingFilter);
assertTrue(filters.next() instanceof AnonymousProcessingFilter);
assertTrue(filters.next() instanceof ExceptionTranslationFilter);
assertTrue(filters.next() instanceof SessionFixationProtectionFilter);
Object fsiObj = filters.next();
assertTrue(fsiObj instanceof FilterSecurityInterceptor);
FilterSecurityInterceptor fsi = (FilterSecurityInterceptor) fsiObj;
@@ -136,7 +138,9 @@ public class HttpSecurityBeanDefinitionParserTests {
" </http>" + AUTH_PROVIDER_XML);
assertEquals(0, getFilters("/imlowercase").size());
// This will be matched by the default pattern ".*"
checkAutoConfigFilters(getFilters("/ImCaughtByTheUniversalMatchPattern"));
List allFilters = getFilters("/ImCaughtByTheUniversalMatchPattern");
checkAutoConfigFilters(allFilters);
assertEquals(false, FieldUtils.getFieldValue(allFilters.get(10), "objectDefinitionSource.stripQueryStringFromUrls"));
}
@Test
@@ -169,12 +173,12 @@ public class HttpSecurityBeanDefinitionParserTests {
" <form-login default-target-url='/default' always-use-default-target='true' />" +
"</http>" + AUTH_PROVIDER_XML);
// These will be matched by the default pattern "/**"
AuthenticationProcessingFilter filter = (AuthenticationProcessingFilter) getFilters("/anything").get(2);
AuthenticationProcessingFilter filter = (AuthenticationProcessingFilter) getFilters("/anything").get(1);
assertEquals("/default", filter.getDefaultTargetUrl());
assertEquals(Boolean.TRUE, FieldUtils.getFieldValue(filter, "alwaysUseDefaultTargetUrl"));
}
@Test(expected=BeanDefinitionParsingException.class)
@Test(expected=BeanCreationException.class)
public void invalidLoginPageIsDetected() throws Exception {
setContext(
"<http>" +
@@ -182,7 +186,7 @@ public class HttpSecurityBeanDefinitionParserTests {
"</http>" + AUTH_PROVIDER_XML);
}
@Test(expected=BeanDefinitionParsingException.class)
@Test(expected=BeanCreationException.class)
public void invalidDefaultTargetUrlIsDetected() throws Exception {
setContext(
"<http>" +
@@ -190,7 +194,7 @@ public class HttpSecurityBeanDefinitionParserTests {
"</http>" + AUTH_PROVIDER_XML);
}
@Test(expected=BeanDefinitionParsingException.class)
@Test(expected=BeanCreationException.class)
public void invalidLogoutUrlIsDetected() throws Exception {
setContext(
"<http>" +
@@ -199,7 +203,7 @@ public class HttpSecurityBeanDefinitionParserTests {
"</http>" + AUTH_PROVIDER_XML);
}
@Test(expected=BeanDefinitionParsingException.class)
@Test(expected=BeanCreationException.class)
public void invalidLogoutSuccessUrlIsDetected() throws Exception {
setContext(
"<http>" +
@@ -260,12 +264,12 @@ public class HttpSecurityBeanDefinitionParserTests {
setContext("<http access-denied-page='/access-denied'><http-basic /></http>" + AUTH_PROVIDER_XML);
List filters = getFilters("/someurl");
ExceptionTranslationFilter etf = (ExceptionTranslationFilter) filters.get(filters.size() - 2);
ExceptionTranslationFilter etf = (ExceptionTranslationFilter) filters.get(filters.size() - 3);
assertEquals("/access-denied", FieldUtils.getFieldValue(etf, "accessDeniedHandler.errorPage"));
}
@Test(expected=BeanDefinitionParsingException.class)
@Test(expected=BeanDefinitionStoreException.class)
public void invalidAccessDeniedUrlIsDetected() throws Exception {
setContext("<http auto-config='true' access-denied-page='noLeadingSlash'/>" + AUTH_PROVIDER_XML);
}
@@ -320,7 +324,7 @@ public class HttpSecurityBeanDefinitionParserTests {
assertEquals(14, filters.size());
assertTrue(filters.get(0) instanceof MockFilter);
assertTrue(filters.get(1) instanceof SecurityContextHolderAwareRequestFilter);
assertTrue(filters.get(5) instanceof SecurityContextHolderAwareRequestFilter);
assertTrue(filters.get(4) instanceof SecurityContextHolderAwareRequestFilter);
}
@Test(expected=BeanCreationException.class)
@@ -336,23 +340,50 @@ public class HttpSecurityBeanDefinitionParserTests {
public void rememberMeServiceWorksWithTokenRepoRef() {
setContext(
"<http auto-config='true'>" +
" <remember-me key='doesntmatter' token-repository-ref='tokenRepo'/>" +
" <remember-me token-repository-ref='tokenRepo'/>" +
"</http>" +
"<b:bean id='tokenRepo' " +
"class='org.springframework.security.ui.rememberme.InMemoryTokenRepositoryImpl'/> " + AUTH_PROVIDER_XML);
Object rememberMeServices = appContext.getBean(BeanIds.REMEMBER_ME_SERVICES);
assertTrue(rememberMeServices instanceof PersistentTokenBasedRememberMeServices);
}
@Test
public void rememberMeServiceWorksWithExternalServicesImpl() throws Exception {
setContext(
"<http auto-config='true'>" +
" <remember-me key='ourkey' services-ref='rms'/>" +
"</http>" +
"<b:bean id='rms' class='org.springframework.security.ui.rememberme.TokenBasedRememberMeServices'> " +
" <b:property name='userDetailsService' ref='us'/>" +
" <b:property name='key' value='ourkey'/>" +
" <b:property name='tokenValiditySeconds' value='5000'/>" +
"</b:bean>" +
AUTH_PROVIDER_XML);
assertEquals(5000, FieldUtils.getFieldValue(appContext.getBean(BeanIds.REMEMBER_ME_SERVICES),
"tokenValiditySeconds"));
}
@Test
public void rememberMeTokenValidityIsParsedCorrectly() throws Exception {
setContext(
"<http auto-config='true'>" +
" <remember-me key='ourkey' token-validity-seconds='10000' />" +
"</http>" + AUTH_PROVIDER_XML);
assertEquals(10000, FieldUtils.getFieldValue(appContext.getBean(BeanIds.REMEMBER_ME_SERVICES),
"tokenValiditySeconds"));
}
@Test
public void rememberMeServiceConfigurationParsesWithCustomUserService() {
setContext(
"<http auto-config='true'>" +
" <remember-me key='doesntmatter' user-service-ref='userService'/>" +
" <remember-me key='somekey' user-service-ref='userService'/>" +
"</http>" +
"<b:bean id='userService' " +
"class='org.springframework.security.userdetails.MockUserDetailsService'/> " + AUTH_PROVIDER_XML);
"<b:bean id='userService' class='org.springframework.security.userdetails.MockUserDetailsService'/> " +
AUTH_PROVIDER_XML);
// AbstractRememberMeServices rememberMeServices = (AbstractRememberMeServices) appContext.getBean(BeanIds.REMEMBER_ME_SERVICES);
}
@@ -364,7 +395,7 @@ public class HttpSecurityBeanDefinitionParserTests {
"</http>" + AUTH_PROVIDER_XML);
List filters = getFilters("/someurl");
assertTrue(filters.get(3) instanceof X509PreAuthenticatedProcessingFilter);
assertTrue(filters.get(2) instanceof X509PreAuthenticatedProcessingFilter);
}
@Test
@@ -372,7 +403,7 @@ public class HttpSecurityBeanDefinitionParserTests {
setContext(
"<http auto-config='true'>" +
" <concurrent-session-control session-registry-alias='seshRegistry' expired-url='/expired'/>" +
"</http>" + AUTH_PROVIDER_XML);
"</http>" + AUTH_PROVIDER_XML);
List filters = getFilters("/someurl");
assertTrue(filters.get(0) instanceof ConcurrentSessionFilter);
@@ -380,6 +411,55 @@ public class HttpSecurityBeanDefinitionParserTests {
assertNotNull(appContext.getBean(BeanIds.CONCURRENT_SESSION_CONTROLLER));
}
@Test
public void externalSessionRegistryBeanIsConfiguredCorrectly() throws Exception {
setContext(
"<http auto-config='true'>" +
" <concurrent-session-control session-registry-ref='seshRegistry' />" +
"</http>" +
"<b:bean id='seshRegistry' class='org.springframework.security.concurrent.SessionRegistryImpl'/>" +
AUTH_PROVIDER_XML);
Object sessionRegistry = appContext.getBean("seshRegistry");
Object sessionRegistryFromFilter = FieldUtils.getFieldValue(
appContext.getBean(BeanIds.CONCURRENT_SESSION_FILTER),"sessionRegistry");
Object sessionRegistryFromController = FieldUtils.getFieldValue(
appContext.getBean(BeanIds.CONCURRENT_SESSION_CONTROLLER),"sessionRegistry");
Object sessionRegistryFromFixationFilter = FieldUtils.getFieldValue(
appContext.getBean(BeanIds.SESSION_FIXATION_PROTECTION_FILTER),"sessionRegistry");
assertSame(sessionRegistry, sessionRegistryFromFilter);
assertSame(sessionRegistry, sessionRegistryFromController);
assertSame(sessionRegistry, sessionRegistryFromFixationFilter);
}
@Test(expected=BeanDefinitionParsingException.class)
public void concurrentSessionSupportCantBeUsedWithIndependentControllerBean() throws Exception {
setContext(
"<authentication-manager alias='authManager' session-controller-ref='sc'/>" +
"<http auto-config='true'>" +
" <concurrent-session-control session-registry-alias='seshRegistry' expired-url='/expired'/>" +
"</http>" +
"<b:bean id='sc' class='org.springframework.security.concurrent.ConcurrentSessionControllerImpl'>" +
" <b:property name='sessionRegistry'>" +
" <b:bean class='org.springframework.security.concurrent.SessionRegistryImpl'/>" +
" </b:property>" +
"</b:bean>" + AUTH_PROVIDER_XML);
}
@Test(expected=BeanDefinitionParsingException.class)
public void concurrentSessionSupportCantBeUsedWithIndependentControllerBean2() throws Exception {
setContext(
"<http auto-config='true'>" +
" <concurrent-session-control session-registry-alias='seshRegistry' expired-url='/expired'/>" +
"</http>" +
"<b:bean id='sc' class='org.springframework.security.concurrent.ConcurrentSessionControllerImpl'>" +
" <b:property name='sessionRegistry'>" +
" <b:bean class='org.springframework.security.concurrent.SessionRegistryImpl'/>" +
" </b:property>" +
"</b:bean>" +
"<authentication-manager alias='authManager' session-controller-ref='sc'/>" + AUTH_PROVIDER_XML);
}
@Test(expected=ConcurrentLoginException.class)
public void concurrentSessionMaxSessionsIsCorrectlyConfigured() throws Exception {
setContext(
@@ -418,7 +498,7 @@ public class HttpSecurityBeanDefinitionParserTests {
"<b:bean id='entryPoint' class='org.springframework.security.MockAuthenticationEntryPoint'>" +
" <b:constructor-arg value='/customlogin'/>" +
"</b:bean>" + AUTH_PROVIDER_XML);
ExceptionTranslationFilter etf = (ExceptionTranslationFilter) getFilters("/someurl").get(9);
ExceptionTranslationFilter etf = (ExceptionTranslationFilter) getFilters("/someurl").get(8);
assertTrue("ExceptionTranslationFilter should be configured with custom entry point",
etf.getAuthenticationEntryPoint() instanceof MockAuthenticationEntryPoint);
}
@@ -505,8 +585,7 @@ public class HttpSecurityBeanDefinitionParserTests {
" <form-login login-page='/login.jsp' default-target-url='/messageList.html'/>" +
" </http>" + AUTH_PROVIDER_XML);
}
private void setContext(String context) {
appContext = new InMemoryXmlApplicationContext(context);
}
@@ -38,8 +38,8 @@ public class LdapProviderBeanDefinitionParserTests {
Authentication auth = provider.authenticate(new UsernamePasswordAuthenticationToken("ben", "benspassword"));
LdapUserDetailsImpl ben = (LdapUserDetailsImpl) auth.getPrincipal();
assertEquals(2, ben.getAuthorities().length);
}
assertEquals(3, ben.getAuthorities().length);
}
@Test(expected = SecurityConfigurationException.class)
public void missingServerEltCausesConfigException() {
@@ -42,7 +42,7 @@ public class LdapUserServiceBeanDefinitionParserTests {
UserDetails ben = uds.loadUserByUsername("ben");
Set authorities = AuthorityUtils.authorityArrayToSet(ben.getAuthorities());
assertEquals(2, authorities.size());
assertEquals(3, authorities.size());
assertTrue(authorities.contains("ROLE_DEVELOPERS"));
}
@@ -88,7 +88,7 @@ public class LdapUserServiceBeanDefinitionParserTests {
UserDetails ben = uds.loadUserByUsername("ben");
Set authorities = AuthorityUtils.authorityArrayToSet(ben.getAuthorities());
assertEquals(2, authorities.size());
assertEquals(3, authorities.size());
assertTrue(authorities.contains(new GrantedAuthorityImpl("ROLE_DEVELOPER")));
}
@@ -17,13 +17,19 @@ package org.springframework.security.ldap;
import org.springframework.ldap.UncategorizedLdapException;
import org.springframework.ldap.core.ContextExecutor;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.ldap.core.DistinguishedName;
import org.springframework.ldap.core.LdapEncoder;
import org.junit.Test;
import static org.junit.Assert.*;
import java.util.Set;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.directory.SearchControls;
import javax.naming.ldap.LdapName;
/**
* @author Luke Taylor
@@ -43,22 +49,22 @@ public class SpringSecurityLdapTemplateTests extends AbstractLdapIntegrationTest
}
@Test
public void testCompareOfCorrectValueSucceeds() {
public void compareOfCorrectValueSucceeds() {
assertTrue(template.compare("uid=bob,ou=people", "uid", "bob"));
}
@Test
public void testCompareOfCorrectByteValueSucceeds() {
public void compareOfCorrectByteValueSucceeds() {
assertTrue(template.compare("uid=bob,ou=people", "userPassword", LdapUtils.getUtf8Bytes("bobspassword")));
}
@Test
public void testCompareOfWrongByteValueFails() {
public void compareOfWrongByteValueFails() {
assertFalse(template.compare("uid=bob,ou=people", "userPassword", LdapUtils.getUtf8Bytes("wrongvalue")));
}
@Test
public void testCompareOfWrongValueFails() {
public void compareOfWrongValueFails() {
assertFalse(template.compare("uid=bob,ou=people", "uid", "wrongvalue"));
}
@@ -73,7 +79,7 @@ public class SpringSecurityLdapTemplateTests extends AbstractLdapIntegrationTest
// }
@Test
public void testNamingExceptionIsTranslatedCorrectly() {
public void namingExceptionIsTranslatedCorrectly() {
try {
template.executeReadOnly(new ContextExecutor() {
public Object executeWithContext(DirContext dirContext) throws NamingException {
@@ -85,7 +91,7 @@ public class SpringSecurityLdapTemplateTests extends AbstractLdapIntegrationTest
}
@Test
public void testRoleSearchReturnsCorrectNumberOfRoles() {
public void roleSearchReturnsCorrectNumberOfRoles() {
String param = "uid=ben,ou=people,dc=springframework,dc=org";
Set values = template.searchForSingleAttributeValues("ou=groups", "(member={0})", new String[] {param}, "ou");
@@ -104,4 +110,44 @@ public class SpringSecurityLdapTemplateTests extends AbstractLdapIntegrationTest
assertEquals(0, values.size());
}
@Test
public void roleSearchWithEscapedCharacterSucceeds() throws Exception {
String param = "cn=mouse\\, jerry,ou=people,dc=springframework,dc=org";
Set values = template.searchForSingleAttributeValues("ou=groups", "(member={0})", new String[] {param}, "cn");
assertEquals(1, values.size());
}
@Test
public void nonSpringLdapSearchCodeTestMethod() throws Exception {
java.util.Hashtable env = new java.util.Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://localhost:53389");
env.put(Context.SECURITY_PRINCIPAL, "");
env.put(Context.SECURITY_CREDENTIALS, "");
DirContext ctx = new javax.naming.directory.InitialDirContext(env);
SearchControls controls = new SearchControls();
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
controls.setReturningObjFlag(true);
controls.setReturningAttributes(null);
String param = "cn=mouse\\, jerry,ou=people,dc=springframework,dc=org";
javax.naming.NamingEnumeration results =
ctx.search("ou=groups,dc=springframework,dc=org",
"(member={0})", new String[] {param},
controls);
assertTrue("Expected a result", results.hasMore());
}
@Test
public void searchForSingleEntryWithEscapedCharsInDnSucceeds() {
String param = "mouse, jerry";
DirContextOperations jerry = template.searchForSingleEntry("ou=people", "(cn={0})", new String[] {param});
}
}
@@ -131,4 +131,18 @@ public class DefaultLdapAuthoritiesPopulatorTests extends AbstractLdapIntegratio
assertTrue(roles.contains("ROLE_SUBMANAGER"));
}
@Test
public void testUserDnWithEscapedCharacterParameterReturnsExpectedRoles() {
populator.setGroupRoleAttribute("ou");
populator.setConvertToUpperCase(true);
populator.setGroupSearchFilter("(member={0})");
DirContextAdapter ctx = new DirContextAdapter(new DistinguishedName("cn=mouse\\, jerry,ou=people,dc=springframework,dc=org"));
GrantedAuthority[] authorities = populator.getGrantedAuthorities(ctx, "notused");
assertEquals("Should have 1 role", 1, authorities.length);
assertEquals("ROLE_MANAGER", authorities[0].getAuthority());
}
}
@@ -60,7 +60,7 @@ public class FilterBasedLdapUserSearchTests extends AbstractLdapIntegrationTests
@Test
public void extraFilterPartToExcludeBob() throws Exception {
FilterBasedLdapUserSearch locator = new FilterBasedLdapUserSearch("ou=people",
"(&(cn=*)(!(|(uid={0})(uid=rod))))", dirCtxFactory);
"(&(cn=*)(!(|(uid={0})(uid=rod)(uid=jerry))))", dirCtxFactory);
// Search for bob, get back ben...
DirContextOperations ben = locator.searchForUser("bob");
@@ -2,19 +2,14 @@ package org.springframework.security.ui;
import static org.junit.Assert.*;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.After;
import org.junit.Test;
import org.springframework.mock.web.MockFilterChain;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.context.HttpSessionContextIntegrationFilter;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.security.providers.TestingAuthenticationToken;
@@ -34,6 +29,7 @@ public class SessionFixationProtectionFilterTests {
public void newSessionShouldNotBeCreatedIfNoSessionExists() throws Exception {
SessionFixationProtectionFilter filter = new SessionFixationProtectionFilter();
HttpServletRequest request = new MockHttpServletRequest();
authenticateUser();
filter.doFilter(request, new MockHttpServletResponse(), new MockFilterChain());
@@ -41,14 +37,15 @@ public class SessionFixationProtectionFilterTests {
}
@Test
public void newSessionShouldNotBeCreatedIfUserIsAuthenticated() throws Exception {
public void newSessionBeCreatedIfAuthenticatedOccurredDuringRequest() throws Exception {
SessionFixationProtectionFilter filter = new SessionFixationProtectionFilter();
HttpServletRequest request = new MockHttpServletRequest();
String sessionId = request.getSession().getId();
authenticateUser();
filter.doFilter(request, new MockHttpServletResponse(), new MockFilterChain());
assertEquals(sessionId, request.getSession().getId());
assertFalse(sessionId.equals(request.getSession().getId()));
}
@Test
@@ -68,91 +65,15 @@ public class SessionFixationProtectionFilterTests {
HttpServletRequest request = new MockHttpServletRequest();
String sessionId = request.getSession().getId();
authenticateUser();
request.getSession().setAttribute(HttpSessionContextIntegrationFilter.SPRING_SECURITY_CONTEXT_KEY,
SecurityContextHolder.getContext());
filter.doFilter(request, new MockHttpServletResponse(), new MockFilterChain());
assertEquals(sessionId, request.getSession().getId());
}
@Test
public void newSessionShouldBeCreatedIfAuthenticationOccursDuringRequest() throws Exception {
SessionFixationProtectionFilter filter = new SessionFixationProtectionFilter();
HttpServletRequest request = new MockHttpServletRequest();
String sessionId = request.getSession().getId();
filter.doFilter(request, new MockHttpServletResponse(), new UserAuthenticatingFilterChain());
assertFalse(sessionId.equals(request.getSession().getId()));
}
@Test
public void newSessionShouldBeCreatedIfAuthenticationAndRedirectOccursDuringRequest() throws Exception {
SessionFixationProtectionFilter filter = new SessionFixationProtectionFilter();
HttpServletRequest request = new MockHttpServletRequest();
String sessionId = request.getSession().getId();
AuthenticateAndRedirectFilterChain chain = new AuthenticateAndRedirectFilterChain();
filter.doFilter(request, new MockHttpServletResponse(), chain);
assertTrue(chain.getResponse() instanceof
SessionFixationProtectionFilter.SessionFixationProtectionResponseWrapper);
assertTrue("New session should have been created by session wrapper",
((SessionFixationProtectionFilter.SessionFixationProtectionResponseWrapper)chain.getResponse()).isNewSessionStarted());
assertFalse(sessionId.equals(request.getSession().getId()));
}
@Test
public void wrapperSendErrorCreatesNewSession() throws Exception {
authenticateUser();
SessionFixationProtectionFilter filter = new SessionFixationProtectionFilter();
HttpServletRequest request = new MockHttpServletRequest();
String sessionId = request.getSession().getId();
SessionFixationProtectionFilter.SessionFixationProtectionResponseWrapper wrapper =
filter.new SessionFixationProtectionResponseWrapper(new MockHttpServletResponse(), request);
wrapper.sendError(HttpServletResponse.SC_FORBIDDEN);
assertFalse(sessionId.equals(request.getSession().getId()));
// Message version
request = new MockHttpServletRequest();
sessionId = request.getSession().getId();
wrapper = filter.new SessionFixationProtectionResponseWrapper(new MockHttpServletResponse(), request);
wrapper.sendError(HttpServletResponse.SC_FORBIDDEN, "Hi. I'm your friendly forbidden message.");
assertFalse(sessionId.equals(request.getSession().getId()));
}
@Test
public void wrapperRedirectCreatesNewSession() throws Exception {
authenticateUser();
SessionFixationProtectionFilter filter = new SessionFixationProtectionFilter();
HttpServletRequest request = new MockHttpServletRequest();
String sessionId = request.getSession().getId();
SessionFixationProtectionFilter.SessionFixationProtectionResponseWrapper wrapper =
filter.new SessionFixationProtectionResponseWrapper(new MockHttpServletResponse(), request);
wrapper.sendRedirect("/somelocation");
assertFalse(sessionId.equals(request.getSession().getId()));
}
private void authenticateUser() {
SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken("user", "pass", null));
}
private class UserAuthenticatingFilterChain implements FilterChain {
public void doFilter(ServletRequest request, ServletResponse response) throws IOException {
authenticateUser();
}
}
private class AuthenticateAndRedirectFilterChain extends UserAuthenticatingFilterChain{
HttpServletResponse response;
public void doFilter(ServletRequest request, ServletResponse response) throws IOException {
super.doFilter(request, response);
this.response = (HttpServletResponse)response;
this.response.sendRedirect("/someUrl");
}
public HttpServletResponse getResponse() {
return response;
}
}
}
@@ -130,7 +130,7 @@ public class J2eeBasedPreAuthenticatedWebAuthenticationDetailsSourceTests extend
result.setAddPrefixIfAlreadyExisting(false);
result.setConvertAttributeToLowerCase(false);
result.setConvertAttributeToUpperCase(false);
result.seAttributePrefix("");
result.setAttributePrefix("");
return result;
}
@@ -342,6 +342,8 @@ public class TokenBasedRememberMeServicesTests extends TestCase {
public void testLoginSuccessNormalWithNonUserDetailsBasedPrincipal() {
TokenBasedRememberMeServices services = new TokenBasedRememberMeServices();
// SEC-822
services.setTokenValiditySeconds(5000000);
MockHttpServletRequest request = new MockHttpServletRequest();
request.setRequestURI("d");
request.addParameter(TokenBasedRememberMeServices.DEFAULT_PARAMETER, "true");
@@ -0,0 +1,14 @@
package org.springframework.security.util;
import static org.junit.Assert.*;
import org.junit.Test;
public class TextUtilsTests {
@Test
public void charactersAreEscapedCorrectly() {
assertEquals("a&lt;script&gt;&#034;&#039;", TextUtils.escapeEntities("a<script>\"'"));
}
}
@@ -0,0 +1,66 @@
package org.springframework.security.wrapper;
import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.security.ui.AbstractProcessingFilter;
import org.springframework.security.ui.savedrequest.SavedRequest;
import org.springframework.security.util.PortResolverImpl;
public class SavedRequestAwareWrapperTests {
@Test
/* SEC-830 */
public void wrappedRequestParameterTakesPrecedenceOverSavedRequest() {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setParameter("action", "foo");
SavedRequest savedRequest = new SavedRequest(request, new PortResolverImpl());
MockHttpServletRequest request2 = new MockHttpServletRequest();
request2.getSession().setAttribute(AbstractProcessingFilter.SPRING_SECURITY_SAVED_REQUEST_KEY, savedRequest);
SavedRequestAwareWrapper wrapper = new SavedRequestAwareWrapper(request2, new PortResolverImpl(), "ROLE_");
assertEquals("foo", wrapper.getParameter("action"));
request2.setParameter("action", "bar");
assertEquals("bar", wrapper.getParameter("action"));
}
@Test
public void savedRequestHeadersTakePrecedence() {
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("Authorization","foo");
SavedRequest savedRequest = new SavedRequest(request, new PortResolverImpl());
MockHttpServletRequest request2 = new MockHttpServletRequest();
request2.addHeader("Authorization","bar");
request2.getSession().setAttribute(AbstractProcessingFilter.SPRING_SECURITY_SAVED_REQUEST_KEY, savedRequest);
SavedRequestAwareWrapper wrapper = new SavedRequestAwareWrapper(request2, new PortResolverImpl(), "ROLE_");
assertEquals("foo", wrapper.getHeader("Authorization"));
}
@Test
public void getParameterValuesReturnsNullIfParameterIsntSet() {
MockHttpServletRequest request = new MockHttpServletRequest();
SavedRequestAwareWrapper wrapper = new SavedRequestAwareWrapper(request, new PortResolverImpl(), "ROLE_");
assertNull(wrapper.getParameterValues("action"));
assertNull(wrapper.getParameterMap().get("action"));
}
@Test
public void getParameterValuesReturnsCombinedValues() {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setParameter("action", "foo");
SavedRequest savedRequest = new SavedRequest(request, new PortResolverImpl());
MockHttpServletRequest request2 = new MockHttpServletRequest();
request2.getSession().setAttribute(AbstractProcessingFilter.SPRING_SECURITY_SAVED_REQUEST_KEY, savedRequest);
SavedRequestAwareWrapper wrapper = new SavedRequestAwareWrapper(request2, new PortResolverImpl(), "ROLE_");
assertArrayEquals(new Object[] {"foo"}, wrapper.getParameterValues("action"));
request2.setParameter("action", "bar");
assertArrayEquals(new Object[] {"bar","foo"}, wrapper.getParameterValues("action"));
// Check map is consistent
String[] valuesFromMap = (String[]) wrapper.getParameterMap().get("action");
assertEquals(2, valuesFromMap.length);
assertEquals("bar", valuesFromMap[0]);
}
}
@@ -9,7 +9,6 @@ http://www.springframework.org/schema/security http://www.springframework.org/sc
<b:bean id="target" class="org.springframework.security.config.TestBusinessBeanImpl">
<!-- This will add a security interceptor to the bean -->
<intercept-methods>
<!-- TODO: It would be better if we didn't need the package/interface names here -->
<protect method="org.springframework.security.config.TestBusinessBean.set*" access="ROLE_ADMIN" />
<protect method="get*" access="ROLE_ADMIN,ROLE_USER" />
<protect method="doSomething" access="ROLE_USER" />
+11
View File
@@ -48,6 +48,16 @@ sn: Smeth
uid: joe
userPassword: joespassword
dn: cn=mouse\, jerry,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Mouse, Jerry
sn: Mouse
uid: jerry
userPassword: jerryspassword
dn: cn=developers,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfNames
@@ -62,6 +72,7 @@ objectclass: groupOfNames
cn: managers
ou: manager
member: uid=ben,ou=people,dc=springframework,dc=org
member: cn=mouse\, jerry,ou=people,dc=springframework,dc=org
dn: cn=submanagers,ou=subgroups,ou=groups,dc=springframework,dc=org
objectclass: top
+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-parent</artifactId>
<version>2.0.1</version>
<version>2.0.2</version>
</parent>
<packaging>jar</packaging>
<artifactId>spring-security-ntlm</artifactId>
+4 -4
View File
@@ -3,12 +3,12 @@
<parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-parent</artifactId>
<version>2.0.1</version>
<version>2.0.2</version>
</parent>
<artifactId>spring-security-openid</artifactId>
<name>Spring Security - OpenID support</name>
<description>Spring Security - Support for OpenID</description>
<version>2.0.1</version>
<version>2.0.2</version>
<packaging>bundle</packaging>
<dependencies>
@@ -41,14 +41,14 @@
<properties>
<spring.osgi.export>
org.springframework.security.*;version=${pom.version}
org.springframework.security.*;version=${pom.version.osgi}
</spring.osgi.export>
<spring.osgi.import>
javax.servlet.*;version="[2.4.0, 3.0.0)",
org.apache.commons.logging.*;version="[1.1.1, 2.0.0)",
org.openid4java.*;version="[0.9.3, 1.0.0)",
org.springframework.security.*;version="[${pom.version},${pom.version}]",
org.springframework.security.*;version="[${pom.version.osgi},${pom.version.osgi}]",
org.springframework.beans.*;version="${spring.version.osgi}",
org.springframework.util.*;version="${spring.version.osgi}"
</spring.osgi.import>
@@ -78,6 +78,8 @@ public class OpenIDAuthenticationProcessingFilter extends AbstractProcessingFilt
throw new AuthenticationServiceException("Consumer error", oice);
}
token.setDetails(authenticationDetailsSource.buildDetails(req));
// delegate to the auth provider
Authentication authentication = this.getAuthenticationManager().authenticate(token);
+6 -5
View File
@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-parent</artifactId>
<version>2.0.1</version>
<version>2.0.2</version>
<name>Spring Security</name>
<packaging>pom</packaging>
@@ -38,9 +38,9 @@
<!-- Note when doing releases: tagBase is set in release plugin configuration below -->
<scm>
<connection>scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/spring-security/tags/spring-security-parent-2.0.1</connection>
<developerConnection>scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/spring-security/tags/spring-security-parent-2.0.1</developerConnection>
<url>http://acegisecurity.svn.sourceforge.net/viewcvs.cgi/acegisecurity/spring-security/tags/spring-security-parent-2.0.1</url>
<connection>scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/spring-security/tags/spring-security-parent-2.0.2</connection>
<developerConnection>scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/spring-security/tags/spring-security-parent-2.0.2</developerConnection>
<url>http://acegisecurity.svn.sourceforge.net/viewcvs.cgi/acegisecurity/spring-security/tags/spring-security-parent-2.0.2</url>
</scm>
<issueManagement>
@@ -731,7 +731,8 @@
</dependency>
</dependencies>
</dependencyManagement>
<properties>
<properties>
<pom.version.osgi>2.0.2.SNAPSHOT</pom.version.osgi>
<spring.version>2.0.8</spring.version>
<spring.version.osgi>[2.0.8, 3.0.0)</spring.version.osgi>
<jstl.version>1.1.2</jstl.version>
+3 -3
View File
@@ -3,18 +3,18 @@
<parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-parent</artifactId>
<version>2.0.1</version>
<version>2.0.2</version>
</parent>
<artifactId>spring-security-portlet</artifactId>
<name>Spring Security - Portlet support</name>
<description>Spring Security - Support for JSR 168 Portlets</description>
<version>2.0.1</version>
<version>2.0.2</version>
<dependencies>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>2.0.1</version>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-samples-cas</artifactId>
<version>2.0.1</version>
<version>2.0.2</version>
</parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-samples-cas-client</artifactId>
+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-samples</artifactId>
<version>2.0.1</version>
<version>2.0.2</version>
</parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-samples-cas</artifactId>
+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-samples-cas</artifactId>
<version>2.0.1</version>
<version>2.0.2</version>
</parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-samples-cas-server</artifactId>
+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-samples</artifactId>
<version>2.0.1</version>
<version>2.0.2</version>
</parent>
<artifactId>spring-security-samples-contacts</artifactId>
<name>Spring Security - Contacts sample</name>
@@ -4,7 +4,7 @@
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.xsd">
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.1.xsd">
<!--
- Application context containing authentication beans.
@@ -30,7 +30,6 @@
<property name="staticField" value="org.springframework.security.acls.domain.BasePermission.DELETE"/>
</bean>
<!-- An access decision voter that reads ROLE_* configuration settings -->
<bean id="roleVoter" class="org.springframework.security.vote.RoleVoter"/>
@@ -27,17 +27,17 @@
</bean>
<bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager"><ref local="transactionManager"/></property>
<property name="transactionManager" ref="transactionManager"/>
<property name="transactionAttributeSource">
<value>
sample.contact.ContactManager.create=PROPAGATION_REQUIRED
sample.contact.ContactManager.getAllRecipients=PROPAGATION_REQUIRED,readOnly
sample.contact.ContactManager.getAll=PROPAGATION_REQUIRED,readOnly
sample.contact.ContactManager.getById=PROPAGATION_REQUIRED,readOnly
sample.contact.ContactManager.delete=PROPAGATION_REQUIRED
sample.contact.ContactManager.deletePermission=PROPAGATION_REQUIRED
sample.contact.ContactManager.addPermission=PROPAGATION_REQUIRED
</value>
<value>
sample.contact.ContactManager.create=PROPAGATION_REQUIRED
sample.contact.ContactManager.getAllRecipients=PROPAGATION_REQUIRED,readOnly
sample.contact.ContactManager.getAll=PROPAGATION_REQUIRED,readOnly
sample.contact.ContactManager.getById=PROPAGATION_REQUIRED,readOnly
sample.contact.ContactManager.delete=PROPAGATION_REQUIRED
sample.contact.ContactManager.deletePermission=PROPAGATION_REQUIRED
sample.contact.ContactManager.addPermission=PROPAGATION_REQUIRED
</value>
</property>
</bean>
@@ -24,7 +24,7 @@
<intercept-url pattern="/j_spring_security_switch_user" access="ROLE_SUPERVISOR"/>
<intercept-url pattern="/**" access="ROLE_USER"/>
<form-login login-page="/login.jsp" authentication-failure-url="/acegilogin.jsp?login_error=1"/>
<form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?login_error=1"/>
<logout logout-success-url="/index.jsp"/>
</http>
@@ -27,28 +27,23 @@
<!-- HTTP invoker exporter for the ContactManager -->
<!-- Spring's HTTP invoker uses Java serialization via HTTP -->
<bean name="/ContactManager-httpinvoker" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
<property name="service"><ref bean="contactManager"/></property>
<property name="serviceInterface">
<value>sample.contact.ContactManager</value>
</property>
<property name="service" ref="contactManager"/>
<property name="serviceInterface" value="sample.contact.ContactManager"/>
</bean>
<!-- Hessian exporter for the ContactManager -->
<!-- Hessian is a slim binary HTTP remoting protocol -->
<bean name="/ContactManager-hessian" class="org.springframework.remoting.caucho.HessianServiceExporter">
<property name="service"><ref bean="contactManager"/></property>
<property name="serviceInterface">
<value>sample.contact.ContactManager</value>
<property name="service" ref="contactManager"/>
<property name="serviceInterface" value="sample.contact.ContactManager"/>
</property>
</bean>
<!-- Burlap exporter for the ContactManager -->
<!-- Burlap is a slim XML-based HTTP remoting protocol -->
<bean name="/ContactManager-burlap" class="org.springframework.remoting.caucho.BurlapServiceExporter">
<property name="service"><ref bean="contactManager"/></property>
<property name="serviceInterface">
<value>sample.contact.ContactManager</value>
</property>
<property name="service" ref="contactManager"/>
<property name="serviceInterface" value="sample.contact.ContactManager"/>
</bean>
</beans>
+3 -3
View File
@@ -27,14 +27,14 @@
--%>
<c:if test="${not empty param.login_error}">
<font color="red">
Your login attempt was not successful, try again.<BR><BR>
Reason: <%= ((AuthenticationException) session.getAttribute(AbstractProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY)).getMessage() %>
Your login attempt was not successful, try again.<br/><br/>
Reason: <c:out value="${SPRING_SECURITY_LAST_EXCEPTION.message}"/>.
</font>
</c:if>
<form name="f" action="<c:url value='j_spring_security_check'/>" method="POST">
<table>
<tr><td>User:</td><td><input type='text' name='j_username' <c:if test="${not empty param.login_error}">value='<%= session.getAttribute(AuthenticationProcessingFilter.SPRING_SECURITY_LAST_USERNAME_KEY) %>'</c:if>></td></tr>
<tr><td>User:</td><td><input type='text' name='j_username' value='<c:if test="${not empty param.login_error}"><c:out value="${SPRING_SECURITY_LAST_USERNAME}"/></c:if>'/></td></tr>
<tr><td>Password:</td><td><input type='password' name='j_password'></td></tr>
<tr><td><input type="checkbox" name="_spring_security_remember_me"></td><td>Don't ask for my password for two weeks</td></tr>
+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-samples</artifactId>
<version>2.0.1</version>
<version>2.0.2</version>
</parent>
<artifactId>spring-security-samples-dms</artifactId>
<name>Spring Security - DMS sample</name>
+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-samples</artifactId>
<version>2.0.1</version>
<version>2.0.2</version>
</parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-samples-ldap</artifactId>
@@ -1,32 +1,63 @@
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:s="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.1.xsd">
<http>
<intercept-url pattern="/secure/extreme/**" access="ROLE_SUPERVISOR"/>
<intercept-url pattern="/secure/**" access="IS_AUTHENTICATED_REMEMBERED" />
<intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<s:http>
<s:intercept-url pattern="/secure/extreme/**" access="ROLE_SUPERVISOR"/>
<s:intercept-url pattern="/secure/**" access="IS_AUTHENTICATED_REMEMBERED" />
<s:intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<form-login />
<anonymous />
<logout />
</http>
<s:form-login />
<s:anonymous />
<s:logout />
</s:http>
<!--
Usernames/Passwords are
rod/koala
dianne/emu
scott/wombat
-->
<ldap-server ldif="classpath:users.ldif" />
<ldap-authentication-provider
group-search-filter="member={0}"
group-search-base="ou=groups"
user-search-base="ou=people"
user-search-filter="uid={0}"
/>
<!-- Simple namespace-based configuration -->
</beans:beans>
<s:ldap-server ldif="classpath:users.ldif" port="33389"/>
<s:ldap-authentication-provider
group-search-filter="member={0}"
group-search-base="ou=groups"
user-search-base="ou=people"
user-search-filter="uid={0}"
/>
<!-- Traditional Bean version of the same configuration -->
<!-- This bean points at the embedded directory server created by the ldap-server element above -->
<bean id="contextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<constructor-arg value="ldap://localhost:33389/dc=springframework,dc=org"/>
</bean>
<bean id="secondLdapProvider" class="org.springframework.security.providers.ldap.LdapAuthenticationProvider">
<s:custom-authentication-provider />
<constructor-arg>
<bean class="org.springframework.security.providers.ldap.authenticator.BindAuthenticator">
<constructor-arg ref="contextSource" />
<property name="userSearch">
<bean id="userSearch" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
<constructor-arg index="0" value="ou=people"/>
<constructor-arg index="1" value="(uid={0})"/>
<constructor-arg index="2" ref="contextSource" />
</bean>
</property>
</bean>
</constructor-arg>
<constructor-arg>
<bean class="org.springframework.security.ldap.populator.DefaultLdapAuthoritiesPopulator">
<constructor-arg ref="contextSource" />
<constructor-arg value="ou=groups" />
<property name="groupSearchFilter" value="member={0}"/>
<property name="groupRoleAttribute" value="ou" />
<property name="rolePrefix" value="ROLE_"/>
<property name="searchSubtree" value="true"/>
<property name="convertToUpperCase" value="true"/>
</bean>
</constructor-arg>
</bean>
</beans>

Some files were not shown because too many files have changed in this diff Show More