1
0
mirror of synced 2026-07-17 08:35:13 +00:00

Compare commits

..

48 Commits

Author SHA1 Message Date
Rob Winch b1a6a574ac Merge pull request #159 from ractive/3.1.x
SEC-2812: Fix german translations
2015-01-14 16:29:47 -06:00
james 979badd550 SEC-2812: Fix german translations 2015-01-12 13:51:33 +01:00
Spring Buildmaster 2263a545ea Next development version 2014-08-15 11:39:31 -07:00
Rob Winch 0d86164a49 SEC-2547: Update to cas-client-core-3.3.3 2014-08-15 12:49:13 -05:00
Rob Winch b78bd897cd SEC-2688: CAS Proxy Ticket Authentication uses Service for host & port 2014-07-24 16:56:58 -05:00
Rob Winch 055eb72d60 Remove explict LDAP dir for integration tests 2014-06-18 09:50:17 -05:00
Rob Winch f4914b2271 Remove ApacheDSContainer import 2014-06-18 09:43:35 -05:00
Rob Winch 541aee8e3e SEC-2449: <ldap-server> default port should fallback to dynamic value 2014-06-18 09:34:49 -05:00
Rob Winch a6cab1be74 SEC-2606: ApacheDSServerIntegrationTests scan for available port 2014-06-12 12:28:40 -05:00
Rob Winch bbfcab0c81 SEC-2607: CAS Server logouts out synchronously 2014-06-12 12:28:40 -05:00
Spring Buildmaster 6e431e7d16 Next development version 2014-03-10 14:26:38 -07:00
Rob Winch 2eec67fe73 SEC-2507: Fix test imports 2014-03-10 15:09:36 -05:00
Rob Winch 88559882e9 SEC-2500: Prevent anonymous bind for ActiveDirectoryLdapAuthenticator 2014-03-10 14:43:59 -05:00
Rob Winch 9270845a21 SEC-2507: WebExpressionVoter.supports support subclasses of FilterInvocation 2014-03-10 14:43:56 -05:00
Spring Buildmaster 1d53dc5e5c Next development version 2014-02-19 07:12:15 -08:00
Rob Winch ac6cf5396a SEC-2468: JdbcUserDetailsManager#createNewAuthentication uses null credentials 2014-02-14 17:00:07 -06:00
james 5a2e99c940 SEC-2467: Fix Small errors in itest-web's jsps 2014-01-28 16:09:09 -06:00
Rob Winch 6f3c6f9b5b SEC-2447: JdbcMutableAclServiceTests should invoke aclCache.clearCache() 2013-12-14 10:20:31 -06:00
Rob Winch 1feb38048b Update to Gradle 1.9
Necessary to work with the latest Bamboo and Artifactory Gradle updates
2013-12-05 09:08:26 -06:00
Rob Winch 905c59f599 SEC-2313: Gradle javadoc hotfix 2013-09-11 15:42:53 -07:00
Rob Winch 241e984ea4 SEC-2171: Include Information about pooling in Spring LDAP documentation 2013-08-25 11:31:05 -05:00
Rob Winch 8053cb0de7 SEC-2103: Fix tests to verify debug logging instead of info 2013-08-25 10:05:48 -05:00
Rob Winch 889bba1d67 SEC-2103: Change log of no results to debug 2013-08-24 23:40:48 -05:00
Rob Winch f9fd448d88 SEC-2139: named-security-filter are all defined and ordered correctly 2013-08-24 15:20:23 -05:00
Rob Winch 7bf6046408 SEC-2108: Fix typo in ldap section of manual 2013-08-24 15:20:23 -05:00
Guillaume Smet 5dfeea7631 SEC-2245: Cast to interface instead of implementation
Makes our life easier when we want to override the
MethodSecurityExpressionRoot.
2013-08-05 17:06:09 -05:00
Luke Taylor f42720b1b6 SEC-2175: Correct XSD docs on auto-config. 2013-06-09 14:46:56 +01:00
Rob Winch 113e28a148 Update to next dev version - 3.1.5.CI-SNAPSHOT 2013-04-29 08:47:30 -05:00
Spring Buildmaster de6bcb80b9 [artifactory-release] Next development version 2013-04-27 13:05:38 -07:00
Rob Winch 102da87080 Disable artifactoryPublish for projects without artifacts 2013-04-27 13:56:12 -05:00
Rob Winch f1ae9da55e Remove ApacheDSContainerTests successfulStartupAndShutdown since it was commented out 2013-04-25 11:17:21 -05:00
Rob Winch 94e2e0c65e SEC-2161: <ldap-server> creates unique dir for embedded LDAP 2013-04-25 11:17:21 -05:00
Rob Winch fa3a30cc20 SEC-2162: ApacheDSContainer throws RuntimeException on failure to start 2013-04-25 11:17:21 -05:00
Rob Winch ee6e6ceafb SEC-2133: Update doc from ChannelAuthenticationFilter to ChannelProcessingFilter 2013-04-25 08:51:51 -05:00
Rob Winch 095594daea SEC-2147: Deprecate .encoding.PasswordEncoding 2013-04-25 08:49:33 -05:00
Rob Winch e9215c4dc3 SEC-2087: GlobalMethodSecurityBeanDefinitionParser uses AuthenticationManager to create AuthenticationManagerDelegator 2013-04-24 17:30:51 -05:00
@fbiville 0d70c703df SEC-2138: Fix code snippet in Hierarchical Roles section
The bean definition of RoleHierarchyVoter was syntactically incorrect.
2013-02-26 09:47:02 -06:00
Rob Winch 73710bf7b8 Add CONTRIBUTING.md 2013-02-25 17:14:21 -06:00
Jean-Pierre Bergamin ca5501eb31 SEC-2118: Fixing spring and aspectj OSGi version ranges [3.2, 3.2) -> [3.2, 3.3) 2013-02-25 17:10:07 -06:00
Rob Winch c703806fa2 Revert "SEC-2078: AbstractPreAuthenticatedProcessingFilter requriesAuthentication support for non-String Principals"
This reverts commit 3fe7791266.
2012-12-06 16:16:50 -06:00
Rob Winch 67eb9821f3 Revert "SEC-2078: Updated Javadoc to reflect that updates to Principal will also trigger reauthentication"
This reverts commit ece4a0f067.
2012-12-06 16:16:33 -06:00
Rob Winch 7b93ffd5d4 SEC-2078: Updated Javadoc to reflect that updates to Principal will also trigger reauthentication 2012-12-06 09:05:21 -06:00
Rob Winch 5fa252b848 SEC-2078: AbstractPreAuthenticatedProcessingFilter requriesAuthentication support for non-String Principals
Previously, if the Principal returned by getPreAuthenticatedPrincipal was not a String,
it prevented requiresAuthentication from detecting when the Principal was the same.
This caused the need to authenticate the user for every request even when the Principal
did not change.

Now requiresAuthentication will check to see if the result of
getPreAuthenticatedPrincipal is equal to the current Authentication.getPrincipal().
2012-12-04 13:04:41 -06:00
Rob Winch 87c3c7edb9 Use AspectJMethodSecurityInterceptor in reference
Change reference to use AspectJMethodSecurityInterceptor instead of
undefined AspectJSecurityInterceptor.
2012-12-04 10:07:30 -06:00
Rob Winch 7f342f3321 SEC-2074: Update reference to use <method-security-metadata-source> 2012-12-04 10:07:30 -06:00
Rob Winch 2f6325f651 SEC-2084: AntPathRequestMatcher and RegexpRequestMatcher support request.getMethod()
Previously a NullPointerException would occur if an HttpServletRequest.getMethod()
returned null.

Now AntPathRequestMatcher and RegexpRequestMatcher will handle if the
HttpServletRequest.getMethod() returns null. While under normal circumstances,
it is unlikely for the method to be null this can occur when using
DefaultWebInvocationPrivilegeEvaluator.isAllowed(String, Authentication).
2012-12-03 15:08:37 -06:00
Rob Winch e3f53908af SEC-2072: <security:anonymous> granted-authority supports multiple authorities again 2012-11-02 16:23:37 -05:00
Rob Winch 4d1127106b Add 3.1.x suffix to eclipse projects 2012-11-02 10:00:45 -05:00
1073 changed files with 15793 additions and 111363 deletions
+1 -1
View File
@@ -18,4 +18,4 @@ build/
*.ipr
*.iws
.gradle/
atlassian-ide-plugin.xml
atlassian-ide-plugin.xml
+2 -2
View File
@@ -7,7 +7,7 @@ _Please refer back to this document as a checklist before issuing any pull reque
Each Spring module is slightly different than another in terms of team size, number of issues, etc. Therefore each project is managed slightly different. You will notice that this document is very similar to the [Spring Framework Contributor guidelines](https://github.com/SpringSource/spring-framework/wiki/Contributor-guidelines). However, there are some subtle differences between the two documents, so please be sure to read this document thoroughly.
# Understand the basics
Not sure what a pull request is, or how to submit one? Take a look at GitHub's excellent [help documentation first](https://help.github.com/articles/using-pull-requests).
Not sure what a pull request is, or how to submit one? Take a look at GitHub's excellent [help documentation first](http://help.github.com/send-pull-requests).
# Search JIRA first; create an issue if necessary
Is there already an issue that addresses your concern? Do a bit of searching in our [JIRA issue tracker](https://jira.springsource.org/browse/SEC) to see if you can find something similar. If not, please create a new issue before submitting a pull request unless the change is not a user facing issue.
@@ -16,7 +16,7 @@ Is there already an issue that addresses your concern? Do a bit of searching in
If you're considering anything more than correcting a typo or fixing a minor bug , please discuss it on the [Spring Security forums](http://forum.springsource.org/forumdisplay.php?33-Security) before submitting a pull request. We're happy to provide guidance but please spend an hour or two researching the subject on your own including searching the forums for prior discussions.
# Sign the Contributor License Agreement
If you have not previously done so, please fill out and submit the [SpringSource CLA form](https://support.springsource.com/spring_committer_signup). You'll receive a token when this process is complete. Keep track of this, you may be asked for it later!
If you have not previously done so, please fill out and submit the SpringSource CLA form. You'll receive a token when this process is complete. Keep track of this, you may be asked for it later!
* For **Project** select _Spring Security_
* For **Project Lead** enter _Rob Winch_
-58
View File
@@ -1,58 +0,0 @@
= Spring Security
Spring Security provides security services for the http://docs.spring.io[Spring IO Platform]. Spring Security 3.1 requires Spring 3.0.3 as
a minimum and also requires Java 5.
For a detailed list of features and access to the latest release, please visit http://spring.io/projects[Spring projects].
== Downloading Artifacts
See https://github.com/spring-projects/spring-framework/wiki/Downloading-Spring-artifacts[downloading Spring artifacts] for Maven repository information.
== Documentation
Be sure to read the http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/[Spring Security Reference].
Extensive JavaDoc for the Spring Security code is also available in the http://docs.spring.io/spring-security/site/docs/current/apidocs/[Spring Security API Documentation].
== Quick Start
We recommend you visit http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/[Spring Security Reference] and read the "Getting Started" page.
== Building from Source
Spring Security uses a http://gradle.org[Gradle]-based build system.
In the instructions below, http://vimeo.com/34436402[`./gradlew`] is invoked from the root of the source tree and serves as
a cross-platform, self-contained bootstrap mechanism for the build.
=== Prerequisites
http://help.github.com/set-up-git-redirect[Git] and the http://www.oracle.com/technetwork/java/javase/downloads[JDK7 build].
Be sure that your `JAVA_HOME` environment variable points to the `jdk1.7.0` folder extracted from the JDK download.
=== Check out sources
[indent=0]
----
git clone git@github.com:spring-projects/spring-security.git
----
=== Install all spring-\* jars into your local Maven cache
[indent=0]
----
./gradlew install
----
=== Compile and test; build all jars, distribution zips, and docs
[indent=0]
----
./gradlew build
----
Discover more commands with `./gradlew tasks`.
See also the https://github.com/spring-projects/spring-framework/wiki/Gradle-build-and-release-FAQ[Gradle build and release FAQ].
== Getting Support
Check out the http://stackoverflow.com/questions/tagged/spring-security[Spring Security tags on Stack Overflow].
http://spring.io/services[Commercial support] is available too.
== Contributing
http://help.github.com/send-pull-requests[Pull requests] are welcome; see the https://github.com/spring-projects/spring-security/blob/master/CONTRIBUTING.md[contributor guidelines] for details.
== License
Spring Security is Open Source software released under the
http://www.apache.org/licenses/LICENSE-2.0.html[Apache 2.0 license].
+2 -4
View File
@@ -2,18 +2,16 @@
dependencies {
compile project(':spring-security-core'),
springCoreDependency,
'aopalliance:aopalliance:1.0',
"net.sf.ehcache:ehcache:$ehcacheVersion",
"org.springframework:spring-aop:$springVersion",
"org.springframework:spring-context:$springVersion",
"org.springframework:spring-tx:$springVersion",
"org.springframework:spring-jdbc:$springVersion"
optional "net.sf.ehcache:ehcache-core:$ehcacheVersion"
testCompile "org.springframework:spring-beans:$springVersion",
"org.springframework:spring-context-support:$springVersion",
"org.springframework:spring-test:$springVersion"
testRuntime "org.hsqldb:hsqldb:$hsqlVersion"
testRuntime "hsqldb:hsqldb:$hsqlVersion"
}
-163
View File
@@ -1,163 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-acl</artifactId>
<version>3.2.9.RELEASE</version>
<name>spring-security-acl</name>
<description>spring-security-acl</description>
<url>http://spring.io/spring-security</url>
<organization>
<name>spring.io</name>
<url>http://spring.io/</url>
</organization>
<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<id>rwinch</id>
<name>Rob Winch</name>
<email>rwinch@gopivotal.com</email>
</developer>
</developers>
<scm>
<connection>scm:git:git://github.com/spring-projects/spring-security</connection>
<developerConnection>scm:git:git://github.com/spring-projects/spring-security</developerConnection>
<url>https://github.com/spring-projects/spring-security</url>
</scm>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>3.2.9.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>3.2.13.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.2.13.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>3.2.13.RELEASE</version>
<scope>compile</scope>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>3.2.13.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>3.2.13.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>1.7.2</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>0.9.29</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-assert</artifactId>
<version>1.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.3.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>3.2.13.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>3.2.13.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>3.2.13.RELEASE</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
@@ -3,7 +3,6 @@ package org.springframework.security.acls;
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -122,9 +121,8 @@ public class AclPermissionEvaluator implements PermissionEvaluator {
try {
p = permissionFactory.buildFromName(permString);
}
catch (IllegalArgumentException notfound) {
p = permissionFactory.buildFromName(permString.toUpperCase(Locale.ENGLISH));
} catch(IllegalArgumentException notfound) {
p = permissionFactory.buildFromName(permString.toUpperCase());
}
if (p != null) {
@@ -31,14 +31,10 @@ import org.springframework.util.Assert;
/**
* Default implementation of {@link AclAuthorizationStrategy}.
* <p>
* Permission will be granted if at least one of the following conditions is true for the current
* principal.
* <ul>
* <li> is the owner (as defined by the ACL). </li>
* <li> holds the relevant system-wide {@link GrantedAuthority} injected into the
* constructor. </li>
* <li> has {@link BasePermission#ADMINISTRATION} permission (as defined by the ACL). </li>
* </ul>
* Permission will be granted provided the current principal is either the owner (as defined by the ACL), has
* {@link BasePermission#ADMINISTRATION} (as defined by the ACL and via a {@link Sid} retrieved for the current
* principal via {@link #sidRetrievalStrategy}), or if the current principal holds the relevant system-wide
* {@link GrantedAuthority} and injected into the constructor.
*
* @author Ben Alex
*/
@@ -98,7 +98,7 @@ public abstract class AclFormattingUtils {
}
private static String printBinary(int i, char on, char off) {
String s = Integer.toBinaryString(i);
String s = Integer.toString(i, 2);
String pattern = Permission.THIRTY_TWO_RESERVED_OFF;
String temp2 = pattern.substring(0, pattern.length() - s.length()) + s;
@@ -1,130 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.domain;
import org.springframework.cache.Cache;
import org.springframework.security.acls.model.AclCache;
import org.springframework.security.acls.model.MutableAcl;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.PermissionGrantingStrategy;
import org.springframework.security.util.FieldUtils;
import org.springframework.util.Assert;
import java.io.Serializable;
/**
* Simple implementation of {@link org.springframework.security.acls.model.AclCache} that delegates to {@link Cache} implementation.
* <p>
* Designed to handle the transient fields in {@link org.springframework.security.acls.domain.AclImpl}. Note that this implementation assumes all
* {@link org.springframework.security.acls.domain.AclImpl} instances share the same {@link org.springframework.security.acls.model.PermissionGrantingStrategy} and {@link org.springframework.security.acls.domain.AclAuthorizationStrategy}
* instances.
*
* @author Marten Deinum
* @since 3.2
*/
public class SpringCacheBasedAclCache implements AclCache {
//~ Instance fields ================================================================================================
private final Cache cache;
private PermissionGrantingStrategy permissionGrantingStrategy;
private AclAuthorizationStrategy aclAuthorizationStrategy;
//~ Constructors ===================================================================================================
public SpringCacheBasedAclCache(Cache cache, PermissionGrantingStrategy permissionGrantingStrategy,
AclAuthorizationStrategy aclAuthorizationStrategy) {
Assert.notNull(cache, "Cache required");
Assert.notNull(permissionGrantingStrategy, "PermissionGrantingStrategy required");
Assert.notNull(aclAuthorizationStrategy, "AclAuthorizationStrategy required");
this.cache = cache;
this.permissionGrantingStrategy = permissionGrantingStrategy;
this.aclAuthorizationStrategy = aclAuthorizationStrategy;
}
//~ Methods ========================================================================================================
public void evictFromCache(Serializable pk) {
Assert.notNull(pk, "Primary key (identifier) required");
MutableAcl acl = getFromCache(pk);
if (acl != null) {
cache.evict(acl.getId());
cache.evict(acl.getObjectIdentity());
}
}
public void evictFromCache(ObjectIdentity objectIdentity) {
Assert.notNull(objectIdentity, "ObjectIdentity required");
MutableAcl acl = getFromCache(objectIdentity);
if (acl != null) {
cache.evict(acl.getId());
cache.evict(acl.getObjectIdentity());
}
}
public MutableAcl getFromCache(ObjectIdentity objectIdentity) {
Assert.notNull(objectIdentity, "ObjectIdentity required");
return getFromCache((Object)objectIdentity);
}
public MutableAcl getFromCache(Serializable pk) {
Assert.notNull(pk, "Primary key (identifier) required");
return getFromCache((Object)pk);
}
public void putInCache(MutableAcl acl) {
Assert.notNull(acl, "Acl required");
Assert.notNull(acl.getObjectIdentity(), "ObjectIdentity required");
Assert.notNull(acl.getId(), "ID required");
if ((acl.getParentAcl() != null) && (acl.getParentAcl() instanceof MutableAcl)) {
putInCache((MutableAcl) acl.getParentAcl());
}
cache.put(acl.getObjectIdentity(), acl);
cache.put(acl.getId(), acl);
}
private MutableAcl getFromCache(Object key) {
Cache.ValueWrapper element = cache.get(key);
if (element == null) {
return null;
}
return initializeTransientFields((MutableAcl) element.get());
}
private MutableAcl initializeTransientFields(MutableAcl value) {
if (value instanceof AclImpl) {
FieldUtils.setProtectedFieldValue("aclAuthorizationStrategy", value, this.aclAuthorizationStrategy);
FieldUtils.setProtectedFieldValue("permissionGrantingStrategy", value, this.permissionGrantingStrategy);
}
if (value.getParentAcl() != null) {
initializeTransientFields((MutableAcl) value.getParentAcl());
}
return value;
}
public void clearCache() {
cache.clear();
}
}
@@ -119,9 +119,6 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
* @param acl containing the ACEs to insert
*/
protected void createEntries(final MutableAcl acl) {
if(acl.getEntries().isEmpty()) {
return;
}
jdbcTemplate.batchUpdate(insertEntry,
new BatchPreparedStatementSetter() {
public int getBatchSize() {
+2 -2
View File
@@ -5,12 +5,12 @@
-- drop table acl_class;
-- drop table acl_sid;
create table acl_sid(
id bigint generated by default as identity(start with 100) not null primary key,
principal boolean not null,
sid varchar_ignorecase(100) not null,
constraint unique_uk_1 unique(sid,principal)
);
constraint unique_uk_1 unique(sid,principal));
create table acl_class(
id bigint generated by default as identity(start with 100) not null primary key,
@@ -1,46 +0,0 @@
-- ACL Schema SQL for MySQL 5.5+ / MariaDB equivalent
-- drop table acl_entry;
-- drop table acl_object_identity;
-- drop table acl_class;
-- drop table acl_sid;
CREATE TABLE acl_sid (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
principal BOOLEAN NOT NULL,
sid VARCHAR(100) NOT NULL,
UNIQUE KEY unique_acl_sid (sid, principal)
) ENGINE=InnoDB;
CREATE TABLE acl_class (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
class VARCHAR(100) NOT NULL,
UNIQUE KEY uk_acl_class (class)
) ENGINE=InnoDB;
CREATE TABLE acl_object_identity (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
object_id_class BIGINT UNSIGNED NOT NULL,
object_id_identity BIGINT NOT NULL,
parent_object BIGINT UNSIGNED,
owner_sid BIGINT UNSIGNED,
entries_inheriting BOOLEAN NOT NULL,
UNIQUE KEY uk_acl_object_identity (object_id_class, object_id_identity),
CONSTRAINT fk_acl_object_identity_parent FOREIGN KEY (parent_object) REFERENCES acl_object_identity (id),
CONSTRAINT fk_acl_object_identity_class FOREIGN KEY (object_id_class) REFERENCES acl_class (id),
CONSTRAINT fk_acl_object_identity_owner FOREIGN KEY (owner_sid) REFERENCES acl_sid (id)
) ENGINE=InnoDB;
CREATE TABLE acl_entry (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
acl_object_identity BIGINT UNSIGNED NOT NULL,
ace_order INTEGER NOT NULL,
sid BIGINT UNSIGNED NOT NULL,
mask INTEGER UNSIGNED NOT NULL,
granting BOOLEAN NOT NULL,
audit_success BOOLEAN NOT NULL,
audit_failure BOOLEAN NOT NULL,
UNIQUE KEY unique_acl_entry (acl_object_identity, ace_order),
CONSTRAINT fk_acl_entry_object FOREIGN KEY (acl_object_identity) REFERENCES acl_object_identity (id),
CONSTRAINT fk_acl_entry_acl FOREIGN KEY (sid) REFERENCES acl_sid (id)
) ENGINE=InnoDB;
@@ -1,82 +0,0 @@
-- ACL Schema SQL for Oracle Database 10g+
-- drop trigger acl_sid_id_trigger;
-- drop trigger acl_class_id_trigger;
-- drop trigger acl_object_identity_id_trigger;
-- drop trigger acl_entry_id_trigger;
-- drop sequence acl_sid_sequence;
-- drop sequence acl_class_sequence;
-- drop sequence acl_object_identity_sequence;
-- drop sequence acl_entry_sequence;
-- drop table acl_entry;
-- drop table acl_object_identity;
-- drop table acl_class;
-- drop table acl_sid;
CREATE TABLE acl_sid (
id NUMBER(38) NOT NULL PRIMARY KEY,
principal NUMBER(1) NOT NULL CHECK (principal in (0, 1)),
sid NVARCHAR2(100) NOT NULL,
CONSTRAINT unique_acl_sid UNIQUE (sid, principal)
);
CREATE SEQUENCE acl_sid_sequence START WITH 1 INCREMENT BY 1 NOMAXVALUE;
CREATE OR REPLACE TRIGGER acl_sid_id_trigger
BEFORE INSERT ON acl_sid
FOR EACH ROW
BEGIN
SELECT acl_sid_sequence.nextval INTO :new.id FROM dual;
END;
CREATE TABLE acl_class (
id NUMBER(38) NOT NULL PRIMARY KEY,
class NVARCHAR2(100) NOT NULL,
CONSTRAINT uk_acl_class UNIQUE (class)
);
CREATE SEQUENCE acl_class_sequence START WITH 1 INCREMENT BY 1 NOMAXVALUE;
CREATE OR REPLACE TRIGGER acl_class_id_trigger
BEFORE INSERT ON acl_class
FOR EACH ROW
BEGIN
SELECT acl_class_sequence.nextval INTO :new.id FROM dual;
END;
CREATE TABLE acl_object_identity (
id NUMBER(38) NOT NULL PRIMARY KEY,
object_id_class NUMBER(38) NOT NULL,
object_id_identity NUMBER(38) NOT NULL,
parent_object NUMBER(38),
owner_sid NUMBER(38),
entries_inheriting NUMBER(1) NOT NULL CHECK (entries_inheriting in (0, 1)),
CONSTRAINT uk_acl_object_identity UNIQUE (object_id_class, object_id_identity),
CONSTRAINT fk_acl_object_identity_parent FOREIGN KEY (parent_object) REFERENCES acl_object_identity (id),
CONSTRAINT fk_acl_object_identity_class FOREIGN KEY (object_id_class) REFERENCES acl_class (id),
CONSTRAINT fk_acl_object_identity_owner FOREIGN KEY (owner_sid) REFERENCES acl_sid (id)
);
CREATE SEQUENCE acl_object_identity_sequence START WITH 1 INCREMENT BY 1 NOMAXVALUE;
CREATE OR REPLACE TRIGGER acl_object_identity_id_trigger
BEFORE INSERT ON acl_object_identity
FOR EACH ROW
BEGIN
SELECT acl_object_identity_sequence.nextval INTO :new.id FROM dual;
END;
CREATE TABLE acl_entry (
id NUMBER(38) NOT NULL PRIMARY KEY,
acl_object_identity NUMBER(38) NOT NULL,
ace_order INTEGER NOT NULL,
sid NUMBER(38) NOT NULL,
mask INTEGER NOT NULL,
granting NUMBER(1) NOT NULL CHECK (granting in (0, 1)),
audit_success NUMBER(1) NOT NULL CHECK (audit_success in (0, 1)),
audit_failure NUMBER(1) NOT NULL CHECK (audit_failure in (0, 1)),
CONSTRAINT unique_acl_entry UNIQUE (acl_object_identity, ace_order),
CONSTRAINT fk_acl_entry_object FOREIGN KEY (acl_object_identity) REFERENCES acl_object_identity (id),
CONSTRAINT fk_acl_entry_acl FOREIGN KEY (sid) REFERENCES acl_sid (id)
);
CREATE SEQUENCE acl_entry_sequence START WITH 1 INCREMENT BY 1 NOMAXVALUE;
CREATE OR REPLACE TRIGGER acl_entry_id_trigger
BEFORE INSERT ON acl_entry
FOR EACH ROW
BEGIN
SELECT acl_entry_sequence.nextval INTO :new.id FROM dual;
END;
@@ -1,46 +0,0 @@
-- ACL Schema SQL for Microsoft SQL Server 2008+
-- drop table acl_entry;
-- drop table acl_object_identity;
-- drop table acl_class;
-- drop table acl_sid;
CREATE TABLE acl_sid (
id BIGINT NOT NULL IDENTITY PRIMARY KEY,
principal BIT NOT NULL,
sid VARCHAR(100) NOT NULL,
CONSTRAINT unique_acl_sid UNIQUE (sid, principal)
);
CREATE TABLE acl_class (
id BIGINT NOT NULL IDENTITY PRIMARY KEY,
class VARCHAR(100) NOT NULL,
CONSTRAINT uk_acl_class UNIQUE (class)
);
CREATE TABLE acl_object_identity (
id BIGINT NOT NULL IDENTITY PRIMARY KEY,
object_id_class BIGINT NOT NULL,
object_id_identity BIGINT NOT NULL,
parent_object BIGINT,
owner_sid BIGINT,
entries_inheriting BIT NOT NULL,
CONSTRAINT uk_acl_object_identity UNIQUE (object_id_class, object_id_identity),
CONSTRAINT fk_acl_object_identity_parent FOREIGN KEY (parent_object) REFERENCES acl_object_identity (id),
CONSTRAINT fk_acl_object_identity_class FOREIGN KEY (object_id_class) REFERENCES acl_class (id),
CONSTRAINT fk_acl_object_identity_owner FOREIGN KEY (owner_sid) REFERENCES acl_sid (id)
);
CREATE TABLE acl_entry (
id BIGINT NOT NULL IDENTITY PRIMARY KEY,
acl_object_identity BIGINT NOT NULL,
ace_order INTEGER NOT NULL,
sid BIGINT NOT NULL,
mask INTEGER NOT NULL,
granting BIT NOT NULL,
audit_success BIT NOT NULL,
audit_failure BIT NOT NULL,
CONSTRAINT unique_acl_entry UNIQUE (acl_object_identity, ace_order),
CONSTRAINT fk_acl_entry_object FOREIGN KEY (acl_object_identity) REFERENCES acl_object_identity (id),
CONSTRAINT fk_acl_entry_acl FOREIGN KEY (sid) REFERENCES acl_sid (id)
);
@@ -124,12 +124,4 @@ public class AclFormattingUtilsTests extends TestCase {
Assert.assertEquals("............................xxxx", AclFormattingUtils.printBinary(15, 'x'));
}
public void testPrintBinaryNegative() {
Assert.assertEquals("*...............................", AclFormattingUtils.printBinary(0x80000000));
}
public void testPrintBinaryMinusOne() {
Assert.assertEquals("********************************", AclFormattingUtils.printBinary(0xffffffff));
}
}
@@ -4,8 +4,6 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
import java.util.Locale;
import org.junit.Test;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.model.AclService;
@@ -38,26 +36,4 @@ public class AclPermissionEvaluatorTests {
assertTrue(pe.hasPermission(mock(Authentication.class), new Object(), "READ"));
}
@Test
public void resolvePermissionNonEnglishLocale() {
Locale systemLocale = Locale.getDefault();
Locale.setDefault(new Locale("tr"));
AclService service = mock(AclService.class);
AclPermissionEvaluator pe = new AclPermissionEvaluator(service);
ObjectIdentity oid = mock(ObjectIdentity.class);
ObjectIdentityRetrievalStrategy oidStrategy = mock(ObjectIdentityRetrievalStrategy.class);
when(oidStrategy.getObjectIdentity(anyObject())).thenReturn(oid);
pe.setObjectIdentityRetrievalStrategy(oidStrategy);
pe.setSidRetrievalStrategy(mock(SidRetrievalStrategy.class));
Acl acl = mock(Acl.class);
when(service.readAclById(any(ObjectIdentity.class), anyList())).thenReturn(acl);
when(acl.isGranted(anyList(), anyList(), eq(false))).thenReturn(true);
assertTrue(pe.hasPermission(mock(Authentication.class), new Object(), "write"));
Locale.setDefault(systemLocale);
}
}
@@ -50,7 +50,7 @@ public class BasicLookupStrategyTests {
//~ Methods ========================================================================================================
@BeforeClass
public static void initCacheManaer() {
cacheManager = CacheManager.create();
cacheManager = new CacheManager();
cacheManager.addCache(new Cache("basiclookuptestcache", 500, false, false, 30, 30));
}
@@ -1,28 +1,11 @@
package org.springframework.security.acls.jdbc;
import static org.mockito.Mockito.*;
import static org.junit.Assert.*;
import static org.fest.assertions.Assertions.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.List;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.junit.*;
import org.springframework.security.acls.domain.AclAuthorizationStrategy;
import org.springframework.security.acls.domain.AclAuthorizationStrategyImpl;
import org.springframework.security.acls.domain.AclImpl;
@@ -36,43 +19,50 @@ import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.util.FieldUtils;
import org.springframework.test.util.ReflectionTestUtils;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.*;
/**
* Tests {@link EhCacheBasedAclCache}
*
* @author Andrei Stefan
*/
@RunWith(MockitoJUnitRunner.class)
public class EhCacheBasedAclCacheTests {
private static final String TARGET_CLASS = "org.springframework.security.acls.TargetObject";
@Mock
private Ehcache cache;
@Captor
private ArgumentCaptor<Element> element;
private static CacheManager cacheManager;
private EhCacheBasedAclCache myCache;
@BeforeClass
public static void initCacheManaer() {
cacheManager = new CacheManager();
// Use disk caching immediately (to test for serialization issue reported in SEC-527)
cacheManager.addCache(new Cache("ehcachebasedacltests", 0, true, false, 600, 300));
}
private MutableAcl acl;
@Before
public void setup() {
myCache = new EhCacheBasedAclCache(cache);
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(100));
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority("ROLE_AUDITING"),
new SimpleGrantedAuthority("ROLE_GENERAL"));
acl = new AclImpl(identity, Long.valueOf(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
@AfterClass
public static void shutdownCacheManager() {
cacheManager.removalAll();
cacheManager.shutdown();
}
@After
public void cleanup() {
public void clearContext() {
SecurityContextHolder.clearContext();
}
private Ehcache getCache() {
Ehcache cache = cacheManager.getCache("ehcachebasedacltests");
cache.removeAll();
return cache;
}
@Test(expected=IllegalArgumentException.class)
public void constructorRejectsNullParameters() throws Exception {
new EhCacheBasedAclCache(null);
@@ -80,6 +70,9 @@ public class EhCacheBasedAclCacheTests {
@Test
public void methodsRejectNullParameters() throws Exception {
Ehcache cache = new MockEhcache();
EhCacheBasedAclCache myCache = new EhCacheBasedAclCache(cache);
try {
Serializable id = null;
myCache.evictFromCache(id);
@@ -129,6 +122,12 @@ public class EhCacheBasedAclCacheTests {
// SEC-527
@Test
public void testDiskSerializationOfMutableAclObjectInstance() throws Exception {
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(100));
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority("ROLE_AUDITING"),
new SimpleGrantedAuthority("ROLE_GENERAL"));
MutableAcl acl = new AclImpl(identity, Long.valueOf(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
// Serialization test
File file = File.createTempFile("SEC_TEST", ".object");
FileOutputStream fos = new FileOutputStream(file);
@@ -151,117 +150,111 @@ public class EhCacheBasedAclCacheTests {
}
@Test
public void clearCache() throws Exception {
myCache.clearCache();
public void cacheOperationsAclWithoutParent() throws Exception {
Ehcache cache = getCache();
EhCacheBasedAclCache myCache = new EhCacheBasedAclCache(cache);
verify(cache).removeAll();
}
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(100));
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority("ROLE_AUDITING"),
new SimpleGrantedAuthority("ROLE_GENERAL"));
MutableAcl acl = new AclImpl(identity, Long.valueOf(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
@Test
public void putInCache() throws Exception {
assertEquals(0, cache.getDiskStoreSize());
myCache.putInCache(acl);
assertEquals(cache.getSize(), 2);
assertEquals(2, cache.getDiskStoreSize());
assertTrue(cache.isElementOnDisk(acl.getObjectIdentity()));
assertFalse(cache.isElementInMemory(acl.getObjectIdentity()));
verify(cache, times(2)).put(element.capture());
assertThat(element.getValue().getKey()).isEqualTo(acl.getId());
assertThat(element.getValue().getObjectValue()).isEqualTo(acl);
assertThat(element.getAllValues().get(0).getKey()).isEqualTo(acl.getObjectIdentity());
assertThat(element.getAllValues().get(0).getObjectValue()).isEqualTo(acl);
// Check we can get from cache the same objects we put in
assertEquals(myCache.getFromCache(Long.valueOf(1)), acl);
assertEquals(myCache.getFromCache(identity), acl);
// Put another object in cache
ObjectIdentity identity2 = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(101));
MutableAcl acl2 = new AclImpl(identity2, Long.valueOf(2), aclAuthorizationStrategy, new ConsoleAuditLogger());
myCache.putInCache(acl2);
assertEquals(cache.getSize(), 4);
assertEquals(4, cache.getDiskStoreSize());
// Try to evict an entry that doesn't exist
myCache.evictFromCache(Long.valueOf(3));
myCache.evictFromCache(new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(102)));
assertEquals(cache.getSize(), 4);
assertEquals(4, cache.getDiskStoreSize());
myCache.evictFromCache(Long.valueOf(1));
assertEquals(cache.getSize(), 2);
assertEquals(2, cache.getDiskStoreSize());
// Check the second object inserted
assertEquals(myCache.getFromCache(Long.valueOf(2)), acl2);
assertEquals(myCache.getFromCache(identity2), acl2);
myCache.evictFromCache(identity2);
assertEquals(cache.getSize(), 0);
}
@SuppressWarnings("unchecked")
@Test
public void putInCacheAclWithParent() throws Exception {
public void cacheOperationsAclWithParent() throws Exception {
Ehcache cache = getCache();
EhCacheBasedAclCache myCache = new EhCacheBasedAclCache(cache);
Authentication auth = new TestingAuthenticationToken("user", "password", "ROLE_GENERAL");
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(1));
ObjectIdentity identityParent = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(2));
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority("ROLE_AUDITING"),
new SimpleGrantedAuthority("ROLE_GENERAL"));
MutableAcl acl = new AclImpl(identity, Long.valueOf(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
MutableAcl parentAcl = new AclImpl(identityParent, Long.valueOf(2), aclAuthorizationStrategy, new ConsoleAuditLogger());
acl.setParent(parentAcl);
assertEquals(0, cache.getDiskStoreSize());
myCache.putInCache(acl);
assertEquals(cache.getSize(), 4);
assertEquals(4, cache.getDiskStoreSize());
assertTrue(cache.isElementOnDisk(acl.getObjectIdentity()));
assertTrue(cache.isElementOnDisk(Long.valueOf(1)));
assertFalse(cache.isElementInMemory(acl.getObjectIdentity()));
assertFalse(cache.isElementInMemory(Long.valueOf(1)));
cache.flush();
// Wait for the spool to be written to disk (it's asynchronous)
Map spool = (Map) FieldUtils.getFieldValue(cache, "diskStore.spool");
verify(cache, times(4)).put(element.capture());
while(spool.size() > 0) {
Thread.sleep(50);
}
List<Element> allValues = element.getAllValues();
assertThat(allValues.get(0).getKey()).isEqualTo(parentAcl.getObjectIdentity());
assertThat(allValues.get(0).getObjectValue()).isEqualTo(parentAcl);
assertThat(allValues.get(1).getKey()).isEqualTo(parentAcl.getId());
assertThat(allValues.get(1).getObjectValue()).isEqualTo(parentAcl);
assertThat(allValues.get(2).getKey()).isEqualTo(acl.getObjectIdentity());
assertThat(allValues.get(2).getObjectValue()).isEqualTo(acl);
assertThat(allValues.get(3).getKey()).isEqualTo(acl.getId());
assertThat(allValues.get(3).getObjectValue()).isEqualTo(acl);
// Check we can get from cache the same objects we put in
AclImpl aclFromCache = (AclImpl) myCache.getFromCache(Long.valueOf(1));
// For the checks on transient fields, we need to be sure that the object is being loaded from the cache,
// not from the ehcache spool or elsewhere...
assertFalse(acl == aclFromCache);
assertEquals(acl, aclFromCache);
// SEC-951 check transient fields are set on parent
assertNotNull(FieldUtils.getFieldValue(aclFromCache.getParentAcl(), "aclAuthorizationStrategy"));
assertNotNull(FieldUtils.getFieldValue(aclFromCache.getParentAcl(), "permissionGrantingStrategy"));
assertEquals(acl, myCache.getFromCache(identity));
assertNotNull(FieldUtils.getFieldValue(aclFromCache, "aclAuthorizationStrategy"));
AclImpl parentAclFromCache = (AclImpl) myCache.getFromCache(Long.valueOf(2));
assertEquals(parentAcl, parentAclFromCache);
assertNotNull(FieldUtils.getFieldValue(parentAclFromCache, "aclAuthorizationStrategy"));
assertEquals(parentAcl, myCache.getFromCache(identityParent));
}
@Test
public void getFromCacheSerializable() throws Exception {
when(cache.get(acl.getId())).thenReturn(new Element(acl.getId(),acl));
//~ Inner Classes ==================================================================================================
assertThat(myCache.getFromCache(acl.getId())).isEqualTo(acl);
}
@Test
public void getFromCacheSerializablePopulatesTransient() throws Exception {
when(cache.get(acl.getId())).thenReturn(new Element(acl.getId(),acl));
myCache.putInCache(acl);
ReflectionTestUtils.setField(acl, "permissionGrantingStrategy", null);
ReflectionTestUtils.setField(acl, "aclAuthorizationStrategy", null);
MutableAcl fromCache = myCache.getFromCache(acl.getId());
assertThat(ReflectionTestUtils.getField(fromCache, "aclAuthorizationStrategy")).isNotNull();
assertThat(ReflectionTestUtils.getField(fromCache, "permissionGrantingStrategy")).isNotNull();
}
@Test
public void getFromCacheObjectIdentity() throws Exception {
when(cache.get(acl.getId())).thenReturn(new Element(acl.getId(),acl));
assertThat(myCache.getFromCache(acl.getId())).isEqualTo(acl);
}
@Test
public void getFromCacheObjectIdentityPopulatesTransient() throws Exception {
when(cache.get(acl.getObjectIdentity())).thenReturn(new Element(acl.getId(),acl));
myCache.putInCache(acl);
ReflectionTestUtils.setField(acl, "permissionGrantingStrategy", null);
ReflectionTestUtils.setField(acl, "aclAuthorizationStrategy", null);
MutableAcl fromCache = myCache.getFromCache(acl.getObjectIdentity());
assertThat(ReflectionTestUtils.getField(fromCache, "aclAuthorizationStrategy")).isNotNull();
assertThat(ReflectionTestUtils.getField(fromCache, "permissionGrantingStrategy")).isNotNull();
}
@Test
public void evictCacheSerializable() throws Exception {
when(cache.get(acl.getObjectIdentity())).thenReturn(new Element(acl.getId(),acl));
myCache.evictFromCache(acl.getObjectIdentity());
verify(cache).remove(acl.getId());
verify(cache).remove(acl.getObjectIdentity());
}
@Test
public void evictCacheObjectIdentity() throws Exception {
when(cache.get(acl.getId())).thenReturn(new Element(acl.getId(),acl));
myCache.evictFromCache(acl.getId());
verify(cache).remove(acl.getId());
verify(cache).remove(acl.getObjectIdentity());
private class MockEhcache extends Cache {
public MockEhcache() {
super("cache", 0, true, true, 0, 0);
}
}
}
@@ -1,142 +0,0 @@
package org.springframework.security.acls.jdbc;
import org.junit.After;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.security.acls.domain.*;
import org.springframework.security.acls.model.MutableAcl;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.PermissionGrantingStrategy;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.util.FieldUtils;
import java.util.Map;
import static org.junit.Assert.*;
/**
* Tests {@link org.springframework.security.acls.domain.SpringCacheBasedAclCache}
*
* @author Marten Deinum
*/
public class SpringCacheBasedAclCacheTests {
private static final String TARGET_CLASS = "org.springframework.security.acls.TargetObject";
private static CacheManager cacheManager;
@BeforeClass
public static void initCacheManaer() {
cacheManager = new ConcurrentMapCacheManager();
// Use disk caching immediately (to test for serialization issue reported in SEC-527)
cacheManager.getCache("springcasebasedacltests");
}
@After
public void clearContext() {
SecurityContextHolder.clearContext();
}
private Cache getCache() {
Cache cache = cacheManager.getCache("springcasebasedacltests");
cache.clear();
return cache;
}
@Test(expected=IllegalArgumentException.class)
public void constructorRejectsNullParameters() throws Exception {
new SpringCacheBasedAclCache(null, null, null);
}
@SuppressWarnings("rawtypes")
@Test
public void cacheOperationsAclWithoutParent() throws Exception {
Cache cache = getCache();
Map realCache = (Map) cache.getNativeCache();
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(100));
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority("ROLE_AUDITING"),
new SimpleGrantedAuthority("ROLE_GENERAL"));
AuditLogger auditLogger = new ConsoleAuditLogger();
PermissionGrantingStrategy permissionGrantingStrategy = new DefaultPermissionGrantingStrategy(auditLogger);
SpringCacheBasedAclCache myCache = new SpringCacheBasedAclCache(cache, permissionGrantingStrategy, aclAuthorizationStrategy);
MutableAcl acl = new AclImpl(identity, Long.valueOf(1), aclAuthorizationStrategy, auditLogger);
assertEquals(0, realCache.size());
myCache.putInCache(acl);
// Check we can get from cache the same objects we put in
assertEquals(myCache.getFromCache(Long.valueOf(1)), acl);
assertEquals(myCache.getFromCache(identity), acl);
// Put another object in cache
ObjectIdentity identity2 = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(101));
MutableAcl acl2 = new AclImpl(identity2, Long.valueOf(2), aclAuthorizationStrategy, new ConsoleAuditLogger());
myCache.putInCache(acl2);
// Try to evict an entry that doesn't exist
myCache.evictFromCache(Long.valueOf(3));
myCache.evictFromCache(new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(102)));
assertEquals(realCache.size(), 4);
myCache.evictFromCache(Long.valueOf(1));
assertEquals(realCache.size(), 2);
// Check the second object inserted
assertEquals(myCache.getFromCache(Long.valueOf(2)), acl2);
assertEquals(myCache.getFromCache(identity2), acl2);
myCache.evictFromCache(identity2);
assertEquals(realCache.size(), 0);
}
@SuppressWarnings("rawtypes")
@Test
public void cacheOperationsAclWithParent() throws Exception {
Cache cache = getCache();
Map realCache = (Map) cache.getNativeCache();
Authentication auth = new TestingAuthenticationToken("user", "password", "ROLE_GENERAL");
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(1));
ObjectIdentity identityParent = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(2));
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority("ROLE_AUDITING"),
new SimpleGrantedAuthority("ROLE_GENERAL"));
AuditLogger auditLogger = new ConsoleAuditLogger();
PermissionGrantingStrategy permissionGrantingStrategy = new DefaultPermissionGrantingStrategy(auditLogger);
SpringCacheBasedAclCache myCache = new SpringCacheBasedAclCache(cache, permissionGrantingStrategy, aclAuthorizationStrategy);
MutableAcl acl = new AclImpl(identity, Long.valueOf(1), aclAuthorizationStrategy, auditLogger);
MutableAcl parentAcl = new AclImpl(identityParent, Long.valueOf(2), aclAuthorizationStrategy, auditLogger);
acl.setParent(parentAcl);
assertEquals(0, realCache.size());
myCache.putInCache(acl);
assertEquals(realCache.size(), 4);
// Check we can get from cache the same objects we put in
AclImpl aclFromCache = (AclImpl) myCache.getFromCache(Long.valueOf(1));
assertEquals(acl, aclFromCache);
// SEC-951 check transient fields are set on parent
assertNotNull(FieldUtils.getFieldValue(aclFromCache.getParentAcl(), "aclAuthorizationStrategy"));
assertNotNull(FieldUtils.getFieldValue(aclFromCache.getParentAcl(), "permissionGrantingStrategy"));
assertEquals(acl, myCache.getFromCache(identity));
assertNotNull(FieldUtils.getFieldValue(aclFromCache, "aclAuthorizationStrategy"));
AclImpl parentAclFromCache = (AclImpl) myCache.getFromCache(Long.valueOf(2));
assertEquals(parentAcl, parentAclFromCache);
assertNotNull(FieldUtils.getFieldValue(parentAclFromCache, "aclAuthorizationStrategy"));
assertEquals(parentAcl, myCache.getFromCache(identityParent));
}
}
-1
View File
@@ -11,7 +11,6 @@ Ignored-Existing-Headers:
Import-Template:
org.aopalliance.*;version="${aopAllianceRange}",
org.apache.commons.logging.*;version="${cloggingRange}",
org.springframework.cache.*;version="${springRange}";resolution:=optional,
org.springframework.security.core.*;version="${secRange}",
org.springframework.security.access.*;version="${secRange}",
org.springframework.security.util.*;version="${secRange}",
+1 -3
View File
@@ -1,10 +1,8 @@
dependencies {
compile project(':spring-security-core'),
springCoreDependency,
"org.springframework:spring-beans:$springVersion",
"org.springframework:spring-context:$springVersion"
testCompile 'aopalliance:aopalliance:1.0',
"org.springframework:spring-aop:$springVersion"
testCompile 'aopalliance:aopalliance:1.0'
}
-139
View File
@@ -1,139 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-aspects</artifactId>
<version>3.2.9.RELEASE</version>
<name>spring-security-aspects</name>
<description>spring-security-aspects</description>
<url>http://spring.io/spring-security</url>
<organization>
<name>spring.io</name>
<url>http://spring.io/</url>
</organization>
<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<id>rwinch</id>
<name>Rob Winch</name>
<email>rwinch@gopivotal.com</email>
</developer>
</developers>
<scm>
<connection>scm:git:git://github.com/spring-projects/spring-security</connection>
<developerConnection>scm:git:git://github.com/spring-projects/spring-security</developerConnection>
<url>https://github.com/spring-projects/spring-security</url>
</scm>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>3.2.9.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>3.2.13.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.2.13.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>3.2.13.RELEASE</version>
<scope>compile</scope>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.10</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>0.9.29</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-assert</artifactId>
<version>1.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>3.2.13.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>3.2.13.RELEASE</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
-9
View File
@@ -1,9 +0,0 @@
apply plugin: 'maven-bom'
apply from: "$rootDir/gradle/maven-deployment.gradle"
generatePom.enabled = false
sonarRunner.skipProject = true
mavenBom {
projects = coreModuleProjects
}
+19 -105
View File
@@ -1,168 +1,80 @@
import groovy.text.SimpleTemplateEngine
buildscript {
repositories {
maven { url "https://repo.spring.io/plugins-release" }
maven { url "http://repo.springsource.org/plugins-release" }
}
dependencies {
classpath("org.springframework.build.gradle:propdeps-plugin:0.0.6")
classpath("org.springframework.build.gradle:bundlor-plugin:0.1.2")
classpath("org.springframework.build.gradle:spring-io-plugin:0.0.3.RELEASE")
classpath("org.gradle.api.plugins:gradle-tomcat-plugin:1.2.3")
classpath('me.champeau.gradle:gradle-javadoc-hotfix-plugin:0.1')
classpath('org.asciidoctor:asciidoctor-gradle-plugin:0.7.0')
classpath('org.asciidoctor:asciidoctor-java-integration:0.1.4.preview.1')
}
}
apply plugin: 'sonar-runner'
apply plugin: 'base'
description = 'Spring Security'
allprojects {
apply plugin: 'idea'
apply plugin: 'eclipse'
ext.releaseBuild = version.endsWith('RELEASE')
ext.snapshotBuild = version.endsWith('SNAPSHOT')
ext.springVersion = '3.2.13.RELEASE'
ext.spring4Version = '4.1.6.RELEASE'
ext.springLdapVersion = '1.3.2.RELEASE'
ext.springLdap2Version = '2.0.1.RELEASE'
group = 'org.springframework.security'
repositories {
mavenCentral()
maven { url "https://repo.spring.io/libs-snapshot" }
maven { url "https://repo.spring.io/plugins-release" }
maven { url "http://repo.terracotta.org/maven2/" }
}
eclipse.project.name = "${project.name}-3.2.x"
}
sonarRunner {
sonarProperties {
property "sonar.java.coveragePlugin", "jacoco"
property "sonar.projectName", "Spring Security"
property "sonar.jacoco.reportPath", "${buildDir.name}/jacoco.exec"
property "sonar.links.homepage", 'https://www.springsource.org/spring-security'
property "sonar.links.ci", 'https://build.springsource.org/browse/SEC-B32X'
property "sonar.links.issue", 'https://jira.springsource.org/browse/SEC'
property "sonar.links.scm", 'https://github.com/SpringSource/spring-security'
property "sonar.links.scm_dev", 'https://github.com/SpringSource/spring-security.git'
property "sonar.java.coveragePlugin", "jacoco"
mavenCentral()
}
}
// Set up different subproject lists for individual configuration
ext.javaProjects = subprojects.findAll { project -> project.name != 'docs' && project.name != 'manual' && project.name != 'guides' && project.name != 'spring-security-bom' }
ext.javaProjects = subprojects.findAll { project -> project.name != 'docs' && project.name != 'faq' && project.name != 'manual' }
ext.sampleProjects = subprojects.findAll { project -> project.name.startsWith('spring-security-samples') }
ext.itestProjects = subprojects.findAll { project -> project.name.startsWith('itest') }
ext.coreModuleProjects = javaProjects - sampleProjects - itestProjects
ext.aspectjProjects = [project(':spring-security-aspects'), project(':spring-security-samples-aspectj-xml'), project(':spring-security-samples-aspectj-jc')]
ext.aspectjProjects = [project(':spring-security-aspects'), project(':spring-security-samples-aspectj')]
configure(allprojects - javaProjects) {
task afterEclipseImport {
ext.srcFile = file('.classpath')
inputs.file srcFile
outputs.dir srcFile
onlyIf { !srcFile.exists() }
doLast {
srcFile << """<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="output" path="bin"/>
</classpath>
"""
}
}
}
configure(subprojects - coreModuleProjects - project(':spring-security-samples-messages-jc') - project(':spring-security-bom')) {
configure(subprojects - coreModuleProjects) {
tasks.findByPath("artifactoryPublish")?.enabled = false
sonarRunner {
skipProject = true
}
}
configure(javaProjects) {
ext.TOMCAT_GRADLE = "$rootDir/gradle/tomcat.gradle"
ext.WAR_SAMPLE_GRADLE = "$rootDir/gradle/war-sample.gradle"
apply from: "$rootDir/gradle/javaprojects.gradle"
apply from: "$rootDir/gradle/release-checks.gradle"
apply from: "$rootDir/gradle/maven-deployment.gradle"
}
configure(coreModuleProjects) {
// Gives better names in structure101 jar diagram
sourceSets.main.output.classesDir = new File(buildDir, "classes/" + project.name.substring("spring-security".length() + 1))
apply plugin: 'bundlor'
bundlor.expansions = bundlorProperties
apply from: "$rootDir/gradle/maven-deployment.gradle"
apply plugin: 'emma'
apply plugin: 'spring-io'
ext.springIoVersion = project.hasProperty('platformVersion') ? platformVersion : 'latest.integration'
bundlor.doFirst {
def templateText = file("template.mf").text
bundlor.manifestTemplate = new SimpleTemplateEngine().createTemplate(templateText).make(bundlorProperties).toString()
}
configurations {
jacoco //Configuration Group used by Sonar to provide Code Coverage using JaCoCo
}
dependencies {
jacoco "org.jacoco:org.jacoco.agent:0.6.2.201302030002:runtime"
springIoVersions "io.spring.platform:platform-versions:${springIoVersion}@properties"
}
test {
jvmArgs "-javaagent:${configurations.jacoco.asPath}=destfile=${buildDir}/jacoco.exec,includes=${project.group}.*"
}
integrationTest {
jvmArgs "-javaagent:${configurations.jacoco.asPath}=destfile=${buildDir}/jacoco.exec,includes=${project.group}.*"
}
}
configure (aspectjProjects) {
apply plugin: 'java'
apply plugin: 'aspectj'
}
task coreBuild {
dependsOn coreModuleProjects*.tasks*.matching { task -> task.name == 'build' }
}
configure (aspectjProjects) {
apply plugin: 'aspectj'
}
// Task for creating the distro zip
task dist(type: Zip) {
dependsOn subprojects*.tasks*.matching { task -> task.name == 'assemble' || task.name.endsWith('Zip') || task.name.endsWith('generatePom') }
dependsOn subprojects*.tasks*.matching { task -> task.name == 'assemble' || task.name.endsWith('Zip') }
classifier = 'dist'
evaluationDependsOn(':docs')
evaluationDependsOn(':docs:manual')
def zipRootDir = "${project.name}-$version"
into(zipRootDir) {
from(rootDir) {
include '*.adoc'
include '*.txt'
}
into('docs') {
with(project(':docs').apiSpec)
with(project(':docs:manual').spec)
with(project(':docs:guides').spec)
}
into('dist') {
from coreModuleProjects.collect {project -> project.libsDir }
}
sampleProjects.each { project->
into("$zipRootDir/samples/$project.name") {
from(project.projectDir) {
include "src/main/**"
include "pom.xml"
}
}
from project(':spring-security-samples-tutorial').libsDir
from project(':spring-security-samples-contacts').libsDir
}
}
}
@@ -173,6 +85,8 @@ artifacts {
archives project(':docs').schemaZip
}
apply from: "$rootDir/gradle/ide-integration.gradle"
task wrapper(type: Wrapper) {
gradleVersion = '1.12'
gradleVersion = '1.9'
}
+22 -4
View File
@@ -12,8 +12,26 @@ repositories {
}
}
// Docbook Plugin
dependencies {
compile gradleApi()
def fopDeps = [ 'org.apache.xmlgraphics:fop:0.95-1@jar',
'org.apache.xmlgraphics:xmlgraphics-commons:1.3',
'org.apache.xmlgraphics:batik-bridge:1.7@jar',
'org.apache.xmlgraphics:batik-util:1.7@jar',
'org.apache.xmlgraphics:batik-css:1.7@jar',
'org.apache.xmlgraphics:batik-dom:1.7',
'org.apache.xmlgraphics:batik-svg-dom:1.7@jar',
'org.apache.avalon.framework:avalon-framework-api:4.3.1']
groovy localGroovy()
compile gradleApi(),
'xml-resolver:xml-resolver:1.2',
'xerces:xercesImpl:2.9.1',
'saxon:saxon:6.5.3',
'net.java.dev.jets3t:jets3t:0.6.1',
fopDeps
runtime 'net.sf.xslthl:xslthl:2.0.1',
'net.sf.docbook:docbook-xsl:1.75.2:ns-resources@zip'
}
// GAE
@@ -25,10 +43,10 @@ dependencies{
compile "emma:emma:2.0.5312"
}
// Trang
// Bundlor
dependencies {
compile 'com.thaiopensource:trang:20091111',
'net.sourceforge.saxon:saxon:9.1.0.8'
compile 'com.springsource.bundlor:com.springsource.bundlor:1.0.0.RELEASE',
'com.springsource.bundlor:com.springsource.bundlor.blint:1.0.0.RELEASE'
}
task ide(type: Copy) {
@@ -1,19 +0,0 @@
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.plugins.*
import org.gradle.api.tasks.*
import org.gradle.api.tasks.TaskAction
public class MavenBomPlugin implements Plugin<Project> {
static String MAVEN_BOM_TASK_NAME = "mavenBom"
public void apply(Project project) {
project.plugins.apply(JavaPlugin)
project.plugins.apply(MavenPlugin)
project.task(MAVEN_BOM_TASK_NAME, type: MavenBomTask, group: 'Generate', description: 'Configures the pom as a Maven Build of Materials (BOM)')
project.install.dependsOn project.mavenBom
}
}
@@ -1,55 +0,0 @@
import groovy.xml.MarkupBuilder
import org.gradle.api.DefaultTask
import org.gradle.api.Project
import org.gradle.api.tasks.*
public class MavenBomTask extends DefaultTask {
Set<Project> projects
File bomFile
public MavenBomTask() {
this.group = "Generate"
this.description = "Generates a Maven Build of Materials (BOM). See http://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Importing_Dependencies"
this.projects = project.subprojects
this.bomFile = project.file("${->project.buildDir}/maven-bom/${->project.name}-${->project.version}.txt")
}
@TaskAction
public void configureBom() {
project.configurations.archives.artifacts.clear()
bomFile.parentFile.mkdirs()
bomFile.write("Maven Build of Materials (BOM). See http://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Importing_Dependencies")
project.artifacts {
// work around GRADLE-2406 by attaching text artifact
archives(bomFile)
}
project.install {
repositories.mavenInstaller {
pom.whenConfigured {
packaging = "pom"
withXml {
asNode().children().last() + {
delegate.dependencyManagement {
delegate.dependencies {
projects.sort { dep -> "$dep.group:$dep.name" }.each { p ->
delegate.dependency {
delegate.groupId(p.group)
delegate.artifactId(p.name)
delegate.version(p.version)
}
}
}
}
}
}
}
}
}
}
}
@@ -9,7 +9,6 @@ import org.gradle.api.tasks.SourceSet
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.plugins.JavaPlugin
import org.gradle.plugins.ide.eclipse.GenerateEclipseProject
import org.gradle.plugins.ide.eclipse.GenerateEclipseClasspath
import org.gradle.plugins.ide.eclipse.EclipsePlugin
@@ -23,8 +22,6 @@ import org.gradle.plugins.ide.eclipse.model.ProjectDependency
class AspectJPlugin implements Plugin<Project> {
void apply(Project project) {
project.plugins.apply(JavaPlugin)
if (!project.hasProperty('aspectjVersion')) {
throw new GradleException("You must set the property 'aspectjVersion' before applying the aspectj plugin")
}
@@ -33,7 +30,7 @@ class AspectJPlugin implements Plugin<Project> {
project.configurations.add('ajtools')
project.dependencies {
ajtools "org.aspectj:aspectjtools:${project.aspectjVersion}"
optional "org.aspectj:aspectjrt:${project.aspectjVersion}"
compile "org.aspectj:aspectjrt:${project.aspectjVersion}"
}
}
@@ -43,21 +40,19 @@ class AspectJPlugin implements Plugin<Project> {
project.tasks.add(name: 'compileAspect', overwrite: true, description: 'Compiles AspectJ Source', type: Ajc) {
dependsOn project.configurations*.getTaskDependencyFromProjectDependency(true, "compileJava")
dependsOn project.processResources
sourceSet = project.sourceSets.main
inputs.files(sourceSet.allSource)
inputs.files(sourceSet.java.srcDirs)
outputs.dir(sourceSet.output.classesDir)
aspectPath = project.configurations.aspectpath
}
project.tasks.compileJava.deleteAllActions()
project.tasks.compileJava.dependsOn project.tasks.compileAspect
project.tasks.add(name: 'compileTestAspect', overwrite: true, description: 'Compiles AspectJ Test Source', type: Ajc) {
dependsOn project.processTestResources, project.compileJava, project.jar
sourceSet = project.sourceSets.test
inputs.files(sourceSet.allSource)
inputs.files(sourceSet.java.srcDirs)
outputs.dir(sourceSet.output.classesDir)
aspectPath = project.files(project.configurations.aspectpath, project.jar.archivePath)
}
@@ -100,11 +95,7 @@ class Ajc extends DefaultTask {
@TaskAction
def compile() {
logger.info("="*30)
logger.info("="*30)
logger.info("Running ajc ...")
logger.info("classpath: ${sourceSet.compileClasspath.asPath}")
logger.info("srcDirs $sourceSet.java.srcDirs")
ant.taskdef(resource: "org/aspectj/tools/ant/taskdefs/aspectjTaskdefs.properties", classpath: project.configurations.ajtools.asPath)
ant.iajc(classpath: sourceSet.compileClasspath.asPath, fork: 'true', destDir: sourceSet.output.classesDir.absolutePath,
source: project.convention.plugins.java.sourceCompatibility,
@@ -112,7 +103,6 @@ class Ajc extends DefaultTask {
aspectPath: aspectPath.asPath, sourceRootCopyFilter: '**/*.java', showWeaveInfo: 'true') {
sourceroots {
sourceSet.java.srcDirs.each {
logger.info(" sourceRoot $it")
pathelement(location: it.absolutePath)
}
}
@@ -0,0 +1,150 @@
package bundlor
import com.springsource.bundlor.ClassPath
import com.springsource.bundlor.ManifestGenerator
import com.springsource.bundlor.ManifestWriter
import com.springsource.bundlor.blint.ManifestValidator
import com.springsource.bundlor.blint.support.DefaultManifestValidatorContributorsFactory
import com.springsource.bundlor.blint.support.StandardManifestValidator
import com.springsource.bundlor.support.DefaultManifestGeneratorContributorsFactory
import com.springsource.bundlor.support.StandardManifestGenerator
import com.springsource.bundlor.support.classpath.FileSystemClassPath
import com.springsource.bundlor.support.manifestwriter.FileSystemManifestWriter
import com.springsource.bundlor.support.properties.EmptyPropertiesSource
import com.springsource.bundlor.support.properties.FileSystemPropertiesSource
import com.springsource.bundlor.support.properties.PropertiesPropertiesSource
import com.springsource.bundlor.support.properties.PropertiesSource
import com.springsource.bundlor.util.BundleManifestUtils
import com.springsource.util.parser.manifest.ManifestContents
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.file.FileCollection
import org.gradle.api.logging.LogLevel
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
/**
* @author Luke Taylor
*/
class BundlorPlugin implements Plugin<Project> {
void apply(Project project) {
Task bundlor = project.tasks.add('bundlor', Bundlor.class)
bundlor.setDescription('Generates OSGi manifest using bundlor tool')
bundlor.dependsOn(project.classes)
project.jar.dependsOn bundlor
}
}
public class Bundlor extends DefaultTask {
@InputFile
@Optional
File manifestTemplate
@OutputDirectory
File bundlorDir = new File("${project.buildDir}/bundlor")
@OutputFile
File manifest = project.file("${bundlorDir}/META-INF/MANIFEST.MF")
@Input
Map<String,String> expansions = [:]
@InputFile
@Optional
File osgiProfile
@InputFiles
@Optional
FileCollection inputPaths
@Input
boolean failOnWarnings = false
Bundlor() {
manifestTemplate = new File(project.projectDir, 'template.mf')
if (!manifestTemplate.exists()) {
logger.info("No bundlor template for project " + project.name)
manifestTemplate = null
}
inputPaths = project.files(project.sourceSets.main.output.classesDir)
if (manifestTemplate != null) {
project.jar.manifest.from manifest
project.jar.inputs.files manifest
}
}
@TaskAction
void createManifest() {
if (manifestTemplate == null) {
return;
}
logging.captureStandardOutput(LogLevel.INFO)
project.mkdir(bundlorDir)
//String inputPath = project.sourceSets.main.classesDir
List<ClassPath> inputClassPath = [] as List;
ManifestWriter manifestWriter = new FileSystemManifestWriter(project.file(bundlorDir.absolutePath));
ManifestContents mfTemplate = BundleManifestUtils.getManifest(manifestTemplate);
inputPaths.each {f ->
inputClassPath.add(new FileSystemClassPath(f))
}
// Must be a better way of doing this...
Properties p = new Properties()
expansions.each {entry ->
p.setProperty(entry.key, entry.value as String)
}
PropertiesSource expansionProps = new PropertiesPropertiesSource(p)
PropertiesSource osgiProfileProps = osgiProfile == null ? new EmptyPropertiesSource() :
new FileSystemPropertiesSource(osgiProfile);
ManifestGenerator manifestGenerator = new StandardManifestGenerator(
DefaultManifestGeneratorContributorsFactory.create(expansionProps, osgiProfileProps));
ManifestContents mf = manifestGenerator.generate(mfTemplate, inputClassPath.toArray(new ClassPath[inputClassPath.size()]));
try {
manifestWriter.write(mf);
} finally {
manifestWriter.close();
}
ManifestValidator manifestValidator = new StandardManifestValidator(DefaultManifestValidatorContributorsFactory.create());
List<String> warnings = manifestValidator.validate(mf);
if (warnings.isEmpty()) {
return
}
logger.warn("Bundlor Warnings:");
for (String warning : warnings) {
logger.warn(" " + warning);
}
if (failOnWarnings) {
throw new GradleException("Bundlor returned warnings. Please fix manifest template at " + manifestTemplate.absolutePath + " and try again.")
}
}
def inputPath(FileCollection paths) {
inputPaths = project.files(inputPaths, paths)
}
}
@@ -0,0 +1,301 @@
package docbook;
import org.gradle.api.Plugin;
import org.gradle.api.GradleException;
import org.gradle.api.DefaultTask;
import org.gradle.api.Task;
import org.gradle.api.Project;
import org.gradle.api.Action;
import org.gradle.api.tasks.*;
import org.gradle.api.file.FileCollection;
import org.xml.sax.XMLReader;
import org.xml.sax.InputSource;
import org.apache.xml.resolver.CatalogManager;
import org.apache.xml.resolver.tools.CatalogResolver;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.*;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.*;
import java.util.*;
import java.util.zip.*;
import java.net.*;
import org.apache.fop.apps.*;
import com.icl.saxon.TransformerFactoryImpl;
/**
* Gradle Docbook plugin implementation.
* <p>
* Creates three tasks: docbookHtml, docbookHtmlSingle and docbookPdf. Each task takes a single File on
* which it operates.
*/
class DocbookPlugin implements Plugin<Project> {
public void apply(Project project) {
// Add the plugin tasks to the project
Task docbookHtml = project.tasks.add('docbookHtml', DocbookHtml.class);
docbookHtml.setDescription('Generates chunked docbook html output');
Task docbookHtmlSingle = project.tasks.add('docbookHtmlSingle', Docbook.class);
docbookHtmlSingle.setDescription('Generates single page docbook html output')
docbookHtmlSingle.suffix = '-single'
Task docbookFoPdf = project.tasks.add("docbookFoPdf", DocbookFoPdf.class);
docbookFoPdf.setDescription('Generates PDF output');
docbookFoPdf.extension = 'fo'
Task docbook = project.tasks.add("docbook", DefaultTask.class);
docbook.dependsOn (docbookHtml, docbookHtmlSingle, docbookFoPdf)
}
}
/**
*/
public class Docbook extends DefaultTask {
@Input
String extension = 'html';
@Input
String suffix = '';
@Input
boolean XIncludeAware = true;
@Input
boolean highlightingEnabled = true;
String admonGraphicsPath;
String imgSrcPath;
@InputDirectory
File sourceDirectory = new File(project.getProjectDir(), "src/docbook");
@Input
String sourceFileName;
@InputFile
File stylesheet;
@OutputDirectory
File docsDir = new File(project.getBuildDir(), "docs");
@TaskAction
public final void transform() {
SAXParserFactory factory = new org.apache.xerces.jaxp.SAXParserFactoryImpl();
factory.setXIncludeAware(XIncludeAware);
docsDir.mkdirs();
File srcFile = new File(filterDocbookSources(sourceDirectory), sourceFileName);
String outputFilename = srcFile.getName().substring(0, srcFile.getName().length() - 4) + suffix + '.' + extension;
File outputFile = new File(getDocsDir(), outputFilename);
Result result = new StreamResult(outputFile.getAbsolutePath());
CatalogResolver resolver = new CatalogResolver(createCatalogManager());
InputSource inputSource = new InputSource(srcFile.getAbsolutePath());
XMLReader reader = factory.newSAXParser().getXMLReader();
reader.setEntityResolver(resolver);
TransformerFactory transformerFactory = new TransformerFactoryImpl();
transformerFactory.setURIResolver(resolver);
URL url = stylesheet.toURL();
Source source = new StreamSource(url.openStream(), url.toExternalForm());
Transformer transformer = transformerFactory.newTransformer(source);
if (highlightingEnabled) {
File highlightingDir = new File(getProject().getBuildDir(), "highlighting");
if (!highlightingDir.exists()) {
highlightingDir.mkdirs();
extractHighlightFiles(highlightingDir);
}
transformer.setParameter("highlight.xslthl.config", new File(highlightingDir, "xslthl-config.xml").toURI().toURL());
}
if (admonGraphicsPath != null) {
transformer.setParameter("admon.graphics", "1");
transformer.setParameter("admon.graphics.path", admonGraphicsPath);
}
if (imgSrcPath != null) {
transformer.setParameter("img.src.path", imgSrcPath);
}
preTransform(transformer, srcFile, outputFile);
transformer.transform(new SAXSource(reader, inputSource), result);
postTransform(outputFile);
}
/**
* @param sourceDir directory of unfiltered sources
* @return directory of filtered sources
* @author Chris Beams
*/
private File filterDocbookSources(File sourceDir) {
def docbookWorkDir = new File("${project.buildDir}/reference-work")
docbookWorkDir.mkdirs()
// copy everything but springsecurity.xml
project.copy {
into(docbookWorkDir)
from(sourceDir) { exclude '**/springsecurity.xml' }
}
// copy index.xml and expand ${...} variables along the way
// e.g.: ${version} needs to be replaced in the header
project.copy {
into(docbookWorkDir)
from(sourceDir) { include '**/springsecurity.xml' }
expand(version: "${project.version}")
}
return docbookWorkDir
}
private void extractHighlightFiles(File toDir) {
URLClassLoader cl = (URLClassLoader) getClass().getClassLoader();
URL[] urls = cl.getURLs();
URL docbookZip = null;
for (URL url : urls) {
if (url.toString().contains("docbook-xsl-")) {
docbookZip = url;
break;
}
}
if (docbookZip == null) {
throw new GradleException("Docbook zip file not found");
}
ZipFile zipFile = new ZipFile(new File(docbookZip.toURI()));
Enumeration e = zipFile.entries();
while (e.hasMoreElements()) {
ZipEntry ze = (ZipEntry) e.nextElement();
if (ze.getName().matches(".*/highlighting/.*\\.xml")) {
String filename = ze.getName().substring(ze.getName().lastIndexOf("/highlighting/") + 14);
copyFile(zipFile.getInputStream(ze), new File(toDir, filename));
}
}
}
private void copyFile(InputStream source, File destFile) {
destFile.createNewFile();
FileOutputStream to = null;
try {
to = new FileOutputStream(destFile);
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = source.read(buffer)) > 0) {
to.write(buffer, 0, bytesRead);
}
} finally {
if (source != null) {
source.close();
}
if (to != null) {
to.close();
}
}
}
protected void preTransform(Transformer transformer, File sourceFile, File outputFile) {
}
protected void postTransform(File outputFile) {
}
private CatalogManager createCatalogManager() {
CatalogManager manager = new CatalogManager();
manager.setIgnoreMissingProperties(true);
ClassLoader classLoader = this.getClass().getClassLoader();
StringBuilder builder = new StringBuilder();
String docbookCatalogName = "docbook/catalog.xml";
URL docbookCatalog = classLoader.getResource(docbookCatalogName);
if (docbookCatalog == null) {
throw new IllegalStateException("Docbook catalog " + docbookCatalogName + " could not be found in " + classLoader);
}
builder.append(docbookCatalog.toExternalForm());
Enumeration enumeration = classLoader.getResources("/catalog.xml");
while (enumeration.hasMoreElements()) {
builder.append(';');
URL resource = (URL) enumeration.nextElement();
builder.append(resource.toExternalForm());
}
String catalogFiles = builder.toString();
manager.setCatalogFiles(catalogFiles);
return manager;
}
}
/**
*/
class DocbookHtml extends Docbook {
@Override
protected void preTransform(Transformer transformer, File sourceFile, File outputFile) {
String rootFilename = outputFile.getName();
rootFilename = rootFilename.substring(0, rootFilename.lastIndexOf('.'));
transformer.setParameter("root.filename", rootFilename);
transformer.setParameter("base.dir", outputFile.getParent() + File.separator);
}
}
/**
*/
class DocbookFoPdf extends Docbook {
/**
* <a href="http://xmlgraphics.apache.org/fop/0.95/embedding.html#render">From the FOP usage guide</a>
*/
@Override
protected void postTransform(File foFile) {
FopFactory fopFactory = FopFactory.newInstance();
OutputStream out = null;
final File pdfFile = getPdfOutputFile(foFile);
logger.debug("Transforming 'fo' file "+ foFile + " to PDF: " + pdfFile);
try {
out = new BufferedOutputStream(new FileOutputStream(pdfFile));
Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, out);
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer();
Source src = new StreamSource(foFile);
Result res = new SAXResult(fop.getDefaultHandler());
transformer.transform(src, res);
} finally {
if (out != null) {
out.close();
}
}
/* if (!foFile.delete()) {
logger.warn("Failed to delete 'fo' file " + foFile);
}*/
}
private File getPdfOutputFile(File foFile) {
String name = foFile.getAbsolutePath();
return new File(name.substring(0, name.length() - 2) + "pdf");
}
}
@@ -1,59 +0,0 @@
package trang;
import com.thaiopensource.relaxng.translate.Driver
import javax.xml.transform.Transformer
import javax.xml.transform.TransformerFactory
import javax.xml.transform.stream.StreamSource
import javax.xml.transform.stream.StreamResult
import org.gradle.api.*;
import org.gradle.api.tasks.*
import org.gradle.api.file.FileCollection
/**
* Used for converting .rnc files to .xsd files.
* @author Rob Winch
*/
class TrangPlugin implements Plugin<Project> {
public void apply(Project project) {
Task rncToXsd = project.tasks.add('rncToXsd', RncToXsd.class)
rncToXsd.description = 'Converts .rnc to .xsd'
rncToXsd.group = 'Build'
}
}
/**
* Converts .rnc files to .xsd files using trang and then applies an xsl file to cleanup the results.
*/
public class RncToXsd extends DefaultTask {
@InputDirectory
File rncDir
@InputFile
File xslFile
@OutputDirectory
File xsdDir
@TaskAction
public final void transform() {
String xslPath = xslFile.absolutePath
rncDir.listFiles( { dir, file -> file.endsWith('.rnc')} as FilenameFilter).each { rncFile ->
File xsdFile = new File(xsdDir, rncFile.name.replace('.rnc', '.xsd'))
String xsdOutputPath = xsdFile.absolutePath
new Driver().run([rncFile.absolutePath, xsdOutputPath] as String[]);
TransformerFactory tFactory = new net.sf.saxon.TransformerFactoryImpl()
Transformer transformer =
tFactory.newTransformer(new StreamSource(xslPath))
File temp = File.createTempFile("gradle-trang-" + xsdFile.name, ".xsd")
xsdFile.withInputStream { is ->
temp << is
}
StreamSource xmlSource = new StreamSource(temp)
transformer.transform(xmlSource, new StreamResult(xsdFile))
temp.delete()
}
}
}
@@ -0,0 +1 @@
implementation-class=bundlor.BundlorPlugin
@@ -0,0 +1 @@
implementation-class=docbook.DocbookPlugin
@@ -1 +0,0 @@
implementation-class=MavenBomPlugin
@@ -1 +0,0 @@
implementation-class=trang.TrangPlugin
+4 -6
View File
@@ -2,13 +2,11 @@
dependencies {
compile project(':spring-security-core'),
project(':spring-security-web'),
springCoreDependency,
"org.springframework:spring-context:$springVersion",
"org.springframework:spring-beans:$springVersion",
"org.springframework:spring-web:$springVersion",
"org.jasig.cas.client:cas-client-core:3.3.3"
"org.jasig.cas.client:cas-client-core:3.3.3",
"net.sf.ehcache:ehcache:$ehcacheVersion"
optional "net.sf.ehcache:ehcache-core:$ehcacheVersion"
provided "javax.servlet:javax.servlet-api:$servletApiVersion"
}
provided 'javax.servlet:servlet-api:2.5'
}
-151
View File
@@ -1,151 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-cas</artifactId>
<version>3.2.9.RELEASE</version>
<name>spring-security-cas</name>
<description>spring-security-cas</description>
<url>http://spring.io/spring-security</url>
<organization>
<name>spring.io</name>
<url>http://spring.io/</url>
</organization>
<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<id>rwinch</id>
<name>Rob Winch</name>
<email>rwinch@gopivotal.com</email>
</developer>
</developers>
<scm>
<connection>scm:git:git://github.com/spring-projects/spring-security</connection>
<developerConnection>scm:git:git://github.com/spring-projects/spring-security</developerConnection>
<url>https://github.com/spring-projects/spring-security</url>
</scm>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.jasig.cas.client</groupId>
<artifactId>cas-client-core</artifactId>
<version>3.3.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>3.2.9.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>3.2.9.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>3.2.13.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.2.13.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>3.2.13.RELEASE</version>
<scope>compile</scope>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>3.2.13.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>1.7.2</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>0.9.29</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-assert</artifactId>
<version>1.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>3.2.13.RELEASE</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
@@ -1,80 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.cas.authentication;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cache.Cache;
import org.springframework.util.Assert;
/**
* Caches tickets using a Spring IoC defined {@link Cache}.
*
* @author Marten Deinum
* @since 3.2
*
*/
public class SpringCacheBasedTicketCache implements StatelessTicketCache {
//~ Static fields/initializers =====================================================================================
private static final Log logger = LogFactory.getLog(SpringCacheBasedTicketCache.class);
//~ Instance fields ================================================================================================
private final Cache cache;
//~ Constructors ===================================================================================================
public SpringCacheBasedTicketCache(Cache cache) throws Exception {
Assert.notNull(cache, "cache mandatory");
this.cache = cache;
}
//~ Methods ========================================================================================================
public CasAuthenticationToken getByTicketId(final String serviceTicket) {
final Cache.ValueWrapper element = serviceTicket != null ? cache.get(serviceTicket) : null;
if (logger.isDebugEnabled()) {
logger.debug("Cache hit: " + (element != null) + "; service ticket: " + serviceTicket);
}
return element == null ? null : (CasAuthenticationToken) element.get();
}
public void putTicketInCache(final CasAuthenticationToken token) {
String key = token.getCredentials().toString();
if (logger.isDebugEnabled()) {
logger.debug("Cache put: " + key);
}
cache.put(key, token);
}
public void removeTicketFromCache(final CasAuthenticationToken token) {
if (logger.isDebugEnabled()) {
logger.debug("Cache remove: " + token.getCredentials().toString());
}
this.removeTicketFromCache(token.getCredentials().toString());
}
public void removeTicketFromCache(final String serviceTicket) {
cache.evict(serviceTicket);
}
}
@@ -39,7 +39,7 @@ public class EhCacheBasedTicketCacheTests extends AbstractStatelessTicketCacheTe
//~ Methods ========================================================================================================
@BeforeClass
public static void initCacheManaer() {
cacheManager = CacheManager.create();
cacheManager = new CacheManager();
cacheManager.addCache(new Cache("castickets", 500, false, false, 30, 30));
}
@@ -1,66 +0,0 @@
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.cas.authentication;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.cache.CacheManager;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import static org.junit.Assert.*;
/**
* Tests {@link org.springframework.security.cas.authentication.SpringCacheBasedTicketCache}.
*
* @author Marten Deinum
* @since 3.2
*/
public class SpringCacheBasedTicketCacheTests extends AbstractStatelessTicketCacheTests {
private static CacheManager cacheManager;
//~ Methods ========================================================================================================
@BeforeClass
public static void initCacheManaer() {
cacheManager = new ConcurrentMapCacheManager();
cacheManager.getCache("castickets");
}
@Test
public void testCacheOperation() throws Exception {
SpringCacheBasedTicketCache cache = new SpringCacheBasedTicketCache(cacheManager.getCache("castickets"));
final CasAuthenticationToken token = getToken();
// Check it gets stored in the cache
cache.putTicketInCache(token);
assertEquals(token, cache.getByTicketId("ST-0-ER94xMJmn6pha35CQRoZ"));
// Check it gets removed from the cache
cache.removeTicketFromCache(getToken());
assertNull(cache.getByTicketId("ST-0-ER94xMJmn6pha35CQRoZ"));
// Check it doesn't return values for null or unknown service tickets
assertNull(cache.getByTicketId(null));
assertNull(cache.getByTicketId("UNKNOWN_SERVICE_TICKET"));
}
@Test(expected = IllegalArgumentException.class)
public void testStartupDetectsMissingCache() throws Exception {
new SpringCacheBasedTicketCache(null);
}
}
@@ -172,7 +172,6 @@ public class CasAuthenticationFilterTests {
serviceProperties.setAuthenticateAllArtifacts(true);
MockHttpServletRequest request = new MockHttpServletRequest();
request.setParameter("ticket", "ST-1-123");
request.setRequestURI("/authenticate");
MockHttpServletResponse response = new MockHttpServletResponse();
FilterChain chain = mock(FilterChain.class);
@@ -12,4 +12,4 @@
</bean>
<bean class="org.springframework.security.cas.web.authentication.ServiceAuthenticationDetailsSource"/>
</beans>
</beans>
-1
View File
@@ -15,7 +15,6 @@ Import-Template:
org.springframework.security.authentication.*;version="${secRange}",
org.springframework.security.web.*;version="${secRange}",
org.springframework.beans.*;version="${springRange}",
org.springframework.cache.*;version="${springRange}";resolution:=optional,
org.springframework.context.*;version="${springRange}",
org.springframework.dao;version="${springRange}",
org.springframework.util;version="${springRange}",
+24 -45
View File
@@ -1,76 +1,55 @@
import javax.security.auth.login.ConfigurationSpi;
// Config Module build file
apply plugin: 'groovy'
apply plugin: 'trang'
compileTestJava.dependsOn(':spring-security-core:compileTestJava')
configurations {
// GRADLE-1124
compile.extendsFrom = []
testCompile.extendsFrom groovy
}
dependencies {
// NB: Don't add other compile time dependencies to the config module as this breaks tooling
compile project(':spring-security-core'),
springCoreDependency,
project(':spring-security-web'),
"org.aspectj:aspectjweaver:$aspectjVersion",
'aopalliance:aopalliance:1.0',
"org.springframework:spring-aop:$springVersion",
"org.springframework:spring-context:$springVersion",
"org.springframework:spring-web:$springVersion",
"org.springframework:spring-beans:$springVersion"
optional project(':spring-security-web'),
project(':spring-security-ldap'),
project(':spring-security-openid'),
project(':spring-security-aspects'),
"org.springframework:spring-web:$springVersion",
"org.springframework:spring-webmvc:$springVersion",
"org.aspectj:aspectjweaver:$aspectjVersion",
"org.springframework:spring-jdbc:$springVersion",
"org.springframework:spring-tx:$springVersion"
provided "javax.servlet:servlet-api:2.5"
provided "javax.servlet:javax.servlet-api:$servletApiVersion"
groovy 'org.codehaus.groovy:groovy:1.8.7'
testCompile project(':spring-security-cas'),
testCompile project(':spring-security-ldap'),
project(':spring-security-openid'),
project(':spring-security-core').sourceSets.test.output,
'javax.annotation:jsr250-api:1.0',
"org.springframework.ldap:spring-ldap-core:$springLdapVersion",
"org.springframework:spring-expression:$springVersion",
"org.springframework:spring-jdbc:$springVersion",
"org.springframework:spring-orm:$springVersion",
"org.springframework:spring-tx:$springVersion",
'org.spockframework:spock-core:0.6-groovy-1.8',
"org.slf4j:jcl-over-slf4j:$slf4jVersion",
"org.eclipse.persistence:javax.persistence:2.0.5",
"org.hibernate:hibernate-entitymanager:4.1.0.Final",
"org.codehaus.groovy:groovy-all:$groovyVersion",
"org.apache.directory.server:apacheds-core:$apacheDsVersion",
"org.apache.directory.server:apacheds-core-entry:$apacheDsVersion",
"org.apache.directory.server:apacheds-protocol-shared:$apacheDsVersion",
"org.apache.directory.server:apacheds-protocol-ldap:$apacheDsVersion",
"org.apache.directory.server:apacheds-server-jndi:$apacheDsVersion",
'org.apache.directory.shared:shared-ldap:0.9.15',
'ldapsdk:ldapsdk:4.1',
powerMockDependencies,
"org.hibernate:hibernate-entitymanager:3.6.10.Final",
"org.hsqldb:hsqldb:2.2.8",
spockDependencies
"org.powermock:powermock-core:$powerMockVersion",
"org.powermock:powermock-api-support:$powerMockVersion",
"org.powermock:powermock-module-junit4-common:$powerMockVersion",
"org.powermock:powermock-module-junit4:$powerMockVersion",
"org.powermock:powermock-api-mockito:$powerMockVersion",
"org.powermock:powermock-reflect:$powerMockVersion"
testCompile('org.openid4java:openid4java-nodeps:0.9.6') {
exclude group: 'com.google.code.guice', module: 'guice'
}
testCompile('org.springframework.data:spring-data-jpa:1.4.1.RELEASE') {
exclude group: 'org.aspectj', module: 'aspectjrt'
exclude group: 'com.google.code.guice', module: 'guice'
}
testRuntime "org.hsqldb:hsqldb:$hsqlVersion",
testRuntime "hsqldb:hsqldb:$hsqlVersion",
"cglib:cglib-nodep:2.2"
}
test {
inputs.file file("$rootDir/docs/manual/src/docbook/appendix-namespace.xml")
}
rncToXsd {
rncDir = file('src/main/resources/org/springframework/security/config/')
xsdDir = rncDir
xslFile = new File(rncDir, 'spring-security.xsl')
}
build.dependsOn rncToXsd
}
+11
View File
@@ -0,0 +1,11 @@
#! /bin/sh
pushd src/main/resources/org/springframework/security/config/
echo "Converting rnc file to xsd ..."
java -jar ~/bin/trang.jar spring-security-3.1.rnc spring-security-3.1.xsd
echo "Applying XSL transformation to xsd ..."
xsltproc --output spring-security-3.1.xsd spring-security.xsl spring-security-3.1.xsd
popd
-405
View File
@@ -1,405 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>3.2.9.RELEASE</version>
<name>spring-security-config</name>
<description>spring-security-config</description>
<url>http://spring.io/spring-security</url>
<organization>
<name>spring.io</name>
<url>http://spring.io/</url>
</organization>
<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<id>rwinch</id>
<name>Rob Winch</name>
<email>rwinch@gopivotal.com</email>
</developer>
</developers>
<scm>
<connection>scm:git:git://github.com/spring-projects/spring-security</connection>
<developerConnection>scm:git:git://github.com/spring-projects/spring-security</developerConnection>
<url>https://github.com/spring-projects/spring-security</url>
</scm>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>3.2.9.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>3.2.13.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>3.2.13.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.2.13.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>3.2.13.RELEASE</version>
<scope>compile</scope>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.10</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-aspects</artifactId>
<version>3.2.9.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-ldap</artifactId>
<version>3.2.9.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-openid</artifactId>
<version>3.2.9.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>3.2.9.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>3.2.13.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>3.2.13.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>3.2.13.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>3.2.13.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>2.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>0.9.29</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>jsr250-api</artifactId>
<version>1.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ldapsdk</groupId>
<artifactId>ldapsdk</artifactId>
<version>4.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-core</artifactId>
<version>1.5.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-core-entry</artifactId>
<version>1.5.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-protocol-ldap</artifactId>
<version>1.5.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-protocol-shared</artifactId>
<version>1.5.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-server-jndi</artifactId>
<version>1.5.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.directory.shared</groupId>
<artifactId>shared-ldap</artifactId>
<version>0.9.15</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>2.0.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-assert</artifactId>
<version>1.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>javax.persistence</artifactId>
<version>2.0.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>4.1.0.Final</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>3.6.10.Final</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.2.8</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.3.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.openid4java</groupId>
<artifactId>openid4java-nodeps</artifactId>
<version>0.9.6</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>guice</artifactId>
<groupId>com.google.code.guice</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>1.6.2</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>mockito-all</artifactId>
<groupId>org.mockito</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-support</artifactId>
<version>1.6.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-core</artifactId>
<version>1.6.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>1.6.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4-common</artifactId>
<version>1.6.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-reflect</artifactId>
<version>1.6.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<version>0.7-groovy-2.0</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>junit-dep</artifactId>
<groupId>junit</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-spring</artifactId>
<version>0.7-groovy-2.0</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>junit-dep</artifactId>
<groupId>junit</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.4.1.RELEASE</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>aspectjrt</artifactId>
<groupId>org.aspectj</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.ldap</groupId>
<artifactId>spring-ldap-core</artifactId>
<version>1.3.2.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-cas</artifactId>
<version>3.2.9.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>3.2.13.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>3.2.13.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>3.2.13.RELEASE</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
@@ -1,220 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.authentication.ldap
import org.springframework.beans.factory.config.AutowireCapableBeanFactory
import org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Import
import org.springframework.ldap.core.ContextSource;
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.authentication.AuthenticationProvider
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.config.annotation.BaseSpringSpec
import org.springframework.security.config.annotation.SecurityBuilder;
import org.springframework.security.config.annotation.authentication.AuthenticationManagerBuilder
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
import org.springframework.security.config.annotation.authentication.configuration.EnableGlobalAuthentication
import org.springframework.security.config.annotation.configuration.AutowireBeanFactoryObjectPostProcessor
import org.springframework.security.config.annotation.configuration.AutowireBeanFactoryObjectPostProcessorTests
import org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
import org.springframework.security.ldap.server.ApacheDSContainer;
import org.springframework.test.util.ReflectionTestUtils;
/**
*
* @author Rob Winch
*
*/
class LdapAuthenticationProviderBuilderSecurityBuilderTests extends BaseSpringSpec {
def "default configuration"() {
when:
loadConfig(DefaultLdapConfig)
LdapAuthenticationProvider provider = ldapProvider()
then:
provider.authoritiesPopulator.groupRoleAttribute == "cn"
provider.authoritiesPopulator.groupSearchBase == ""
provider.authoritiesPopulator.groupSearchFilter == "(uniqueMember={0})"
ReflectionTestUtils.getField(provider,"authoritiesMapper").prefix == "ROLE_"
}
@Configuration
static class DefaultLdapConfig extends BaseLdapProviderConfig {
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.contextSource(contextSource())
.userDnPatterns("uid={0},ou=people")
}
}
def "group roles custom"() {
when:
loadConfig(GroupRolesConfig)
LdapAuthenticationProvider provider = ldapProvider()
then:
provider.authoritiesPopulator.groupRoleAttribute == "group"
}
@Configuration
static class GroupRolesConfig extends BaseLdapProviderConfig {
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.contextSource(contextSource())
.userDnPatterns("uid={0},ou=people")
.groupRoleAttribute("group")
}
}
def "group search custom"() {
when:
loadConfig(GroupSearchConfig)
LdapAuthenticationProvider provider = ldapProvider()
then:
provider.authoritiesPopulator.groupSearchFilter == "ou=groupName"
}
@Configuration
static class GroupSearchConfig extends BaseLdapProviderConfig {
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.contextSource(contextSource())
.userDnPatterns("uid={0},ou=people")
.groupSearchFilter("ou=groupName");
}
}
def "role prefix custom"() {
when:
loadConfig(RolePrefixConfig)
LdapAuthenticationProvider provider = ldapProvider()
then:
ReflectionTestUtils.getField(provider,"authoritiesMapper").prefix == "role_"
}
@Configuration
static class RolePrefixConfig extends BaseLdapProviderConfig {
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.contextSource(contextSource())
.userDnPatterns("uid={0},ou=people")
.rolePrefix("role_")
}
}
def "bind authentication"() {
when:
loadConfig(BindAuthenticationConfig)
AuthenticationManager auth = context.getBean(AuthenticationManager)
then:
auth
auth.authenticate(new UsernamePasswordAuthenticationToken("bob","bobspassword")).authorities.collect { it.authority }.sort() == ["ROLE_DEVELOPERS"]
}
@Configuration
static class BindAuthenticationConfig extends BaseLdapServerConfig {
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.contextSource(contextSource())
.groupSearchBase("ou=groups")
.groupSearchFilter("(member={0})")
.userDnPatterns("uid={0},ou=people");
}
}
def "SEC-2472: Can use crypto PasswordEncoder"() {
setup:
loadConfig(PasswordEncoderConfig)
when:
AuthenticationManager auth = context.getBean(AuthenticationManager)
then:
auth.authenticate(new UsernamePasswordAuthenticationToken("bcrypt","password")).authorities.collect { it.authority }.sort() == ["ROLE_DEVELOPERS"]
}
@Configuration
static class PasswordEncoderConfig extends BaseLdapServerConfig {
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.contextSource(contextSource())
.passwordEncoder(new BCryptPasswordEncoder())
.groupSearchBase("ou=groups")
.groupSearchFilter("(member={0})")
.userDnPatterns("uid={0},ou=people");
}
}
def ldapProvider() {
context.getBean(AuthenticationManager).providers[0]
}
@Configuration
static abstract class BaseLdapServerConfig extends BaseLdapProviderConfig {
@Bean
public ApacheDSContainer ldapServer() throws Exception {
ApacheDSContainer apacheDSContainer = new ApacheDSContainer("dc=springframework,dc=org", "classpath:/test-server.ldif");
apacheDSContainer.setPort(getPort());
return apacheDSContainer;
}
}
@Configuration
@EnableGlobalAuthentication
@Import(ObjectPostProcessorConfiguration)
static abstract class BaseLdapProviderConfig {
@Bean
public BaseLdapPathContextSource contextSource() throws Exception {
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(
"ldap://127.0.0.1:"+ getPort() + "/dc=springframework,dc=org")
contextSource.userDn = "uid=admin,ou=system"
contextSource.password = "secret"
contextSource.afterPropertiesSet()
return contextSource;
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationManagerBuilder auth) {
configure(auth)
auth.build()
}
abstract protected void configure(AuthenticationManagerBuilder auth)
}
static Integer port;
static int getPort() {
if(port == null) {
ServerSocket socket = new ServerSocket(0)
port = socket.localPort
socket.close()
}
port
}
}
@@ -1,61 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.authentication.ldap
import org.springframework.context.annotation.Configuration
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.config.annotation.BaseSpringSpec
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
import org.springframework.security.config.annotation.authentication.ldap.NamespaceLdapAuthenticationProviderTestsConfigs.LdapAuthenticationProviderConfig
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider
import org.springframework.security.ldap.authentication.PasswordComparisonAuthenticator
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator
import org.springframework.security.ldap.userdetails.PersonContextMapper
import org.springframework.test.util.ReflectionTestUtils
import static org.springframework.security.config.annotation.authentication.ldap.NamespaceLdapAuthenticationProviderTestsConfigs.*
/**
*
* @author Rob Winch
*
*/
class LdapAuthenticationProviderConfigurerTests extends BaseSpringSpec {
def "authentication-manager support multiple default ldap contexts (ports dynamically allocated)"() {
when:
loadConfig(MultiLdapAuthenticationProvidersConfig)
then:
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("bob","bobspassword"))
}
@EnableWebSecurity
@Configuration
static class MultiLdapAuthenticationProvidersConfig extends WebSecurityConfigurerAdapter {
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.groupSearchBase("ou=groups")
.userDnPatterns("uid={0},ou=people")
.and()
.ldapAuthentication()
.groupSearchBase("ou=groups")
.userDnPatterns("uid={0},ou=people")
}
}
}
@@ -1,80 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.authentication.ldap
import static org.springframework.security.config.annotation.authentication.ldap.NamespaceLdapAuthenticationProviderTestsConfigs.*
import org.springframework.ldap.core.support.BaseLdapPathContextSource
import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.authentication.AuthenticationProvider
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.config.annotation.BaseSpringSpec
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.ldap.NamespaceLdapAuthenticationProviderTestsConfigs.LdapAuthenticationProviderConfig;
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
import org.springframework.security.ldap.authentication.PasswordComparisonAuthenticator;
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;
import org.springframework.security.ldap.userdetails.PersonContextMapper;
import org.springframework.test.util.ReflectionTestUtils;
/**
*
* @author Rob Winch
*
*/
class NamespaceLdapAuthenticationProviderTests extends BaseSpringSpec {
def "ldap-authentication-provider"() {
when:
loadConfig(LdapAuthenticationProviderConfig)
then:
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("bob","bobspassword"))
}
def "ldap-authentication-provider custom"() {
when:
loadConfig(CustomLdapAuthenticationProviderConfig)
LdapAuthenticationProvider provider = findAuthenticationProvider(LdapAuthenticationProvider)
then:
provider.authoritiesPopulator.groupRoleAttribute == "cn"
provider.authoritiesPopulator.groupSearchBase == "ou=groups"
provider.authoritiesPopulator.groupSearchFilter == "(member={0})"
ReflectionTestUtils.getField(provider,"authoritiesMapper").prefix == "PREFIX_"
provider.userDetailsContextMapper instanceof PersonContextMapper
provider.authenticator.getUserDns("user") == ["uid=user,ou=people"]
provider.authenticator.userSearch.searchBase == "ou=users"
provider.authenticator.userSearch.searchFilter == "(uid={0})"
}
def "SEC-2490: ldap-authentication-provider custom LdapAuthoritiesPopulator"() {
setup:
LdapAuthoritiesPopulator LAP = Mock()
CustomAuthoritiesPopulatorConfig.LAP = LAP
when:
loadConfig(CustomAuthoritiesPopulatorConfig)
LdapAuthenticationProvider provider = findAuthenticationProvider(LdapAuthenticationProvider)
then:
provider.authoritiesPopulator == LAP
}
def "ldap-authentication-provider password compare"() {
when:
loadConfig(PasswordCompareLdapConfig)
LdapAuthenticationProvider provider = findAuthenticationProvider(LdapAuthenticationProvider)
then:
provider.authenticator instanceof PasswordComparisonAuthenticator
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("bob","bobspassword"))
}
}
@@ -1,13 +1,7 @@
package org.springframework.security.config.ldap
import static org.mockito.Mockito.*
import java.text.MessageFormat
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.security.config.AbstractXmlConfigTests
import org.springframework.security.config.BeanIds
import org.springframework.security.util.FieldUtils
@@ -42,7 +36,7 @@ class LdapProviderBeanDefinitionParserTests extends AbstractXmlConfigTests {
def simpleProviderAuthenticatesCorrectly() {
xml.'ldap-server'(ldif:'test-server.ldif')
xml.'ldap-server'()
xml.'authentication-manager'{
'ldap-authentication-provider'('group-search-filter':'member={0}')
}
@@ -72,7 +66,7 @@ class LdapProviderBeanDefinitionParserTests extends AbstractXmlConfigTests {
}
def supportsPasswordComparisonAuthentication() {
xml.'ldap-server'(ldif:'test-server.ldif')
xml.'ldap-server'()
xml.'authentication-manager'{
'ldap-authentication-provider'('user-dn-pattern': 'uid={0},ou=people')
'password-compare'
@@ -89,7 +83,7 @@ class LdapProviderBeanDefinitionParserTests extends AbstractXmlConfigTests {
}
def supportsPasswordComparisonAuthenticationWithHashAttribute() {
xml.'ldap-server'(ldif:'test-server.ldif')
xml.'ldap-server'()
xml.'authentication-manager'{
'ldap-authentication-provider'('user-dn-pattern': 'uid={0},ou=people') {
'password-compare'('password-attribute': 'uid', hash: 'plaintext')
@@ -108,7 +102,7 @@ class LdapProviderBeanDefinitionParserTests extends AbstractXmlConfigTests {
}
def supportsPasswordComparisonAuthenticationWithPasswordEncoder() {
xml.'ldap-server'(ldif:'test-server.ldif')
xml.'ldap-server'()
xml.'authentication-manager'{
'ldap-authentication-provider'('user-dn-pattern': 'uid={0},ou=people') {
'password-compare'('password-attribute': 'uid') {
@@ -128,28 +122,6 @@ class LdapProviderBeanDefinitionParserTests extends AbstractXmlConfigTests {
notThrown(AuthenticationException)
}
def 'SEC-2472: Supports Crypto PasswordEncoder'() {
setup:
xml.'ldap-server'(ldif:'test-server.ldif')
xml.'authentication-manager'{
'ldap-authentication-provider'('user-dn-pattern': 'uid={0},ou=people') {
'password-compare'() {
'password-encoder'(ref: 'pe')
}
}
}
xml.'b:bean'(id:'pe','class':BCryptPasswordEncoder.class.name)
createAppContext('')
def am = appContext.getBean(BeanIds.AUTHENTICATION_MANAGER)
when:
def auth = am.authenticate(new UsernamePasswordAuthenticationToken("bcrypt", 'password'))
then:
auth != null
}
def inetOrgContextMapperIsSupported() {
xml.'ldap-server'(url: 'ldap://127.0.0.1:343/dc=springframework,dc=org')
xml.'authentication-manager'{
@@ -1,94 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.authentication.ldap;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.encoding.PlaintextPasswordEncoder;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;
import org.springframework.security.ldap.userdetails.PersonContextMapper;
/**
* @author Rob Winch
*
*/
public class NamespaceLdapAuthenticationProviderTestsConfigs {
@Configuration
@EnableWebSecurity
static class LdapAuthenticationProviderConfig extends WebSecurityConfigurerAdapter {
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.groupSearchBase("ou=groups")
.userDnPatterns("uid={0},ou=people"); // ldap-server@user-dn-pattern
}
}
@Configuration
@EnableWebSecurity
static class CustomLdapAuthenticationProviderConfig extends WebSecurityConfigurerAdapter {
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.groupRoleAttribute("cn") // ldap-authentication-provider@group-role-attribute
.groupSearchBase("ou=groups") // ldap-authentication-provider@group-search-base
.groupSearchFilter("(member={0})") // ldap-authentication-provider@group-search-filter
.rolePrefix("PREFIX_") // ldap-authentication-provider@group-search-filter
.userDetailsContextMapper(new PersonContextMapper()) // ldap-authentication-provider@user-context-mapper-ref / ldap-authentication-provider@user-details-class
.userDnPatterns("uid={0},ou=people") // ldap-authentication-provider@user-dn-pattern
.userSearchBase("ou=users") // ldap-authentication-provider@user-dn-pattern
.userSearchFilter("(uid={0})") // ldap-authentication-provider@user-search-filter
// .contextSource(contextSource) // ldap-authentication-provider@server-ref
.contextSource()
.ldif("classpath:users.xldif") // ldap-server@ldif
.managerDn("uid=admin,ou=system") // ldap-server@manager-dn
.managerPassword("secret") // ldap-server@manager-password
.port(33399) // ldap-server@port
.root("dc=springframework,dc=org") // ldap-server@root
// .url("ldap://localhost:33389/dc-springframework,dc=org") this overrides root and port and is used for external
;
}
}
@Configuration
@EnableWebSecurity
static class CustomAuthoritiesPopulatorConfig extends WebSecurityConfigurerAdapter {
static LdapAuthoritiesPopulator LAP;
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.userSearchFilter("(uid={0})")
.ldapAuthoritiesPopulator(LAP);
}
}
@Configuration
@EnableWebSecurity
static class PasswordCompareLdapConfig extends WebSecurityConfigurerAdapter {
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.groupSearchBase("ou=groups")
.userSearchFilter("(uid={0})")
.passwordCompare()
.passwordEncoder(new PlaintextPasswordEncoder()) // ldap-authentication-provider/password-compare/password-encoder@ref
.passwordAttribute("userPassword"); // ldap-authentication-provider/password-compare@password-attribute
}
}
}
@@ -28,16 +28,6 @@ sn: Alex
uid: ben
userPassword: {SHA}nFCebWjxfaLbHHG1Qk5UU4trbvQ=
dn: uid=bcrypt,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: BCrypt user
sn: BCrypt
uid: bcrypt
userPassword: $2a$10$FBAKClV1zBIOOC9XMXf3AO8RoGXYVYsfvUdoLxGkd/BnXEn4tqT3u
dn: uid=bob,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
@@ -68,22 +58,11 @@ sn: Mouse
uid: jerry
userPassword: jerryspassword
dn: uid=bcrypt,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: BCrypt user
sn: BCrypt
uid: bcrypt
userPassword: $2a$10$lDa0YFNHAt63MjIzK/wUqeM0qjIhzPhp3RNI/MLUQEAUbzhB/SnnS
dn: cn=developers,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfNames
cn: developers
ou: developer
member: uid=bcrypt,ou=people,dc=springframework,dc=org
member: uid=ben,ou=people,dc=springframework,dc=org
member: uid=bob,ou=people,dc=springframework,dc=org
@@ -1,92 +0,0 @@
dn: ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: groups
dn: ou=subgroups,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: subgroups
dn: ou=people,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: people
dn: ou=otherpeople,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: otherpeople
dn: uid=ben,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Ben Alex
sn: Alex
uid: ben
userPassword: {SHA}nFCebWjxfaLbHHG1Qk5UU4trbvQ=
dn: uid=bcrypt,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: BCrypt user
sn: BCrypt
uid: bcrypt
userPassword: $2a$10$lDa0YFNHAt63MjIzK/wUqeM0qjIhzPhp3RNI/MLUQEAUbzhB/SnnS
dn: uid=bob,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Bob Hamilton
sn: Hamilton
uid: bob
userPassword: bobspassword
dn: uid=joe,ou=otherpeople,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Joe Smeth
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
cn: developers
ou: developer
member: uid=ben,ou=people,dc=springframework,dc=org
member: uid=bob,ou=people,dc=springframework,dc=org
dn: cn=managers,ou=groups,dc=springframework,dc=org
objectclass: top
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
objectclass: groupOfNames
cn: submanagers
ou: submanager
member: uid=ben,ou=people,dc=springframework,dc=org
@@ -54,6 +54,4 @@ public abstract class Elements {
public static final String LDAP_PASSWORD_COMPARE = "password-compare";
public static final String DEBUG = "debug";
public static final String HTTP_FIREWALL = "http-firewall";
public static final String HEADERS = "headers";
public static final String CSRF = "csrf";
}
@@ -1,23 +1,5 @@
/*
* Copyright 2009-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.config.BeanDefinition;
@@ -46,6 +28,8 @@ import org.springframework.util.ClassUtils;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import java.util.*;
/**
* Parses elements from the "security" namespace (http://www.springframework.org/schema/security).
*
@@ -81,8 +65,8 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
public BeanDefinition parse(Element element, ParserContext pc) {
if (!namespaceMatchesVersion(element)) {
pc.getReaderContext().fatal("You cannot use a spring-security-2.0.xsd or spring-security-3.0.xsd or spring-security-3.1.xsd schema " +
"with Spring Security 3.2. Please update your schema declarations to the 3.2 schema.", element);
pc.getReaderContext().fatal("You cannot use a spring-security-2.0.xsd or spring-security-3.0.xsd schema " +
"with Spring Security 3.1. Please update your schema declarations to the 3.1 schema.", element);
}
String name = pc.getDelegate().getLocalName(element);
BeanDefinitionParser parser = parsers.get(name);
@@ -196,7 +180,7 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
private boolean matchesVersionInternal(Element element) {
String schemaLocation = element.getAttributeNS("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation");
return schemaLocation.matches("(?m).*spring-security-3\\.2.*.xsd.*")
return schemaLocation.matches("(?m).*spring-security-3\\.1.*.xsd.*")
|| schemaLocation.matches("(?m).*spring-security.xsd.*")
|| !schemaLocation.matches("(?m).*spring-security.*");
}
@@ -1,463 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.util.Assert;
import org.springframework.web.filter.DelegatingFilterProxy;
/**
* <p>A base {@link SecurityBuilder} that allows {@link SecurityConfigurer} to be
* applied to it. This makes modifying the {@link SecurityBuilder} a strategy
* that can be customized and broken up into a number of
* {@link SecurityConfigurer} objects that have more specific goals than that
* of the {@link SecurityBuilder}.</p>
*
* <p>For example, a {@link SecurityBuilder} may build an
* {@link DelegatingFilterProxy}, but a {@link SecurityConfigurer} might
* populate the {@link SecurityBuilder} with the filters necessary for session
* management, form based login, authorization, etc.</p>
*
* @see WebSecurity
*
* @author Rob Winch
*
* @param <O>
* The object that this builder returns
* @param <B>
* The type of this builder (that is returned by the base class)
*/
public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBuilder<O>> extends AbstractSecurityBuilder<O> {
private final Log logger = LogFactory.getLog(getClass());
private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers =
new LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>>();
private final List<SecurityConfigurer<O, B>> configurersAddedInInitializing = new ArrayList<SecurityConfigurer<O, B>>();
private final Map<Class<Object>,Object> sharedObjects = new HashMap<Class<Object>,Object>();
private final boolean allowConfigurersOfSameType;
private BuildState buildState = BuildState.UNBUILT;
private ObjectPostProcessor<Object> objectPostProcessor;
/***
* Creates a new instance with the provided {@link ObjectPostProcessor}.
* This post processor must support Object since there are many types of
* objects that may be post processed.
*
* @param objectPostProcessor the {@link ObjectPostProcessor} to use
*/
protected AbstractConfiguredSecurityBuilder(ObjectPostProcessor<Object> objectPostProcessor) {
this(objectPostProcessor,false);
}
/***
* Creates a new instance with the provided {@link ObjectPostProcessor}.
* This post processor must support Object since there are many types of
* objects that may be post processed.
*
* @param objectPostProcessor the {@link ObjectPostProcessor} to use
* @param allowConfigurersOfSameType if true, will not override other {@link SecurityConfigurer}'s when performing apply
*/
protected AbstractConfiguredSecurityBuilder(ObjectPostProcessor<Object> objectPostProcessor, boolean allowConfigurersOfSameType) {
Assert.notNull(objectPostProcessor, "objectPostProcessor cannot be null");
this.objectPostProcessor = objectPostProcessor;
this.allowConfigurersOfSameType = allowConfigurersOfSameType;
}
/**
* Similar to {@link #build()} and {@link #getObject()} but checks the state
* to determine if {@link #build()} needs to be called first.
*
* @return the result of {@link #build()} or {@link #getObject()}. If an
* error occurs while building, returns null.
*/
public O getOrBuild() {
if(isUnbuilt()) {
try {
return build();
} catch(Exception e) {
logger.debug("Failed to perform build. Returning null", e);
return null;
}
} else {
return getObject();
}
}
/**
* Applies a {@link SecurityConfigurerAdapter} to this
* {@link SecurityBuilder} and invokes
* {@link SecurityConfigurerAdapter#setBuilder(SecurityBuilder)}.
*
* @param configurer
* @return
* @throws Exception
*/
@SuppressWarnings("unchecked")
public <C extends SecurityConfigurerAdapter<O, B>> C apply(C configurer)
throws Exception {
configurer.addObjectPostProcessor(objectPostProcessor);
configurer.setBuilder((B) this);
add(configurer);
return configurer;
}
/**
* Applies a {@link SecurityConfigurer} to this {@link SecurityBuilder}
* overriding any {@link SecurityConfigurer} of the exact same class. Note
* that object hierarchies are not considered.
*
* @param configurer
* @return
* @throws Exception
*/
public <C extends SecurityConfigurer<O, B>> C apply(C configurer)
throws Exception {
add(configurer);
return configurer;
}
/**
* Sets an object that is shared by multiple {@link SecurityConfigurer}.
*
* @param sharedType the Class to key the shared object by.
* @param object the Object to store
*/
@SuppressWarnings("unchecked")
public <C> void setSharedObject(Class<C> sharedType, C object) {
this.sharedObjects.put((Class<Object>) sharedType, object);
}
/**
* Gets a shared Object. Note that object heirarchies are not considered.
*
* @param sharedType the type of the shared Object
* @return the shared Object or null if it is not found
*/
@SuppressWarnings("unchecked")
public <C> C getSharedObject(Class<C> sharedType) {
return (C) this.sharedObjects.get(sharedType);
}
/**
* Gets the shared objects
* @return
*/
public Map<Class<Object>,Object> getSharedObjects() {
return Collections.unmodifiableMap(this.sharedObjects);
}
/**
* Adds {@link SecurityConfigurer} ensuring that it is allowed and
* invoking {@link SecurityConfigurer#init(SecurityBuilder)} immediately
* if necessary.
*
* @param configurer the {@link SecurityConfigurer} to add
* @throws Exception if an error occurs
*/
@SuppressWarnings("unchecked")
private <C extends SecurityConfigurer<O, B>> void add(C configurer) throws Exception {
Assert.notNull(configurer, "configurer cannot be null");
Class<? extends SecurityConfigurer<O, B>> clazz = (Class<? extends SecurityConfigurer<O, B>>) configurer
.getClass();
synchronized(configurers) {
if(buildState.isConfigured()) {
throw new IllegalStateException("Cannot apply "+configurer+" to already built object");
}
List<SecurityConfigurer<O, B>> configs = allowConfigurersOfSameType ? this.configurers.get(clazz) : null;
if(configs == null) {
configs = new ArrayList<SecurityConfigurer<O,B>>(1);
}
configs.add(configurer);
this.configurers.put(clazz, configs);
if(buildState.isInitializing()) {
this.configurersAddedInInitializing.add(configurer);
}
}
}
/**
* Gets all the {@link SecurityConfigurer} instances by its class name or an
* empty List if not found. Note that object hierarchies are not considered.
*
* @param clazz the {@link SecurityConfigurer} class to look for
* @return
*/
@SuppressWarnings("unchecked")
public <C extends SecurityConfigurer<O, B>> List<C> getConfigurers(
Class<C> clazz) {
List<C> configs = (List<C>) this.configurers.get(clazz);
if(configs == null) {
return new ArrayList<C>();
}
return new ArrayList<C>(configs);
}
/**
* Removes all the {@link SecurityConfigurer} instances by its class name or an
* empty List if not found. Note that object hierarchies are not considered.
*
* @param clazz the {@link SecurityConfigurer} class to look for
* @return
*/
@SuppressWarnings("unchecked")
public <C extends SecurityConfigurer<O, B>> List<C> removeConfigurers(
Class<C> clazz) {
List<C> configs = (List<C>) this.configurers.remove(clazz);
if(configs == null) {
return new ArrayList<C>();
}
return new ArrayList<C>(configs);
}
/**
* Gets the {@link SecurityConfigurer} by its class name or
* <code>null</code> if not found. Note that object hierarchies are not
* considered.
*
* @param clazz
* @return
*/
@SuppressWarnings("unchecked")
public <C extends SecurityConfigurer<O, B>> C getConfigurer(
Class<C> clazz) {
List<SecurityConfigurer<O,B>> configs = this.configurers.get(clazz);
if(configs == null) {
return null;
}
if(configs.size() != 1) {
throw new IllegalStateException("Only one configurer expected for type " + clazz + ", but got " + configs);
}
return (C) configs.get(0);
}
/**
* Removes and returns the {@link SecurityConfigurer} by its class name or
* <code>null</code> if not found. Note that object hierarchies are not
* considered.
*
* @param clazz
* @return
*/
@SuppressWarnings("unchecked")
public <C extends SecurityConfigurer<O,B>> C removeConfigurer(Class<C> clazz) {
List<SecurityConfigurer<O,B>> configs = this.configurers.remove(clazz);
if(configs == null) {
return null;
}
if(configs.size() != 1) {
throw new IllegalStateException("Only one configurer expected for type " + clazz + ", but got " + configs);
}
return (C) configs.get(0);
}
/**
* Specifies the {@link ObjectPostProcessor} to use.
* @param objectPostProcessor the {@link ObjectPostProcessor} to use. Cannot be null
* @return the {@link SecurityBuilder} for further customizations
*/
@SuppressWarnings("unchecked")
public O objectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {
Assert.notNull(objectPostProcessor,"objectPostProcessor cannot be null");
this.objectPostProcessor = objectPostProcessor;
return (O) this;
}
/**
* Performs post processing of an object. The default is to delegate to the
* {@link ObjectPostProcessor}.
*
* @param object the Object to post process
* @return the possibly modified Object to use
*/
protected <P> P postProcess(P object) {
return (P) this.objectPostProcessor.postProcess(object);
}
/**
* Executes the build using the {@link SecurityConfigurer}'s that have been applied using the following steps:
*
* <ul>
* <li>Invokes {@link #beforeInit()} for any subclass to hook into</li>
* <li>Invokes {@link SecurityConfigurer#init(SecurityBuilder)} for any {@link SecurityConfigurer} that was applied to this builder.</li>
* <li>Invokes {@link #beforeConfigure()} for any subclass to hook into</li>
* <li>Invokes {@link #performBuild()} which actually builds the Object</li>
* </ul>
*/
@Override
protected final O doBuild() throws Exception {
synchronized(configurers) {
buildState = BuildState.INITIALIZING;
beforeInit();
init();
buildState = BuildState.CONFIGURING;
beforeConfigure();
configure();
buildState = BuildState.BUILDING;
O result = performBuild();
buildState = BuildState.BUILT;
return result;
}
}
/**
* Invoked prior to invoking each
* {@link SecurityConfigurer#init(SecurityBuilder)} method. Subclasses may
* override this method to hook into the lifecycle without using a
* {@link SecurityConfigurer}.
*/
protected void beforeInit() throws Exception {
}
/**
* Invoked prior to invoking each
* {@link SecurityConfigurer#configure(SecurityBuilder)} method.
* Subclasses may override this method to hook into the lifecycle without
* using a {@link SecurityConfigurer}.
*/
protected void beforeConfigure() throws Exception {
}
/**
* Subclasses must implement this method to build the object that is being returned.
*
* @return
*/
protected abstract O performBuild() throws Exception;
@SuppressWarnings("unchecked")
private void init() throws Exception {
Collection<SecurityConfigurer<O,B>> configurers = getConfigurers();
for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.init((B) this);
}
for (SecurityConfigurer<O, B> configurer : configurersAddedInInitializing) {
configurer.init((B) this);
}
}
@SuppressWarnings("unchecked")
private void configure() throws Exception {
Collection<SecurityConfigurer<O,B>> configurers = getConfigurers();
for(SecurityConfigurer<O,B> configurer : configurers ) {
configurer.configure((B) this);
}
}
private Collection<SecurityConfigurer<O, B>> getConfigurers() {
List<SecurityConfigurer<O,B>> result = new ArrayList<SecurityConfigurer<O,B>>();
for(List<SecurityConfigurer<O,B>> configs : this.configurers.values()) {
result.addAll(configs);
}
return result;
}
/**
* Determines if the object is unbuilt.
* @return true, if unbuilt else false
*/
private boolean isUnbuilt() {
synchronized(configurers) {
return buildState == BuildState.UNBUILT;
}
}
/**
* The build state for the application
*
* @author Rob Winch
* @since 3.2
*/
private static enum BuildState {
/**
* This is the state before the {@link Builder#build()} is invoked
*/
UNBUILT(0),
/**
* The state from when {@link Builder#build()} is first invoked until
* all the {@link SecurityConfigurer#init(SecurityBuilder)} methods
* have been invoked.
*/
INITIALIZING(1),
/**
* The state from after all
* {@link SecurityConfigurer#init(SecurityBuilder)} have been invoked
* until after all the
* {@link SecurityConfigurer#configure(SecurityBuilder)} methods have
* been invoked.
*/
CONFIGURING(2),
/**
* From the point after all the
* {@link SecurityConfigurer#configure(SecurityBuilder)} have
* completed to just after
* {@link AbstractConfiguredSecurityBuilder#performBuild()}.
*/
BUILDING(3),
/**
* After the object has been completely built.
*/
BUILT(4);
private final int order;
BuildState(int order) {
this.order = order;
}
public boolean isInitializing() {
return INITIALIZING.order == order;
}
/**
* Determines if the state is CONFIGURING or later
* @return
*/
public boolean isConfigured() {
return order >= CONFIGURING.order;
}
}
}
@@ -1,66 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* A base {@link SecurityBuilder} that ensures the object being built is only
* built one time.
*
* @param <O> the type of Object that is being built
*
* @author Rob Winch
*
*/
public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> {
private AtomicBoolean building = new AtomicBoolean();
private O object;
/* (non-Javadoc)
* @see org.springframework.security.config.annotation.SecurityBuilder#build()
*/
public final O build() throws Exception {
if(building.compareAndSet(false, true)) {
object = doBuild();
return object;
}
throw new AlreadyBuiltException("This object has already been built");
}
/**
* Gets the object that was built. If it has not been built yet an Exception
* is thrown.
*
* @return the Object that was built
*/
public final O getObject() {
if(!building.get()) {
throw new IllegalStateException("This object has not been built");
}
return object;
}
/**
* Subclasses should implement this to perform the build.
*
* @return the object that should be returned by {@link #build()}.
*
* @throws Exception if an error occurs
*/
protected abstract O doBuild() throws Exception;
}
@@ -1,31 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation;
/**
* Thrown when {@link AbstractSecurityBuilder#build()} is two or more times.
*
* @author Rob Winch
* @since 3.2
*/
public class AlreadyBuiltException extends IllegalStateException {
public AlreadyBuiltException(String message) {
super(message);
}
private static final long serialVersionUID = -5891004752785553015L;
}
@@ -1,42 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation;
import org.springframework.beans.factory.Aware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
/**
* Allows initialization of Objects. Typically this is used to call the
* {@link Aware} methods, {@link InitializingBean#afterPropertiesSet()}, and
* ensure that {@link DisposableBean#destroy()} has been invoked.
*
* @param <T> the bound of the types of Objects this {@link ObjectPostProcessor} supports.
*
* @author Rob Winch
* @since 3.2
*/
public interface ObjectPostProcessor<T> {
/**
* Initialize the object possibly returning a modified instance that should
* be used instead.
*
* @param object the object to initialize
* @return the initialized version of the object
*/
<O extends T> O postProcess(O object);
}
@@ -1,35 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation;
/**
* Interface for building an Object
*
* @author Rob Winch
* @since 3.2
*
* @param <O> The type of the Object being built
*/
public interface SecurityBuilder<O> {
/**
* Builds the object and returns it or null.
*
* @return the Object to be built or null if the implementation allows it.
* @throws Exception if an error occurred when building the Object
*/
O build() throws Exception;
}
@@ -1,55 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation;
/**
* Allows for configuring a {@link SecurityBuilder}. All
* {@link SecurityConfigurer} first have their {@link #init(SecurityBuilder)}
* method invoked. After all {@link #init(SecurityBuilder)} methods have been
* invoked, each {@link #configure(SecurityBuilder)} method is invoked.
*
* @see AbstractConfiguredSecurityBuilder
*
* @author Rob Winch
*
* @param <O>
* The object being built by the {@link SecurityBuilder} B
* @param <B>
* The {@link SecurityBuilder} that builds objects of type O. This is
* also the {@link SecurityBuilder} that is being configured.
*/
public interface SecurityConfigurer<O, B extends SecurityBuilder<O>> {
/**
* Initialize the {@link SecurityBuilder}. Here only shared state should be
* created and modified, but not properties on the {@link SecurityBuilder}
* used for building the object. This ensures that the
* {@link #configure(SecurityBuilder)} method uses the correct shared
* objects when building.
*
* @param builder
* @throws Exception
*/
void init(B builder) throws Exception;
/**
* Configure the {@link SecurityBuilder} by setting the necessary properties
* on the {@link SecurityBuilder}.
*
* @param builder
* @throws Exception
*/
void configure(B builder) throws Exception;
}
@@ -1,132 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation;
import java.util.ArrayList;
import java.util.List;
import org.springframework.core.GenericTypeResolver;
/**
* A base class for {@link SecurityConfigurer} that allows subclasses to only
* implement the methods they are interested in. It also provides a mechanism
* for using the {@link SecurityConfigurer} and when done gaining access to the
* {@link SecurityBuilder} that is being configured.
*
* @author Rob Winch
*
* @param <O>
* The Object being built by B
* @param <B>
* The Builder that is building O and is configured by {@link SecurityConfigurerAdapter}
*/
public abstract class SecurityConfigurerAdapter<O,B extends SecurityBuilder<O>> implements SecurityConfigurer<O,B> {
private B securityBuilder;
private CompositeObjectPostProcessor objectPostProcessor = new CompositeObjectPostProcessor();
public void init(B builder) throws Exception {}
public void configure(B builder) throws Exception {}
/**
* Return the {@link SecurityBuilder} when done using the
* {@link SecurityConfigurer}. This is useful for method chaining.
*
* @return
*/
public B and() {
return getBuilder();
}
/**
* Gets the {@link SecurityBuilder}. Cannot be null.
*
* @return the {@link SecurityBuilder}
* @throw {@link IllegalStateException} if {@link SecurityBuilder} is null
*/
protected final B getBuilder() {
if(securityBuilder == null) {
throw new IllegalStateException("securityBuilder cannot be null");
}
return securityBuilder;
}
/**
* Performs post processing of an object. The default is to delegate to the
* {@link ObjectPostProcessor}.
*
* @param object the Object to post process
* @return the possibly modified Object to use
*/
@SuppressWarnings("unchecked")
protected <T> T postProcess(T object) {
return (T) this.objectPostProcessor.postProcess(object);
}
/**
* Adds an {@link ObjectPostProcessor} to be used for this
* {@link SecurityConfigurerAdapter}. The default implementation does
* nothing to the object.
*
* @param objectPostProcessor the {@link ObjectPostProcessor} to use
*/
public void addObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
this.objectPostProcessor.addObjectPostProcessor(objectPostProcessor);
}
/**
* Sets the {@link SecurityBuilder} to be used. This is automatically set
* when using
* {@link AbstractConfiguredSecurityBuilder#apply(SecurityConfigurerAdapter)}
*
* @param builder the {@link SecurityBuilder} to set
*/
public void setBuilder(B builder) {
this.securityBuilder = builder;
}
/**
* An {@link ObjectPostProcessor} that delegates work to numerous
* {@link ObjectPostProcessor} implementations.
*
* @author Rob Winch
*/
private static final class CompositeObjectPostProcessor implements ObjectPostProcessor<Object> {
private List<ObjectPostProcessor<? extends Object>> postProcessors = new ArrayList<ObjectPostProcessor<?>>();
@SuppressWarnings({ "rawtypes", "unchecked" })
public Object postProcess(Object object) {
for(ObjectPostProcessor opp : postProcessors) {
Class<?> oppClass = opp.getClass();
Class<?> oppType = GenericTypeResolver.resolveTypeArgument(oppClass,ObjectPostProcessor.class);
if(oppType == null || oppType.isAssignableFrom(object.getClass())) {
object = opp.postProcess(object);
}
}
return object;
}
/**
* Adds an {@link ObjectPostProcessor} to use
* @param objectPostProcessor the {@link ObjectPostProcessor} to add
* @return true if the {@link ObjectPostProcessor} was added, else false
*/
private boolean addObjectPostProcessor(ObjectPostProcessor<?extends Object> objectPostProcessor) {
return this.postProcessors.add(objectPostProcessor);
}
}
}
@@ -1,44 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.authentication;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.config.annotation.SecurityBuilder;
/**
* Interface for operating on a SecurityBuilder that creates a {@link ProviderManager}
*
* @author Rob Winch
*
* @param <B> the type of the {@link SecurityBuilder}
*/
public interface ProviderManagerBuilder<B extends ProviderManagerBuilder<B>> extends SecurityBuilder<AuthenticationManager> {
/**
* Add authentication based upon the custom {@link AuthenticationProvider}
* that is passed in. Since the {@link AuthenticationProvider}
* implementation is unknown, all customizations must be done externally and
* the {@link ProviderManagerBuilder} is returned immediately.
*
* @return a {@link ProviderManagerBuilder} to allow further authentication
* to be provided to the {@link ProviderManagerBuilder}
* @throws Exception
* if an error occurs when adding the {@link AuthenticationProvider}
*/
B authenticationProvider(AuthenticationProvider authenticationProvider);
}
@@ -1,298 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.authentication.builders;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.authentication.AuthenticationEventPublisher;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.SecurityBuilder;
import org.springframework.security.config.annotation.SecurityConfigurer;
import org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;
import org.springframework.security.config.annotation.authentication.configurers.ldap.LdapAuthenticationProviderConfigurer;
import org.springframework.security.config.annotation.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer;
import org.springframework.security.config.annotation.authentication.configurers.provisioning.JdbcUserDetailsManagerConfigurer;
import org.springframework.security.config.annotation.authentication.configurers.userdetails.DaoAuthenticationConfigurer;
import org.springframework.security.config.annotation.authentication.configurers.userdetails.UserDetailsAwareConfigurer;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.util.Assert;
/**
* {@link SecurityBuilder} used to create an {@link AuthenticationManager}.
* Allows for easily building in memory authentication, LDAP authentication,
* JDBC based authentication, adding {@link UserDetailsService}, and adding
* {@link AuthenticationProvider}'s.
*
* @author Rob Winch
* @since 3.2
*/
public class AuthenticationManagerBuilder extends AbstractConfiguredSecurityBuilder<AuthenticationManager, AuthenticationManagerBuilder> implements ProviderManagerBuilder<AuthenticationManagerBuilder> {
private final Log logger = LogFactory.getLog(getClass());
private AuthenticationManager parentAuthenticationManager;
private List<AuthenticationProvider> authenticationProviders = new ArrayList<AuthenticationProvider>();
private UserDetailsService defaultUserDetailsService;
private Boolean eraseCredentials;
private AuthenticationEventPublisher eventPublisher;
/**
* Creates a new instance
* @param the {@link ObjectPostProcessor} instance to use.
*/
public AuthenticationManagerBuilder(ObjectPostProcessor<Object> objectPostProcessor) {
super(objectPostProcessor,true);
}
/**
* Allows providing a parent {@link AuthenticationManager} that will be
* tried if this {@link AuthenticationManager} was unable to attempt to
* authenticate the provided {@link Authentication}.
*
* @param authenticationManager
* the {@link AuthenticationManager} that should be used if the
* current {@link AuthenticationManager} was unable to attempt to
* authenticate the provided {@link Authentication}.
* @return the {@link AuthenticationManagerBuilder} for further adding types
* of authentication
*/
public AuthenticationManagerBuilder parentAuthenticationManager(
AuthenticationManager authenticationManager) {
if(authenticationManager instanceof ProviderManager) {
eraseCredentials(((ProviderManager) authenticationManager).isEraseCredentialsAfterAuthentication());
}
this.parentAuthenticationManager = authenticationManager;
return this;
}
/**
* Sets the {@link AuthenticationEventPublisher}
*
* @param eventPublisher
* the {@link AuthenticationEventPublisher} to use
* @return the {@link AuthenticationManagerBuilder} for further
* customizations
*/
public AuthenticationManagerBuilder authenticationEventPublisher(AuthenticationEventPublisher eventPublisher) {
Assert.notNull(eventPublisher, "AuthenticationEventPublisher cannot be null");
this.eventPublisher = eventPublisher;
return this;
}
/**
*
*
* @param eraseCredentials
* true if {@link AuthenticationManager} should clear the
* credentials from the {@link Authentication} object after
* authenticating
* @return the {@link AuthenticationManagerBuilder} for further customizations
*/
public AuthenticationManagerBuilder eraseCredentials(boolean eraseCredentials) {
this.eraseCredentials = eraseCredentials;
return this;
}
/**
* Add in memory authentication to the {@link AuthenticationManagerBuilder}
* and return a {@link InMemoryUserDetailsManagerConfigurer} to
* allow customization of the in memory authentication.
*
* <p>
* This method also ensure that a {@link UserDetailsService} is available
* for the {@link #getDefaultUserDetailsService()} method. Note that
* additional {@link UserDetailsService}'s may override this
* {@link UserDetailsService} as the default.
* </p>
*
* @return a {@link InMemoryUserDetailsManagerConfigurer} to allow
* customization of the in memory authentication
* @throws Exception
* if an error occurs when adding the in memory authentication
*/
public InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> inMemoryAuthentication()
throws Exception {
return apply(new InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder>());
}
/**
* Add JDBC authentication to the {@link AuthenticationManagerBuilder} and
* return a {@link JdbcUserDetailsManagerConfigurer} to allow customization
* of the JDBC authentication.
*
* <p>
* When using with a persistent data store, it is best to add users external
* of configuration using something like <a
* href="http://flywaydb.org/">Flyway</a> or <a
* href="http://www.liquibase.org/">Liquibase</a> to create the schema and
* adding users to ensure these steps are only done once and that the
* optimal SQL is used.
* </p>
*
* <p>
* This method also ensure that a {@link UserDetailsService} is available
* for the {@link #getDefaultUserDetailsService()} method. Note that
* additional {@link UserDetailsService}'s may override this
* {@link UserDetailsService} as the default. See the <a href=
* "http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#user-schema"
* >User Schema</a> section of the reference for the default schema.
* </p>
*
* @return a {@link JdbcUserDetailsManagerConfigurer} to allow customization
* of the JDBC authentication
* @throws Exception
* if an error occurs when adding the JDBC authentication
*/
public JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder> jdbcAuthentication()
throws Exception {
return apply(new JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder>());
}
/**
* Add authentication based upon the custom {@link UserDetailsService} that
* is passed in. It then returns a {@link DaoAuthenticationConfigurer} to
* allow customization of the authentication.
*
* <p>
* This method also ensure that the {@link UserDetailsService} is available
* for the {@link #getDefaultUserDetailsService()} method. Note that
* additional {@link UserDetailsService}'s may override this
* {@link UserDetailsService} as the default.
* </p>
*
* @return a {@link DaoAuthenticationConfigurer} to allow customization
* of the DAO authentication
* @throws Exception
* if an error occurs when adding the {@link UserDetailsService}
* based authentication
*/
public <T extends UserDetailsService> DaoAuthenticationConfigurer<AuthenticationManagerBuilder,T> userDetailsService(
T userDetailsService) throws Exception {
this.defaultUserDetailsService = userDetailsService;
return apply(new DaoAuthenticationConfigurer<AuthenticationManagerBuilder,T>(userDetailsService));
}
/**
* Add LDAP authentication to the {@link AuthenticationManagerBuilder} and
* return a {@link LdapAuthenticationProviderConfigurer} to allow
* customization of the LDAP authentication.
*
* <p>
* This method <b>does NOT</b> ensure that a {@link UserDetailsService} is
* available for the {@link #getDefaultUserDetailsService()} method.
* </p>
*
* @return a {@link LdapAuthenticationProviderConfigurer} to allow
* customization of the LDAP authentication
* @throws Exception
* if an error occurs when adding the LDAP authentication
*/
public LdapAuthenticationProviderConfigurer<AuthenticationManagerBuilder> ldapAuthentication()
throws Exception {
return apply(new LdapAuthenticationProviderConfigurer<AuthenticationManagerBuilder>());
}
/**
* Add authentication based upon the custom {@link AuthenticationProvider}
* that is passed in. Since the {@link AuthenticationProvider}
* implementation is unknown, all customizations must be done externally and
* the {@link AuthenticationManagerBuilder} is returned immediately.
*
* <p>
* This method <b>does NOT</b> ensure that the {@link UserDetailsService} is
* available for the {@link #getDefaultUserDetailsService()} method.
* </p>
*
* @return a {@link AuthenticationManagerBuilder} to allow further authentication
* to be provided to the {@link AuthenticationManagerBuilder}
* @throws Exception
* if an error occurs when adding the {@link AuthenticationProvider}
*/
public AuthenticationManagerBuilder authenticationProvider(
AuthenticationProvider authenticationProvider) {
this.authenticationProviders.add(authenticationProvider);
return this;
}
@Override
protected ProviderManager performBuild() throws Exception {
if(!isConfigured()) {
logger.debug("No authenticationProviders and no parentAuthenticationManager defined. Returning null.");
return null;
}
ProviderManager providerManager = new ProviderManager(authenticationProviders, parentAuthenticationManager);
if(eraseCredentials != null) {
providerManager.setEraseCredentialsAfterAuthentication(eraseCredentials);
}
if(eventPublisher != null) {
providerManager.setAuthenticationEventPublisher(eventPublisher);
}
providerManager = postProcess(providerManager);
return providerManager;
}
/**
* Determines if the {@link AuthenticationManagerBuilder} is configured to
* build a non null {@link AuthenticationManager}. This means that either a
* non-null parent is specified or at least one
* {@link AuthenticationProvider} has been specified.
*
* <p>
* When using {@link SecurityConfigurer} instances, the
* {@link AuthenticationManagerBuilder} will not be configured until the
* {@link SecurityConfigurer#configure(SecurityBuilder)} methods. This means
* a {@link SecurityConfigurer} that is last could check this method and
* provide a default configuration in the
* {@link SecurityConfigurer#configure(SecurityBuilder)} method.
*
* @return
*/
public boolean isConfigured() {
return !authenticationProviders.isEmpty() || parentAuthenticationManager != null;
}
/**
* Gets the default {@link UserDetailsService} for the
* {@link AuthenticationManagerBuilder}. The result may be null in some
* circumstances.
*
* @return the default {@link UserDetailsService} for the
* {@link AuthenticationManagerBuilder}
*/
public UserDetailsService getDefaultUserDetailsService() {
return this.defaultUserDetailsService;
}
/**
* Captures the {@link UserDetailsService} from any {@link UserDetailsAwareConfigurer}.
*
* @param configurer the {@link UserDetailsAwareConfigurer} to capture the {@link UserDetailsService} from.
* @return the {@link UserDetailsAwareConfigurer} for further customizations
* @throws Exception if an error occurs
*/
private <C extends UserDetailsAwareConfigurer<AuthenticationManagerBuilder,? extends UserDetailsService>> C apply(C configurer) throws Exception {
this.defaultUserDetailsService = configurer.getUserDetailsService();
return (C) super.apply(configurer);
}
}
@@ -1,141 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.authentication.configuration;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.aop.target.LazyInitTargetSource;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configurers.GlobalAuthenticationConfigurerAdapter;
import org.springframework.util.Assert;
/**
* Exports the authentication {@link Configuration}
*
* @author Rob Winch
* @since 3.2
*
*/
@Configuration
public class AuthenticationConfiguration {
private ApplicationContext applicationContext;
private AuthenticationManager authenticationManager;
private boolean authenticationManagerInitialized;
private List<GlobalAuthenticationConfigurerAdapter> globalAuthConfigures = Collections.emptyList();
private ObjectPostProcessor<Object> objectPostProcessor;
@Bean
public AuthenticationManagerBuilder authenticationManagerBuilder(ObjectPostProcessor<Object> objectPostProcessor) {
return new AuthenticationManagerBuilder(objectPostProcessor);
}
@Bean
public static GlobalAuthenticationConfigurerAdapter enableGlobalAuthenticationAutowiredConfigurer(ApplicationContext context) {
return new EnableGlobalAuthenticationAutowiredConfigurer(context);
}
public AuthenticationManager getAuthenticationManager() throws Exception {
if(authenticationManagerInitialized) {
return authenticationManager;
}
AuthenticationManagerBuilder authBuilder = authenticationManagerBuilder(objectPostProcessor);
for(GlobalAuthenticationConfigurerAdapter config : globalAuthConfigures) {
authBuilder.apply(config);
}
authenticationManager = authBuilder.build();
if(authenticationManager == null) {
authenticationManager = getAuthenticationMangerBean();
}
this.authenticationManagerInitialized = true;
return authenticationManager;
}
@Autowired(required = false)
public void setGlobalAuthenticationConfigurers(List<GlobalAuthenticationConfigurerAdapter> configurers) throws Exception {
Collections.sort(configurers, AnnotationAwareOrderComparator.INSTANCE);
this.globalAuthConfigures = configurers;
}
@Autowired
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Autowired
public void setObjectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {
this.objectPostProcessor = objectPostProcessor;
}
@SuppressWarnings("unchecked")
private <T> T lazyBean(Class<T> interfaceName) {
LazyInitTargetSource lazyTargetSource = new LazyInitTargetSource();
String[] beanNamesForType = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, interfaceName);
if(beanNamesForType.length == 0) {
return null;
}
Assert.isTrue(beanNamesForType.length == 1 , "Expecting to only find a single bean for type " + interfaceName + ", but found " + Arrays.asList(beanNamesForType));
lazyTargetSource.setTargetBeanName(beanNamesForType[0]);
lazyTargetSource.setBeanFactory(applicationContext);
ProxyFactoryBean proxyFactory = new ProxyFactoryBean();
proxyFactory = objectPostProcessor.postProcess(proxyFactory);
proxyFactory.setTargetSource(lazyTargetSource);
return (T) proxyFactory.getObject();
}
private AuthenticationManager getAuthenticationMangerBean() {
return lazyBean(AuthenticationManager.class);
}
private static class EnableGlobalAuthenticationAutowiredConfigurer extends GlobalAuthenticationConfigurerAdapter {
private final ApplicationContext context;
private static final Log logger = LogFactory.getLog(EnableGlobalAuthenticationAutowiredConfigurer.class);
public EnableGlobalAuthenticationAutowiredConfigurer(ApplicationContext context) {
this.context = context;
}
@Override
public void init(AuthenticationManagerBuilder auth) {
Map<String, Object> beansWithAnnotation = context.getBeansWithAnnotation(EnableGlobalAuthentication.class);
if(logger.isDebugEnabled()) {
logger.debug("Eagerly initializing " + beansWithAnnotation);
}
}
}
}
@@ -1,91 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.authentication.configuration;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
/**
* The {@link EnableGlobalAuthentication} annotation signals that the annotated
* class can be used to configure a global instance of
* {@link AuthenticationManagerBuilder}. For example:
*
* <pre class="code">
* &#064;Configuration
* &#064;EnableGlobalAuthentication
* public class MyGlobalAuthenticationConfiguration {
*
* &#064;Autowired
* public void configureGlobal(AuthenticationManagerBuilder auth) {
* auth
* .inMemoryAuthentication()
* .withUser(&quot;user&quot;).password(&quot;password&quot;).roles(&quot;USER&quot;).and()
* .withUser(&quot;admin&quot;).password(&quot;password&quot;).roles(&quot;USER&quot;, &quot;ADMIN&quot;);
* }
* }
* </pre>
*
* Annotations that are annotated with {@link EnableGlobalAuthentication} also
* signal that the annotated class can be used to configure a global instance of
* {@link AuthenticationManagerBuilder}. For example:
*
* <pre class="code">
* &#064;Configuration
* &#064;EnableWebSecurity
* public class MyWebSecurityConfiguration extends WebSecurityConfigurerAdapter {
*
* &#064;Autowired
* public void configureGlobal(AuthenticationManagerBuilder auth) {
* auth
* .inMemoryAuthentication()
* .withUser(&quot;user&quot;).password(&quot;password&quot;).roles(&quot;USER&quot;).and()
* .withUser(&quot;admin&quot;).password(&quot;password&quot;).roles(&quot;USER&quot;, &quot;ADMIN&quot;);
* }
*
* // Possibly overridden methods ...
* }
* </pre>
*
* The following annotations are annotated with {@link EnableGlobalAuthentication}
*
* <ul>
* <li> {@link EnableWebSecurity} </li>
* <li> {@link EnableWebMvcSecurity} </li>
* <li> {@link EnableGlobalMethodSecurity} </li>
* </ul>
*
* Configuring {@link AuthenticationManagerBuilder} in a class without the {@link EnableGlobalAuthentication} annotation has
* unpredictable results.
*
* @see EnableWebMvcSecurity
* @see EnableWebSecurity
* @see EnableGlobalMethodSecurity
*
* @author Rob Winch
*
*/
@Retention(value=java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value={java.lang.annotation.ElementType.TYPE})
@Documented
@Import(AuthenticationConfiguration.class)
public @interface EnableGlobalAuthentication {}
@@ -1,39 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.authentication.configurers;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.SecurityConfigurer;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
/**
* A {@link SecurityConfigurer} that can be exposed as a bean to configure the
* global {@link AuthenticationManagerBuilder}. Beans of this type are
* automatically used by {@link AuthenticationConfiguration} to configure the
* global {@link AuthenticationManagerBuilder}.
*
* @since 3.2.1
* @author Rob Winch
*/
@Order(100)
public abstract class GlobalAuthenticationConfigurerAdapter implements SecurityConfigurer<AuthenticationManager, AuthenticationManagerBuilder> {
public void init(AuthenticationManagerBuilder auth) throws Exception {}
public void configure(AuthenticationManagerBuilder auth) throws Exception {}
}
@@ -1,561 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.authentication.configurers.ldap;
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.encoding.PasswordEncoder;
import org.springframework.security.authentication.encoding.PlaintextPasswordEncoder;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;
import org.springframework.security.config.annotation.web.configurers.ChannelSecurityConfigurer;
import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.authentication.AbstractLdapAuthenticator;
import org.springframework.security.ldap.authentication.BindAuthenticator;
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
import org.springframework.security.ldap.authentication.LdapAuthenticator;
import org.springframework.security.ldap.authentication.PasswordComparisonAuthenticator;
import org.springframework.security.ldap.search.FilterBasedLdapUserSearch;
import org.springframework.security.ldap.search.LdapUserSearch;
import org.springframework.security.ldap.server.ApacheDSContainer;
import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator;
import org.springframework.security.ldap.userdetails.InetOrgPersonContextMapper;
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;
import org.springframework.security.ldap.userdetails.LdapUserDetailsMapper;
import org.springframework.security.ldap.userdetails.PersonContextMapper;
import org.springframework.security.ldap.userdetails.UserDetailsContextMapper;
import org.springframework.util.Assert;
import java.io.IOException;
import java.net.ServerSocket;
/**
* Configures LDAP {@link AuthenticationProvider} in the {@link ProviderManagerBuilder}.
*
* @param <B> the {@link ProviderManagerBuilder} type that this is configuring.
*
* @author Rob Winch
* @since 3.2
*/
public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuilder<B>> extends SecurityConfigurerAdapter<AuthenticationManager,B> {
private String groupRoleAttribute = "cn";
private String groupSearchBase = "";
private String groupSearchFilter = "(uniqueMember={0})";
private String rolePrefix = "ROLE_";
private String userSearchBase = ""; // only for search
private String userSearchFilter = null;//"uid={0}"; // only for search
private String[] userDnPatterns;
private BaseLdapPathContextSource contextSource;
private ContextSourceBuilder contextSourceBuilder = new ContextSourceBuilder();
private UserDetailsContextMapper userDetailsContextMapper;
private Object passwordEncoder;
private String passwordAttribute;
private LdapAuthoritiesPopulator ldapAuthoritiesPopulator;
private LdapAuthenticationProvider build() throws Exception {
BaseLdapPathContextSource contextSource = getContextSource();
LdapAuthenticator ldapAuthenticator = createLdapAuthenticator(contextSource);
LdapAuthoritiesPopulator authoritiesPopulator = getLdapAuthoritiesPopulator();
LdapAuthenticationProvider ldapAuthenticationProvider = new LdapAuthenticationProvider(
ldapAuthenticator, authoritiesPopulator);
SimpleAuthorityMapper simpleAuthorityMapper = new SimpleAuthorityMapper();
simpleAuthorityMapper.setPrefix(rolePrefix);
simpleAuthorityMapper.afterPropertiesSet();
ldapAuthenticationProvider.setAuthoritiesMapper(simpleAuthorityMapper);
if(userDetailsContextMapper != null) {
ldapAuthenticationProvider.setUserDetailsContextMapper(userDetailsContextMapper);
}
return ldapAuthenticationProvider;
}
/**
* Specifies the {@link LdapAuthoritiesPopulator}.
*
* @param ldapAuthoritiesPopulator the {@link LdapAuthoritiesPopulator} the default is {@link DefaultLdapAuthoritiesPopulator}
* @return the {@link LdapAuthenticationProviderConfigurer} for further customizations
*/
public LdapAuthenticationProviderConfigurer<B> ldapAuthoritiesPopulator(LdapAuthoritiesPopulator ldapAuthoritiesPopulator) {
this.ldapAuthoritiesPopulator = ldapAuthoritiesPopulator;
return this;
}
/**
* Adds an {@link ObjectPostProcessor} for this class.
*
* @param objectPostProcessor
* @return the {@link ChannelSecurityConfigurer} for further customizations
*/
public LdapAuthenticationProviderConfigurer<B> withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
addObjectPostProcessor(objectPostProcessor);
return this;
}
/**
* Gets the {@link LdapAuthoritiesPopulator} and defaults to {@link DefaultLdapAuthoritiesPopulator}
*
* @return the {@link LdapAuthoritiesPopulator}
*/
private LdapAuthoritiesPopulator getLdapAuthoritiesPopulator() {
if(ldapAuthoritiesPopulator != null) {
return ldapAuthoritiesPopulator;
}
DefaultLdapAuthoritiesPopulator defaultAuthoritiesPopulator = new DefaultLdapAuthoritiesPopulator(
contextSource, groupSearchBase);
defaultAuthoritiesPopulator.setGroupRoleAttribute(groupRoleAttribute);
defaultAuthoritiesPopulator.setGroupSearchFilter(groupSearchFilter);
this.ldapAuthoritiesPopulator = defaultAuthoritiesPopulator;
return defaultAuthoritiesPopulator;
}
/**
* Creates the {@link LdapAuthenticator} to use
*
* @param contextSource the {@link BaseLdapPathContextSource} to use
* @return the {@link LdapAuthenticator} to use
*/
private LdapAuthenticator createLdapAuthenticator(BaseLdapPathContextSource contextSource) {
AbstractLdapAuthenticator ldapAuthenticator = passwordEncoder == null ? createBindAuthenticator(contextSource) : createPasswordCompareAuthenticator(contextSource);
LdapUserSearch userSearch = createUserSearch();
if(userSearch != null) {
ldapAuthenticator.setUserSearch(userSearch);
}
if(userDnPatterns != null && userDnPatterns.length > 0) {
ldapAuthenticator.setUserDnPatterns(userDnPatterns);
}
return postProcess(ldapAuthenticator);
}
/**
* Creates {@link PasswordComparisonAuthenticator}
*
* @param contextSource the {@link BaseLdapPathContextSource} to use
* @return
*/
private PasswordComparisonAuthenticator createPasswordCompareAuthenticator(BaseLdapPathContextSource contextSource) {
PasswordComparisonAuthenticator ldapAuthenticator = new PasswordComparisonAuthenticator(contextSource);
if(passwordAttribute != null) {
ldapAuthenticator.setPasswordAttributeName(passwordAttribute);
}
ldapAuthenticator.setPasswordEncoder(passwordEncoder);
return ldapAuthenticator;
}
/**
* Creates a {@link BindAuthenticator}
*
* @param contextSource the {@link BaseLdapPathContextSource} to use
* @return the {@link BindAuthenticator} to use
*/
private BindAuthenticator createBindAuthenticator(
BaseLdapPathContextSource contextSource) {
return new BindAuthenticator(contextSource);
}
private LdapUserSearch createUserSearch() {
if(userSearchFilter == null) {
return null;
}
return new FilterBasedLdapUserSearch(userSearchBase, userSearchFilter, contextSource);
}
/**
* Specifies the {@link BaseLdapPathContextSource} to be used. If not
* specified, an embedded LDAP server will be created using
* {@link #contextSource()}.
*
* @param contextSource
* the {@link BaseLdapPathContextSource} to use
* @return the {@link LdapAuthenticationProviderConfigurer} for further
* customizations
* @see #contextSource()
*/
public LdapAuthenticationProviderConfigurer<B> contextSource(BaseLdapPathContextSource contextSource) {
this.contextSource = contextSource;
return this;
}
/**
* Allows easily configuring of a {@link BaseLdapPathContextSource} with
* defaults pointing to an embedded LDAP server that is created.
*
* @return the {@link ContextSourceBuilder} for further customizations
*/
public ContextSourceBuilder contextSource() {
return contextSourceBuilder;
}
/**
* Specifies the {@link PasswordEncoder} to be used when authenticating with
* password comparison.
*
* @param passwordEncoder the {@link PasswordEncoder} to use
* @return the {@link LdapAuthenticationProviderConfigurer} for further customization
* @deprecated Use {@link #passwordEncoder(org.springframework.security.crypto.password.PasswordEncoder)} instead
*/
public LdapAuthenticationProviderConfigurer<B> passwordEncoder(PasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
return this;
}
/**
* Specifies the {@link org.springframework.security.crypto.password.PasswordEncoder} to be used when authenticating with
* password comparison.
*
* @param passwordEncoder the {@link org.springframework.security.crypto.password.PasswordEncoder} to use
* @return the {@link LdapAuthenticationProviderConfigurer} for further customization
*/
public LdapAuthenticationProviderConfigurer<B> passwordEncoder(final org.springframework.security.crypto.password.PasswordEncoder passwordEncoder) {
Assert.notNull(passwordEncoder, "passwordEncoder must not be null.");
this.passwordEncoder = passwordEncoder;
return this;
}
/**
* If your users are at a fixed location in the directory (i.e. you can work
* out the DN directly from the username without doing a directory search),
* you can use this attribute to map directly to the DN. It maps directly to
* the userDnPatterns property of AbstractLdapAuthenticator. The value is a
* specific pattern used to build the user's DN, for example
* "uid={0},ou=people". The key "{0}" must be present and will be
* substituted with the username.
*
* @param userDnPatterns the LDAP patterns for finding the usernames
* @return the {@link LdapAuthenticationProviderConfigurer} for further customizations
*/
public LdapAuthenticationProviderConfigurer<B> userDnPatterns(String...userDnPatterns) {
this.userDnPatterns = userDnPatterns;
return this;
}
/**
* Allows explicit customization of the loaded user object by specifying a
* UserDetailsContextMapper bean which will be called with the context
* information from the user's directory entry.
*
* @param userDetailsContextMapper the {@link UserDetailsContextMapper} to use
* @return the {@link LdapAuthenticationProviderConfigurer} for further
* customizations
*
* @see PersonContextMapper
* @see InetOrgPersonContextMapper
* @see LdapUserDetailsMapper
*/
public LdapAuthenticationProviderConfigurer<B> userDetailsContextMapper(UserDetailsContextMapper userDetailsContextMapper) {
this.userDetailsContextMapper = userDetailsContextMapper;
return this;
}
/**
* Specifies the attribute name which contains the role name. Default is "cn".
* @param groupRoleAttribute the attribute name that maps a group to a role.
* @return
*/
public LdapAuthenticationProviderConfigurer<B> groupRoleAttribute(String groupRoleAttribute) {
this.groupRoleAttribute = groupRoleAttribute;
return this;
}
/**
* The search base for group membership searches. Defaults to "".
* @param groupSearchBase
* @return the {@link LdapAuthenticationProviderConfigurer} for further customizations
*/
public LdapAuthenticationProviderConfigurer<B> groupSearchBase(String groupSearchBase) {
this.groupSearchBase = groupSearchBase;
return this;
}
/**
* The LDAP filter to search for groups. Defaults to "(uniqueMember={0})".
* The substituted parameter is the DN of the user.
*
* @param groupSearchFilter the LDAP filter to search for groups
* @return the {@link LdapAuthenticationProviderConfigurer} for further customizations
*/
public LdapAuthenticationProviderConfigurer<B> groupSearchFilter(String groupSearchFilter) {
this.groupSearchFilter = groupSearchFilter;
return this;
}
/**
* A non-empty string prefix that will be added as a prefix to the existing
* roles. The default is "ROLE_".
*
* @param rolePrefix the prefix to be added to the roles that are loaded.
* @return the {@link LdapAuthenticationProviderConfigurer} for further customizations
* @see SimpleAuthorityMapper#setPrefix(String)
*/
public LdapAuthenticationProviderConfigurer<B> rolePrefix(String rolePrefix) {
this.rolePrefix = rolePrefix;
return this;
}
/**
* Search base for user searches. Defaults to "". Only used with {@link #userSearchFilter(String)}.
*
* @param userSearchBase search base for user searches
* @return the {@link LdapAuthenticationProviderConfigurer} for further customizations
*/
public LdapAuthenticationProviderConfigurer<B> userSearchBase(String userSearchBase) {
this.userSearchBase = userSearchBase;
return this;
}
/**
* The LDAP filter used to search for users (optional). For example
* "(uid={0})". The substituted parameter is the user's login name.
*
* @param userSearchFilter
* the LDAP filter used to search for users
* @return the {@link LdapAuthenticationProviderConfigurer} for further
* customizations
*/
public LdapAuthenticationProviderConfigurer<B> userSearchFilter(String userSearchFilter) {
this.userSearchFilter = userSearchFilter;
return this;
}
@Override
public void configure(B builder) throws Exception {
LdapAuthenticationProvider provider = postProcess(build());
builder.authenticationProvider(provider);
}
/**
* Sets up Password based comparison
*
* @author Rob Winch
*/
public final class PasswordCompareConfigurer {
/**
* Allows specifying the {@link PasswordEncoder} to use. The default is {@link PlaintextPasswordEncoder}.
* @param passwordEncoder the {@link PasswordEncoder} to use
* @return the {@link PasswordEncoder} to use
*/
public PasswordCompareConfigurer passwordEncoder(PasswordEncoder passwordEncoder) {
LdapAuthenticationProviderConfigurer.this.passwordEncoder = passwordEncoder;
return this;
}
/**
* The attribute in the directory which contains the user password. Defaults to "userPassword".
*
* @param passwordAttribute the attribute in the directory which contains the user password
* @return the {@link PasswordCompareConfigurer} for further customizations
*/
public PasswordCompareConfigurer passwordAttribute(String passwordAttribute) {
LdapAuthenticationProviderConfigurer.this.passwordAttribute = passwordAttribute;
return this;
}
/**
* Allows obtaining a reference to the
* {@link LdapAuthenticationProviderConfigurer} for further
* customizations
*
* @return attribute in the directory which contains the user password
*/
public LdapAuthenticationProviderConfigurer<B> and() {
return LdapAuthenticationProviderConfigurer.this;
}
private PasswordCompareConfigurer() {}
}
/**
* Allows building a {@link BaseLdapPathContextSource} and optionally
* creating an embedded LDAP instance.
*
* @author Rob Winch
* @since 3.2
*/
public final class ContextSourceBuilder {
private String ldif = "classpath*:*.ldif";
private String managerPassword;
private String managerDn;
private Integer port;
private static final int DEFAULT_PORT = 33389;
private String root = "dc=springframework,dc=org";
private String url;
/**
* Specifies an ldif to load at startup for an embedded LDAP server.
* This only loads if using an embedded instance. The default is
* "classpath*:*.ldif".
*
* @param ldif
* the ldif to load at startup for an embedded LDAP server.
* @return the {@link ContextSourceBuilder} for further customization
*/
public ContextSourceBuilder ldif(String ldif) {
this.ldif = ldif;
return this;
}
/**
* Username (DN) of the "manager" user identity (i.e.
* "uid=admin,ou=system") which will be used to authenticate to a
* (non-embedded) LDAP server. If omitted, anonymous access will be
* used.
*
* @param managerDn
* the username (DN) of the "manager" user identity used to
* authenticate to a LDAP server.
* @return the {@link ContextSourceBuilder} for further customization
*/
public ContextSourceBuilder managerDn(String managerDn) {
this.managerDn = managerDn;
return this;
}
/**
* The password for the manager DN. This is required if the manager-dn is specified.
* @param managerPassword password for the manager DN
* @return the {@link ContextSourceBuilder} for further customization
*/
public ContextSourceBuilder managerPassword(String managerPassword) {
this.managerPassword = managerPassword;
return this;
}
/**
* The port to connect to LDAP to (the default is 33389 or random available port if unavailable).
* @param port the port to connect to
* @return the {@link ContextSourceBuilder} for further customization
*/
public ContextSourceBuilder port(int port) {
this.port = port;
return this;
}
/**
* Optional root suffix for the embedded LDAP server. Default is
* "dc=springframework,dc=org"
*
* @param root
* root suffix for the embedded LDAP server
* @return the {@link ContextSourceBuilder} for further customization
*/
public ContextSourceBuilder root(String root) {
this.root = root;
return this;
}
/**
* Specifies the ldap server URL when not using the embedded LDAP
* server. For example, "ldaps://ldap.example.com:33389/dc=myco,dc=org".
*
* @param url
* the ldap server URL
* @return the {@link ContextSourceBuilder} for further customization
*/
public ContextSourceBuilder url(String url) {
this.url = url;
return this;
}
/**
* Gets the {@link LdapAuthenticationProviderConfigurer} for further
* customizations
*
* @return the {@link LdapAuthenticationProviderConfigurer} for further
* customizations
*/
public LdapAuthenticationProviderConfigurer<B> and() {
return LdapAuthenticationProviderConfigurer.this;
}
private DefaultSpringSecurityContextSource build() throws Exception {
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(getProviderUrl());
if(managerDn != null) {
contextSource.setUserDn(managerDn);
if(managerPassword == null) {
throw new IllegalStateException("managerPassword is required if managerDn is supplied");
}
contextSource.setPassword(managerPassword);
}
contextSource = postProcess(contextSource);
if(url != null) {
return contextSource;
}
ApacheDSContainer apacheDsContainer = new ApacheDSContainer(root, ldif);
apacheDsContainer.setPort(getPort());
postProcess(apacheDsContainer);
return contextSource;
}
private int getPort() {
if(port == null) {
port = getDefaultPort();
}
return port;
}
private int getDefaultPort() {
ServerSocket serverSocket = null;
try {
try {
serverSocket = new ServerSocket(DEFAULT_PORT);
} catch (IOException e) {
try {
serverSocket = new ServerSocket(0);
} catch(IOException e2) {
return DEFAULT_PORT;
}
}
return serverSocket.getLocalPort();
} finally {
if(serverSocket != null) {
try {
serverSocket.close();
} catch (IOException e) {}
}
}
}
private String getProviderUrl() {
if(url == null) {
return "ldap://127.0.0.1:" + getPort() + "/" + root;
}
return url;
}
private ContextSourceBuilder() {}
}
private BaseLdapPathContextSource getContextSource() throws Exception {
if(contextSource == null) {
contextSource = contextSourceBuilder.build();
}
return contextSource;
}
/**
* @return
*/
public PasswordCompareConfigurer passwordCompare() {
return new PasswordCompareConfigurer()
.passwordAttribute("password")
.passwordEncoder(new PlaintextPasswordEncoder());
}
}
@@ -1,42 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.authentication.configurers.provisioning;
import java.util.ArrayList;
import org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
/**
* Configures an {@link org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder} to
* have in memory authentication. It also allows easily adding users to the in memory authentication.
*
* @param <B> the type of the {@link SecurityBuilder} that is being configured
*
* @author Rob Winch
* @since 3.2
*/
public class InMemoryUserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B>> extends
UserDetailsManagerConfigurer<B,InMemoryUserDetailsManagerConfigurer<B>> {
/**
* Creates a new instance
*/
public InMemoryUserDetailsManagerConfigurer() {
super(new InMemoryUserDetailsManager(new ArrayList<UserDetails>()));
}
}
@@ -1,192 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.authentication.configurers.provisioning;
import java.util.ArrayList;
import java.util.List;
import javax.sql.DataSource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.datasource.init.DataSourceInitializer;
import org.springframework.jdbc.datasource.init.DatabasePopulator;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
import org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;
import org.springframework.security.core.userdetails.UserCache;
import org.springframework.security.provisioning.JdbcUserDetailsManager;
/**
* Configures an {@link org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder} to
* have JDBC authentication. It also allows easily adding users to the database used for authentication and setting up
* the schema.
*
* <p>
* The only required method is the {@link #dataSource(javax.sql.DataSource)} all other methods have reasonable defaults.
* </p>
*
* @param <B> the type of the {@link ProviderManagerBuilder} that is being configured
*
* @author Rob Winch
* @since 3.2
*/
public class JdbcUserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B>> extends
UserDetailsManagerConfigurer<B,JdbcUserDetailsManagerConfigurer<B>> {
private DataSource dataSource;
private List<Resource> initScripts = new ArrayList<Resource>();
public JdbcUserDetailsManagerConfigurer(JdbcUserDetailsManager manager) {
super(manager);
}
public JdbcUserDetailsManagerConfigurer() {
this(new JdbcUserDetailsManager());
}
/**
* Populates the {@link DataSource} to be used. This is the only required attribute.
*
* @param dataSource the {@link DataSource} to be used. Cannot be null.
* @return
* @throws Exception
*/
public JdbcUserDetailsManagerConfigurer<B> dataSource(DataSource dataSource) throws Exception {
this.dataSource = dataSource;
getUserDetailsService().setDataSource(dataSource);
return this;
}
/**
* Sets the query to be used for finding a user by their username. For example:
*
* <code>
* select username,password,enabled from users where username = ?
* </code>
* @param query The query to use for selecting the username, password, and if the user is enabled by username.
* Must contain a single parameter for the username.
* @return The {@link JdbcUserDetailsManagerRegistry} used for additional customizations
* @throws Exception
*/
public JdbcUserDetailsManagerConfigurer<B> usersByUsernameQuery(String query) throws Exception {
getUserDetailsService().setUsersByUsernameQuery(query);
return this;
}
/**
* Sets the query to be used for finding a user's authorities by their username. For example:
*
* <code>
* select username,authority from authorities where username = ?
* </code>
*
* @param query The query to use for selecting the username, authority by username.
* Must contain a single parameter for the username.
* @return The {@link JdbcUserDetailsManagerRegistry} used for additional customizations
* @throws Exception
*/
public JdbcUserDetailsManagerConfigurer<B> authoritiesByUsernameQuery(String query) throws Exception {
getUserDetailsService().setAuthoritiesByUsernameQuery(query);
return this;
}
/**
* An SQL statement to query user's group authorities given a username. For example:
*
* <code>
* select
* g.id, g.group_name, ga.authority
* from
* groups g, group_members gm, group_authorities ga
* where
* gm.username = ? and g.id = ga.group_id and g.id = gm.group_id
* </code>
*
* @param query The query to use for selecting the authorities by group.
* Must contain a single parameter for the username.
* @return The {@link JdbcUserDetailsManagerRegistry} used for additional customizations
* @throws Exception
*/
public JdbcUserDetailsManagerConfigurer<B> groupAuthoritiesByUsername(String query) throws Exception {
JdbcUserDetailsManager userDetailsService = getUserDetailsService();
userDetailsService.setEnableGroups(true);
userDetailsService.setGroupAuthoritiesByUsernameQuery(query);
return this;
}
/**
* A non-empty string prefix that will be added to role strings loaded from persistent storage (default is "").
*
* @param rolePrefix
* @return
* @throws Exception
*/
public JdbcUserDetailsManagerConfigurer<B> rolePrefix(String rolePrefix) throws Exception {
getUserDetailsService().setRolePrefix(rolePrefix);
return this;
}
/**
* Defines the {@link UserCache} to use
*
* @param userCache the {@link UserCache} to use
* @return the {@link JdbcUserDetailsManagerConfigurer} for further customizations
* @throws Exception
*/
public JdbcUserDetailsManagerConfigurer<B> userCache(UserCache userCache) throws Exception {
getUserDetailsService().setUserCache(userCache);
return this;
}
@Override
protected void initUserDetailsService() throws Exception {
if(!initScripts.isEmpty()) {
getDataSourceInit().afterPropertiesSet();
}
super.initUserDetailsService();
}
@Override
public JdbcUserDetailsManager getUserDetailsService() {
return (JdbcUserDetailsManager) super.getUserDetailsService();
}
/**
* Populates the default schema that allows users and authorities to be stored.
*
* @return The {@link JdbcUserDetailsManagerRegistry} used for additional customizations
*/
public JdbcUserDetailsManagerConfigurer<B> withDefaultSchema() {
this.initScripts.add(new ClassPathResource("org/springframework/security/core/userdetails/jdbc/users.ddl"));
return this;
}
protected DatabasePopulator getDatabasePopulator() {
ResourceDatabasePopulator dbp = new ResourceDatabasePopulator();
dbp.setScripts(initScripts.toArray(new Resource[initScripts.size()]));
return dbp;
}
private DataSourceInitializer getDataSourceInit() {
DataSourceInitializer dsi = new DataSourceInitializer();
dsi.setDatabasePopulator(getDatabasePopulator());
dsi.setDataSource(dataSource);
return dsi;
}
}
@@ -1,263 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.authentication.configurers.provisioning;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.security.config.annotation.SecurityBuilder;
import org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;
import org.springframework.security.config.annotation.authentication.configurers.userdetails.UserDetailsServiceConfigurer;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.provisioning.UserDetailsManager;
import org.springframework.util.Assert;
/**
* Base class for populating an
* {@link org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder} with a
* {@link UserDetailsManager}.
*
* @param <B> the type of the {@link SecurityBuilder} that is being configured
* @param <C> the type of {@link UserDetailsManagerConfigurer}
*
* @author Rob Winch
* @since 3.2
*/
public class UserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B>, C extends UserDetailsManagerConfigurer<B,C>> extends
UserDetailsServiceConfigurer<B,C,UserDetailsManager> {
private final List<UserDetailsBuilder> userBuilders = new ArrayList<UserDetailsBuilder>();
protected UserDetailsManagerConfigurer(UserDetailsManager userDetailsManager) {
super(userDetailsManager);
}
/**
* Populates the users that have been added.
*
* @throws Exception
*/
@Override
protected void initUserDetailsService() throws Exception {
for(UserDetailsBuilder userBuilder : userBuilders) {
getUserDetailsService().createUser(userBuilder.build());
}
}
/**
* Allows adding a user to the {@link UserDetailsManager} that is being created. This method can be invoked
* multiple times to add multiple users.
*
* @param username the username for the user being added. Cannot be null.
* @return
*/
@SuppressWarnings("unchecked")
public final UserDetailsBuilder withUser(String username) {
UserDetailsBuilder userBuilder = new UserDetailsBuilder((C)this);
userBuilder.username(username);
this.userBuilders.add(userBuilder);
return userBuilder;
}
/**
* Builds the user to be added. At minimum the username, password, and authorities should provided. The remaining
* attributes have reasonable defaults.
*
* @param <T> the type of {@link UserDetailsManagerConfigurer} to return for chaining methods.
*/
public class UserDetailsBuilder {
private String username;
private String password;
private List<GrantedAuthority> authorities;
private boolean accountExpired;
private boolean accountLocked;
private boolean credentialsExpired;
private boolean disabled;
private final C builder;
/**
* Creates a new instance
* @param builder the builder to return
*/
private UserDetailsBuilder(C builder) {
this.builder = builder;
}
/**
* Returns the {@link UserDetailsManagerRegistry} for method chaining (i.e. to add another user)
*
* @return the {@link UserDetailsManagerRegistry} for method chaining (i.e. to add another user)
*/
public C and() {
return builder;
}
/**
* Populates the username. This attribute is required.
*
* @param username the username. Cannot be null.
* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate additional attributes for this
* user)
*/
private UserDetailsBuilder username(String username) {
Assert.notNull(username, "username cannot be null");
this.username = username;
return this;
}
/**
* Populates the password. This attribute is required.
*
* @param password the password. Cannot be null.
* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate additional attributes for this
* user)
*/
public UserDetailsBuilder password(String password) {
Assert.notNull(password, "password cannot be null");
this.password = password;
return this;
}
/**
* Populates the roles. This method is a shortcut for calling {@link #authorities(String...)}, but automatically
* prefixes each entry with "ROLE_". This means the following:
*
* <code>
* builder.roles("USER","ADMIN");
* </code>
*
* is equivalent to
*
* <code>
* builder.authorities("ROLE_USER","ROLE_ADMIN");
* </code>
*
* <p>This attribute is required, but can also be populated with {@link #authorities(String...)}.</p>
*
* @param roles the roles for this user (i.e. USER, ADMIN, etc). Cannot be null, contain null values or start
* with "ROLE_"
* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate additional attributes for this
* user)
*/
public UserDetailsBuilder roles(String... roles) {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(roles.length);
for(String role : roles) {
Assert.isTrue(!role.startsWith("ROLE_"), role + " cannot start with ROLE_ (it is automatically added)");
authorities.add(new SimpleGrantedAuthority("ROLE_"+role));
}
return authorities(authorities);
}
/**
* Populates the authorities. This attribute is required.
*
* @param authorities the authorities for this user. Cannot be null, or contain null
* values
* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate additional attributes for this
* user)
* @see #roles(String...)
*/
public UserDetailsBuilder authorities(GrantedAuthority...authorities) {
return authorities(Arrays.asList(authorities));
}
/**
* Populates the authorities. This attribute is required.
*
* @param authorities the authorities for this user. Cannot be null, or contain null
* values
* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate additional attributes for this
* user)
* @see #roles(String...)
*/
public UserDetailsBuilder authorities(List<? extends GrantedAuthority> authorities) {
this.authorities = new ArrayList<GrantedAuthority>(authorities);
return this;
}
/**
* Populates the authorities. This attribute is required.
*
* @param authorities the authorities for this user (i.e. ROLE_USER, ROLE_ADMIN, etc). Cannot be null, or contain null
* values
* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate additional attributes for this
* user)
* @see #roles(String...)
*/
public UserDetailsBuilder authorities(String... authorities) {
return authorities(AuthorityUtils.createAuthorityList(authorities));
}
/**
* Defines if the account is expired or not. Default is false.
*
* @param accountExpired true if the account is expired, false otherwise
* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate additional attributes for this
* user)
*/
public UserDetailsBuilder accountExpired(boolean accountExpired) {
this.accountExpired = accountExpired;
return this;
}
/**
* Defines if the account is locked or not. Default is false.
*
* @param accountLocked true if the account is locked, false otherwise
* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate additional attributes for this
* user)
*/
public UserDetailsBuilder accountLocked(boolean accountLocked) {
this.accountLocked = accountLocked;
return this;
}
/**
* Defines if the credentials are expired or not. Default is false.
*
* @param credentialsExpired true if the credentials are expired, false otherwise
* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate additional attributes for this
* user)
*/
public UserDetailsBuilder credentialsExpired(boolean credentialsExpired) {
this.credentialsExpired = credentialsExpired;
return this;
}
/**
* Defines if the account is disabled or not. Default is false.
*
* @param disabled true if the account is disabled, false otherwise
* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate additional attributes for this
* user)
*/
public UserDetailsBuilder disabled(boolean disabled) {
this.disabled = disabled;
return this;
}
private UserDetails build() {
return new User(username, password, !disabled, !accountExpired,
!credentialsExpired, !accountLocked, authorities);
}
}
}
@@ -1,108 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.authentication.configurers.userdetails;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.SecurityBuilder;
import org.springframework.security.config.annotation.SecurityConfigurer;
import org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* Allows configuring a {@link DaoAuthenticationProvider}
*
* @author Rob Winch
* @since 3.2
*
* @param <B> the type of the {@link SecurityBuilder}
* @param <C> the type of {@link AbstractDaoAuthenticationConfigurer} this is
* @param <U> The type of {@link UserDetailsService} that is being used
*
*/
abstract class AbstractDaoAuthenticationConfigurer<B extends ProviderManagerBuilder<B>, C extends AbstractDaoAuthenticationConfigurer<B,C,U>,U extends UserDetailsService> extends UserDetailsAwareConfigurer<B,U> {
private DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
private final U userDetailsService;
/**
* Creates a new instance
*
* @param userDetailsService
*/
protected AbstractDaoAuthenticationConfigurer(U userDetailsService) {
this.userDetailsService = userDetailsService;
provider.setUserDetailsService(userDetailsService);
}
/**
* Adds an {@link ObjectPostProcessor} for this class.
*
* @param objectPostProcessor
* @return the {@link AbstractDaoAuthenticationConfigurer} for further customizations
*/
@SuppressWarnings("unchecked")
public C withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
addObjectPostProcessor(objectPostProcessor);
return (C) this;
}
/**
* Allows specifying the {@link PasswordEncoder} to use with the {@link DaoAuthenticationProvider}. The default is
* is to use plain text.
*
* @param passwordEncoder The {@link PasswordEncoder} to use.
* @return
*/
@SuppressWarnings("unchecked")
public C passwordEncoder(PasswordEncoder passwordEncoder) {
provider.setPasswordEncoder(passwordEncoder);
return (C) this;
}
/**
* Allows specifying the
* {@link org.springframework.security.authentication.encoding.PasswordEncoder}
* to use with the {@link DaoAuthenticationProvider}. The default is is to
* use plain text.
*
* @param passwordEncoder
* The
* {@link org.springframework.security.authentication.encoding.PasswordEncoder}
* to use.
* @return the {@link SecurityConfigurer} for further customizations
*/
@SuppressWarnings("unchecked")
public C passwordEncoder(org.springframework.security.authentication.encoding.PasswordEncoder passwordEncoder) {
provider.setPasswordEncoder(passwordEncoder);
return (C) this;
}
@Override
public void configure(B builder) throws Exception {
provider = postProcess(provider);
builder.authenticationProvider(provider);
}
/**
* Gets the {@link UserDetailsService} that is used with the {@link DaoAuthenticationProvider}
*
* @return the {@link UserDetailsService} that is used with the {@link DaoAuthenticationProvider}
*/
public U getUserDetailsService() {
return userDetailsService;
}
}
@@ -1,41 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.authentication.configurers.userdetails;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;
import org.springframework.security.core.userdetails.UserDetailsService;
/**
* Allows configuring a {@link DaoAuthenticationProvider}
*
* @author Rob Winch
* @since 3.2
*
* @param <B> The type of {@link ProviderManagerBuilder} this is
* @param <U> The type of {@link UserDetailsService} that is being used
*
*/
public class DaoAuthenticationConfigurer<B extends ProviderManagerBuilder<B>, U extends UserDetailsService> extends AbstractDaoAuthenticationConfigurer<B,DaoAuthenticationConfigurer<B,U>, U>{
/**
* Creates a new instance
* @param userDetailsService
*/
public DaoAuthenticationConfigurer(U userDetailsService) {
super(userDetailsService);
}
}
@@ -1,39 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.authentication.configurers.userdetails;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.core.userdetails.UserDetailsService;
/**
* Base class that allows access to the {@link UserDetailsService} for using as a default value with {@link AuthenticationManagerBuilder}.
*
* @author Rob Winch
*
* @param <B> the type of the {@link ProviderManagerBuilder}
* @param <U> the type of {@link UserDetailsService}
*/
public abstract class UserDetailsAwareConfigurer<B extends ProviderManagerBuilder<B>, U extends UserDetailsService> extends SecurityConfigurerAdapter<AuthenticationManager,B> {
/**
* Gets the {@link UserDetailsService} or null if it is not available
* @return the {@link UserDetailsService} or null if it is not available
*/
public abstract U getUserDetailsService();
}
@@ -1,58 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.authentication.configurers.userdetails;
import org.springframework.security.config.annotation.SecurityConfigurer;
import org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.core.userdetails.UserDetailsService;
/**
* Allows configuring a {@link UserDetailsService} within a {@link AuthenticationManagerBuilder}.
*
* @author Rob Winch
* @since 3.2
*
* @param <B> the type of the {@link SecurityBuilder}
* @param <C> the {@link SecurityConfigurer} (or this)
* @param <U> the type of UserDetailsService being used to allow for returning the concrete UserDetailsService.
*/
public class UserDetailsServiceConfigurer<B extends ProviderManagerBuilder<B>,
C extends UserDetailsServiceConfigurer<B, C, U>,
U extends UserDetailsService>
extends AbstractDaoAuthenticationConfigurer<B, C, U> {
/**
* Creates a new instance
* @param userDetailsService the {@link UserDetailsService} that should be used
*/
public UserDetailsServiceConfigurer(U userDetailsService) {
super(userDetailsService);
}
@Override
public void configure(B builder) throws Exception {
initUserDetailsService();
super.configure(builder);
}
/**
* Allows subclasses to initialize the {@link UserDetailsService}. For example, it might add users, initialize
* schema, etc.
*/
protected void initUserDetailsService() throws Exception {}
}
@@ -1,84 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.configuration;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.Aware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.util.Assert;
/**
* Allows registering Objects to participate with an
* {@link AutowireCapableBeanFactory}'s post processing of {@link Aware}
* methods, {@link InitializingBean#afterPropertiesSet()}, and
* {@link DisposableBean#destroy()}.
*
* @author Rob Winch
* @since 3.2
*/
final class AutowireBeanFactoryObjectPostProcessor implements ObjectPostProcessor<Object>, DisposableBean {
private final Log logger = LogFactory.getLog(getClass());
private final AutowireCapableBeanFactory autowireBeanFactory;
private final List<DisposableBean> disposableBeans = new ArrayList<DisposableBean>();
public AutowireBeanFactoryObjectPostProcessor(
AutowireCapableBeanFactory autowireBeanFactory) {
Assert.notNull(autowireBeanFactory, "autowireBeanFactory cannot be null");
this.autowireBeanFactory = autowireBeanFactory;
}
/* (non-Javadoc)
* @see org.springframework.security.config.annotation.web.Initializer#initialize(java.lang.Object)
*/
@SuppressWarnings("unchecked")
public <T> T postProcess(T object) {
if(object == null) {
return null;
}
T result = null;
try {
result = (T) autowireBeanFactory.initializeBean(object, object.toString());
} catch (RuntimeException e) {
Class<?> type = object.getClass();
throw new RuntimeException("Could not postProcess " + object + " of type " + type, e);
}
if(result instanceof DisposableBean) {
disposableBeans.add((DisposableBean) result);
}
return result;
}
/* (non-Javadoc)
* @see org.springframework.beans.factory.DisposableBean#destroy()
*/
public void destroy() throws Exception {
for(DisposableBean disposable : disposableBeans) {
try {
disposable.destroy();
} catch(Exception error) {
logger.error(error);
}
}
}
}
@@ -1,44 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.configuration;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
/**
* Spring {@link Configuration} that exports the default
* {@link ObjectPostProcessor}. This class is not intended to be imported
* manually rather it is imported automatically when using
* {@link EnableWebSecurity} or {@link EnableGlobalMethodSecurity}.
*
* @see EnableWebSecurity
* @see EnableGlobalMethodSecurity
*
* @author Rob Winch
* @since 3.2
*/
@Configuration
public class ObjectPostProcessorConfiguration {
@Bean
public ObjectPostProcessor<Object> objectPostProcessor(AutowireCapableBeanFactory beanFactory) {
return new AutowireBeanFactoryObjectPostProcessor(beanFactory);
}
}
@@ -1,103 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.method.configuration;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.Import;
import org.springframework.core.Ordered;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.config.annotation.authentication.configuration.EnableGlobalAuthentication;
import org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration;
/**
* <p>Enables Spring Security global method security similar to the
* <global-method-security> xml support.</p>
*
* <p>
* More advanced configurations may wish to extend
* {@link GlobalMethodSecurityConfiguration} and override the protected methods
* to provide custom implementations. Note that
* {@link EnableGlobalMethodSecurity} still must be included on the class
* extending {@link GlobalMethodSecurityConfiguration} to determine the
* settings.
*
* @author Rob Winch
* @since 3.2
*/
@Retention(value=java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value={java.lang.annotation.ElementType.TYPE})
@Documented
@Import({GlobalMethodSecuritySelector.class,ObjectPostProcessorConfiguration.class})
@EnableGlobalAuthentication
public @interface EnableGlobalMethodSecurity {
/**
* Determines if Spring Security's pre post annotations should be enabled. Default is false.
* @return true if pre post annotations should be enabled false otherwise.
*/
boolean prePostEnabled() default false;
/**
* Determines if Spring Security's {@link Secured} annotations should be enabled.
* @return true if {@link Secured} annotations should be enabled false otherwise. Default is false.
*/
boolean securedEnabled() default false;
/**
* Determines if JSR-250 annotations should be enabled. Default is false.
* @return true if JSR-250 should be enabled false otherwise.
*/
boolean jsr250Enabled() default false;
/**
* Indicate whether subclass-based (CGLIB) proxies are to be created ({@code true}) as
* opposed to standard Java interface-based proxies ({@code false}). The default is
* {@code false}. <strong>Applicable only if {@link #mode()} is set to
* {@link AdviceMode#PROXY}</strong>.
*
* <p>Note that setting this attribute to {@code true} will affect <em>all</em>
* Spring-managed beans requiring proxying, not just those marked with
* the Security annotations. For example, other beans marked with Spring's
* {@code @Transactional} annotation will be upgraded to subclass proxying at the same
* time. This approach has no negative impact in practice unless one is explicitly
* expecting one type of proxy vs another, e.g. in tests.
*
* @return true if CGILIB proxies should be created instead of interface based proxies, else false
*/
boolean proxyTargetClass() default false;
/**
* Indicate how security advice should be applied. The default is
* {@link AdviceMode#PROXY}.
* @see AdviceMode
*
* @return the {@link AdviceMode} to use
*/
AdviceMode mode() default AdviceMode.PROXY;
/**
* Indicate the ordering of the execution of the security advisor
* when multiple advices are applied at a specific joinpoint.
* The default is {@link Ordered#LOWEST_PRECEDENCE}.
*
* @return the order the security advisor should be applied
*/
int order() default Ordered.LOWEST_PRECEDENCE;
}
@@ -1,42 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.method.configuration;
import org.aopalliance.intercept.MethodInterceptor;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Role;
import org.springframework.security.access.intercept.aspectj.AspectJMethodSecurityInterceptor;
import org.springframework.security.access.intercept.aspectj.aspect.AnnotationSecurityAspect;
/**
* Creates the AnnotationSecurityAspect for use with AspectJ based security.
*
* @author Rob Winch
* @since 3.2
*/
class GlobalMethodSecurityAspectJConfiguration {
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@Bean
public AnnotationSecurityAspect annotationSecurityAspect(@Qualifier("methodSecurityInterceptor") MethodInterceptor methodSecurityInterceptor) {
AnnotationSecurityAspect result = AnnotationSecurityAspect.aspectOf();
result.setSecurityInterceptor((AspectJMethodSecurityInterceptor )methodSecurityInterceptor);
return result;
}
}
@@ -1,399 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.method.configuration;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.aopalliance.intercept.MethodInterceptor;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.*;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.AfterInvocationProvider;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.access.annotation.Jsr250MethodSecurityMetadataSource;
import org.springframework.security.access.annotation.Jsr250Voter;
import org.springframework.security.access.annotation.SecuredAnnotationSecurityMetadataSource;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.ExpressionBasedAnnotationAttributeFactory;
import org.springframework.security.access.expression.method.ExpressionBasedPostInvocationAdvice;
import org.springframework.security.access.expression.method.ExpressionBasedPreInvocationAdvice;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.access.intercept.AfterInvocationManager;
import org.springframework.security.access.intercept.AfterInvocationProviderManager;
import org.springframework.security.access.intercept.RunAsManager;
import org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor;
import org.springframework.security.access.intercept.aopalliance.MethodSecurityMetadataSourceAdvisor;
import org.springframework.security.access.intercept.aspectj.AspectJMethodSecurityInterceptor;
import org.springframework.security.access.method.DelegatingMethodSecurityMetadataSource;
import org.springframework.security.access.method.MethodSecurityMetadataSource;
import org.springframework.security.access.prepost.PostInvocationAdviceProvider;
import org.springframework.security.access.prepost.PreInvocationAuthorizationAdvice;
import org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter;
import org.springframework.security.access.prepost.PrePostAnnotationSecurityMetadataSource;
import org.springframework.security.access.vote.AffirmativeBased;
import org.springframework.security.access.vote.AuthenticatedVoter;
import org.springframework.security.access.vote.RoleVoter;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.util.Assert;
/**
* Base {@link Configuration} for enabling global method security. Classes may
* extend this class to customize the defaults, but must be sure to specify the
* {@link EnableGlobalMethodSecurity} annotation on the subclass.
*
* @author Rob Winch
* @since 3.2
* @see EnableGlobalMethodSecurity
*/
@Configuration
public class GlobalMethodSecurityConfiguration implements ImportAware {
private static final Log logger = LogFactory.getLog(GlobalMethodSecurityConfiguration.class);
private ObjectPostProcessor<Object> objectPostProcessor = new ObjectPostProcessor<Object>() {
public <T> T postProcess(T object) {
throw new IllegalStateException(ObjectPostProcessor.class.getName()+ " is a required bean. Ensure you have used @"+EnableGlobalMethodSecurity.class.getName());
}
};
private DefaultMethodSecurityExpressionHandler defaultMethodExpressionHandler = new DefaultMethodSecurityExpressionHandler();
private AuthenticationManager authenticationManager;
private AuthenticationManagerBuilder auth;
private boolean disableAuthenticationRegistry;
private AnnotationAttributes enableMethodSecurity;
private ApplicationContext context;
private MethodSecurityExpressionHandler expressionHandler;
/**
* Creates the default MethodInterceptor which is a MethodSecurityInterceptor using the following methods to
* construct it.
* <ul>
* <li>{@link #accessDecisionManager()}</li>
* <li>{@link #afterInvocationManager()}</li>
* <li>{@link #authenticationManager()}</li>
* <li>{@link #methodSecurityMetadataSource()}</li>
* <li>{@link #runAsManager()}</li>
*
* </ul>
*
* <p>
* Subclasses can override this method to provide a different {@link MethodInterceptor}.
* </p>
*
* @return
* @throws Exception
*/
@Bean
public MethodInterceptor methodSecurityInterceptor() throws Exception {
MethodSecurityInterceptor methodSecurityInterceptor = isAspectJ() ? new AspectJMethodSecurityInterceptor() : new MethodSecurityInterceptor();
methodSecurityInterceptor
.setAccessDecisionManager(accessDecisionManager());
methodSecurityInterceptor
.setAfterInvocationManager(afterInvocationManager());
methodSecurityInterceptor
.setAuthenticationManager(authenticationManager());
methodSecurityInterceptor
.setSecurityMetadataSource(methodSecurityMetadataSource());
RunAsManager runAsManager = runAsManager();
if (runAsManager != null) {
methodSecurityInterceptor.setRunAsManager(runAsManager);
}
return methodSecurityInterceptor;
}
/**
* Provide a custom {@link AfterInvocationManager} for the default
* implementation of {@link #methodSecurityInterceptor()}. The default is
* null if pre post is not enabled. Otherwise, it returns a {@link AfterInvocationProviderManager}.
*
* <p>
* Subclasses should override this method to provide a custom {@link AfterInvocationManager}
* </p>
*
* @return
*/
protected AfterInvocationManager afterInvocationManager() {
if(prePostEnabled()) {
AfterInvocationProviderManager invocationProviderManager = new AfterInvocationProviderManager();
ExpressionBasedPostInvocationAdvice postAdvice = new ExpressionBasedPostInvocationAdvice(getExpressionHandler());
PostInvocationAdviceProvider postInvocationAdviceProvider = new PostInvocationAdviceProvider(postAdvice);
List<AfterInvocationProvider> afterInvocationProviders = new ArrayList<AfterInvocationProvider>();
afterInvocationProviders.add(postInvocationAdviceProvider);
invocationProviderManager.setProviders(afterInvocationProviders);
return invocationProviderManager;
}
return null;
}
/**
* Provide a custom {@link RunAsManager} for the default implementation of
* {@link #methodSecurityInterceptor()}. The default is null.
*
* @return
*/
protected RunAsManager runAsManager() {
return null;
}
/**
* Allows subclasses to provide a custom {@link AccessDecisionManager}. The default is a {@link AffirmativeBased}
* with the following voters:
*
* <ul>
* <li>{@link PreInvocationAuthorizationAdviceVoter}</li>
* <li>{@link RoleVoter} </li>
* <li>{@link AuthenticatedVoter} </li>
* </ul>
*
* @return
*/
@SuppressWarnings("rawtypes")
protected AccessDecisionManager accessDecisionManager() {
List<AccessDecisionVoter> decisionVoters = new ArrayList<AccessDecisionVoter>();
ExpressionBasedPreInvocationAdvice expressionAdvice = new ExpressionBasedPreInvocationAdvice();
expressionAdvice.setExpressionHandler(getExpressionHandler());
if(prePostEnabled()) {
decisionVoters.add(new PreInvocationAuthorizationAdviceVoter(
expressionAdvice));
}
if(jsr250Enabled()) {
decisionVoters.add(new Jsr250Voter());
}
decisionVoters.add(new RoleVoter());
decisionVoters.add(new AuthenticatedVoter());
return new AffirmativeBased(decisionVoters);
}
/**
* Provide a {@link MethodSecurityExpressionHandler} that is registered with
* the {@link ExpressionBasedPreInvocationAdvice}. The default is
* {@link DefaultMethodSecurityExpressionHandler} which optionally will
* Autowire an {@link AuthenticationTrustResolver}.
*
* <p>
* Subclasses may override this method to provide a custom
* {@link MethodSecurityExpressionHandler}
* </p>
*
* @return
*/
protected MethodSecurityExpressionHandler createExpressionHandler() {
return defaultMethodExpressionHandler;
}
/**
* Gets the {@link MethodSecurityExpressionHandler} or creates it using {@link #expressionHandler}.
*
* @return a non {@code null} {@link MethodSecurityExpressionHandler}
*/
protected final MethodSecurityExpressionHandler getExpressionHandler() {
if(expressionHandler == null) {
expressionHandler = createExpressionHandler();
}
return expressionHandler;
}
/**
* Provides a custom {@link MethodSecurityMetadataSource} that is registered
* with the {@link #methodSecurityMetadataSource()}. Default is null.
*
* @return a custom {@link MethodSecurityMetadataSource} that is registered
* with the {@link #methodSecurityMetadataSource()}
*/
protected MethodSecurityMetadataSource customMethodSecurityMetadataSource() {
return null;
}
/**
* Allows providing a custom {@link AuthenticationManager}. The default is
* to use any authentication mechanisms registered by {@link #configure(AuthenticationManagerBuilder)}. If
* {@link #configure(AuthenticationManagerBuilder)} was not overridden, then an {@link AuthenticationManager}
* is attempted to be autowired by type.
*
* @return
*/
protected AuthenticationManager authenticationManager() throws Exception {
if(authenticationManager == null) {
DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor.postProcess(new DefaultAuthenticationEventPublisher());
auth = new AuthenticationManagerBuilder(objectPostProcessor);
auth.authenticationEventPublisher(eventPublisher);
configure(auth);
if(disableAuthenticationRegistry) {
authenticationManager = getAuthenticationConfiguration().getAuthenticationManager();
} else {
authenticationManager = auth.build();
}
}
return authenticationManager;
}
/**
* Sub classes can override this method to register different types of authentication. If not overridden,
* {@link #configure(AuthenticationManagerBuilder)} will attempt to autowire by type.
*
* @param auth the {@link AuthenticationManagerBuilder} used to register different authentication mechanisms for the
* global method security.
* @throws Exception
*/
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
this.disableAuthenticationRegistry = true;
}
/**
* Provides the default {@link MethodSecurityMetadataSource} that will be
* used. It creates a {@link DelegatingMethodSecurityMetadataSource} based
* upon {@link #customMethodSecurityMetadataSource()} and the attributes on
* {@link EnableGlobalMethodSecurity}.
*
* @return
*/
@Bean
public MethodSecurityMetadataSource methodSecurityMetadataSource() {
List<MethodSecurityMetadataSource> sources = new ArrayList<MethodSecurityMetadataSource>();
ExpressionBasedAnnotationAttributeFactory attributeFactory = new ExpressionBasedAnnotationAttributeFactory(
getExpressionHandler());
MethodSecurityMetadataSource customMethodSecurityMetadataSource = customMethodSecurityMetadataSource();
if (customMethodSecurityMetadataSource != null) {
sources.add(customMethodSecurityMetadataSource);
}
if (prePostEnabled()) {
sources.add(new PrePostAnnotationSecurityMetadataSource(
attributeFactory));
}
if (securedEnabled()) {
sources.add(new SecuredAnnotationSecurityMetadataSource());
}
if (jsr250Enabled()) {
sources.add(new Jsr250MethodSecurityMetadataSource());
}
return new DelegatingMethodSecurityMetadataSource(sources);
}
/**
* Creates the {@link PreInvocationAuthorizationAdvice} to be used. The
* default is {@link ExpressionBasedPreInvocationAdvice}.
*
* @return
*/
@Bean
public PreInvocationAuthorizationAdvice preInvocationAuthorizationAdvice() {
ExpressionBasedPreInvocationAdvice preInvocationAdvice = new ExpressionBasedPreInvocationAdvice();
preInvocationAdvice.setExpressionHandler(getExpressionHandler());
return preInvocationAdvice;
}
/**
* Obtains the {@link MethodSecurityMetadataSourceAdvisor} to be used.
*
* @return
*/
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@Bean
public MethodSecurityMetadataSourceAdvisor metaDataSourceAdvisor() {
MethodSecurityMetadataSourceAdvisor methodAdvisor = new MethodSecurityMetadataSourceAdvisor(
"methodSecurityInterceptor", methodSecurityMetadataSource(),
"methodSecurityMetadataSource");
methodAdvisor.setOrder(order());
return methodAdvisor;
}
/**
* Obtains the attributes from {@link EnableGlobalMethodSecurity} if this class was imported using the {@link EnableGlobalMethodSecurity} annotation.
*/
public final void setImportMetadata(AnnotationMetadata importMetadata) {
Map<String, Object> annotationAttributes = importMetadata
.getAnnotationAttributes(EnableGlobalMethodSecurity.class
.getName());
enableMethodSecurity = AnnotationAttributes
.fromMap(annotationAttributes);
}
@Autowired(required = false)
public void setAuthenticationTrustResolver(AuthenticationTrustResolver trustResolver) {
this.defaultMethodExpressionHandler.setTrustResolver(trustResolver);
}
@Autowired(required=false)
public void setObjectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {
this.objectPostProcessor = objectPostProcessor;
this.defaultMethodExpressionHandler = objectPostProcessor.postProcess(defaultMethodExpressionHandler);
}
@Autowired(required = false)
public void setPermissionEvaluator(List<PermissionEvaluator> permissionEvaluators) {
if(permissionEvaluators.size() != 1) {
logger.debug("Not autwiring PermissionEvaluator since size != 1. Got " + permissionEvaluators);
}
this.defaultMethodExpressionHandler.setPermissionEvaluator(permissionEvaluators.get(0));
}
@Autowired
public void setApplicationContext(ApplicationContext context) {
this.context = context;
}
private AuthenticationConfiguration getAuthenticationConfiguration() {
return context.getBean(AuthenticationConfiguration.class);
}
private boolean prePostEnabled() {
return enableMethodSecurity().getBoolean("prePostEnabled");
}
private boolean securedEnabled() {
return enableMethodSecurity().getBoolean("securedEnabled");
}
private boolean jsr250Enabled() {
return enableMethodSecurity().getBoolean("jsr250Enabled");
}
private int order() {
return (Integer) enableMethodSecurity().get("order");
}
private boolean isAspectJ() {
return enableMethodSecurity().getEnum("mode") == AdviceMode.ASPECTJ;
}
private AnnotationAttributes enableMethodSecurity() {
if (enableMethodSecurity == null) {
// if it is null look at this instance (i.e. a subclass was used)
EnableGlobalMethodSecurity methodSecurityAnnotation = AnnotationUtils
.findAnnotation(getClass(),
EnableGlobalMethodSecurity.class);
Assert.notNull(methodSecurityAnnotation,
EnableGlobalMethodSecurity.class.getName() + " is required");
Map<String, Object> methodSecurityAttrs = AnnotationUtils
.getAnnotationAttributes(methodSecurityAnnotation);
this.enableMethodSecurity = AnnotationAttributes
.fromMap(methodSecurityAttrs);
}
return this.enableMethodSecurity;
}
}
@@ -1,58 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.method.configuration;
import java.util.Map;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.AutoProxyRegistrar;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* Dynamically determines which imports to include using the
* {@link EnableGlobalMethodSecurity} annotation.
*
* @author Rob Winch
* @since 3.2
*/
final class GlobalMethodSecuritySelector implements ImportSelector {
public final String[] selectImports(AnnotationMetadata importingClassMetadata) {
Class<EnableGlobalMethodSecurity> annoType = EnableGlobalMethodSecurity.class;
Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(annoType.getName(), false);
AnnotationAttributes attributes = AnnotationAttributes.fromMap(annotationAttributes);
Assert.notNull(attributes, String.format(
"@%s is not present on importing class '%s' as expected",
annoType.getSimpleName(), importingClassMetadata.getClassName()));
// TODO would be nice if could use BeanClassLoaderAware (does not work)
Class<?> importingClass = ClassUtils.resolveClassName(importingClassMetadata.getClassName(), ClassUtils.getDefaultClassLoader());
boolean skipMethodSecurityConfiguration = GlobalMethodSecurityConfiguration.class.isAssignableFrom(importingClass);
AdviceMode mode = attributes.getEnum("mode");
String autoProxyClassName = AdviceMode.PROXY == mode ? AutoProxyRegistrar.class.getName()
: GlobalMethodSecurityAspectJConfiguration.class.getName();
if(skipMethodSecurityConfiguration) {
return new String[] { autoProxyClassName };
}
return new String[] { autoProxyClassName,
GlobalMethodSecurityConfiguration.class.getName()};
}
}
@@ -1,209 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.configurers.AbstractConfigAttributeRequestMatcherRegistry;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
import org.springframework.security.web.util.matcher.RegexRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.ObjectUtils;
/**
* A base class for registering {@link RequestMatcher}'s. For example, it might allow for specifying which
* {@link RequestMatcher} require a certain level of authorization.
*
*
* @param <C> The object that is returned or Chained after creating the RequestMatcher
*
* @author Rob Winch
* @since 3.2
*/
public abstract class AbstractRequestMatcherRegistry<C> {
private static final RequestMatcher ANY_REQUEST = AnyRequestMatcher.INSTANCE;
/**
* Maps any request.
*
* @param method the {@link HttpMethod} to use or {@code null} for any {@link HttpMethod}.
* @param antPatterns the ant patterns to create {@link org.springframework.security.web.util.matcher.AntPathRequestMatcher}
* from
*
* @return the object that is chained after creating the {@link RequestMatcher}
*/
public C anyRequest() {
return requestMatchers(ANY_REQUEST);
}
/**
* Maps a {@link List} of
* {@link org.springframework.security.web.util.matcher.AntPathRequestMatcher}
* instances.
*
* @param method the {@link HttpMethod} to use for any
* {@link HttpMethod}.
*
* @return the object that is chained after creating the {@link RequestMatcher}
*/
public C antMatchers(HttpMethod method) {
return antMatchers(method, new String[] { "/**" });
}
/**
* Maps a {@link List} of {@link org.springframework.security.web.util.matcher.AntPathRequestMatcher} instances.
*
* @param method the {@link HttpMethod} to use or {@code null} for any {@link HttpMethod}.
* @param antPatterns the ant patterns to create {@link org.springframework.security.web.util.matcher.AntPathRequestMatcher}
* from
*
* @return the object that is chained after creating the {@link RequestMatcher}
*/
public C antMatchers(HttpMethod method, String... antPatterns) {
return chainRequestMatchers(RequestMatchers.antMatchers(method, antPatterns));
}
/**
* Maps a {@link List} of {@link org.springframework.security.web.util.matcher.AntPathRequestMatcher} instances that do
* not care which {@link HttpMethod} is used.
*
* @param antPatterns the ant patterns to create {@link org.springframework.security.web.util.matcher.AntPathRequestMatcher}
* from
*
* @return the object that is chained after creating the {@link RequestMatcher}
*/
public C antMatchers(String... antPatterns) {
return chainRequestMatchers(RequestMatchers.antMatchers(antPatterns));
}
/**
* Maps a {@link List} of {@link org.springframework.security.web.util.matcher.RegexRequestMatcher} instances.
*
* @param method the {@link HttpMethod} to use or {@code null} for any {@link HttpMethod}.
* @param regexPatterns the regular expressions to create
* {@link org.springframework.security.web.util.matcher.RegexRequestMatcher} from
*
* @return the object that is chained after creating the {@link RequestMatcher}
*/
public C regexMatchers(HttpMethod method, String... regexPatterns) {
return chainRequestMatchers(RequestMatchers.regexMatchers(method,
regexPatterns));
}
/**
* Create a {@link List} of {@link org.springframework.security.web.util.matcher.RegexRequestMatcher} instances that do not
* specify an {@link HttpMethod}.
*
* @param regexPatterns the regular expressions to create
* {@link org.springframework.security.web.util.matcher.RegexRequestMatcher} from
*
* @return the object that is chained after creating the {@link RequestMatcher}
*/
public C regexMatchers(String... regexPatterns) {
return chainRequestMatchers(RequestMatchers.regexMatchers(regexPatterns));
}
/**
* Associates a list of {@link RequestMatcher} instances with the {@link AbstractConfigAttributeRequestMatcherRegistry}
*
* @param requestMatchers the {@link RequestMatcher} instances
*
* @return the object that is chained after creating the {@link RequestMatcher}
*/
public C requestMatchers(RequestMatcher... requestMatchers) {
return chainRequestMatchers(Arrays.asList(requestMatchers));
}
/**
* Subclasses should implement this method for returning the object that is chained to the creation of the
* {@link RequestMatcher} instances.
*
* @param requestMatchers the {@link RequestMatcher} instances that were created
* @return the chained Object for the subclass which allows association of something else to the
* {@link RequestMatcher}
*/
protected abstract C chainRequestMatchers(List<RequestMatcher> requestMatchers);
/**
* Utilities for creating {@link RequestMatcher} instances.
*
* @author Rob Winch
* @since 3.2
*/
private static final class RequestMatchers {
/**
* Create a {@link List} of {@link AntPathRequestMatcher} instances.
*
* @param httpMethod the {@link HttpMethod} to use or {@code null} for any {@link HttpMethod}.
* @param antPatterns the ant patterns to create {@link AntPathRequestMatcher} from
*
* @return a {@link List} of {@link AntPathRequestMatcher} instances
*/
public static List<RequestMatcher> antMatchers(HttpMethod httpMethod, String...antPatterns) {
String method = httpMethod == null ? null : httpMethod.toString();
List<RequestMatcher> matchers = new ArrayList<RequestMatcher>();
for(String pattern : antPatterns) {
matchers.add(new AntPathRequestMatcher(pattern, method));
}
return matchers;
}
/**
* Create a {@link List} of {@link AntPathRequestMatcher} instances that do not specify an {@link HttpMethod}.
*
* @param antPatterns the ant patterns to create {@link AntPathRequestMatcher} from
*
* @return a {@link List} of {@link AntPathRequestMatcher} instances
*/
public static List<RequestMatcher> antMatchers(String...antPatterns) {
return antMatchers(null, antPatterns);
}
/**
* Create a {@link List} of {@link RegexRequestMatcher} instances.
*
* @param httpMethod the {@link HttpMethod} to use or {@code null} for any {@link HttpMethod}.
* @param regexPatterns the regular expressions to create {@link RegexRequestMatcher} from
*
* @return a {@link List} of {@link RegexRequestMatcher} instances
*/
public static List<RequestMatcher> regexMatchers(HttpMethod httpMethod, String...regexPatterns) {
String method = httpMethod == null ? null : httpMethod.toString();
List<RequestMatcher> matchers = new ArrayList<RequestMatcher>();
for(String pattern : regexPatterns) {
matchers.add(new RegexRequestMatcher(pattern, method));
}
return matchers;
}
/**
* Create a {@link List} of {@link RegexRequestMatcher} instances that do not specify an {@link HttpMethod}.
*
* @param regexPatterns the regular expressions to create {@link RegexRequestMatcher} from
*
* @return a {@link List} of {@link RegexRequestMatcher} instances
*/
public static List<RequestMatcher> regexMatchers(String...regexPatterns) {
return regexMatchers(null, regexPatterns);
}
private RequestMatchers() {}
}
}
@@ -1,174 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web;
import javax.servlet.Filter;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.SecurityBuilder;
import org.springframework.security.config.annotation.SecurityConfigurer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.openid.OpenIDAuthenticationFilter;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.access.ExceptionTranslationFilter;
import org.springframework.security.web.access.channel.ChannelProcessingFilter;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter;
import org.springframework.security.web.authentication.switchuser.SwitchUserFilter;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.authentication.www.DigestAuthenticationFilter;
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
import org.springframework.security.web.jaasapi.JaasApiIntegrationFilter;
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
import org.springframework.security.web.session.ConcurrentSessionFilter;
import org.springframework.security.web.session.SessionManagementFilter;
/**
*
* @author Rob Winch
*
* @param <H>
*/
public interface HttpSecurityBuilder<H extends HttpSecurityBuilder<H>> extends SecurityBuilder<DefaultSecurityFilterChain> {
/**
* Gets the {@link SecurityConfigurer} by its class name or
* <code>null</code> if not found. Note that object hierarchies are not
* considered.
*
* @param clazz the Class of the {@link SecurityConfigurer} to attempt to get.
*/
<C extends SecurityConfigurer<DefaultSecurityFilterChain, H>> C getConfigurer(
Class<C> clazz);
/**
* Removes the {@link SecurityConfigurer} by its class name or
* <code>null</code> if not found. Note that object hierarchies are not
* considered.
*
* @param clazz the Class of the {@link SecurityConfigurer} to attempt to remove.
* @return the {@link SecurityConfigurer} that was removed or null if not found
*/
<C extends SecurityConfigurer<DefaultSecurityFilterChain, H>> C removeConfigurer(Class<C> clazz);
/**
* Sets an object that is shared by multiple {@link SecurityConfigurer}.
*
* @param sharedType the Class to key the shared object by.
* @param object the Object to store
*/
<C> void setSharedObject(Class<C> sharedType, C object);
/**
* Gets a shared Object. Note that object heirarchies are not considered.
*
* @param sharedType the type of the shared Object
* @return the shared Object or null if it is not found
*/
<C> C getSharedObject(Class<C> sharedType);
/**
* Allows adding an additional {@link AuthenticationProvider} to be used
*
* @param authenticationProvider the {@link AuthenticationProvider} to be added
* @return the {@link HttpSecurity} for further customizations
*/
H authenticationProvider(
AuthenticationProvider authenticationProvider);
/**
* Allows adding an additional {@link UserDetailsService} to be used
*
* @param userDetailsService the {@link UserDetailsService} to be added
* @return the {@link HttpSecurity} for further customizations
*/
H userDetailsService(
UserDetailsService userDetailsService) throws Exception;
/**
* Allows adding a {@link Filter} after one of the known {@link Filter}
* classes. The known {@link Filter} instances are either a {@link Filter}
* listed in {@link #addFilter(Filter)} or a {@link Filter} that has already
* been added using {@link #addFilterAfter(Filter, Class)} or
* {@link #addFilterBefore(Filter, Class)}.
*
* @param filter the {@link Filter} to register after the type {@code afterFilter}
* @param afterFilter the Class of the known {@link Filter}.
* @return the {@link HttpSecurity} for further customizations
*/
H addFilterAfter(Filter filter,
Class<? extends Filter> afterFilter);
/**
* Allows adding a {@link Filter} before one of the known {@link Filter}
* classes. The known {@link Filter} instances are either a {@link Filter}
* listed in {@link #addFilter(Filter)} or a {@link Filter} that has already
* been added using {@link #addFilterAfter(Filter, Class)} or
* {@link #addFilterBefore(Filter, Class)}.
*
* @param filter the {@link Filter} to register before the type {@code beforeFilter}
* @param beforeFilter the Class of the known {@link Filter}.
* @return the {@link HttpSecurity} for further customizations
*/
H addFilterBefore(Filter filter,
Class<? extends Filter> beforeFilter);
/**
* Adds a {@link Filter} that must be an instance of or extend one of the
* Filters provided within the Security framework. The method ensures that
* the ordering of the Filters is automatically taken care of.
*
* The ordering of the Filters is:
*
* <ul>
* <li>{@link ChannelProcessingFilter}</li>
* <li>{@link ConcurrentSessionFilter}</li>
* <li>{@link SecurityContextPersistenceFilter}</li>
* <li>{@link LogoutFilter}</li>
* <li>{@link X509AuthenticationFilter}</li>
* <li>{@link AbstractPreAuthenticatedProcessingFilter}</li>
* <li>{@link org.springframework.security.cas.web.CasAuthenticationFilter}</li>
* <li>{@link UsernamePasswordAuthenticationFilter}</li>
* <li>{@link ConcurrentSessionFilter}</li>
* <li>{@link OpenIDAuthenticationFilter}</li>
* <li>{@link DefaultLoginPageGeneratingFilter}</li>
* <li>{@link ConcurrentSessionFilter}</li>
* <li>{@link DigestAuthenticationFilter}</li>
* <li>{@link BasicAuthenticationFilter}</li>
* <li>{@link RequestCacheAwareFilter}</li>
* <li>{@link SecurityContextHolderAwareRequestFilter}</li>
* <li>{@link JaasApiIntegrationFilter}</li>
* <li>{@link RememberMeAuthenticationFilter}</li>
* <li>{@link AnonymousAuthenticationFilter}</li>
* <li>{@link SessionManagementFilter}</li>
* <li>{@link ExceptionTranslationFilter}</li>
* <li>{@link FilterSecurityInterceptor}</li>
* <li>{@link SwitchUserFilter}</li>
* </ul>
*
* @param filter the {@link Filter} to add
* @return the {@link HttpSecurity} for further customizations
*/
H addFilter(Filter filter);
}
@@ -1,41 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web;
import javax.servlet.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.SecurityBuilder;
import org.springframework.security.config.annotation.SecurityConfigurer;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
/**
* Allows customization to the {@link WebSecurity}. In most instances
* users will use {@link EnableWebSecurity} and a create {@link Configuration}
* that extends {@link WebSecurityConfigurerAdapter} which will automatically be
* applied to the {@link WebSecurity} by the {@link EnableWebSecurity}
* annotation.
*
* @see WebSecurityConfigurerAdapter
*
* @author Rob Winch
* @since 3.2
*/
public interface WebSecurityConfigurer<T extends SecurityBuilder<Filter>> extends SecurityConfigurer<Filter, T> {
}
@@ -1,181 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.builders;
import java.io.Serializable;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.Filter;
import org.springframework.security.web.access.ExceptionTranslationFilter;
import org.springframework.security.web.access.channel.ChannelProcessingFilter;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter;
import org.springframework.security.web.authentication.switchuser.SwitchUserFilter;
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.authentication.www.DigestAuthenticationFilter;
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;
import org.springframework.security.web.csrf.CsrfFilter;
import org.springframework.security.web.header.HeaderWriterFilter;
import org.springframework.security.web.jaasapi.JaasApiIntegrationFilter;
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
import org.springframework.security.web.session.ConcurrentSessionFilter;
import org.springframework.security.web.session.SessionManagementFilter;
/**
* An internal use only {@link Comparator} that sorts the Security {@link Filter} instances to ensure they are in the
* correct order.
*
* @author Rob Winch
* @since 3.2
*/
@SuppressWarnings("serial")
final class FilterComparator implements Comparator<Filter>, Serializable {
private static final int STEP = 100;
private Map<String,Integer> filterToOrder = new HashMap<String,Integer>();
FilterComparator() {
int order = 100;
put(ChannelProcessingFilter.class, order);
order += STEP;
put(ConcurrentSessionFilter.class, order);
order += STEP;
put(WebAsyncManagerIntegrationFilter.class, order);
order += STEP;
put(SecurityContextPersistenceFilter.class, order);
order += STEP;
put(HeaderWriterFilter.class, order);
order += STEP;
put(CsrfFilter.class, order);
order += STEP;
put(LogoutFilter.class, order);
order += STEP;
put(X509AuthenticationFilter.class, order);
order += STEP;
put(AbstractPreAuthenticatedProcessingFilter.class, order);
order += STEP;
filterToOrder.put("org.springframework.security.cas.web.CasAuthenticationFilter", order);
order += STEP;
put(UsernamePasswordAuthenticationFilter.class, order);
order += STEP;
put(ConcurrentSessionFilter.class, order);
order += STEP;
filterToOrder.put("org.springframework.security.openid.OpenIDAuthenticationFilter", order);
order += STEP;
put(DefaultLoginPageGeneratingFilter.class, order);
order += STEP;
put(ConcurrentSessionFilter.class, order);
order += STEP;
put(DigestAuthenticationFilter.class, order);
order += STEP;
put(BasicAuthenticationFilter.class, order);
order += STEP;
put(RequestCacheAwareFilter.class, order);
order += STEP;
put(SecurityContextHolderAwareRequestFilter.class, order);
order += STEP;
put(JaasApiIntegrationFilter.class, order);
order += STEP;
put(RememberMeAuthenticationFilter.class, order);
order += STEP;
put(AnonymousAuthenticationFilter.class, order);
order += STEP;
put(SessionManagementFilter.class, order);
order += STEP;
put(ExceptionTranslationFilter.class, order);
order += STEP;
put(FilterSecurityInterceptor.class, order);
order += STEP;
put(SwitchUserFilter.class, order);
}
public int compare(Filter lhs, Filter rhs) {
Integer left = getOrder(lhs.getClass());
Integer right = getOrder(rhs.getClass());
return left - right;
}
/**
* Determines if a particular {@link Filter} is registered to be sorted
*
* @param filter
* @return
*/
public boolean isRegistered(Class<? extends Filter> filter) {
return getOrder(filter) != null;
}
/**
* Registers a {@link Filter} to exist after a particular {@link Filter} that is already registered.
* @param filter the {@link Filter} to register
* @param afterFilter the {@link Filter} that is already registered and that {@code filter} should be placed after.
*/
public void registerAfter(Class<? extends Filter> filter, Class<? extends Filter> afterFilter) {
Integer position = getOrder(afterFilter);
if(position == null) {
throw new IllegalArgumentException("Cannot register after unregistered Filter "+afterFilter);
}
put(filter, position + 1);
}
/**
* Registers a {@link Filter} to exist before a particular {@link Filter} that is already registered.
* @param filter the {@link Filter} to register
* @param beforeFilter the {@link Filter} that is already registered and that {@code filter} should be placed before.
*/
public void registerBefore(Class<? extends Filter> filter, Class<? extends Filter> beforeFilter) {
Integer position = getOrder(beforeFilter);
if(position == null) {
throw new IllegalArgumentException("Cannot register after unregistered Filter "+beforeFilter);
}
put(filter, position - 1);
}
private void put(Class<? extends Filter> filter, int position) {
String className = filter.getName();
filterToOrder.put(className, position);
}
/**
* Gets the order of a particular {@link Filter} class taking into consideration superclasses.
*
* @param clazz the {@link Filter} class to determine the sort order
* @return the sort order or null if not defined
*/
private Integer getOrder(Class<?> clazz) {
while(clazz != null) {
Integer result = filterToOrder.get(clazz.getName());
if(result != null) {
return result;
}
clazz = clazz.getSuperclass();
}
return null;
}
}
@@ -1,344 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.builders;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.Filter;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.security.access.expression.SecurityExpressionHandler;
import org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.SecurityBuilder;
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator;
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.debug.DebugFilter;
import org.springframework.security.web.firewall.DefaultHttpFirewall;
import org.springframework.security.web.firewall.HttpFirewall;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.web.filter.DelegatingFilterProxy;
/**
* <p>
* The {@link WebSecurity} is created by {@link WebSecurityConfiguration}
* to create the {@link FilterChainProxy} known as the Spring Security Filter
* Chain (springSecurityFilterChain). The springSecurityFilterChain is the
* {@link Filter} that the {@link DelegatingFilterProxy} delegates to.
* </p>
*
* <p>
* Customizations to the {@link WebSecurity} can be made by creating a
* {@link WebSecurityConfigurer} or more likely by overriding
* {@link WebSecurityConfigurerAdapter}.
* </p>
*
* @see EnableWebSecurity
* @see WebSecurityConfiguration
*
* @author Rob Winch
* @since 3.2
*/
public final class WebSecurity extends
AbstractConfiguredSecurityBuilder<Filter, WebSecurity> implements SecurityBuilder<Filter>, ApplicationContextAware {
private final Log logger = LogFactory.getLog(getClass());
private final List<RequestMatcher> ignoredRequests = new ArrayList<RequestMatcher>();
private final List<SecurityBuilder<? extends SecurityFilterChain>> securityFilterChainBuilders =
new ArrayList<SecurityBuilder<? extends SecurityFilterChain>>();
private final IgnoredRequestConfigurer ignoredRequestRegistry =
new IgnoredRequestConfigurer();
private FilterSecurityInterceptor filterSecurityInterceptor;
private HttpFirewall httpFirewall;
private boolean debugEnabled;
private WebInvocationPrivilegeEvaluator privilegeEvaluator;
private DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler();
private SecurityExpressionHandler<FilterInvocation> expressionHandler = defaultWebSecurityExpressionHandler;
private Runnable postBuildAction = new Runnable() {
public void run() {}
};
/**
* Creates a new instance
* @param objectPostProcessor the {@link ObjectPostProcessor} to use
* @see WebSecurityConfiguration
*/
public WebSecurity(ObjectPostProcessor<Object> objectPostProcessor) {
super(objectPostProcessor);
}
/**
* <p>
* Allows adding {@link RequestMatcher} instances that should that Spring
* Security should ignore. Web Security provided by Spring Security
* (including the {@link SecurityContext}) will not be available on
* {@link HttpServletRequest} that match. Typically the requests that are
* registered should be that of only static resources. For requests that are
* dynamic, consider mapping the request to allow all users instead.
* </p>
*
* Example Usage:
*
* <pre>
* webSecurityBuilder
* .ignoring()
* // ignore all URLs that start with /resources/ or /static/
* .antMatchers(&quot;/resources/**&quot;, &quot;/static/**&quot;);
* </pre>
*
* Alternatively this will accomplish the same result:
*
* <pre>
* webSecurityBuilder
* .ignoring()
* // ignore all URLs that start with /resources/ or /static/
* .antMatchers(&quot;/resources/**&quot;)
* .antMatchers(&quot;/static/**&quot;);
* </pre>
*
* Multiple invocations of ignoring() are also additive, so the following is
* also equivalent to the previous two examples:
*
* Alternatively this will accomplish the same result:
*
* <pre>
* webSecurityBuilder
* .ignoring()
* // ignore all URLs that start with /resources/
* .antMatchers(&quot;/resources/**&quot;);
* webSecurityBuilder
* .ignoring()
* // ignore all URLs that start with /static/
* .antMatchers(&quot;/static/**&quot;);
* // now both URLs that start with /resources/ and /static/ will be ignored
* </pre>
*
* @return the {@link IgnoredRequestConfigurer} to use for registering request
* that should be ignored
*/
public IgnoredRequestConfigurer ignoring() {
return ignoredRequestRegistry;
}
/**
* Allows customizing the {@link HttpFirewall}. The default is
* {@link DefaultHttpFirewall}.
*
* @param httpFirewall the custom {@link HttpFirewall}
* @return the {@link WebSecurity} for further customizations
*/
public WebSecurity httpFirewall(HttpFirewall httpFirewall) {
this.httpFirewall = httpFirewall;
return this;
}
/**
* Controls debugging support for Spring Security.
*
* @param debugEnabled
* if true, enables debug support with Spring Security. Default
* is false.
*
* @return the {@link WebSecurity} for further customization.
* @see EnableWebSecurity#debug()
*/
public WebSecurity debug(boolean debugEnabled) {
this.debugEnabled = debugEnabled;
return this;
}
/**
* <p>
* Adds builders to create {@link SecurityFilterChain} instances.
* </p>
*
* <p>
* Typically this method is invoked automatically within the framework from
* {@link WebSecurityConfigurerAdapter#init(WebSecurity)}
* </p>
*
* @param securityFilterChainBuilder
* the builder to use to create the {@link SecurityFilterChain}
* instances
* @return the {@link WebSecurity} for further customizations
*/
public WebSecurity addSecurityFilterChainBuilder(SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder) {
this.securityFilterChainBuilders.add(securityFilterChainBuilder);
return this;
}
/**
* Set the {@link WebInvocationPrivilegeEvaluator} to be used. If this is
* null, then a {@link DefaultWebInvocationPrivilegeEvaluator} will be
* created when {@link #securityInterceptor(FilterSecurityInterceptor)}
* is non null.
*
* @param privilegeEvaluator
* the {@link WebInvocationPrivilegeEvaluator} to use
* @return the {@link WebSecurity} for further customizations
*/
public WebSecurity privilegeEvaluator(WebInvocationPrivilegeEvaluator privilegeEvaluator) {
this.privilegeEvaluator = privilegeEvaluator;
return this;
}
/**
* Set the {@link SecurityExpressionHandler} to be used. If this is null,
* then a {@link DefaultWebSecurityExpressionHandler} will be used.
*
* @param expressionHandler
* the {@link SecurityExpressionHandler} to use
* @return the {@link WebSecurity} for further customizations
*/
public WebSecurity expressionHandler(SecurityExpressionHandler<FilterInvocation> expressionHandler) {
Assert.notNull(expressionHandler, "expressionHandler cannot be null");
this.expressionHandler = expressionHandler;
return this;
}
/**
* Gets the {@link SecurityExpressionHandler} to be used.
* @return
*/
public SecurityExpressionHandler<FilterInvocation> getExpressionHandler() {
return expressionHandler;
}
/**
* Gets the {@link WebInvocationPrivilegeEvaluator} to be used.
* @return
*/
public WebInvocationPrivilegeEvaluator getPrivilegeEvaluator() {
if(privilegeEvaluator != null) {
return privilegeEvaluator;
}
return filterSecurityInterceptor == null ? null : new DefaultWebInvocationPrivilegeEvaluator(filterSecurityInterceptor);
}
/**
* Sets the {@link FilterSecurityInterceptor}. This is typically invoked by {@link WebSecurityConfigurerAdapter}.
* @param securityInterceptor the {@link FilterSecurityInterceptor} to use
* @return the {@link WebSecurity} for further customizations
*/
public WebSecurity securityInterceptor(FilterSecurityInterceptor securityInterceptor) {
this.filterSecurityInterceptor = securityInterceptor;
return this;
}
/**
* Executes the Runnable immediately after the build takes place
*
* @param postBuildAction
* @return the {@link WebSecurity} for further customizations
*/
public WebSecurity postBuildAction(Runnable postBuildAction) {
this.postBuildAction = postBuildAction;
return this;
}
@Override
protected Filter performBuild() throws Exception {
Assert.state(!securityFilterChainBuilders.isEmpty(),
"At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. Typically this done by adding a @Configuration that extends WebSecurityConfigurerAdapter. More advanced users can invoke "
+ WebSecurity.class.getSimpleName()
+ ".addSecurityFilterChainBuilder directly");
int chainSize = ignoredRequests.size() + securityFilterChainBuilders.size();
List<SecurityFilterChain> securityFilterChains = new ArrayList<SecurityFilterChain>(chainSize);
for(RequestMatcher ignoredRequest : ignoredRequests) {
securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
}
for(SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
securityFilterChains.add(securityFilterChainBuilder.build());
}
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
if(httpFirewall != null) {
filterChainProxy.setFirewall(httpFirewall);
}
filterChainProxy.afterPropertiesSet();
Filter result = filterChainProxy;
if(debugEnabled) {
logger.warn("\n\n" +
"********************************************************************\n" +
"********** Security debugging is enabled. *************\n" +
"********** This may include sensitive information. *************\n" +
"********** Do not use in a production system! *************\n" +
"********************************************************************\n\n");
result = new DebugFilter(filterChainProxy);
}
postBuildAction.run();
return result;
}
/**
* Allows registering {@link RequestMatcher} instances that should be
* ignored by Spring Security.
*
* @author Rob Winch
* @since 3.2
*/
public final class IgnoredRequestConfigurer extends AbstractRequestMatcherRegistry<IgnoredRequestConfigurer> {
@Override
protected IgnoredRequestConfigurer chainRequestMatchers(List<RequestMatcher> requestMatchers) {
ignoredRequests.addAll(requestMatchers);
return this;
}
/**
* Returns the {@link WebSecurity} to be returned for chaining.
*/
public WebSecurity and() {
return WebSecurity.this;
}
private IgnoredRequestConfigurer(){}
}
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
defaultWebSecurityExpressionHandler.setApplicationContext(applicationContext);
}
}
@@ -1,58 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configuration;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.servlet.Filter;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.security.config.annotation.SecurityConfigurer;
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.util.Assert;
/**
* A class used to get all the {@link WebSecurityConfigurer} instances from the
* current {@link ApplicationContext} but ignoring the parent.
*
* @author Rob Winch
*
*/
final class AutowiredWebSecurityConfigurersIgnoreParents {
private final ConfigurableListableBeanFactory beanFactory;
public AutowiredWebSecurityConfigurersIgnoreParents(ConfigurableListableBeanFactory beanFactory) {
Assert.notNull(beanFactory,"beanFactory cannot be null");
this.beanFactory = beanFactory;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public List<SecurityConfigurer<Filter, WebSecurity>> getWebSecurityConfigurers() {
List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new ArrayList<SecurityConfigurer<Filter, WebSecurity>>();
Map<String, WebSecurityConfigurer> beansOfType = beanFactory.getBeansOfType(WebSecurityConfigurer.class);
for(Entry<String,WebSecurityConfigurer> entry : beansOfType.entrySet()) {
webSecurityConfigurers.add(entry.getValue());
}
return webSecurityConfigurers;
}
}
@@ -1,89 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configuration;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;
import org.springframework.security.config.annotation.authentication.configuration.EnableGlobalAuthentication;
import org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration;
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
/**
* Add this annotation to an {@code @Configuration} class to have the Spring Security
* configuration defined in any {@link WebSecurityConfigurer} or more likely by extending the
* {@link WebSecurityConfigurerAdapter} base class and overriding individual methods:
*
* <pre class="code">
* &#064;Configuration
* &#064;EnableWebSecurity
* public class MyWebSecurityConfiguration extends WebSecurityConfigurerAdapter {
*
* &#064;Override
* public void configure(WebSecurity web) throws Exception {
* web
* .ignoring()
* // Spring Security should completely ignore URLs starting with /resources/
* .antMatchers("/resources/**");
* }
*
* &#064;Override
* protected void configure(HttpSecurity http) throws Exception {
* http
* .authorizeRequests()
* .antMatchers("/public/**").permitAll()
* .anyRequest().hasRole("USER")
* .and()
* // Possibly more configuration ...
* .formLogin() // enable form based log in
* // set permitAll for all URLs associated with Form Login
* .permitAll();
* }
*
* &#064;Override
* protected void configure(AuthenticationManagerBuilder auth) {
* auth
* // enable in memory based authentication with a user named "user" and "admin"
* .inMemoryAuthentication()
* .withUser("user").password("password").roles("USER").and()
* .withUser("admin").password("password").roles("USER", "ADMIN");
* }
*
* // Possibly more overridden methods ...
* }
* </pre>
*
* @see WebSecurityConfigurer
* @see WebSecurityConfigurerAdapter
*
* @author Rob Winch
* @since 3.2
*/
@Retention(value=java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value={java.lang.annotation.ElementType.TYPE})
@Documented
@Import({WebSecurityConfiguration.class,ObjectPostProcessorConfiguration.class})
@EnableGlobalAuthentication
public @interface EnableWebSecurity {
/**
* Controls debugging support for Spring Security. Default is false.
* @return if true, enables debug support with Spring Security
*/
boolean debug() default false;
}
@@ -1,210 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configuration;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.servlet.Filter;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.ImportAware;
import org.springframework.core.OrderComparator;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.security.access.expression.SecurityExpressionHandler;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.SecurityConfigurer;
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.context.DelegatingApplicationListener;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
import org.springframework.util.ClassUtils;
/**
* Uses a {@link WebSecurity} to create the {@link FilterChainProxy} that
* performs the web based security for Spring Security. It then exports the
* necessary beans. Customizations can be made to {@link WebSecurity} by
* extending {@link WebSecurityConfigurerAdapter} and exposing it as a
* {@link Configuration} or implementing {@link WebSecurityConfigurer} and
* exposing it as a {@link Configuration}. This configuration is imported when
* using {@link EnableWebSecurity}.
*
* @see EnableWebSecurity
* @see WebSecurity
*
* @author Rob Winch
* @author Keesun Baik
* @since 3.2
*/
@Configuration
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
private WebSecurity webSecurity;
private Boolean debugEnabled;
private List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers;
private ClassLoader beanClassLoader;
@Bean
public static DelegatingApplicationListener delegatingApplicationListener() {
return new DelegatingApplicationListener();
}
@Bean
@DependsOn(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public SecurityExpressionHandler<FilterInvocation> webSecurityExpressionHandler() {
return webSecurity.getExpressionHandler();
}
/**
* Creates the Spring Security Filter Chain
* @return
* @throws Exception
*/
@Bean(name=AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain() throws Exception {
boolean hasConfigurers = webSecurityConfigurers != null && !webSecurityConfigurers.isEmpty();
if(!hasConfigurers) {
throw new IllegalStateException("At least one non-null instance of "+ WebSecurityConfigurer.class.getSimpleName()+" must be exposed as a @Bean when using @EnableWebSecurity. Hint try extending "+ WebSecurityConfigurerAdapter.class.getSimpleName());
}
return webSecurity.build();
}
/**
* Creates the {@link WebInvocationPrivilegeEvaluator} that is necessary for the JSP tag support.
* @return the {@link WebInvocationPrivilegeEvaluator}
* @throws Exception
*/
@Bean
@DependsOn(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public WebInvocationPrivilegeEvaluator privilegeEvaluator() throws Exception {
return webSecurity.getPrivilegeEvaluator();
}
/**
* Sets the {@code <SecurityConfigurer<FilterChainProxy, WebSecurityBuilder>} instances used to create the web configuration.
*
* @param objectPostProcessor the {@link ObjectPostProcessor} used to create a {@link WebSecurity} instance
* @param webSecurityConfigurers the {@code <SecurityConfigurer<FilterChainProxy, WebSecurityBuilder>} instances used to create the web configuration
* @throws Exception
*/
@Autowired(required = false)
public void setFilterChainProxySecurityConfigurer(ObjectPostProcessor<Object> objectPostProcessor,
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers) throws Exception {
webSecurity = objectPostProcessor.postProcess(new WebSecurity(objectPostProcessor));
if(debugEnabled != null) {
webSecurity.debug(debugEnabled);
}
Collections.sort(webSecurityConfigurers, AnnotationAwareOrderComparator.INSTANCE);
Integer previousOrder = null;
for(SecurityConfigurer<Filter, WebSecurity> config : webSecurityConfigurers) {
Integer order = AnnotationAwareOrderComparator.lookupOrder(config);
if(previousOrder != null && previousOrder.equals(order)) {
throw new IllegalStateException("@Order on WebSecurityConfigurers must be unique. Order of " + order + " was already used, so it cannot be used on " + config + " too.");
}
previousOrder = order;
}
for(SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
webSecurity.apply(webSecurityConfigurer);
}
this.webSecurityConfigurers = webSecurityConfigurers;
}
@Bean
public AutowiredWebSecurityConfigurersIgnoreParents autowiredWebSecurityConfigurersIgnoreParents(ConfigurableListableBeanFactory beanFactory) {
return new AutowiredWebSecurityConfigurersIgnoreParents(beanFactory);
}
/**
* A custom verision of the Spring provided AnnotationAwareOrderComparator
* that uses {@link AnnotationUtils#findAnnotation(Class, Class)} to look on
* super class instances for the {@link Order} annotation.
*
* @author Rob Winch
* @since 3.2
*/
private static class AnnotationAwareOrderComparator extends OrderComparator {
private static final AnnotationAwareOrderComparator INSTANCE = new AnnotationAwareOrderComparator();
@Override
protected int getOrder(Object obj) {
return lookupOrder(obj);
}
private static int lookupOrder(Object obj) {
if (obj instanceof Ordered) {
return ((Ordered) obj).getOrder();
}
if (obj != null) {
Class<?> clazz = (obj instanceof Class ? (Class<?>) obj : obj.getClass());
Order order = AnnotationUtils.findAnnotation(clazz,Order.class);
if (order != null) {
return order.value();
}
}
return Ordered.LOWEST_PRECEDENCE;
}
}
/* (non-Javadoc)
* @see org.springframework.context.annotation.ImportAware#setImportMetadata(org.springframework.core.type.AnnotationMetadata)
*/
public void setImportMetadata(AnnotationMetadata importMetadata) {
Map<String, Object> enableWebSecurityAttrMap = importMetadata.getAnnotationAttributes(EnableWebSecurity.class.getName());
AnnotationAttributes enableWebSecurityAttrs = AnnotationAttributes.fromMap(enableWebSecurityAttrMap);
if(enableWebSecurityAttrs == null) {
// search parent classes
Class<?> currentClass = ClassUtils.resolveClassName(importMetadata.getClassName(), beanClassLoader);
for(Class<?> classToInspect = currentClass ;classToInspect != null; classToInspect = classToInspect.getSuperclass()) {
EnableWebSecurity enableWebSecurityAnnotation = AnnotationUtils.findAnnotation(classToInspect, EnableWebSecurity.class);
if(enableWebSecurityAnnotation == null) {
continue;
}
enableWebSecurityAttrMap = AnnotationUtils
.getAnnotationAttributes(enableWebSecurityAnnotation);
enableWebSecurityAttrs = AnnotationAttributes.fromMap(enableWebSecurityAttrMap);
}
}
debugEnabled = enableWebSecurityAttrs.getBoolean("debug");
if(webSecurity != null) {
webSecurity.debug(debugEnabled);
}
}
/* (non-Javadoc)
* @see org.springframework.beans.factory.BeanClassLoaderAware#setBeanClassLoader(java.lang.ClassLoader)
*/
public void setBeanClassLoader(ClassLoader classLoader) {
this.beanClassLoader = classLoader;
}
}
@@ -1,470 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configuration;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.TargetSource;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.target.LazyInitTargetSource;
import org.springframework.beans.FatalBeanException;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configurers.DefaultLoginPageConfigurer;
import org.springframework.security.config.annotation.web.configurers.SecurityContextConfigurer;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.accept.ContentNegotiationStrategy;
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
/**
* Provides a convenient base class for creating a {@link WebSecurityConfigurer}
* instance. The implementation allows customization by overriding methods.
*
* @see EnableWebSecurity
*
* @author Rob Winch
*/
@Order(100)
public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigurer<WebSecurity> {
private final Log logger = LogFactory.getLog(WebSecurityConfigurerAdapter.class);
private ApplicationContext context;
private ContentNegotiationStrategy contentNegotiationStrategy = new HeaderContentNegotiationStrategy();
private ObjectPostProcessor<Object> objectPostProcessor = new ObjectPostProcessor<Object>() {
public <T> T postProcess(T object) {
throw new IllegalStateException(ObjectPostProcessor.class.getName()+ " is a required bean. Ensure you have used @EnableWebSecurity and @Configuration");
}
};
private AuthenticationConfiguration authenticationConfiguration;
private AuthenticationManagerBuilder authenticationBuilder;
private AuthenticationManagerBuilder localConfigureAuthenticationBldr;
private boolean disableLocalConfigureAuthenticationBldr;
private boolean authenticationManagerInitialized;
private AuthenticationManager authenticationManager;
private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
private HttpSecurity http;
private boolean disableDefaults;
/**
* Creates an instance with the default configuration enabled.
*/
protected WebSecurityConfigurerAdapter() {
this(false);
}
/**
* Creates an instance which allows specifying if the default configuration
* should be enabled. Disabling the default configuration should be
* considered more advanced usage as it requires more understanding of how
* the framework is implemented.
*
* @param disableDefaults
* true if the default configuration should be enabled, else
* false
*/
protected WebSecurityConfigurerAdapter(boolean disableDefaults) {
this.disableDefaults = disableDefaults;
}
/**
* Used by the default implementation of {@link #authenticationManager()} to
* attempt to obtain an {@link AuthenticationManager}. If overridden, the
* {@link AuthenticationManagerBuilder} should be used to specify the
* {@link AuthenticationManager}.
*
* <p>
* The {@link #authenticationManagerBean()} method can be used to expose the
* resulting {@link AuthenticationManager} as a Bean. The
* {@link #userDetailsServiceBean()} can be used to expose the last
* populated {@link UserDetailsService} that is created with the
* {@link AuthenticationManagerBuilder} as a Bean. The
* {@link UserDetailsService} will also automatically be populated on
* {@link HttpSecurity#getSharedObject(Class)} for use with other
* {@link SecurityContextConfigurer} (i.e. RememberMeConfigurer )
* </p>
*
* <p>
* For example, the following configuration could be used to register in
* memory authentication that exposes an in memory
* {@link UserDetailsService}:
* </p>
*
* <pre>
* &#064;Override
* protected void configure(AuthenticationManagerBuilder auth) {
* auth
* // enable in memory based authentication with a user named
* // &quot;user&quot; and &quot;admin&quot;
* .inMemoryAuthentication()
* .withUser(&quot;user&quot;).password(&quot;password&quot;).roles(&quot;USER&quot;).and()
* .withUser(&quot;admin&quot;).password(&quot;password&quot;).roles(&quot;USER&quot;, &quot;ADMIN&quot;);
* }
*
* // Expose the UserDetailsService as a Bean
* &#064;Bean
* &#064;Override
* public UserDetailsService userDetailsServiceBean() throws Exception {
* return super.userDetailsServiceBean();
* }
*
* </pre>
*
* @param auth
* the {@link AuthenticationManagerBuilder} to use
* @throws Exception
*/
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
this.disableLocalConfigureAuthenticationBldr = true;
}
/**
* Creates the {@link HttpSecurity} or returns the current instance
*
* @return the {@link HttpSecurity}
* @throws Exception
*/
protected final HttpSecurity getHttp() throws Exception {
if(http != null) {
return http;
}
DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor.postProcess(new DefaultAuthenticationEventPublisher());
localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
AuthenticationManager authenticationManager = authenticationManager();
authenticationBuilder.parentAuthenticationManager(authenticationManager);
http = new HttpSecurity(objectPostProcessor,authenticationBuilder, localConfigureAuthenticationBldr.getSharedObjects());
http.setSharedObject(UserDetailsService.class, userDetailsService());
http.setSharedObject(ApplicationContext.class, context);
http.setSharedObject(ContentNegotiationStrategy.class, contentNegotiationStrategy);
http.setSharedObject(AuthenticationTrustResolver.class, trustResolver);
if(!disableDefaults) {
http
.csrf().and()
.addFilter(new WebAsyncManagerIntegrationFilter())
.exceptionHandling().and()
.headers().and()
.sessionManagement().and()
.securityContext().and()
.requestCache().and()
.anonymous().and()
.servletApi().and()
.apply(new DefaultLoginPageConfigurer<HttpSecurity>()).and()
.logout();
}
configure(http);
return http;
}
/**
* Override this method to expose the {@link AuthenticationManager} from
* {@link #configure(AuthenticationManagerBuilder)} to be exposed as
* a Bean. For example:
*
* <pre>
* &#064;Bean(name name="myAuthenticationManager")
* &#064;Override
* public AuthenticationManager authenticationManagerBean() throws Exception {
* return super.authenticationManagerBean();
* }
* </pre>
*
* @return the {@link AuthenticationManager}
* @throws Exception
*/
public AuthenticationManager authenticationManagerBean() throws Exception {
return new AuthenticationManagerDelegator(authenticationBuilder, context);
}
/**
* Gets the {@link AuthenticationManager} to use. The default strategy is if
* {@link #configure(AuthenticationManagerBuilder)} method is
* overridden to use the {@link AuthenticationManagerBuilder} that was passed in.
* Otherwise, autowire the {@link AuthenticationManager} by type.
*
* @return
* @throws Exception
*/
protected AuthenticationManager authenticationManager() throws Exception {
if(!authenticationManagerInitialized) {
configure(localConfigureAuthenticationBldr);
if(disableLocalConfigureAuthenticationBldr) {
authenticationManager = authenticationConfiguration.getAuthenticationManager();
} else {
authenticationManager = localConfigureAuthenticationBldr.build();
}
authenticationManagerInitialized = true;
}
return authenticationManager;
}
/**
* Override this method to expose a {@link UserDetailsService} created from
* {@link #configure(AuthenticationManagerBuilder)} as a bean. In
* general only the following override should be done of this method:
*
* <pre>
* &#064;Bean(name = "myUserDetailsService") // any or no name specified is allowed
* &#064;Override
* public UserDetailsService userDetailsServiceBean() throws Exception {
* return super.userDetailsServiceBean();
* }
* </pre>
*
* To change the instance returned, developers should change
* {@link #userDetailsService()} instead
* @return
* @throws Exception
* @see {@link #userDetailsService()}
*/
public UserDetailsService userDetailsServiceBean() throws Exception {
AuthenticationManagerBuilder globalAuthBuilder = context.getBean(AuthenticationManagerBuilder.class);
return new UserDetailsServiceDelegator(Arrays.asList(localConfigureAuthenticationBldr, globalAuthBuilder));
}
/**
* Allows modifying and accessing the {@link UserDetailsService} from
* {@link #userDetailsServiceBean()()} without interacting with the
* {@link ApplicationContext}. Developers should override this method when
* changing the instance of {@link #userDetailsServiceBean()}.
*
* @return
*/
protected UserDetailsService userDetailsService() {
AuthenticationManagerBuilder globalAuthBuilder = context.getBean(AuthenticationManagerBuilder.class);
return new UserDetailsServiceDelegator(Arrays.asList(localConfigureAuthenticationBldr, globalAuthBuilder));
}
public void init(final WebSecurity web) throws Exception {
final HttpSecurity http = getHttp();
web
.addSecurityFilterChainBuilder(http)
.postBuildAction(new Runnable() {
public void run() {
FilterSecurityInterceptor securityInterceptor = http.getSharedObject(FilterSecurityInterceptor.class);
web.securityInterceptor(securityInterceptor);
}
});
}
/**
* Override this method to configure {@link WebSecurity}. For
* example, if you wish to ignore certain requests.
*/
public void configure(WebSecurity web) throws Exception {
}
/**
* Override this method to configure the {@link HttpSecurity}.
* Typically subclasses should not invoke this method by calling super
* as it may override their configuration. The default configuration is:
*
* <pre>
* http
* .authorizeRequests()
* .anyRequest().authenticated().and()
* .formLogin().and()
* .httpBasic();
* </pre>
*
* @param http
* the {@link HttpSecurity} to modify
* @throws Exception
* if an error occurs
*/
protected void configure(HttpSecurity http) throws Exception {
logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity).");
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().and()
.httpBasic();
}
@Autowired
public void setApplicationContext(ApplicationContext context) {
this.context = context;
}
@Autowired(required=false)
public void setTrustResolver(AuthenticationTrustResolver trustResolver) {
this.trustResolver = trustResolver;
}
@Autowired(required=false)
public void setContentNegotationStrategy(ContentNegotiationStrategy contentNegotiationStrategy) {
this.contentNegotiationStrategy = contentNegotiationStrategy;
}
@Autowired
public void setObjectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {
this.objectPostProcessor = objectPostProcessor;
authenticationBuilder = new AuthenticationManagerBuilder(objectPostProcessor);
localConfigureAuthenticationBldr = new AuthenticationManagerBuilder(objectPostProcessor) {
@Override
public AuthenticationManagerBuilder eraseCredentials(boolean eraseCredentials) {
authenticationBuilder.eraseCredentials(eraseCredentials);
return super.eraseCredentials(eraseCredentials);
}
};
}
@Autowired
public void setAuthenticationConfiguration(AuthenticationConfiguration authenticationConfiguration) {
this.authenticationConfiguration = authenticationConfiguration;
}
/**
* Delays the use of the {@link UserDetailsService} from the
* {@link AuthenticationManagerBuilder} to ensure that it has been fully
* configured.
*
* @author Rob Winch
* @since 3.2
*/
static final class UserDetailsServiceDelegator implements UserDetailsService {
private List<AuthenticationManagerBuilder> delegateBuilders;
private UserDetailsService delegate;
private final Object delegateMonitor = new Object();
UserDetailsServiceDelegator(List<AuthenticationManagerBuilder> delegateBuilders) {
if(delegateBuilders.contains(null)) {
throw new IllegalArgumentException("delegateBuilders cannot contain null values. Got " + delegateBuilders);
}
this.delegateBuilders = delegateBuilders;
}
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if(delegate != null) {
return delegate.loadUserByUsername(username);
}
synchronized(delegateMonitor) {
if (delegate == null) {
for(AuthenticationManagerBuilder delegateBuilder : delegateBuilders) {
delegate = delegateBuilder.getDefaultUserDetailsService();
if(delegate != null) {
break;
}
}
if(delegate == null) {
throw new IllegalStateException("UserDetailsService is required.");
}
this.delegateBuilders = null;
}
}
return delegate.loadUserByUsername(username);
}
}
/**
* Delays the use of the {@link AuthenticationManager} build from the
* {@link AuthenticationManagerBuilder} to ensure that it has been fully
* configured.
*
* @author Rob Winch
* @since 3.2
*/
static final class AuthenticationManagerDelegator implements AuthenticationManager {
private AuthenticationManagerBuilder delegateBuilder;
private AuthenticationManager delegate;
private final Object delegateMonitor = new Object();
private Set<String> beanNames;
AuthenticationManagerDelegator(AuthenticationManagerBuilder delegateBuilder, ApplicationContext context) {
Assert.notNull(delegateBuilder,"delegateBuilder cannot be null");
Field parentAuthMgrField = ReflectionUtils.findField(AuthenticationManagerBuilder.class, "parentAuthenticationManager");
ReflectionUtils.makeAccessible(parentAuthMgrField);
beanNames = getAuthenticationManagerBeanNames(context);
validateBeanCycle(ReflectionUtils.getField(parentAuthMgrField, delegateBuilder), beanNames);
this.delegateBuilder = delegateBuilder;
}
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if(delegate != null) {
return delegate.authenticate(authentication);
}
synchronized(delegateMonitor) {
if (delegate == null) {
delegate = this.delegateBuilder.getObject();
this.delegateBuilder = null;
}
}
return delegate.authenticate(authentication);
}
private static Set<String> getAuthenticationManagerBeanNames(ApplicationContext applicationContext) {
String[] beanNamesForType = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, AuthenticationManager.class);
return new HashSet<String>(Arrays.asList(beanNamesForType));
}
private static void validateBeanCycle(Object auth, Set<String> beanNames) {
if(auth != null && !beanNames.isEmpty()) {
if(auth instanceof Advised){
Advised advised = (Advised) auth;
TargetSource targetSource = advised.getTargetSource();
if(targetSource instanceof LazyInitTargetSource) {
LazyInitTargetSource lits = (LazyInitTargetSource) targetSource;
if(beanNames.contains(lits.getTargetBeanName())) {
throw new FatalBeanException("A dependency cycle was detected when trying to resolve the AuthenticationManager. Please ensure you have configured authentication.");
}
}
}
beanNames = Collections.emptySet();
}
}
}
}
@@ -1,370 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers;
import java.util.Collections;
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.openid.OpenIDLoginConfigurer;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.PortMapper;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.authentication.RememberMeServices;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
import org.springframework.web.accept.ContentNegotiationStrategy;
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
/**
* Base class for confuring {@link AbstractAuthenticationFilterConfigurer}. This is intended for internal use only.
*
* @see FormLoginConfigurer
* @see OpenIDLoginConfigurer
*
* @param T refers to "this" for returning the current configurer
* @param F refers to the {@link AbstractAuthenticationProcessingFilter} that is being built
*
* @author Rob Winch
* @since 3.2
*/
public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecurityBuilder<B>,T extends AbstractAuthenticationFilterConfigurer<B,T, F>, F extends AbstractAuthenticationProcessingFilter> extends AbstractHttpConfigurer<T,B> {
private final F authFilter;
private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource;
private AuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
private LoginUrlAuthenticationEntryPoint authenticationEntryPoint;
private boolean customLoginPage;
private String loginPage;
private String loginProcessingUrl;
private AuthenticationFailureHandler failureHandler;
private boolean permitAll;
private String failureUrl;
/**
* Creates a new instance
* @param authenticationFilter the {@link AbstractAuthenticationProcessingFilter} to use
* @param defaultLoginProcessingUrl the default URL to use for {@link #loginProcessingUrl(String)}
*/
protected AbstractAuthenticationFilterConfigurer(F authenticationFilter, String defaultLoginProcessingUrl) {
this.authFilter = authenticationFilter;
setLoginPage("/login");
if(defaultLoginProcessingUrl != null) {
loginProcessingUrl(defaultLoginProcessingUrl);
}
}
/**
* Specifies where users will go after authenticating successfully if they
* have not visited a secured page prior to authenticating. This is a
* shortcut for calling {@link #defaultSuccessUrl(String)}.
*
* @param defaultSuccessUrl
* the default success url
* @return the {@link FormLoginConfigurer} for additional customization
*/
public final T defaultSuccessUrl(String defaultSuccessUrl) {
return defaultSuccessUrl(defaultSuccessUrl, false);
}
/**
* Specifies where users will go after authenticating successfully if they
* have not visited a secured page prior to authenticating or
* {@code alwaysUse} is true. This is a shortcut for calling
* {@link #successHandler(AuthenticationSuccessHandler)}.
*
* @param defaultSuccessUrl
* the default success url
* @param alwaysUse
* true if the {@code defaultSuccesUrl} should be used after
* authentication despite if a protected page had been previously
* visited
* @return the {@link FormLoginConfigurer} for additional customization
*/
public final T defaultSuccessUrl(String defaultSuccessUrl, boolean alwaysUse) {
SavedRequestAwareAuthenticationSuccessHandler handler = new SavedRequestAwareAuthenticationSuccessHandler();
handler.setDefaultTargetUrl(defaultSuccessUrl);
handler.setAlwaysUseDefaultTargetUrl(alwaysUse);
return successHandler(handler);
}
/**
* Specifies the URL to validate the credentials.
*
* @param loginProcessingUrl
* the URL to validate username and password
* @return the {@link FormLoginConfigurer} for additional customization
*/
public T loginProcessingUrl(String loginProcessingUrl) {
this.loginProcessingUrl = loginProcessingUrl;
authFilter.setRequiresAuthenticationRequestMatcher(createLoginProcessingUrlMatcher(loginProcessingUrl));
return getSelf();
}
/**
* Create the {@link RequestMatcher} given a loginProcessingUrl
* @param loginProcessingUrl creates the {@link RequestMatcher} based upon the loginProcessingUrl
* @return the {@link RequestMatcher} to use based upon the loginProcessingUrl
*/
protected abstract RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingUrl);
/**
* Specifies a custom {@link AuthenticationDetailsSource}. The default is {@link WebAuthenticationDetailsSource}.
*
* @param authenticationDetailsSource the custom {@link AuthenticationDetailsSource}
* @return the {@link FormLoginConfigurer} for additional customization
*/
public final T authenticationDetailsSource(AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {
this.authenticationDetailsSource = authenticationDetailsSource;
return getSelf();
}
/**
* Specifies the {@link AuthenticationSuccessHandler} to be used. The
* default is {@link SavedRequestAwareAuthenticationSuccessHandler} with no
* additional properites set.
*
* @param successHandler
* the {@link AuthenticationSuccessHandler}.
* @return the {@link FormLoginConfigurer} for additional customization
*/
public final T successHandler(AuthenticationSuccessHandler successHandler) {
this.successHandler = successHandler;
return getSelf();
}
/**
* Equivalent of invoking permitAll(true)
* @return
*/
public final T permitAll() {
return permitAll(true);
}
/**
* Ensures the urls for {@link #failureUrl(String)} and
* {@link #authenticationUrls(String)} are granted access to any user.
*
* @param permitAll true to grant access to the URLs false to skip this step
* @return the {@link FormLoginConfigurer} for additional customization
*/
public final T permitAll(boolean permitAll) {
this.permitAll = permitAll;
return getSelf();
}
/**
* The URL to send users if authentication fails. This is a shortcut for
* invoking {@link #failureHandler(AuthenticationFailureHandler)}. The
* default is "/login?error".
*
* @param authenticationFailureUrl
* the URL to send users if authentication fails (i.e.
* "/login?error").
* @return the {@link FormLoginConfigurer} for additional customization
*/
public final T failureUrl(String authenticationFailureUrl) {
T result = failureHandler(new SimpleUrlAuthenticationFailureHandler(authenticationFailureUrl));
this.failureUrl = authenticationFailureUrl;
return result;
}
/**
* Specifies the {@link AuthenticationFailureHandler} to use when
* authentication fails. The default is redirecting to "/login?error" using
* {@link SimpleUrlAuthenticationFailureHandler}
*
* @param authenticationFailureHandler
* the {@link AuthenticationFailureHandler} to use when
* authentication fails.
* @return the {@link FormLoginConfigurer} for additional customization
*/
public final T failureHandler(AuthenticationFailureHandler authenticationFailureHandler) {
this.failureUrl = null;
this.failureHandler = authenticationFailureHandler;
return getSelf();
}
@Override
public void init(B http) throws Exception {
updateAuthenticationDefaults();
if(permitAll) {
PermitAllSupport.permitAll(http, loginPage, loginProcessingUrl, failureUrl);
}
registerDefaultAuthenticationEntryPoint(http);
}
@SuppressWarnings("unchecked")
private void registerDefaultAuthenticationEntryPoint(B http) {
ExceptionHandlingConfigurer<B> exceptionHandling = http.getConfigurer(ExceptionHandlingConfigurer.class);
if(exceptionHandling == null) {
return;
}
ContentNegotiationStrategy contentNegotiationStrategy = http.getSharedObject(ContentNegotiationStrategy.class);
if(contentNegotiationStrategy == null) {
contentNegotiationStrategy = new HeaderContentNegotiationStrategy();
}
MediaTypeRequestMatcher preferredMatcher = new MediaTypeRequestMatcher(contentNegotiationStrategy, MediaType.APPLICATION_XHTML_XML, new MediaType("image","*"), MediaType.TEXT_HTML, MediaType.TEXT_PLAIN);
preferredMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));
exceptionHandling.defaultAuthenticationEntryPointFor(postProcess(authenticationEntryPoint), preferredMatcher);
}
@Override
public void configure(B http) throws Exception {
PortMapper portMapper = http.getSharedObject(PortMapper.class);
if(portMapper != null) {
authenticationEntryPoint.setPortMapper(portMapper);
}
authFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
authFilter.setAuthenticationSuccessHandler(successHandler);
authFilter.setAuthenticationFailureHandler(failureHandler);
if(authenticationDetailsSource != null) {
authFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
}
SessionAuthenticationStrategy sessionAuthenticationStrategy = http.getSharedObject(SessionAuthenticationStrategy.class);
if(sessionAuthenticationStrategy != null) {
authFilter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy);
}
RememberMeServices rememberMeServices = http.getSharedObject(RememberMeServices.class);
if(rememberMeServices != null) {
authFilter.setRememberMeServices(rememberMeServices);
}
F filter = postProcess(authFilter);
http.addFilter(filter);
}
/**
* <p>
* Specifies the URL to send users to if login is required. If used with
* {@link WebSecurityConfigurerAdapter} a default login page will be
* generated when this attribute is not specified.
* </p>
*
* <p>
* If a URL is specified or this is not being used in conjuction with
* {@link WebSecurityConfigurerAdapter}, users are required to process the
* specified URL to generate a login page.
* </p>
*/
protected T loginPage(String loginPage) {
setLoginPage(loginPage);
updateAuthenticationDefaults();
this.customLoginPage = true;
return getSelf();
}
/**
*
* @return true if a custom login page has been specified, else false
*/
public final boolean isCustomLoginPage() {
return customLoginPage;
}
/**
* Gets the Authentication Filter
*
* @return
*/
protected final F getAuthenticationFilter() {
return authFilter;
}
/**
* Gets the login page
*
* @return the login page
*/
protected final String getLoginPage() {
return loginPage;
}
/**
* Gets the URL to submit an authentication request to (i.e. where
* username/password must be submitted)
*
* @return the URL to submit an authentication request to
*/
protected final String getLoginProcessingUrl() {
return loginProcessingUrl;
}
/**
* Gets the URL to send users to if authentication fails
*
* @return
*/
protected final String getFailureUrl() {
return failureUrl;
}
/**
* Updates the default values for authentication.
*
* @throws Exception
*/
private void updateAuthenticationDefaults() {
if (loginProcessingUrl == null) {
loginProcessingUrl(loginPage);
}
if (failureHandler == null) {
failureUrl(loginPage + "?error");
}
final LogoutConfigurer<B> logoutConfigurer = getBuilder()
.getConfigurer(LogoutConfigurer.class);
if (logoutConfigurer != null
&& !logoutConfigurer.isCustomLogoutSuccess()) {
logoutConfigurer.logoutSuccessUrl(loginPage + "?logout");
}
}
/**
* Sets the loginPage and updates the {@link AuthenticationEntryPoint}.
* @param loginPage
*/
private void setLoginPage(String loginPage) {
this.loginPage = loginPage;
this.authenticationEntryPoint = new LoginUrlAuthenticationEntryPoint(
loginPage);
}
@SuppressWarnings("unchecked")
private T getSelf() {
return (T) this;
}
}
@@ -1,141 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
import org.springframework.security.web.util.matcher.RequestMatcher;
/**
* A base class for registering {@link RequestMatcher}'s. For example, it might allow for specifying which
* {@link RequestMatcher} require a certain level of authorization.
*
* @author Rob Winch
* @since 3.2
*
* @param <C> The object that is returned or Chained after creating the RequestMatcher
*
* @see ChannelSecurityConfigurer
* @see UrlAuthorizationConfigurer
* @see ExpressionUrlAuthorizationConfigurer
*/
public abstract class AbstractConfigAttributeRequestMatcherRegistry<C> extends AbstractRequestMatcherRegistry<C> {
private List<UrlMapping> urlMappings = new ArrayList<UrlMapping>();
private List<RequestMatcher> unmappedMatchers;
/**
* Gets the {@link UrlMapping} added by subclasses in {@link #chainRequestMatchers(java.util.List)}. May be empty.
*
* @return the {@link UrlMapping} added by subclasses in {@link #chainRequestMatchers(java.util.List)}
*/
final List<UrlMapping> getUrlMappings() {
return urlMappings;
}
/**
* Adds a {@link UrlMapping} added by subclasses in
* {@link #chainRequestMatchers(java.util.List)} and resets the unmapped
* {@link RequestMatcher}'s.
*
* @param urlMapping
* {@link UrlMapping} the mapping to add
*/
final void addMapping(UrlMapping urlMapping) {
this.unmappedMatchers = null;
this.urlMappings.add(urlMapping);
}
/**
* Marks the {@link RequestMatcher}'s as unmapped and then calls {@link #chainRequestMatchersInternal(List)}.
*
* @param requestMatchers the {@link RequestMatcher} instances that were created
* @return the chained Object for the subclass which allows association of something else to the
* {@link RequestMatcher}
*/
protected final C chainRequestMatchers(List<RequestMatcher> requestMatchers) {
this.unmappedMatchers = requestMatchers;
return chainRequestMatchersInternal(requestMatchers);
}
/**
* Subclasses should implement this method for returning the object that is chained to the creation of the
* {@link RequestMatcher} instances.
*
* @param requestMatchers the {@link RequestMatcher} instances that were created
* @return the chained Object for the subclass which allows association of something else to the
* {@link RequestMatcher}
*/
protected abstract C chainRequestMatchersInternal(List<RequestMatcher> requestMatchers);
/**
* Adds a {@link UrlMapping} added by subclasses in {@link #chainRequestMatchers(java.util.List)} at a particular
* index.
*
* @param index the index to add a {@link UrlMapping}
* @param urlMapping {@link UrlMapping} the mapping to add
*/
final void addMapping(int index, UrlMapping urlMapping) {
this.urlMappings.add(index, urlMapping);
}
/**
* Creates the mapping of {@link RequestMatcher} to {@link Collection} of {@link ConfigAttribute} instances
*
* @return the mapping of {@link RequestMatcher} to {@link Collection} of {@link ConfigAttribute} instances. Cannot
* be null.
*/
final LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> createRequestMap() {
if(unmappedMatchers != null) {
throw new IllegalStateException("An incomplete mapping was found for " + unmappedMatchers +". Try completing it with something like requestUrls().<something>.hasRole('USER')");
}
LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = new LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>>();
for (UrlMapping mapping : getUrlMappings()) {
RequestMatcher matcher = mapping.getRequestMatcher();
Collection<ConfigAttribute> configAttrs = mapping.getConfigAttrs();
requestMap.put(matcher,configAttrs);
}
return requestMap;
}
/**
* A mapping of {@link RequestMatcher} to {@link Collection} of {@link ConfigAttribute} instances
*/
static final class UrlMapping {
private RequestMatcher requestMatcher;
private Collection<ConfigAttribute> configAttrs;
UrlMapping(RequestMatcher requestMatcher,
Collection<ConfigAttribute> configAttrs) {
this.requestMatcher = requestMatcher;
this.configAttrs = configAttrs;
}
public RequestMatcher getRequestMatcher() {
return requestMatcher;
}
public Collection<ConfigAttribute> getConfigAttrs() {
return configAttrs;
}
}
}
@@ -1,51 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.SecurityConfigurer;
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.DefaultSecurityFilterChain;
/**
* Adds a convenient base class for {@link SecurityConfigurer} instances that
* operate on {@link HttpSecurity}.
*
* @author Rob Winch
*
*/
abstract class AbstractHttpConfigurer<T extends AbstractHttpConfigurer<T, B>,B extends HttpSecurityBuilder<B>> extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, B> {
/**
* Disables the {@link AbstractHttpConfigurer} by removing it. After doing
* so a fresh version of the configuration can be applied.
*
* @return the {@link HttpSecurityBuilder} for additional customizations
*/
@SuppressWarnings("unchecked")
public B disable() {
getBuilder().removeConfigurer(getClass());
return getBuilder();
}
@SuppressWarnings("unchecked")
public T withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
addObjectPostProcessor(objectPostProcessor);
return (T) this;
}
}
@@ -1,191 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers;
import java.util.List;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.vote.AffirmativeBased;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.SecurityConfigurer;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
/**
* A base class for configuring the {@link FilterSecurityInterceptor}.
*
* <h2>Security Filters</h2>
*
* The following Filters are populated
*
* <ul>
* <li>{@link FilterSecurityInterceptor}</li>
* </ul>
*
* <h2>Shared Objects Created</h2>
*
* The following shared objects are populated to allow other {@link SecurityConfigurer}'s to customize:
* <ul>
* <li>{@link FilterSecurityInterceptor}</li>
* </ul>
*
* <h2>Shared Objects Used</h2>
*
* The following shared objects are used:
*
* <ul>
* <li>{@link org.springframework.security.config.annotation.web.builders.HttpSecurity#getAuthenticationManager()}</li>
* </ul>
*
*
* @param <C> the AbstractInterceptUrlConfigurer
* @param <H> the type of {@link HttpSecurityBuilder} that is being configured
*
* @author Rob Winch
* @since 3.2
* @see ExpressionUrlAuthorizationConfigurer
* @see UrlAuthorizationConfigurer
*/
abstract class AbstractInterceptUrlConfigurer<C extends AbstractInterceptUrlConfigurer<C,H>, H extends HttpSecurityBuilder<H>> extends
AbstractHttpConfigurer<C, H>{
private Boolean filterSecurityInterceptorOncePerRequest;
private AccessDecisionManager accessDecisionManager;
@Override
public void configure(H http) throws Exception {
FilterInvocationSecurityMetadataSource metadataSource = createMetadataSource(http);
if(metadataSource == null) {
return;
}
FilterSecurityInterceptor securityInterceptor = createFilterSecurityInterceptor(http, metadataSource, http.getSharedObject(AuthenticationManager.class));
if(filterSecurityInterceptorOncePerRequest != null) {
securityInterceptor.setObserveOncePerRequest(filterSecurityInterceptorOncePerRequest);
}
securityInterceptor = postProcess(securityInterceptor);
http.addFilter(securityInterceptor);
http.setSharedObject(FilterSecurityInterceptor.class, securityInterceptor);
}
/**
* Subclasses should implement this method to provide a {@link FilterInvocationSecurityMetadataSource} for the
* {@link FilterSecurityInterceptor}.
*
* @param http the builder to use
*
* @return the {@link FilterInvocationSecurityMetadataSource} to set on the {@link FilterSecurityInterceptor}.
* Cannot be null.
*/
abstract FilterInvocationSecurityMetadataSource createMetadataSource(H http);
/**
* Subclasses should implement this method to provide the {@link AccessDecisionVoter} instances used to create the
* default {@link AccessDecisionManager}
*
* @param http the builder to use
*
* @return the {@link AccessDecisionVoter} instances used to create the
* default {@link AccessDecisionManager}
*/
@SuppressWarnings("rawtypes")
abstract List<AccessDecisionVoter> getDecisionVoters(H http);
abstract class AbstractInterceptUrlRegistry<R extends AbstractInterceptUrlRegistry<R,T>,T> extends AbstractConfigAttributeRequestMatcherRegistry<T> {
/**
* Allows setting the {@link AccessDecisionManager}. If none is provided, a default {@l AccessDecisionManager} is
* created.
*
* @param accessDecisionManager the {@link AccessDecisionManager} to use
* @return the {@link AbstractInterceptUrlConfigurer} for further customization
*/
public R accessDecisionManager(
AccessDecisionManager accessDecisionManager) {
AbstractInterceptUrlConfigurer.this.accessDecisionManager = accessDecisionManager;
return getSelf();
}
/**
* Allows setting if the {@link FilterSecurityInterceptor} should be only applied once per request (i.e. if the
* filter intercepts on a forward, should it be applied again).
*
* @param filterSecurityInterceptorOncePerRequest if the {@link FilterSecurityInterceptor} should be only applied
* once per request
* @return the {@link AbstractInterceptUrlConfigurer} for further customization
*/
public R filterSecurityInterceptorOncePerRequest(
boolean filterSecurityInterceptorOncePerRequest) {
AbstractInterceptUrlConfigurer.this.filterSecurityInterceptorOncePerRequest = filterSecurityInterceptorOncePerRequest;
return getSelf();
}
/**
* Returns a reference to the current object with a single suppression of
* the type
*
* @return a reference to the current object
*/
@SuppressWarnings("unchecked")
private R getSelf() {
return (R) this;
}
}
/**
* Creates the default {@code AccessDecisionManager}
* @return the default {@code AccessDecisionManager}
*/
private AccessDecisionManager createDefaultAccessDecisionManager(H http) {
AffirmativeBased result = new AffirmativeBased(getDecisionVoters(http));
return postProcess(result);
}
/**
* If currently null, creates a default {@link AccessDecisionManager} using
* {@link #createDefaultAccessDecisionManager()}. Otherwise returns the {@link AccessDecisionManager}.
*
* @param http the builder to use
*
* @return the {@link AccessDecisionManager} to use
*/
private AccessDecisionManager getAccessDecisionManager(H http) {
if (accessDecisionManager == null) {
accessDecisionManager = createDefaultAccessDecisionManager(http);
}
return accessDecisionManager;
}
/**
* Creates the {@link FilterSecurityInterceptor}
*
* @param http the builder to use
* @param metadataSource the {@link FilterInvocationSecurityMetadataSource} to use
* @param authenticationManager the {@link AuthenticationManager} to use
* @return the {@link FilterSecurityInterceptor}
* @throws Exception
*/
private FilterSecurityInterceptor createFilterSecurityInterceptor(H http, FilterInvocationSecurityMetadataSource metadataSource,
AuthenticationManager authenticationManager) throws Exception {
FilterSecurityInterceptor securityInterceptor = new FilterSecurityInterceptor();
securityInterceptor.setSecurityMetadataSource(metadataSource);
securityInterceptor.setAccessDecisionManager(getAccessDecisionManager(http));
securityInterceptor.setAuthenticationManager(authenticationManager);
securityInterceptor.afterPropertiesSet();
return securityInterceptor;
}
}
@@ -1,151 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers;
import java.util.List;
import java.util.UUID;
import org.springframework.security.authentication.AnonymousAuthenticationProvider;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.SecurityConfigurer;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
/**
* Configures Anonymous authentication (i.e. populate an {@link Authentication} that represents an anonymous user
* instead of having a null value) for an {@link HttpSecurity}. Specifically this will configure an
* {@link AnonymousAuthenticationFilter} and an {@link AnonymousAuthenticationProvider}. All properties have reasonable
* defaults, so no additional configuration is required other than applying this {@link SecurityConfigurer}.
*
* @author Rob Winch
* @since 3.2
*/
public final class AnonymousConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<AnonymousConfigurer<H>,H> {
private String key;
private AuthenticationProvider authenticationProvider;
private AnonymousAuthenticationFilter authenticationFilter;
private Object principal = "anonymousUser";
private List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS");
/**
* Creates a new instance
* @see HttpSecurity#anonymous()
*/
public AnonymousConfigurer() {
}
/**
* Sets the key to identify tokens created for anonymous authentication. Default is a secure randomly generated
* key.
*
* @param key the key to identify tokens created for anonymous authentication. Default is a secure randomly generated
* key.
* @return the {@link AnonymousConfigurer} for further customization of anonymous authentication
*/
public AnonymousConfigurer<H> key(String key) {
this.key = key;
return this;
}
/**
* Sets the principal for {@link Authentication} objects of anonymous users
*
* @param principal used for the {@link Authentication} object of anonymous users
* @return the {@link AnonymousConfigurer} for further customization of anonymous authentication
*/
public AnonymousConfigurer<H> principal(Object principal) {
this.principal = principal;
return this;
}
/**
* Sets the {@link org.springframework.security.core.Authentication#getAuthorities()} for anonymous users
*
* @param authorities Sets the {@link org.springframework.security.core.Authentication#getAuthorities()} for anonymous users
* @return the {@link AnonymousConfigurer} for further customization of anonymous authentication
*/
public AnonymousConfigurer<H> authorities(List<GrantedAuthority> authorities) {
this.authorities = authorities;
return this;
}
/**
* Sets the {@link org.springframework.security.core.Authentication#getAuthorities()} for anonymous users
*
* @param authorities Sets the {@link org.springframework.security.core.Authentication#getAuthorities()} for
* anonymous users (i.e. "ROLE_ANONYMOUS")
* @return the {@link AnonymousConfigurer} for further customization of anonymous authentication
*/
public AnonymousConfigurer<H> authorities(String... authorities) {
return authorities(AuthorityUtils.createAuthorityList(authorities));
}
/**
* Sets the {@link AuthenticationProvider} used to validate an anonymous user. If this is set, no attributes
* on the {@link AnonymousConfigurer} will be set on the {@link AuthenticationProvider}.
*
* @param authenticationProvider the {@link AuthenticationProvider} used to validate an anonymous user. Default is
* {@link AnonymousAuthenticationProvider}
*
* @return the {@link AnonymousConfigurer} for further customization of anonymous authentication
*/
public AnonymousConfigurer<H> authenticationProvider(AuthenticationProvider authenticationProvider) {
this.authenticationProvider = authenticationProvider;
return this;
}
/**
* Sets the {@link AnonymousAuthenticationFilter} used to populate an anonymous user. If this is set, no attributes
* on the {@link AnonymousConfigurer} will be set on the {@link AnonymousAuthenticationFilter}.
*
* @param authenticationFilter the {@link AnonymousAuthenticationFilter} used to populate an anonymous user.
*
* @return the {@link AnonymousConfigurer} for further customization of anonymous authentication
*/
public AnonymousConfigurer<H> authenticationFilter(AnonymousAuthenticationFilter authenticationFilter) {
this.authenticationFilter = authenticationFilter;
return this;
}
@Override
public void init(H http) throws Exception {
if(authenticationProvider == null) {
authenticationProvider = new AnonymousAuthenticationProvider(getKey());
}
if(authenticationFilter == null) {
authenticationFilter = new AnonymousAuthenticationFilter(getKey(), principal, authorities);
}
authenticationProvider = postProcess(authenticationProvider);
http.authenticationProvider(authenticationProvider);
}
@Override
public void configure(H http) throws Exception {
authenticationFilter.afterPropertiesSet();
http.addFilter(authenticationFilter);
}
private String getKey() {
if(key == null) {
key = UUID.randomUUID().toString();
}
return key;
}
}
@@ -1,202 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.SecurityBuilder;
import org.springframework.security.config.annotation.SecurityConfigurer;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.PortMapper;
import org.springframework.security.web.access.channel.ChannelDecisionManagerImpl;
import org.springframework.security.web.access.channel.ChannelProcessingFilter;
import org.springframework.security.web.access.channel.ChannelProcessor;
import org.springframework.security.web.access.channel.InsecureChannelProcessor;
import org.springframework.security.web.access.channel.RetryWithHttpEntryPoint;
import org.springframework.security.web.access.channel.RetryWithHttpsEntryPoint;
import org.springframework.security.web.access.channel.SecureChannelProcessor;
import org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource;
import org.springframework.security.web.util.matcher.RequestMatcher;
/**
* Adds channel security (i.e. requires HTTPS or HTTP) to an application. In order for
* {@link ChannelSecurityConfigurer} to be useful, at least one {@link RequestMatcher} should be mapped to HTTP
* or HTTPS.
*
* <p>
* By default an {@link InsecureChannelProcessor} and a {@link SecureChannelProcessor} will be registered.
* </p>
*
* <h2>Security Filters</h2>
*
* The following Filters are populated
*
* <ul>
* <li>{@link ChannelProcessingFilter}</li>
* </ul>
*
* <h2>Shared Objects Created</h2>
*
* No shared objects are created.
*
* <h2>Shared Objects Used</h2>
*
* The following shared objects are used:
*
* <ul>
* <li>{@link PortMapper} is used to create the default {@link ChannelProcessor} instances</li>
* </ul>
*
* @param <H> the type of {@link HttpSecurityBuilder} that is being configured
*
* @author Rob Winch
* @since 3.2
*/
public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>> extends
AbstractHttpConfigurer<ChannelSecurityConfigurer<H>, H> {
private ChannelProcessingFilter channelFilter = new ChannelProcessingFilter();
private LinkedHashMap<RequestMatcher,Collection<ConfigAttribute>> requestMap = new LinkedHashMap<RequestMatcher,Collection<ConfigAttribute>>();
private List<ChannelProcessor> channelProcessors;
private final ChannelRequestMatcherRegistry REGISTRY = new ChannelRequestMatcherRegistry();
/**
* Creates a new instance
* @see HttpSecurity#requiresChannel()
*/
public ChannelSecurityConfigurer() {
}
public ChannelRequestMatcherRegistry getRegistry() {
return REGISTRY;
}
@Override
public void configure(H http) throws Exception {
ChannelDecisionManagerImpl channelDecisionManager = new ChannelDecisionManagerImpl();
channelDecisionManager.setChannelProcessors(getChannelProcessors(http));
channelDecisionManager = postProcess(channelDecisionManager);
channelFilter.setChannelDecisionManager(channelDecisionManager);
DefaultFilterInvocationSecurityMetadataSource filterInvocationSecurityMetadataSource =
new DefaultFilterInvocationSecurityMetadataSource(requestMap);
channelFilter.setSecurityMetadataSource(filterInvocationSecurityMetadataSource);
channelFilter = postProcess(channelFilter);
http.addFilter(channelFilter);
}
private List<ChannelProcessor> getChannelProcessors(H http) {
if(channelProcessors != null) {
return channelProcessors;
}
InsecureChannelProcessor insecureChannelProcessor = new InsecureChannelProcessor();
SecureChannelProcessor secureChannelProcessor = new SecureChannelProcessor();
PortMapper portMapper = http.getSharedObject(PortMapper.class);
if(portMapper != null) {
RetryWithHttpEntryPoint httpEntryPoint = new RetryWithHttpEntryPoint();
httpEntryPoint.setPortMapper(portMapper);
insecureChannelProcessor.setEntryPoint(httpEntryPoint);
RetryWithHttpsEntryPoint httpsEntryPoint = new RetryWithHttpsEntryPoint();
httpsEntryPoint.setPortMapper(portMapper);
secureChannelProcessor.setEntryPoint(httpsEntryPoint);
}
insecureChannelProcessor = postProcess(insecureChannelProcessor);
secureChannelProcessor = postProcess(secureChannelProcessor);
return Arrays.<ChannelProcessor>asList(insecureChannelProcessor, secureChannelProcessor);
}
private ChannelRequestMatcherRegistry addAttribute(String attribute, List<RequestMatcher> matchers) {
for(RequestMatcher matcher : matchers) {
Collection<ConfigAttribute> attrs = Arrays.<ConfigAttribute>asList(new SecurityConfig(attribute));
requestMap.put(matcher, attrs);
}
return REGISTRY;
}
public final class ChannelRequestMatcherRegistry extends AbstractConfigAttributeRequestMatcherRegistry<RequiresChannelUrl> {
@Override
protected RequiresChannelUrl chainRequestMatchersInternal(List<RequestMatcher> requestMatchers) {
return new RequiresChannelUrl(requestMatchers);
}
/**
* Adds an {@link ObjectPostProcessor} for this class.
*
* @param objectPostProcessor
* @return the {@link ChannelSecurityConfigurer} for further customizations
*/
public ChannelRequestMatcherRegistry withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
addObjectPostProcessor(objectPostProcessor);
return this;
}
/**
* Sets the {@link ChannelProcessor} instances to use in {@link ChannelDecisionManagerImpl}
* @param channelProcessors
* @return
*/
public ChannelRequestMatcherRegistry channelProcessors(List<ChannelProcessor> channelProcessors) {
ChannelSecurityConfigurer.this.channelProcessors = channelProcessors;
return this;
}
/**
* Return the {@link SecurityBuilder} when done using the
* {@link SecurityConfigurer}. This is useful for method chaining.
*
* @return
*/
public H and() {
return ChannelSecurityConfigurer.this.and();
}
private ChannelRequestMatcherRegistry() {}
}
public final class RequiresChannelUrl {
private List<RequestMatcher> requestMatchers;
private RequiresChannelUrl(List<RequestMatcher> requestMatchers) {
this.requestMatchers = requestMatchers;
}
public ChannelRequestMatcherRegistry requiresSecure() {
return requires("REQUIRES_SECURE_CHANNEL");
}
public ChannelRequestMatcherRegistry requiresInsecure() {
return requires("REQUIRES_INSECURE_CHANNEL");
}
public ChannelRequestMatcherRegistry requires(String attribute) {
return addAttribute(attribute, requestMatchers);
}
}
}
@@ -1,193 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers;
import java.util.LinkedHashMap;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
import org.springframework.security.web.access.DelegatingAccessDeniedHandler;
import org.springframework.security.web.csrf.CsrfAuthenticationStrategy;
import org.springframework.security.web.csrf.CsrfFilter;
import org.springframework.security.web.csrf.CsrfLogoutHandler;
import org.springframework.security.web.csrf.CsrfTokenRepository;
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
import org.springframework.security.web.csrf.MissingCsrfTokenException;
import org.springframework.security.web.session.InvalidSessionAccessDeniedHandler;
import org.springframework.security.web.session.InvalidSessionStrategy;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
/**
* Adds <a
* href="https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)"
* >CSRF</a> protection for the methods as specified by
* {@link #requireCsrfProtectionMatcher(RequestMatcher)}.
*
* <h2>Security Filters</h2>
*
* The following Filters are populated
*
* <ul>
* <li>{@link CsrfFilter}</li>
* </ul>
*
* <h2>Shared Objects Created</h2>
*
* No shared objects are created.
*
* <h2>Shared Objects Used</h2>
*
* <ul>
* <li>
* {@link ExceptionHandlingConfigurer#accessDeniedHandler(AccessDeniedHandler)}
* is used to determine how to handle CSRF attempts</li>
* <li>{@link InvalidSessionStrategy}</li>
* </ul>
*
* @author Rob Winch
* @since 3.2
*/
public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<CsrfConfigurer<H>,H> {
private CsrfTokenRepository csrfTokenRepository = new HttpSessionCsrfTokenRepository();
private RequestMatcher requireCsrfProtectionMatcher;
/**
* Creates a new instance
* @see HttpSecurity#csrf()
*/
public CsrfConfigurer() {
}
/**
* Specify the {@link CsrfTokenRepository} to use. The default is an {@link HttpSessionCsrfTokenRepository}.
*
* @param csrfTokenRepository the {@link CsrfTokenRepository} to use
* @return the {@link CsrfConfigurer} for further customizations
*/
public CsrfConfigurer<H> csrfTokenRepository(CsrfTokenRepository csrfTokenRepository) {
Assert.notNull(csrfTokenRepository, "csrfTokenRepository cannot be null");
this.csrfTokenRepository = csrfTokenRepository;
return this;
}
/**
* Specify the {@link RequestMatcher} to use for determining when CSRF
* should be applied. The default is to ignore GET, HEAD, TRACE, OPTIONS and
* process all other requests.
*
* @param requireCsrfProtectionMatcher
* the {@link RequestMatcher} to use
* @return the {@link CsrfConfigurer} for further customizations
*/
public CsrfConfigurer<H> requireCsrfProtectionMatcher(RequestMatcher requireCsrfProtectionMatcher) {
Assert.notNull(requireCsrfProtectionMatcher, "requireCsrfProtectionMatcher cannot be null");
this.requireCsrfProtectionMatcher = requireCsrfProtectionMatcher;
return this;
}
@SuppressWarnings("unchecked")
@Override
public void configure(H http) throws Exception {
CsrfFilter filter = new CsrfFilter(csrfTokenRepository);
if(requireCsrfProtectionMatcher != null) {
filter.setRequireCsrfProtectionMatcher(requireCsrfProtectionMatcher);
}
AccessDeniedHandler accessDeniedHandler = createAccessDeniedHandler(http);
if(accessDeniedHandler != null) {
filter.setAccessDeniedHandler(accessDeniedHandler);
}
LogoutConfigurer<H> logoutConfigurer = http.getConfigurer(LogoutConfigurer.class);
if(logoutConfigurer != null) {
logoutConfigurer.addLogoutHandler(new CsrfLogoutHandler(csrfTokenRepository));
}
SessionManagementConfigurer<H> sessionConfigurer = http.getConfigurer(SessionManagementConfigurer.class);
if(sessionConfigurer != null) {
sessionConfigurer.addSessionAuthenticationStrategy(new CsrfAuthenticationStrategy(csrfTokenRepository));
}
filter = postProcess(filter);
http.addFilter(filter);
}
/**
* Gets the default {@link AccessDeniedHandler} from the
* {@link ExceptionHandlingConfigurer#getAccessDeniedHandler()} or create a
* {@link AccessDeniedHandlerImpl} if not available.
*
* @param http the {@link HttpSecurityBuilder}
* @return the {@link AccessDeniedHandler}
*/
@SuppressWarnings("unchecked")
private AccessDeniedHandler getDefaultAccessDeniedHandler(H http) {
ExceptionHandlingConfigurer<H> exceptionConfig = http.getConfigurer(ExceptionHandlingConfigurer.class);
AccessDeniedHandler handler = null;
if(exceptionConfig != null) {
handler = exceptionConfig.getAccessDeniedHandler();
}
if(handler == null) {
handler = new AccessDeniedHandlerImpl();
}
return handler;
}
/**
* Gets the default {@link InvalidSessionStrategy} from the
* {@link SessionManagementConfigurer#getInvalidSessionStrategy()} or null
* if not available.
*
* @param http
* the {@link HttpSecurityBuilder}
* @return the {@link InvalidSessionStrategy}
*/
@SuppressWarnings("unchecked")
private InvalidSessionStrategy getInvalidSessionStrategy(H http) {
SessionManagementConfigurer<H> sessionManagement = http.getConfigurer(SessionManagementConfigurer.class);
if(sessionManagement == null) {
return null;
}
return sessionManagement.getInvalidSessionStrategy();
}
/**
* Creates the {@link AccessDeniedHandler} from the result of
* {@link #getDefaultAccessDeniedHandler(HttpSecurityBuilder)} and
* {@link #getInvalidSessionStrategy(HttpSecurityBuilder)}. If
* {@link #getInvalidSessionStrategy(HttpSecurityBuilder)} is non-null, then
* a {@link DelegatingAccessDeniedHandler} is used in combination with
* {@link InvalidSessionAccessDeniedHandler} and the
* {@link #getDefaultAccessDeniedHandler(HttpSecurityBuilder)}. Otherwise,
* only {@link #getDefaultAccessDeniedHandler(HttpSecurityBuilder)} is used.
*
* @param http the {@link HttpSecurityBuilder}
* @return the {@link AccessDeniedHandler}
*/
private AccessDeniedHandler createAccessDeniedHandler(H http) {
InvalidSessionStrategy invalidSessionStrategy = getInvalidSessionStrategy(http);
AccessDeniedHandler defaultAccessDeniedHandler = getDefaultAccessDeniedHandler(http);
if(invalidSessionStrategy == null) {
return defaultAccessDeniedHandler;
}
InvalidSessionAccessDeniedHandler invalidSessionDeniedHandler = new InvalidSessionAccessDeniedHandler(invalidSessionStrategy);
LinkedHashMap<Class<? extends AccessDeniedException>, AccessDeniedHandler> handlers =
new LinkedHashMap<Class<? extends AccessDeniedException>, AccessDeniedHandler>();
handlers.put(MissingCsrfTokenException.class, invalidSessionDeniedHandler);
return new DelegatingAccessDeniedHandler(handlers, defaultAccessDeniedHandler);
}
}
@@ -1,82 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
/**
* Adds a Filter that will generate a login page if one is not specified otherwise when using {@link WebSecurityConfigurerAdapter}.
*
* <p>
* By default an {@link org.springframework.security.web.access.channel.InsecureChannelProcessor} and a {@link org.springframework.security.web.access.channel.SecureChannelProcessor} will be registered.
* </p>
*
* <h2>Security Filters</h2>
*
* The following Filters are conditionally populated
*
* <ul>
* <li>{@link DefaultLoginPageGeneratingFilter} if the {@link FormLoginConfigurer} did not have a login page specified</li>
* </ul>
*
* <h2>Shared Objects Created</h2>
*
* No shared objects are created.
*isLogoutRequest
* <h2>Shared Objects Used</h2>
*
* The following shared objects are used:
*
* <ul>
* <li>{@link org.springframework.security.web.PortMapper} is used to create the default {@link org.springframework.security.web.access.channel.ChannelProcessor} instances</li>
* <li>{@link FormLoginConfigurer} is used to determine if the {@link DefaultLoginPageConfigurer} should be added and how to configure it.</li>
* </ul>
*
* @see WebSecurityConfigurerAdapter
*
* @author Rob Winch
* @since 3.2
*/
public final class DefaultLoginPageConfigurer<H extends HttpSecurityBuilder<H>> extends
AbstractHttpConfigurer<DefaultLoginPageConfigurer<H>,H> {
private DefaultLoginPageGeneratingFilter loginPageGeneratingFilter = new DefaultLoginPageGeneratingFilter();
@Override
public void init(H http) throws Exception {
http.setSharedObject(DefaultLoginPageGeneratingFilter.class, loginPageGeneratingFilter);
}
@Override
@SuppressWarnings("unchecked")
public void configure(H http) throws Exception {
AuthenticationEntryPoint authenticationEntryPoint = null;
ExceptionHandlingConfigurer<?> exceptionConf = http.getConfigurer(ExceptionHandlingConfigurer.class);
if(exceptionConf != null) {
authenticationEntryPoint = exceptionConf.getAuthenticationEntryPoint();
}
if(loginPageGeneratingFilter.isEnabled() && authenticationEntryPoint == null) {
loginPageGeneratingFilter = postProcess(loginPageGeneratingFilter);
http.addFilter(loginPageGeneratingFilter);
}
}
}
@@ -1,216 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers;
import java.util.LinkedHashMap;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
import org.springframework.security.web.access.ExceptionTranslationFilter;
import org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint;
import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.util.matcher.RequestMatcher;
/**
* Adds exception handling for Spring Security related exceptions to an application. All properties have reasonable
* defaults, so no additional configuration is required other than applying this
* {@link org.springframework.security.config.annotation.SecurityConfigurer}.
*
* <h2>Security Filters</h2>
*
* The following Filters are populated
*
* <ul>
* <li>{@link ExceptionTranslationFilter}</li>
* </ul>
*
* <h2>Shared Objects Created</h2>
*
* No shared objects are created.
*
* <h2>Shared Objects Used</h2>
*
* The following shared objects are used:
*
* <ul>
* <li>If no explicit {@link RequestCache}, is provided a {@link RequestCache} shared object is used to replay
* the request after authentication is successful</li>
* <li>{@link AuthenticationEntryPoint} - see {@link #authenticationEntryPoint(AuthenticationEntryPoint)} </li>
* </ul>
*
* @author Rob Winch
* @since 3.2
*/
public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<ExceptionHandlingConfigurer<H>,H> {
private AuthenticationEntryPoint authenticationEntryPoint;
private AccessDeniedHandler accessDeniedHandler;
private LinkedHashMap<RequestMatcher,AuthenticationEntryPoint> defaultEntryPointMappings = new LinkedHashMap<RequestMatcher, AuthenticationEntryPoint>();
/**
* Creates a new instance
* @see HttpSecurity#exceptionHandling()
*/
public ExceptionHandlingConfigurer() {
}
/**
* Shortcut to specify the {@link AccessDeniedHandler} to be used is a specific error page
*
* @param accessDeniedUrl the URL to the access denied page (i.e. /errors/401)
* @return the {@link ExceptionHandlingConfigurer} for further customization
* @see AccessDeniedHandlerImpl
* @see {@link #accessDeniedHandler(org.springframework.security.web.access.AccessDeniedHandler)}
*/
public ExceptionHandlingConfigurer<H> accessDeniedPage(String accessDeniedUrl) {
AccessDeniedHandlerImpl accessDeniedHandler = new AccessDeniedHandlerImpl();
accessDeniedHandler.setErrorPage(accessDeniedUrl);
return accessDeniedHandler(accessDeniedHandler);
}
/**
* Specifies the {@link AccessDeniedHandler} to be used
*
* @param accessDeniedHandler the {@link AccessDeniedHandler} to be used
* @return the {@link ExceptionHandlingConfigurer} for further customization
*/
public ExceptionHandlingConfigurer<H> accessDeniedHandler(AccessDeniedHandler accessDeniedHandler) {
this.accessDeniedHandler = accessDeniedHandler;
return this;
}
/**
* Sets the {@link AuthenticationEntryPoint} to be used.
*
* <p>
* If no {@link #authenticationEntryPoint(AuthenticationEntryPoint)} is
* specified, then
* {@link #defaultAuthenticationEntryPointFor(AuthenticationEntryPoint, RequestMatcher)}
* will be used. The first {@link AuthenticationEntryPoint} will be used as
* the default is no matches were found.
* </p>
*
* <p>
* If that is not provided defaults to {@link Http403ForbiddenEntryPoint}.
* </p>
*
* @param authenticationEntryPoint
* the {@link AuthenticationEntryPoint} to use
* @return the {@link ExceptionHandlingConfigurer} for further
* customizations
*/
public ExceptionHandlingConfigurer<H> authenticationEntryPoint(AuthenticationEntryPoint authenticationEntryPoint) {
this.authenticationEntryPoint = authenticationEntryPoint;
return this;
}
/**
* Sets a default {@link AuthenticationEntryPoint} to be used which prefers
* being invoked for the provided {@link RequestMatcher}. If only a single
* default {@link AuthenticationEntryPoint} is specified, it will be what is
* used for the default {@link AuthenticationEntryPoint}. If multiple
* default {@link AuthenticationEntryPoint} instances are configured, then a
* {@link DelegatingAuthenticationEntryPoint} will be used.
*
* @param entryPoint
* the {@link AuthenticationEntryPoint} to use
* @param preferredMatcher
* the {@link RequestMatcher} for this default
* {@link AuthenticationEntryPoint}
* @return the {@link ExceptionHandlingConfigurer} for further
* customizations
*/
public ExceptionHandlingConfigurer<H> defaultAuthenticationEntryPointFor(AuthenticationEntryPoint entryPoint, RequestMatcher preferredMatcher) {
this.defaultEntryPointMappings.put(preferredMatcher, entryPoint);
return this;
}
/**
* Gets any explicitly configured {@link AuthenticationEntryPoint}
* @return
*/
AuthenticationEntryPoint getAuthenticationEntryPoint() {
return this.authenticationEntryPoint;
}
/**
* Gets the {@link AccessDeniedHandler} that is configured.
*
* @return the {@link AccessDeniedHandler}
*/
AccessDeniedHandler getAccessDeniedHandler() {
return this.accessDeniedHandler;
}
@Override
public void configure(H http) throws Exception {
AuthenticationEntryPoint entryPoint = getAuthenticationEntryPoint(http);
ExceptionTranslationFilter exceptionTranslationFilter = new ExceptionTranslationFilter(entryPoint, getRequestCache(http));
if(accessDeniedHandler != null) {
exceptionTranslationFilter.setAccessDeniedHandler(accessDeniedHandler);
}
exceptionTranslationFilter = postProcess(exceptionTranslationFilter);
http.addFilter(exceptionTranslationFilter);
}
/**
* Gets the {@link AuthenticationEntryPoint} according to the rules specified by {@link #authenticationEntryPoint(AuthenticationEntryPoint)}
* @param http the {@link HttpSecurity} used to look up shared {@link AuthenticationEntryPoint}
* @return the {@link AuthenticationEntryPoint} to use
*/
AuthenticationEntryPoint getAuthenticationEntryPoint(H http) {
AuthenticationEntryPoint entryPoint = this.authenticationEntryPoint;
if(entryPoint == null) {
entryPoint = createDefaultEntryPoint(http);
}
return entryPoint;
}
private AuthenticationEntryPoint createDefaultEntryPoint(H http) {
if(defaultEntryPointMappings.isEmpty()) {
return new Http403ForbiddenEntryPoint();
}
if(defaultEntryPointMappings.size() == 1) {
return defaultEntryPointMappings.values().iterator().next();
}
DelegatingAuthenticationEntryPoint entryPoint = new DelegatingAuthenticationEntryPoint(defaultEntryPointMappings);
entryPoint.setDefaultEntryPoint(defaultEntryPointMappings.values().iterator().next());
return entryPoint;
}
/**
* Gets the {@link RequestCache} to use. If one is defined using
* {@link #requestCache(org.springframework.security.web.savedrequest.RequestCache)}, then it is used. Otherwise, an
* attempt to find a {@link RequestCache} shared object is made. If that fails, an {@link HttpSessionRequestCache}
* is used
*
* @param http the {@link HttpSecurity} to attempt to fined the shared object
* @return the {@link RequestCache} to use
*/
private RequestCache getRequestCache(H http) {
RequestCache result = http.getSharedObject(RequestCache.class);
if(result != null) {
return result;
}
return new HttpSessionRequestCache();
}
}

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