Compare commits
204 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d2263c855d | |||
| 0a7d6225f7 | |||
| 75360d1358 | |||
| a84b405744 | |||
| 4c8f61ec90 | |||
| c2a13b0aa8 | |||
| ad45967cef | |||
| bdd78ced7f | |||
| 9174fb7746 | |||
| b5ac6e42e3 | |||
| e3247231d7 | |||
| ab022a5ed3 | |||
| 18d8084744 | |||
| 1216673deb | |||
| 8e6e373a3b | |||
| 2b0ee23396 | |||
| 89cde2507d | |||
| ac0e308354 | |||
| 724b9e23ba | |||
| 99c5479f9a | |||
| c3dc3a3a4f | |||
| 859ce607cd | |||
| d6fd9980bd | |||
| 831981ac69 | |||
| 7b981d3e84 | |||
| 38a0bf7ca4 | |||
| e243855822 | |||
| e6e461d9a0 | |||
| 0ff8662f12 | |||
| 0878d7d563 | |||
| ae4b5907b9 | |||
| 5da9a0c0c0 | |||
| 95d5c61115 | |||
| 98a91f5ac1 | |||
| 8d4b97f685 | |||
| ac30552c42 | |||
| c8b6111418 | |||
| 21c0c8e5f5 | |||
| 293545eb4e | |||
| e23d6fb29b | |||
| b011acbfe6 | |||
| 72cca2c483 | |||
| e8e3f93843 | |||
| d752611acd | |||
| 6469c2f563 | |||
| de21cde132 | |||
| ac9c516434 | |||
| 343a2d786c | |||
| 590b2e859f | |||
| 74d7d8ebad | |||
| 09b8575c30 | |||
| 545a3263f4 | |||
| 2532518ffd | |||
| 5d8076056e | |||
| 66b47f5f67 | |||
| c2c2fb24be | |||
| 58bf2ffbc8 | |||
| 8cd7a03fac | |||
| 31360ecce6 | |||
| 699f5389f4 | |||
| e691f3630c | |||
| 248e9c85fd | |||
| bf433bb5a7 | |||
| e971c15390 | |||
| 289540ae5c | |||
| 10ddd576f6 | |||
| 6988be2ef2 | |||
| 0ee0084969 | |||
| 7cab6197a2 | |||
| d905160b5b | |||
| 3de8745494 | |||
| 6289503643 | |||
| 3612674644 | |||
| d7b7d36314 | |||
| db13131a07 | |||
| 01610bdd94 | |||
| 944fcf1484 | |||
| f76e762641 | |||
| 22ec394ea4 | |||
| 34527c3305 | |||
| 4cce316f89 | |||
| a98405eb37 | |||
| d14fb1e87d | |||
| e09b145803 | |||
| 7207dd6f19 | |||
| 2ff9a5507e | |||
| e4f5bd0a09 | |||
| 15ee5b2364 | |||
| 10b3ab6f8a | |||
| f9e16d6ee3 | |||
| 4e452046ec | |||
| edd7bbeceb | |||
| b2799985f2 | |||
| 219b865c01 | |||
| 976fdb0371 | |||
| 0d8be5012d | |||
| c021bf4682 | |||
| 0adf0d6f1c | |||
| bc411c7c3b | |||
| ea61964f56 | |||
| 0c4916ee98 | |||
| 301626fd6e | |||
| 8cb836c6cf | |||
| 2e8d16c538 | |||
| ad43d433b4 | |||
| aa4ee54f86 | |||
| 7fcdd4a6ff | |||
| 510cd5050f | |||
| 6c169d9acf | |||
| e87956358f | |||
| 5f993e5627 | |||
| 1467527c0a | |||
| 5b7ed79b6a | |||
| d7cef1ba31 | |||
| 47c5a6d43f | |||
| f7a6129657 | |||
| d1be9f9980 | |||
| 3dd0716611 | |||
| fa63d8ecfb | |||
| ce3eb599ed | |||
| ba88214d1d | |||
| 27ef2caf45 | |||
| e8d11f28f2 | |||
| bcf69cbe3d | |||
| 6651a240de | |||
| 6fe00b3433 | |||
| 036ea034ac | |||
| 4ba77fa736 | |||
| e189bc685f | |||
| c8077c5e87 | |||
| 3f123e1478 | |||
| 87d6b8dedd | |||
| f47ccd81a6 | |||
| dda88e3931 | |||
| 57f3d268a1 | |||
| 1c72b7989e | |||
| 82599a72ba | |||
| f8689b18b2 | |||
| 0425d3b638 | |||
| ed944fa537 | |||
| 6a36ae7a0d | |||
| c682a79e46 | |||
| 709dba101c | |||
| 70875a3c70 | |||
| cbc74de7c6 | |||
| 5474b3a78c | |||
| 93b303e343 | |||
| db3024f9a4 | |||
| 7a5c1ee328 | |||
| 8b6c592180 | |||
| 2b4d8a6378 | |||
| 3fbc7beb88 | |||
| fd0d4cd8b0 | |||
| 8396f04ae6 | |||
| 0efdd5d2bf | |||
| f70cba5d0e | |||
| bc30b903f8 | |||
| 99cc55b94a | |||
| 156965b370 | |||
| ea42164af2 | |||
| 5d64b86875 | |||
| fe4bbe0fbf | |||
| a499e74102 | |||
| b646a06443 | |||
| aea1148ffb | |||
| c5cc42e16c | |||
| 5ea8232f84 | |||
| e70f01c260 | |||
| 918f7ca008 | |||
| 0e46e5307c | |||
| 02cf570be7 | |||
| 35c6aea8e8 | |||
| 97a568c078 | |||
| b1a39fe1d1 | |||
| c658efbc69 | |||
| 1dfc2baea2 | |||
| ef38844a6d | |||
| 61d44954ee | |||
| 683fc597d2 | |||
| 0159b617cf | |||
| e0956920c7 | |||
| 3cdce8662e | |||
| b2c30277f4 | |||
| 6e82a9bdfc | |||
| 4b4807c138 | |||
| 207e8b932e | |||
| 23024b20ad | |||
| 2987b62893 | |||
| 1ee2a26e8f | |||
| 5249befa25 | |||
| 6c22fea917 | |||
| 10c2294aad | |||
| 66d7f754c1 | |||
| 1665e21873 | |||
| a0cb72cc3c | |||
| 4d783d7375 | |||
| 917030f0a3 | |||
| e9e89b835d | |||
| 82b22563e5 | |||
| 165d2c0122 | |||
| 942b5d7345 | |||
| e047ce6eba | |||
| e8e4f9c297 | |||
| 3f9988531a |
-86
@@ -1,86 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="core/src/main/java"/>
|
||||
<classpathentry kind="src" path="core/src/main/resources"/>
|
||||
<classpathentry kind="src" path="core/src/test/java"/>
|
||||
<classpathentry kind="src" path="core/src/test/resources"/>
|
||||
<classpathentry kind="src" path="samples/dms/src/test/java"/>
|
||||
<classpathentry kind="src" path="samples/dms/src/main/resources"/>
|
||||
<classpathentry kind="src" path="samples/dms/src/main/java"/>
|
||||
<classpathentry kind="src" path="samples/dms/src/test/resources"/>
|
||||
<classpathentry kind="src" path="samples/contacts/src/main/java"/>
|
||||
<classpathentry kind="src" path="samples/contacts/src/main/resources"/>
|
||||
<classpathentry kind="src" path="samples/contacts/src/test/java"/>
|
||||
<classpathentry kind="src" path="samples/contacts/src/test/resources"/>
|
||||
<classpathentry kind="src" path="samples/acegifier/src/main/java"/>
|
||||
<classpathentry kind="src" path="samples/acegifier/src/main/resources"/>
|
||||
<classpathentry kind="src" path="samples/acegifier/src/test/java"/>
|
||||
<classpathentry kind="src" path="samples/acegifier/src/test/resources"/>
|
||||
<classpathentry kind="src" path="samples/attributes/src/main/java"/>
|
||||
<classpathentry kind="src" path="samples/attributes/src/main/resources"/>
|
||||
<classpathentry kind="src" path="samples/attributes/src/test/java"/>
|
||||
<classpathentry kind="src" path="adapters/cas/src/test/resources"/>
|
||||
<classpathentry kind="src" path="adapters/cas/src/main/resources"/>
|
||||
<classpathentry kind="src" path="adapters/cas/src/main/java"/>
|
||||
<classpathentry kind="src" path="adapters/cas/src/test/java"/>
|
||||
<classpathentry kind="src" path="adapters/catalina/src/main/java"/>
|
||||
<classpathentry kind="src" path="adapters/catalina/src/main/resources"/>
|
||||
<classpathentry kind="src" path="adapters/catalina/src/test/java"/>
|
||||
<classpathentry kind="src" path="adapters/jboss/src/main/java"/>
|
||||
<classpathentry kind="src" path="adapters/jboss/src/test/java"/>
|
||||
<classpathentry kind="src" path="adapters/jetty/src/main/java"/>
|
||||
<classpathentry kind="src" path="adapters/jetty/src/test/java"/>
|
||||
<classpathentry kind="src" path="adapters/resin/src/main/java"/>
|
||||
<classpathentry kind="src" path="adapters/resin/src/test/java"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry kind="var" path="MAVEN_REPO/com.caucho/jars/resin-3.0.9.jar"/>
|
||||
<classpathentry kind="var" path="MAVEN_REPO/org.springframework/jars/spring-1.2.9.jar" sourcepath="/MAVEN_REPO/org.springframework/src/spring-1.2.9-sources.jar"/>
|
||||
<classpathentry kind="var" path="MAVEN_REPO/org.springframework/jars/spring-mock-1.2.9.jar"/>
|
||||
<classpathentry kind="var" path="MAVEN_REPO/aopalliance/jars/aopalliance-1.0.jar"/>
|
||||
<classpathentry kind="var" path="MAVEN_REPO/aspectj/jars/aspectjrt-1.2.jar"/>
|
||||
<classpathentry kind="var" path="MAVEN_REPO/cas/jars/casclient-2.0.11.jar"/>
|
||||
<classpathentry kind="var" path="MAVEN_REPO/cas/jars/cas-2.0.12.jar"/>
|
||||
<classpathentry kind="var" path="MAVEN_REPO/commons-codec/jars/commons-codec-1.3.jar"/>
|
||||
<classpathentry kind="var" path="MAVEN_REPO/commons-collections/jars/commons-collections-3.1.jar"/>
|
||||
<classpathentry kind="var" path="MAVEN_REPO/commons-logging/jars/commons-logging-1.0.4.jar"/>
|
||||
<classpathentry kind="var" path="MAVEN_REPO/hsqldb/jars/hsqldb-1.8.0.4.jar"/>
|
||||
<classpathentry kind="var" path="MAVEN_REPO/oro/jars/oro-2.0.8.jar"/>
|
||||
<classpathentry kind="var" path="MAVEN_REPO/javax.servlet/jars/servlet-api-2.4.jar"/>
|
||||
<classpathentry kind="var" path="MAVEN_REPO/tomcat/jars/catalina-4.1.9.jar"/>
|
||||
<classpathentry kind="var" path="MAVEN_REPO/jetty/jars/org.mortbay.jetty-4.2.22.jar"/>
|
||||
<classpathentry kind="var" path="MAVEN_REPO/jboss/jars/jboss-common-3.2.3.jar"/>
|
||||
<classpathentry kind="var" path="MAVEN_REPO/jboss/jars/jbosssx-3.2.3.jar"/>
|
||||
<classpathentry kind="var" path="MAVEN_REPO/junit/jars/junit-3.8.1.jar"/>
|
||||
<classpathentry kind="var" path="MAVEN_REPO/net.sf.ehcache/jars/ehcache-1.2.4.jar"/>
|
||||
<classpathentry kind="var" path="MAVEN_REPO/javax.servlet/jars/jsp-api-2.0.jar"/>
|
||||
<classpathentry kind="var" path="MAVEN_REPO/hibernate/jars/hibernate-3.0.3.jar"/>
|
||||
<classpathentry kind="var" path="MAVEN_REPO/commons-beanutils/jars/commons-beanutils-1.6.1.jar" sourcepath="DIST_BASE/commons-beanutils-1.6.1-src/src/java"/>
|
||||
<classpathentry kind="src" path="samples/contacts-tiger/src/main/java"/>
|
||||
<classpathentry kind="src" path="core-tiger/src/main/java"/>
|
||||
<classpathentry kind="src" path="core-tiger/src/main/resources"/>
|
||||
<classpathentry kind="src" path="core-tiger/src/test/java"/>
|
||||
<classpathentry kind="src" path="core-tiger/src/test/resources"/>
|
||||
<classpathentry kind="src" path="samples/annotations/src/main/java"/>
|
||||
<classpathentry kind="src" path="samples/annotations/src/main/resources"/>
|
||||
<classpathentry kind="src" path="samples/annotations/src/test/java"/>
|
||||
<classpathentry kind="var" path="MAVEN_REPO/org.samba.jcifs/jars/jcifs-1.2.6.jar"/>
|
||||
<classpathentry kind="var" path="MAVEN_REPO/dom4j/jars/dom4j-1.6.jar"/>
|
||||
<classpathentry kind="var" path="MAVEN_REPO/xerces/jars/xercesImpl-2.6.2.jar"/>
|
||||
<classpathentry kind="var" path="MAVEN_REPO/jmock/jars/jmock-1.0.1.jar" sourcepath="MAVEN_REPO/jmock/distributions/jmock-1.0.1-src.jar"/>
|
||||
<classpathentry kind="var" path="MAVEN_REPO/jdbm/jars/jdbm-1.0.jar"/>
|
||||
<classpathentry kind="var" path="MAVEN_REPO/regexp/jars/regexp-1.2.jar"/>
|
||||
<classpathentry kind="var" path="MAVEN_REPO/org.slf4j/jars/slf4j-log4j12-1.0-rc5.jar"/>
|
||||
<classpathentry kind="var" path="MAVEN_REPO/org.apache.directory.server/jars/apacheds-core-1.0.0.jar"/>
|
||||
<classpathentry kind="var" path="MAVEN_REPO/org.apache.directory.server/jars/apacheds-core-shared-1.0.0.jar"/>
|
||||
<classpathentry kind="var" path="MAVEN_REPO/org.apache.directory.shared/jars/shared-asn1-0.9.5.jar"/>
|
||||
<classpathentry kind="var" path="MAVEN_REPO/org.apache.directory.shared/jars/shared-ldap-0.9.5.jar"/>
|
||||
<classpathentry kind="var" path="MAVEN_REPO/antlr/jars/antlr-2.7.2.jar"/>
|
||||
<classpathentry kind="var" path="MAVEN_REPO/ldapsdk/jars/ldapsdk-4.1.jar"/>
|
||||
<classpathentry kind="var" path="MAVEN_REPO/cas/jars/cas-server-3.0.4.jar"/>
|
||||
<classpathentry kind="var" path="MAVEN_REPO/net.sourceforge.retroweaver/jars/retroweaver-1.0fcs.jar"/>
|
||||
<classpathentry kind="var" path="MAVEN_REPO/taglibs/jars/standard-1.0.6.jar"/>
|
||||
<classpathentry kind="var" path="MAVEN_REPO/commons-attributes/jars/commons-attributes-api-2.1.jar"/>
|
||||
<classpathentry kind="var" path="MAVEN_REPO/log4j/jars/log4j-1.2.9.jar"/>
|
||||
<classpathentry kind="var" path="M2_REPO/postgresql/postgresql/8.1-407.jdbc3/postgresql-8.1-407.jdbc3.jar"/>
|
||||
<classpathentry kind="output" path="target/eclipseclasses"/>
|
||||
</classpath>
|
||||
@@ -1,17 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>acegisecurity</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
@@ -1,39 +0,0 @@
|
||||
<!--
|
||||
* ========================================================================
|
||||
*
|
||||
* Copyright 2004 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.
|
||||
*
|
||||
* ========================================================================
|
||||
-->
|
||||
|
||||
<project
|
||||
xmlns:j="jelly:core"
|
||||
xmlns:ant="jelly:ant"
|
||||
xmlns:util="jelly:util"
|
||||
xmlns:maven="jelly:maven"
|
||||
>
|
||||
|
||||
<postGoal name="jar:jar">
|
||||
<j:if test="${context.getVariable('signature.alias') != null}">
|
||||
<echo>signature.alias defined; signing JAR(s)...</echo>
|
||||
<ant:signjar lazy="true" alias="${signature.alias}" storepass="${signature.storepass}" keystore="${signature.keystore}">
|
||||
<fileset dir="${maven.build.dir}">
|
||||
<include name="*.jar"/>
|
||||
</fileset>
|
||||
</ant:signjar>
|
||||
</j:if>
|
||||
</postGoal>
|
||||
|
||||
</project>
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>org.acegisecurity</groupId>
|
||||
<artifactId>acegi-security-adapters</artifactId>
|
||||
<version>1.0.4-SNAPSHOT</version>
|
||||
<version>1.0.6</version>
|
||||
</parent>
|
||||
<artifactId>acegi-security-cas</artifactId>
|
||||
<name>Acegi Security System for Spring - CAS adapter</name>
|
||||
@@ -18,5 +18,9 @@
|
||||
<artifactId>cas</artifactId>
|
||||
<version>2.0.12</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
</project>
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project>
|
||||
<extend>${basedir}/../project.xml</extend>
|
||||
<pomVersion>3</pomVersion>
|
||||
<artifactId>acegi-security-cas</artifactId>
|
||||
<name>Acegi Security System for Spring - CAS adapter</name>
|
||||
<siteDirectory>/home/groups/a/ac/acegisecurity/htdocs/multiproject/acegi-security-cas</siteDirectory>
|
||||
<repository>
|
||||
<connection>scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity</connection>
|
||||
<developerConnection>scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity</developerConnection>
|
||||
<url>http://acegisecurity.svn.sourceforge.net/viewcvs.cgi/acegisecurity/trunk/acegisecurity/adapters/cas/</url>
|
||||
</repository>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cas</groupId>
|
||||
<artifactId>cas</artifactId>
|
||||
<version>2.0.12</version>
|
||||
<type>jar</type>
|
||||
<url>http://www.yale.edu/tp/cas</url>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cas</groupId>
|
||||
<artifactId>cas-server</artifactId>
|
||||
<version>3.0.4</version>
|
||||
<type>jar</type>
|
||||
<url>http://www.ja-sig.org/products/cas/</url>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>${basedir}/../../</directory>
|
||||
<targetPath>META-INF</targetPath>
|
||||
<includes>
|
||||
<include>notice.txt</include>
|
||||
</includes>
|
||||
<filtering>false</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
</project>
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
|
||||
<project name="Acegi Security CAS Adapter">
|
||||
<body>
|
||||
<menu ref="parent"/>
|
||||
<menu ref="reports"/>
|
||||
</body>
|
||||
|
||||
</project>
|
||||
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
<!--
|
||||
* ========================================================================
|
||||
*
|
||||
* Copyright 2004 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.
|
||||
*
|
||||
* ========================================================================
|
||||
-->
|
||||
|
||||
<project
|
||||
xmlns:j="jelly:core"
|
||||
xmlns:ant="jelly:ant"
|
||||
xmlns:util="jelly:util"
|
||||
xmlns:maven="jelly:maven"
|
||||
>
|
||||
|
||||
<postGoal name="jar:jar">
|
||||
<j:if test="${context.getVariable('signature.alias') != null}">
|
||||
<echo>signature.alias defined; signing JAR(s)...</echo>
|
||||
<ant:signjar lazy="true" alias="${signature.alias}" storepass="${signature.storepass}" keystore="${signature.keystore}">
|
||||
<fileset dir="${maven.build.dir}">
|
||||
<include name="*.jar"/>
|
||||
</fileset>
|
||||
</ant:signjar>
|
||||
</j:if>
|
||||
</postGoal>
|
||||
|
||||
</project>
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>org.acegisecurity</groupId>
|
||||
<artifactId>acegi-security-adapters</artifactId>
|
||||
<version>1.0.4-SNAPSHOT</version>
|
||||
<version>1.0.6</version>
|
||||
</parent>
|
||||
<artifactId>acegi-security-catalina</artifactId>
|
||||
<name>Acegi Security System for Spring - Catalina adapter</name>
|
||||
@@ -14,4 +14,4 @@
|
||||
<version>4.1.9</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
</project>
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<project>
|
||||
<extend>${basedir}/../project.xml</extend>
|
||||
<pomVersion>3</pomVersion>
|
||||
<artifactId>acegi-security-catalina</artifactId>
|
||||
<name>Acegi Security System for Spring - Catalina adapter</name>
|
||||
<siteDirectory>/home/groups/a/ac/acegisecurity/htdocs/multiproject/acegi-security-catalina</siteDirectory>
|
||||
<repository>
|
||||
<connection>scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity</connection>
|
||||
<developerConnection>scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity</developerConnection>
|
||||
<url>http://acegisecurity.svn.sourceforge.net/viewcvs.cgi/acegisecurity/trunk/acegisecurity/adapters/catalina/</url>
|
||||
</repository>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>tomcat</groupId>
|
||||
<artifactId>catalina</artifactId>
|
||||
<version>4.1.9</version>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<unitTest>
|
||||
<includes>
|
||||
<include>**/*Tests.java</include>
|
||||
</includes>
|
||||
<resources>
|
||||
<!-- <resource>
|
||||
<directory>${basedir}/../../core/src/test/resources</directory>
|
||||
<includes>
|
||||
<include>**/**</include>
|
||||
</includes>
|
||||
<filtering>false</filtering>
|
||||
</resource>-->
|
||||
<resource>
|
||||
<directory>${basedir}/src/main/resources</directory>
|
||||
<includes>
|
||||
<include>**/**</include>
|
||||
</includes>
|
||||
<filtering>false</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</unitTest>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>${basedir}/../../</directory>
|
||||
<targetPath>META-INF</targetPath>
|
||||
<includes>
|
||||
<include>notice.txt</include>
|
||||
</includes>
|
||||
<filtering>false</filtering>
|
||||
</resource>
|
||||
<!-- <resource>
|
||||
<directory>${basedir}/../../src/main/resources</directory>
|
||||
<includes>
|
||||
<include>**/**</include>
|
||||
</includes>
|
||||
<filtering>false</filtering>
|
||||
</resource>-->
|
||||
</resources>
|
||||
</build>
|
||||
</project>
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
<!--
|
||||
* ========================================================================
|
||||
*
|
||||
* Copyright 2004 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.
|
||||
*
|
||||
* ========================================================================
|
||||
-->
|
||||
|
||||
<project
|
||||
xmlns:j="jelly:core"
|
||||
xmlns:ant="jelly:ant"
|
||||
xmlns:util="jelly:util"
|
||||
xmlns:maven="jelly:maven"
|
||||
>
|
||||
|
||||
<postGoal name="jar:jar">
|
||||
<j:if test="${context.getVariable('signature.alias') != null}">
|
||||
<echo>signature.alias defined; signing JAR(s)...</echo>
|
||||
<ant:signjar lazy="true" alias="${signature.alias}" storepass="${signature.storepass}" keystore="${signature.keystore}">
|
||||
<fileset dir="${maven.build.dir}">
|
||||
<include name="*.jar"/>
|
||||
</fileset>
|
||||
</ant:signjar>
|
||||
</j:if>
|
||||
</postGoal>
|
||||
|
||||
</project>
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>org.acegisecurity</groupId>
|
||||
<artifactId>acegi-security-adapters</artifactId>
|
||||
<version>1.0.4-SNAPSHOT</version>
|
||||
<version>1.0.6</version>
|
||||
</parent>
|
||||
<artifactId>acegi-security-jboss</artifactId>
|
||||
<name>Acegi Security System for Spring - JBoss adapter</name>
|
||||
@@ -20,4 +20,4 @@
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
</project>
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<project>
|
||||
<extend>${basedir}/../project.xml</extend>
|
||||
<pomVersion>3</pomVersion>
|
||||
<artifactId>acegi-security-jboss</artifactId>
|
||||
<name>Acegi Security System for Spring - JBoss adapter</name>
|
||||
<siteDirectory>/home/groups/a/ac/acegisecurity/htdocs/multiproject/acegi-security-jboss</siteDirectory>
|
||||
<repository>
|
||||
<connection>scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity</connection>
|
||||
<developerConnection>scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity</developerConnection>
|
||||
<url>http://acegisecurity.svn.sourceforge.net/viewcvs.cgi/acegisecurity/trunk/acegisecurity/adapters/jboss/</url>
|
||||
</repository>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>jboss</groupId>
|
||||
<artifactId>jboss-common</artifactId>
|
||||
<version>3.2.3</version>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jboss</groupId>
|
||||
<artifactId>jbosssx</artifactId>
|
||||
<version>3.2.3</version>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<unitTest>
|
||||
<includes>
|
||||
<include>**/*Tests.java</include>
|
||||
</includes>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>${basedir}/../../</directory>
|
||||
<targetPath>META-INF</targetPath>
|
||||
<includes>
|
||||
<include>notice.txt</include>
|
||||
</includes>
|
||||
<filtering>false</filtering>
|
||||
</resource>
|
||||
<resource>
|
||||
<directory>${basedir}/../../core/src/main/resources</directory>
|
||||
<includes>
|
||||
<include>**/**</include>
|
||||
</includes>
|
||||
<filtering>false</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</unitTest>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>${basedir}/../../</directory>
|
||||
<targetPath>META-INF</targetPath>
|
||||
<includes>
|
||||
<include>notice.txt</include>
|
||||
</includes>
|
||||
<filtering>false</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
</project>
|
||||
+6
-4
@@ -18,6 +18,8 @@ package org.acegisecurity.adapters.jboss;
|
||||
import org.acegisecurity.Authentication;
|
||||
|
||||
import org.acegisecurity.context.SecurityContextHolder;
|
||||
import org.acegisecurity.context.HttpSessionContextIntegrationFilter;
|
||||
import org.acegisecurity.context.SecurityContext;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
@@ -43,10 +45,10 @@ import javax.servlet.ServletResponse;
|
||||
|
||||
|
||||
/**
|
||||
* Populates a {@link org.acegisecurity.context.security.SecureContext} from JBoss'
|
||||
* <code>java:comp/env/security/subject</code>.<p>This filter <b>never</b> preserves the
|
||||
* <code>Authentication</code> on the <code>ContextHolder</code> - it is replaced every request.</p>
|
||||
* <p>See {@link HttpSessionContextIntegrationFilter} for further information.</p>
|
||||
* Populates a {@link SecurityContext} from JBoss' <code>java:comp/env/security/subject</code>.
|
||||
* <p>This filter <b>never</b> preserves the <code>Authentication</code> on the <code>ContextHolder</code> -
|
||||
* it is replaced every request.</p>
|
||||
* <p>See {@link HttpSessionContextIntegrationFilter} for further information.</p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
|
||||
+4
-5
@@ -20,6 +20,7 @@ import junit.framework.TestCase;
|
||||
import org.acegisecurity.adapters.PrincipalAcegiUserToken;
|
||||
|
||||
import org.jboss.security.SimplePrincipal;
|
||||
import org.jboss.security.SimpleGroup;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -27,6 +28,7 @@ import java.security.Principal;
|
||||
import java.security.acl.Group;
|
||||
|
||||
import java.util.Properties;
|
||||
import java.util.Enumeration;
|
||||
|
||||
import javax.security.auth.Subject;
|
||||
import javax.security.auth.callback.Callback;
|
||||
@@ -318,7 +320,8 @@ public class JbossAcegiLoginModuleTests extends TestCase {
|
||||
assertTrue(adapter.login());
|
||||
|
||||
Group[] result = adapter.getRoleSets();
|
||||
assertEquals(1, result.length); // SimpleGroup called "Roles"
|
||||
// Expect Roles group.
|
||||
assertEquals(1, result.length);
|
||||
|
||||
Group roles = result[0];
|
||||
assertTrue(roles.isMember(new SimplePrincipal("ROLE_TELLER")));
|
||||
@@ -336,10 +339,6 @@ public class JbossAcegiLoginModuleTests extends TestCase {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
private MockCallbackHandler() {
|
||||
super();
|
||||
}
|
||||
|
||||
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
|
||||
for (int i = 0; i < callbacks.length; i++) {
|
||||
if (callbacks[i] instanceof NameCallback) {
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
<!--
|
||||
* ========================================================================
|
||||
*
|
||||
* Copyright 2004 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.
|
||||
*
|
||||
* ========================================================================
|
||||
-->
|
||||
|
||||
<project
|
||||
xmlns:j="jelly:core"
|
||||
xmlns:ant="jelly:ant"
|
||||
xmlns:util="jelly:util"
|
||||
xmlns:maven="jelly:maven"
|
||||
>
|
||||
|
||||
<postGoal name="jar:jar">
|
||||
<j:if test="${context.getVariable('signature.alias') != null}">
|
||||
<echo>signature.alias defined; signing JAR(s)...</echo>
|
||||
<ant:signjar lazy="true" alias="${signature.alias}" storepass="${signature.storepass}" keystore="${signature.keystore}">
|
||||
<fileset dir="${maven.build.dir}">
|
||||
<include name="*.jar"/>
|
||||
</fileset>
|
||||
</ant:signjar>
|
||||
</j:if>
|
||||
</postGoal>
|
||||
|
||||
</project>
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>org.acegisecurity</groupId>
|
||||
<artifactId>acegi-security-adapters</artifactId>
|
||||
<version>1.0.4-SNAPSHOT</version>
|
||||
<version>1.0.6</version>
|
||||
</parent>
|
||||
<artifactId>acegi-security-jetty</artifactId>
|
||||
<name>Acegi Security System for Spring - Jetty adapter</name>
|
||||
@@ -14,4 +14,4 @@
|
||||
<version>4.2.22</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
</project>
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<project>
|
||||
<extend>${basedir}/../project.xml</extend>
|
||||
<pomVersion>3</pomVersion>
|
||||
<artifactId>acegi-security-jetty</artifactId>
|
||||
<name>Acegi Security System for Spring - Jetty adapter</name>
|
||||
<siteDirectory>/home/groups/a/ac/acegisecurity/htdocs/multiproject/acegi-security-jetty</siteDirectory>
|
||||
<repository>
|
||||
<connection>scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity</connection>
|
||||
<developerConnection>scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity</developerConnection>
|
||||
<url>http://acegisecurity.svn.sourceforge.net/viewcvs.cgi/acegisecurity/trunk/acegisecurity/adapters/jetty/</url>
|
||||
</repository>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>jetty</groupId>
|
||||
<artifactId>org.mortbay.jetty</artifactId>
|
||||
<version>4.2.22</version>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<unitTest>
|
||||
<includes>
|
||||
<include>**/*Tests.java</include>
|
||||
</includes>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>${basedir}/../../core/src/main/resources</directory>
|
||||
<includes>
|
||||
<include>**/**</include>
|
||||
</includes>
|
||||
<filtering>false</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</unitTest>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>${basedir}/../../</directory>
|
||||
<targetPath>META-INF</targetPath>
|
||||
<includes>
|
||||
<include>notice.txt</include>
|
||||
</includes>
|
||||
<filtering>false</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
</project>
|
||||
|
||||
+2
-2
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>org.acegisecurity</groupId>
|
||||
<artifactId>acegi-security-parent</artifactId>
|
||||
<version>1.0.4-SNAPSHOT</version>
|
||||
<version>1.0.6</version>
|
||||
</parent>
|
||||
<artifactId>acegi-security-adapters</artifactId>
|
||||
<name>Acegi Security System for Spring - Adapters</name>
|
||||
@@ -27,4 +27,4 @@
|
||||
<module>jetty</module>
|
||||
<module>resin</module>
|
||||
</modules>
|
||||
</project>
|
||||
</project>
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project>
|
||||
<extend>${basedir}/../project.xml</extend>
|
||||
<pomVersion>3</pomVersion>
|
||||
<artifactId>acegi-security-adapters</artifactId>
|
||||
<name>Acegi Security System for Spring - Adapters</name>
|
||||
<repository>
|
||||
<connection>scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity</connection>
|
||||
<developerConnection>scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity</developerConnection>
|
||||
<url>http://acegisecurity.svn.sourceforge.net/viewcvs.cgi/acegisecurity/trunk/acegisecurity/adapters/</url>
|
||||
</repository>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.acegisecurity</groupId>
|
||||
<artifactId>acegi-security</artifactId>
|
||||
<version>1.0.4</version>
|
||||
<type>jar</type>
|
||||
<url>http://acegisecurity.org</url>
|
||||
<properties>
|
||||
<war.bundle>true</war.bundle>
|
||||
</properties>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
<!--
|
||||
* ========================================================================
|
||||
*
|
||||
* Copyright 2004 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.
|
||||
*
|
||||
* ========================================================================
|
||||
-->
|
||||
|
||||
<project
|
||||
xmlns:j="jelly:core"
|
||||
xmlns:ant="jelly:ant"
|
||||
xmlns:util="jelly:util"
|
||||
xmlns:maven="jelly:maven"
|
||||
>
|
||||
|
||||
<postGoal name="jar:jar">
|
||||
<j:if test="${context.getVariable('signature.alias') != null}">
|
||||
<echo>signature.alias defined; signing JAR(s)...</echo>
|
||||
<ant:signjar lazy="true" alias="${signature.alias}" storepass="${signature.storepass}" keystore="${signature.keystore}">
|
||||
<fileset dir="${maven.build.dir}">
|
||||
<include name="*.jar"/>
|
||||
</fileset>
|
||||
</ant:signjar>
|
||||
</j:if>
|
||||
</postGoal>
|
||||
|
||||
</project>
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>org.acegisecurity</groupId>
|
||||
<artifactId>acegi-security-adapters</artifactId>
|
||||
<version>1.0.4-SNAPSHOT</version>
|
||||
<version>1.0.6</version>
|
||||
</parent>
|
||||
<artifactId>acegi-security-resin</artifactId>
|
||||
<name>Acegi Security System for Spring - Resin adapter</name>
|
||||
@@ -20,4 +20,4 @@
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
</project>
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<project>
|
||||
<extend>${basedir}/../project.xml</extend>
|
||||
<pomVersion>3</pomVersion>
|
||||
<artifactId>acegi-security-resin</artifactId>
|
||||
<name>Acegi Security System for Spring - Resin adapter</name>
|
||||
<siteDirectory>/home/groups/a/ac/acegisecurity/htdocs/multiproject/acegi-security-resin</siteDirectory>
|
||||
<repository>
|
||||
<connection>scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity</connection>
|
||||
<developerConnection>scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity</developerConnection>
|
||||
<url>http://acegisecurity.svn.sourceforge.net/viewcvs.cgi/acegisecurity/trunk/acegisecurity/adapters/resin/</url>
|
||||
</repository>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.caucho</groupId>
|
||||
<artifactId>resin</artifactId>
|
||||
<version>3.0.9</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sourceforge.retroweaver</groupId>
|
||||
<artifactId>retroweaver</artifactId>
|
||||
<version>1.0fcs</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<unitTest>
|
||||
<includes>
|
||||
<include>**/*Tests.java</include>
|
||||
</includes>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>${basedir}/../../core/src/test/resources</directory>
|
||||
<includes>
|
||||
<include>**/**</include>
|
||||
</includes>
|
||||
<filtering>false</filtering>
|
||||
</resource>
|
||||
<resource>
|
||||
<directory>${basedir}/../../core/src/main/resources</directory>
|
||||
<includes>
|
||||
<include>**/**</include>
|
||||
</includes>
|
||||
<filtering>false</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</unitTest>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>${basedir}/../../</directory>
|
||||
<targetPath>META-INF</targetPath>
|
||||
<includes>
|
||||
<include>notice.txt</include>
|
||||
</includes>
|
||||
<filtering>false</filtering>
|
||||
</resource>
|
||||
<!-- <resource>
|
||||
<directory>${basedir}/../../src/main/resources</directory>
|
||||
<includes>
|
||||
<include>**/**</include>
|
||||
</includes>
|
||||
<filtering>false</filtering>
|
||||
</resource>-->
|
||||
</resources>
|
||||
</build>
|
||||
</project>
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
<!--
|
||||
* ========================================================================
|
||||
*
|
||||
* Copyright 2004, 2005 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.
|
||||
*
|
||||
* ========================================================================
|
||||
-->
|
||||
|
||||
<project
|
||||
xmlns:j="jelly:core"
|
||||
xmlns:ant="jelly:ant"
|
||||
xmlns:util="jelly:util"
|
||||
xmlns:maven="jelly:maven"
|
||||
>
|
||||
|
||||
<postGoal name="jar:jar">
|
||||
<j:if test="${context.getVariable('signature.alias') != null}">
|
||||
<echo>signature.alias defined; signing JAR(s)...</echo>
|
||||
<ant:signjar lazy="true" alias="${signature.alias}" storepass="${signature.storepass}" keystore="${signature.keystore}">
|
||||
<fileset dir="${maven.build.dir}">
|
||||
<include name="*.jar"/>
|
||||
</fileset>
|
||||
</ant:signjar>
|
||||
</j:if>
|
||||
</postGoal>
|
||||
|
||||
</project>
|
||||
+2
-8
@@ -3,17 +3,11 @@
|
||||
<parent>
|
||||
<groupId>org.acegisecurity</groupId>
|
||||
<artifactId>acegi-security-parent</artifactId>
|
||||
<version>1.0.4-SNAPSHOT</version>
|
||||
<version>1.0.6</version>
|
||||
</parent>
|
||||
<artifactId>acegi-security-tiger</artifactId>
|
||||
<name>Acegi Security System for Spring - Java 5 (Tiger)</name>
|
||||
|
||||
<scm>
|
||||
<connection>scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity/core-tiger</connection>
|
||||
<developerConnection>scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity/core-tiger</developerConnection>
|
||||
<url>http://acegisecurity.svn.sourceforge.net/viewcvs.cgi/acegisecurity/trunk/acegisecurity/core-tiger/</url>
|
||||
</scm>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.acegisecurity</groupId>
|
||||
@@ -23,7 +17,7 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-aop</artifactId>
|
||||
<version>2.0.4</version>
|
||||
<version>${spring.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
# $Id$
|
||||
|
||||
# Values in this file will be overriden by any values with the same name
|
||||
# in the user-created build.properties file.
|
||||
|
||||
# Compile settings
|
||||
#
|
||||
# Java 1.5 is required due to the use of annotations for metadata.
|
||||
# (main Acegi Security project / parent) is Java 1.3 compatible
|
||||
#
|
||||
maven.compile.target=1.5
|
||||
maven.compile.source=1.5
|
||||
@@ -1,42 +0,0 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<project>
|
||||
<extend>${basedir}/../project.xml</extend>
|
||||
<pomVersion>3</pomVersion>
|
||||
<artifactId>acegi-security-tiger</artifactId>
|
||||
<name>Acegi Security System for Spring - Java 5 (Tiger)</name>
|
||||
<siteDirectory>/home/groups/a/ac/acegisecurity/htdocs/multiproject/acegi-security-tiger</siteDirectory>
|
||||
<repository>
|
||||
<connection>scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity</connection>
|
||||
<developerConnection>scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity</developerConnection>
|
||||
<url>http://acegisecurity.svn.sourceforge.net/viewcvs.cgi/acegisecurity/trunk/acegisecurity/core-tiger/</url>
|
||||
</repository>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.acegisecurity</groupId>
|
||||
<artifactId>acegi-security</artifactId>
|
||||
<version>1.0.4</version>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>${basedir}/src/main/resources/</directory>
|
||||
<targetPath>/</targetPath>
|
||||
<includes>
|
||||
<include>*.xsl</include>
|
||||
</includes>
|
||||
<filtering>false</filtering>
|
||||
</resource>
|
||||
<resource>
|
||||
<directory>${basedir}/../</directory>
|
||||
<targetPath>META-INF</targetPath>
|
||||
<includes>
|
||||
<include>notice.txt</include>
|
||||
</includes>
|
||||
<filtering>false</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
</project>
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
<!--
|
||||
* ========================================================================
|
||||
*
|
||||
* Copyright 2004 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.
|
||||
*
|
||||
* ========================================================================
|
||||
-->
|
||||
|
||||
<project
|
||||
xmlns:j="jelly:core"
|
||||
xmlns:ant="jelly:ant"
|
||||
xmlns:util="jelly:util"
|
||||
xmlns:maven="jelly:maven"
|
||||
>
|
||||
|
||||
<postGoal name="jar:jar">
|
||||
<j:if test="${context.getVariable('signature.alias') != null}">
|
||||
<echo>signature.alias defined; signing JAR(s)...</echo>
|
||||
<!-- To create your own free signing certificate, see http://www.dallaway.com/acad/webstart/ -->
|
||||
<ant:signjar lazy="true" alias="${signature.alias}" storepass="${signature.storepass}" keystore="${signature.keystore}">
|
||||
<fileset dir="${maven.build.dir}">
|
||||
<include name="*.jar"/>
|
||||
</fileset>
|
||||
</ant:signjar>
|
||||
</j:if>
|
||||
</postGoal>
|
||||
|
||||
</project>
|
||||
+20
-18
@@ -1,30 +1,20 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.acegisecurity</groupId>
|
||||
<artifactId>acegi-security-parent</artifactId>
|
||||
<version>1.0.4-SNAPSHOT</version>
|
||||
<version>1.0.6</version>
|
||||
</parent>
|
||||
<packaging>jar</packaging>
|
||||
<artifactId>acegi-security</artifactId>
|
||||
<name>Acegi Security System for Spring</name>
|
||||
|
||||
<scm>
|
||||
<connection>
|
||||
scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity/core
|
||||
</connection>
|
||||
<developerConnection>
|
||||
scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity/core
|
||||
</developerConnection>
|
||||
<url>
|
||||
http://acegisecurity.svn.sourceforge.net/viewcvs.cgi/acegisecurity/trunk/acegisecurity/core/
|
||||
</url>
|
||||
</scm>
|
||||
<name>Acegi Security Core</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-remoting</artifactId>
|
||||
</dependency>
|
||||
@@ -38,11 +28,23 @@
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-mock</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<dependency>
|
||||
<!-- TODO: Upgrade to 1.2 before 2.0 release -->
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-ldap</artifactId>
|
||||
<version>1.1.2</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sf.ehcache</groupId>
|
||||
<artifactId>ehcache</artifactId>
|
||||
<version>1.2.4</version>
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<project>
|
||||
<extend>${basedir}/../project.xml</extend>
|
||||
<pomVersion>3</pomVersion>
|
||||
<groupId>org.acegisecurity</groupId>
|
||||
<artifactId>acegi-security</artifactId>
|
||||
<name>Acegi Security System for Spring</name>
|
||||
<siteDirectory>/home/groups/a/ac/acegisecurity/htdocs/multiproject/acegi-security</siteDirectory>
|
||||
<repository>
|
||||
<connection>scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity</connection>
|
||||
<developerConnection>scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity</developerConnection>
|
||||
<url>http://acegisecurity.svn.sourceforge.net/viewcvs.cgi/acegisecurity/trunk/acegisecurity/core/</url>
|
||||
</repository>
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>${basedir}/../</directory>
|
||||
<targetPath>META-INF</targetPath>
|
||||
<includes>
|
||||
<include>notice.txt</include>
|
||||
</includes>
|
||||
<filtering>false</filtering>
|
||||
</resource>
|
||||
<resource>
|
||||
<directory>${basedir}/src/main/resources/org/acegisecurity/taglibs</directory>
|
||||
<targetPath>META-INF</targetPath>
|
||||
<includes>
|
||||
<include>*.tld</include>
|
||||
</includes>
|
||||
<filtering>false</filtering>
|
||||
</resource>
|
||||
<resource>
|
||||
<directory>${basedir}/src/main/resources/</directory>
|
||||
<targetPath>/</targetPath>
|
||||
<includes>
|
||||
<include>*.xsl</include>
|
||||
<include>**/*.properties</include>
|
||||
</includes>
|
||||
<filtering>false</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
</project>
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
|
||||
package org.acegisecurity;
|
||||
|
||||
import org.acegisecurity.providers.AbstractAuthenticationToken;
|
||||
|
||||
|
||||
/**
|
||||
* An abstract implementation of the {@link AuthenticationManager}.
|
||||
@@ -42,35 +40,17 @@ public abstract class AbstractAuthenticationManager implements AuthenticationMan
|
||||
public final Authentication authenticate(Authentication authRequest)
|
||||
throws AuthenticationException {
|
||||
try {
|
||||
Authentication authResult = doAuthentication(authRequest);
|
||||
copyDetails(authRequest, authResult);
|
||||
|
||||
return authResult;
|
||||
return doAuthentication(authRequest);
|
||||
} catch (AuthenticationException e) {
|
||||
e.setAuthentication(authRequest);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the authentication details from a source Authentication object to a destination one, provided the
|
||||
* latter does not already have one set.
|
||||
*
|
||||
* @param source source authentication
|
||||
* @param dest the destination authentication object
|
||||
*/
|
||||
private void copyDetails(Authentication source, Authentication dest) {
|
||||
if ((dest instanceof AbstractAuthenticationToken) && (dest.getDetails() == null)) {
|
||||
AbstractAuthenticationToken token = (AbstractAuthenticationToken) dest;
|
||||
|
||||
token.setDetails(source.getDetails());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Concrete implementations of this class override this method to provide the authentication service.</p>
|
||||
* <p>The contract for this method is documented in the {@link
|
||||
* AuthenticationManager#authenticate(org.acegisecurity.Authentication)}.</p>
|
||||
* AuthenticationManager#authenticate(Authentication)}.</p>
|
||||
*
|
||||
* @param authentication the authentication request object
|
||||
*
|
||||
|
||||
+7
-7
@@ -15,9 +15,9 @@
|
||||
|
||||
package org.acegisecurity.acl.basic.cache;
|
||||
|
||||
import net.sf.ehcache.Cache;
|
||||
import net.sf.ehcache.CacheException;
|
||||
import net.sf.ehcache.Element;
|
||||
import net.sf.ehcache.Ehcache;
|
||||
|
||||
import org.acegisecurity.acl.basic.AclObjectIdentity;
|
||||
import org.acegisecurity.acl.basic.BasicAclEntry;
|
||||
@@ -47,7 +47,7 @@ public class EhCacheBasedAclEntryCache implements BasicAclEntryCache, Initializi
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private Cache cache;
|
||||
private Ehcache cache;
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
@@ -55,10 +55,6 @@ public class EhCacheBasedAclEntryCache implements BasicAclEntryCache, Initializi
|
||||
Assert.notNull(cache, "cache mandatory");
|
||||
}
|
||||
|
||||
public Cache getCache() {
|
||||
return cache;
|
||||
}
|
||||
|
||||
public BasicAclEntry[] getEntriesFromCache(AclObjectIdentity aclObjectIdentity) {
|
||||
Element element = null;
|
||||
|
||||
@@ -101,7 +97,11 @@ public class EhCacheBasedAclEntryCache implements BasicAclEntryCache, Initializi
|
||||
cache.remove(aclObjectIdentity);
|
||||
}
|
||||
|
||||
public void setCache(Cache cache) {
|
||||
public Ehcache getCache() {
|
||||
return cache;
|
||||
}
|
||||
|
||||
public void setCache(Ehcache cache) {
|
||||
this.cache = cache;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
*/
|
||||
package org.acegisecurity.acls.jdbc;
|
||||
|
||||
import net.sf.ehcache.Cache;
|
||||
import net.sf.ehcache.CacheException;
|
||||
import net.sf.ehcache.Element;
|
||||
import net.sf.ehcache.Ehcache;
|
||||
|
||||
import org.acegisecurity.acls.MutableAcl;
|
||||
import org.acegisecurity.acls.objectidentity.ObjectIdentity;
|
||||
@@ -35,11 +35,11 @@ import java.io.Serializable;
|
||||
public class EhCacheBasedAclCache implements AclCache {
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private Cache cache;
|
||||
private Ehcache cache;
|
||||
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
public EhCacheBasedAclCache(Cache cache) {
|
||||
public EhCacheBasedAclCache(Ehcache cache) {
|
||||
Assert.notNull(cache, "Cache required");
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.MessageSourceAware;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -39,23 +38,14 @@ import org.springframework.util.Assert;
|
||||
* <code>AuthByAdapterProvider</code>-configured key.</p>
|
||||
* <P>If the key does not match, a <code>BadCredentialsException</code> is thrown.</p>
|
||||
*/
|
||||
public class AuthByAdapterProvider implements InitializingBean, AuthenticationProvider, MessageSourceAware, Ordered {
|
||||
public class AuthByAdapterProvider implements InitializingBean, AuthenticationProvider, MessageSourceAware {
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor();
|
||||
private String key;
|
||||
private int order = -1; // default: same as non-Ordered
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(key, "A Key is required and should match that configured for the adapters");
|
||||
Assert.notNull(messages, "A message source must be set");
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ public class ConcurrentSessionControllerImpl implements ConcurrentSessionControl
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor();
|
||||
private SessionRegistry sessionRegistry = new SessionRegistryImpl();
|
||||
private SessionRegistry sessionRegistry;
|
||||
private boolean exceptionIfMaximumExceeded = false;
|
||||
private int maximumSessions = 1;
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.acegisecurity.concurrent;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.Date;
|
||||
import java.io.Serializable;
|
||||
|
||||
|
||||
/**
|
||||
@@ -32,7 +33,7 @@ import java.util.Date;
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class SessionInformation {
|
||||
public class SessionInformation implements Serializable {
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private Date lastRequest;
|
||||
@@ -51,8 +52,6 @@ public class SessionInformation {
|
||||
this.lastRequest = lastRequest;
|
||||
}
|
||||
|
||||
private SessionInformation() {}
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public void expireNow() {
|
||||
|
||||
@@ -21,6 +21,8 @@ import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -34,126 +36,140 @@ import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
|
||||
/**
|
||||
* Base implementation of {@link org.acegisecurity.concurrent.SessionRegistry}
|
||||
* which also listens for {@link
|
||||
* org.acegisecurity.ui.session.HttpSessionDestroyedEvent}s published in the
|
||||
* Spring application context.
|
||||
*
|
||||
* which also listens for {@link org.acegisecurity.ui.session.HttpSessionDestroyedEvent}s
|
||||
* published in the Spring application context.
|
||||
*
|
||||
* <p>
|
||||
* NB: It is important that you register the {@link
|
||||
* org.acegisecurity.ui.session.HttpSessionEventPublisher} in
|
||||
* <code>web.xml</code> so that this class is notified of sessions that
|
||||
* expire.
|
||||
* NB: It is important that you register the {@link org.acegisecurity.ui.session.HttpSessionEventPublisher} in
|
||||
* <code>web.xml</code> so that this class is notified of sessions that expire.
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id${date}
|
||||
* @version $Id$
|
||||
*/
|
||||
public class SessionRegistryImpl implements SessionRegistry,
|
||||
ApplicationListener {
|
||||
//~ Instance fields ========================================================
|
||||
public class SessionRegistryImpl implements SessionRegistry, ApplicationListener {
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
|
||||
private Map principals = Collections.synchronizedMap(new HashMap()); // <principal:Object,SessionIdSet>
|
||||
private Map sessionIds = Collections.synchronizedMap(new HashMap()); // <sessionId:Object,SessionInformation>
|
||||
protected static final Log logger = LogFactory.getLog(SessionRegistryImpl.class);
|
||||
|
||||
//~ Methods ================================================================
|
||||
// ~ Instance fields ===============================================================================================
|
||||
|
||||
public Object[] getAllPrincipals() {
|
||||
return principals.keySet().toArray();
|
||||
}
|
||||
private Map principals = Collections.synchronizedMap(new HashMap()); // <principal:Object,SessionIdSet>
|
||||
private Map sessionIds = Collections.synchronizedMap(new HashMap()); // <sessionId:Object,SessionInformation>
|
||||
|
||||
public SessionInformation[] getAllSessions(Object principal,
|
||||
boolean includeExpiredSessions) {
|
||||
Set sessionsUsedByPrincipal = (Set) principals.get(principal);
|
||||
// ~ Methods =======================================================================================================
|
||||
|
||||
public Object[] getAllPrincipals() {
|
||||
return principals.keySet().toArray();
|
||||
}
|
||||
|
||||
public SessionInformation[] getAllSessions(Object principal, boolean includeExpiredSessions) {
|
||||
Set sessionsUsedByPrincipal = (Set) principals.get(principal);
|
||||
|
||||
if (sessionsUsedByPrincipal == null) {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
List list = new ArrayList();
|
||||
Iterator iter = sessionsUsedByPrincipal.iterator();
|
||||
List list = new ArrayList();
|
||||
|
||||
while (iter.hasNext()) {
|
||||
synchronized (sessionsUsedByPrincipal) {
|
||||
String sessionId = (String) iter.next();
|
||||
SessionInformation sessionInformation = getSessionInformation(sessionId);
|
||||
synchronized (sessionsUsedByPrincipal) {
|
||||
for (Iterator iter = sessionsUsedByPrincipal.iterator(); iter.hasNext();) {
|
||||
String sessionId = (String) iter.next();
|
||||
SessionInformation sessionInformation = getSessionInformation(sessionId);
|
||||
|
||||
if (sessionInformation == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (includeExpiredSessions || !sessionInformation.isExpired()) {
|
||||
list.add(sessionInformation);
|
||||
}
|
||||
}
|
||||
list.add(sessionInformation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (SessionInformation[]) list.toArray(new SessionInformation[] {});
|
||||
}
|
||||
|
||||
public SessionInformation getSessionInformation(String sessionId) {
|
||||
Assert.hasText(sessionId, "SessionId required as per interface contract");
|
||||
|
||||
return (SessionInformation) sessionIds.get(sessionId);
|
||||
}
|
||||
|
||||
public void onApplicationEvent(ApplicationEvent event) {
|
||||
if (event instanceof HttpSessionDestroyedEvent) {
|
||||
String sessionId = ((HttpSession) event.getSource()).getId();
|
||||
removeSessionInformation(sessionId);
|
||||
}
|
||||
}
|
||||
|
||||
public void refreshLastRequest(String sessionId) {
|
||||
Assert.hasText(sessionId, "SessionId required as per interface contract");
|
||||
|
||||
SessionInformation info = getSessionInformation(sessionId);
|
||||
|
||||
if (info != null) {
|
||||
info.refreshLastRequest();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void registerNewSession(String sessionId, Object principal) {
|
||||
Assert.hasText(sessionId, "SessionId required as per interface contract");
|
||||
Assert.notNull(principal, "Principal required as per interface contract");
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Registering session " + sessionId +", for principal " + principal);
|
||||
}
|
||||
|
||||
return (SessionInformation[]) list.toArray(new SessionInformation[] {});
|
||||
}
|
||||
|
||||
public SessionInformation getSessionInformation(String sessionId) {
|
||||
Assert.hasText(sessionId, "SessionId required as per interface contract");
|
||||
|
||||
return (SessionInformation) sessionIds.get(sessionId);
|
||||
}
|
||||
|
||||
public void onApplicationEvent(ApplicationEvent event) {
|
||||
if (event instanceof HttpSessionDestroyedEvent) {
|
||||
String sessionId = ((HttpSession) event.getSource()).getId();
|
||||
removeSessionInformation(sessionId);
|
||||
}
|
||||
}
|
||||
|
||||
public void refreshLastRequest(String sessionId) {
|
||||
Assert.hasText(sessionId, "SessionId required as per interface contract");
|
||||
|
||||
SessionInformation info = getSessionInformation(sessionId);
|
||||
|
||||
if (info != null) {
|
||||
info.refreshLastRequest();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void registerNewSession(String sessionId, Object principal) {
|
||||
Assert.hasText(sessionId, "SessionId required as per interface contract");
|
||||
Assert.notNull(principal, "Principal required as per interface contract");
|
||||
|
||||
if (getSessionInformation(sessionId) != null) {
|
||||
removeSessionInformation(sessionId);
|
||||
}
|
||||
removeSessionInformation(sessionId);
|
||||
}
|
||||
|
||||
sessionIds.put(sessionId,
|
||||
new SessionInformation(principal, sessionId, new Date()));
|
||||
sessionIds.put(sessionId, new SessionInformation(principal, sessionId, new Date()));
|
||||
|
||||
Set sessionsUsedByPrincipal = (Set) principals.get(principal);
|
||||
|
||||
if (sessionsUsedByPrincipal == null) {
|
||||
sessionsUsedByPrincipal = Collections.synchronizedSet(new HashSet());
|
||||
}
|
||||
if (sessionsUsedByPrincipal == null) {
|
||||
sessionsUsedByPrincipal = Collections.synchronizedSet(new HashSet());
|
||||
}
|
||||
|
||||
sessionsUsedByPrincipal.add(sessionId);
|
||||
sessionsUsedByPrincipal.add(sessionId);
|
||||
|
||||
principals.put(principal, sessionsUsedByPrincipal);
|
||||
}
|
||||
principals.put(principal, sessionsUsedByPrincipal);
|
||||
}
|
||||
|
||||
public void removeSessionInformation(String sessionId) {
|
||||
Assert.hasText(sessionId, "SessionId required as per interface contract");
|
||||
public void removeSessionInformation(String sessionId) {
|
||||
Assert.hasText(sessionId, "SessionId required as per interface contract");
|
||||
|
||||
SessionInformation info = getSessionInformation(sessionId);
|
||||
SessionInformation info = getSessionInformation(sessionId);
|
||||
|
||||
if (info != null) {
|
||||
if (info != null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Removing session " + sessionId + " from set of registered sessions");
|
||||
}
|
||||
sessionIds.remove(sessionId);
|
||||
|
||||
Set sessionsUsedByPrincipal = (Set) principals.get(info.getPrincipal());
|
||||
|
||||
if (sessionsUsedByPrincipal != null) {
|
||||
synchronized (sessionsUsedByPrincipal) {
|
||||
sessionsUsedByPrincipal.remove(sessionId);
|
||||
|
||||
if (sessionsUsedByPrincipal.size() == 0) {
|
||||
// No need to keep object in principals Map anymore
|
||||
principals.remove(info.getPrincipal());
|
||||
synchronized (sessionsUsedByPrincipal) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Removing session " + sessionId + " from principal's set of registered sessions");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sessionsUsedByPrincipal.remove(sessionId);
|
||||
|
||||
if (sessionsUsedByPrincipal.size() == 0) {
|
||||
// No need to keep object in principals Map anymore
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Removing principal " + info.getPrincipal() + " from registry");
|
||||
}
|
||||
principals.remove(info.getPrincipal());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+418
-282
@@ -25,6 +25,8 @@ import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpServletResponseWrapper;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
@@ -36,8 +38,8 @@ import org.springframework.util.ReflectionUtils;
|
||||
/**
|
||||
* Populates the {@link SecurityContextHolder} with information obtained from
|
||||
* the <code>HttpSession</code>.
|
||||
*
|
||||
* <p>
|
||||
* <p/>
|
||||
* <p/>
|
||||
* The <code>HttpSession</code> will be queried to retrieve the
|
||||
* <code>SecurityContext</code> that should be stored against the
|
||||
* <code>SecurityContextHolder</code> for the duration of the web request. At
|
||||
@@ -45,15 +47,14 @@ import org.springframework.util.ReflectionUtils;
|
||||
* <code>SecurityContextHolder</code> will be persisted back to the
|
||||
* <code>HttpSession</code> by this filter.
|
||||
* </p>
|
||||
* <p>
|
||||
* <p/>
|
||||
* If a valid <code>SecurityContext</code> cannot be obtained from the
|
||||
* <code>HttpSession</code> for whatever reason, a fresh
|
||||
* <code>SecurityContext</code> will be created and used instead. The created
|
||||
* object will be of the instance defined by the {@link #setContext(Class)}
|
||||
* method (which defaults to {@link
|
||||
* org.acegisecurity.context.SecurityContextImpl}.
|
||||
* method (which defaults to {@link org.acegisecurity.context.SecurityContextImpl}.
|
||||
* </p>
|
||||
* <p>
|
||||
* <p/>
|
||||
* No <code>HttpSession</code> will be created by this filter if one does not
|
||||
* already exist. If at the end of the web request the <code>HttpSession</code>
|
||||
* does not exist, a <code>HttpSession</code> will <b>only</b> be created if
|
||||
@@ -67,11 +68,11 @@ import org.springframework.util.ReflectionUtils;
|
||||
* irrespective of normal session-minimisation logic (the default is
|
||||
* <code>false</code>, as this is resource intensive and not recommended).
|
||||
* </p>
|
||||
* <p>
|
||||
* <p/>
|
||||
* This filter will only execute once per request, to resolve servlet container
|
||||
* (specifically Weblogic) incompatibilities.
|
||||
* </p>
|
||||
* <p>
|
||||
* <p/>
|
||||
* If for whatever reason no <code>HttpSession</code> should <b>ever</b> be
|
||||
* created (eg this filter is only being used with Basic authentication or
|
||||
* similar clients that will never present the same <code>jsessionid</code>
|
||||
@@ -84,332 +85,467 @@ import org.springframework.util.ReflectionUtils;
|
||||
* <code>true</code> (setting it to <code>false</code> will cause a startup
|
||||
* time error).
|
||||
* </p>
|
||||
* <p>
|
||||
* <p/>
|
||||
* This filter MUST be executed BEFORE any authentication processing mechanisms.
|
||||
* Authentication processing mechanisms (eg BASIC, CAS processing filters etc)
|
||||
* expect the <code>SecurityContextHolder</code> to contain a valid
|
||||
* <code>SecurityContext</code> by the time they execute.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Patrick Burleson
|
||||
* @version $Id: HttpSessionContextIntegrationFilter.java 1784 2007-02-24
|
||||
* 21:00:24Z luke_t $
|
||||
* @author Luke Taylor
|
||||
* @author Martin Algesten
|
||||
*
|
||||
* @version $Id$
|
||||
*/
|
||||
public class HttpSessionContextIntegrationFilter implements InitializingBean, Filter {
|
||||
// ~ Static fields/initializers
|
||||
// =====================================================================================
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
|
||||
protected static final Log logger = LogFactory.getLog(HttpSessionContextIntegrationFilter.class);
|
||||
protected static final Log logger = LogFactory.getLog(HttpSessionContextIntegrationFilter.class);
|
||||
|
||||
static final String FILTER_APPLIED = "__acegi_session_integration_filter_applied";
|
||||
static final String FILTER_APPLIED = "__acegi_session_integration_filter_applied";
|
||||
|
||||
public static final String ACEGI_SECURITY_CONTEXT_KEY = "ACEGI_SECURITY_CONTEXT";
|
||||
public static final String ACEGI_SECURITY_CONTEXT_KEY = "ACEGI_SECURITY_CONTEXT";
|
||||
|
||||
// ~ Instance fields
|
||||
// ================================================================================================
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private Class context = SecurityContextImpl.class;
|
||||
private Class context = SecurityContextImpl.class;
|
||||
|
||||
private Object contextObject;
|
||||
private Object contextObject;
|
||||
|
||||
/**
|
||||
* Indicates if this filter can create a <code>HttpSession</code> if
|
||||
* needed (sessions are always created sparingly, but setting this value to
|
||||
* <code>false</code> will prohibit sessions from ever being created).
|
||||
* Defaults to <code>true</code>. Do not set to <code>false</code> if
|
||||
* you are have set {@link #forceEagerSessionCreation} to <code>true</code>,
|
||||
* as the properties would be in conflict.
|
||||
*/
|
||||
private boolean allowSessionCreation = true;
|
||||
/**
|
||||
* Indicates if this filter can create a <code>HttpSession</code> if
|
||||
* needed (sessions are always created sparingly, but setting this value to
|
||||
* <code>false</code> will prohibit sessions from ever being created).
|
||||
* Defaults to <code>true</code>. Do not set to <code>false</code> if
|
||||
* you are have set {@link #forceEagerSessionCreation} to <code>true</code>,
|
||||
* as the properties would be in conflict.
|
||||
*/
|
||||
private boolean allowSessionCreation = true;
|
||||
|
||||
/**
|
||||
* Indicates if this filter is required to create a <code>HttpSession</code>
|
||||
* for every request before proceeding through the filter chain, even if the
|
||||
* <code>HttpSession</code> would not ordinarily have been created. By
|
||||
* default this is <code>false</code>, which is entirely appropriate for
|
||||
* most circumstances as you do not want a <code>HttpSession</code>
|
||||
* created unless the filter actually needs one. It is envisaged the main
|
||||
* situation in which this property would be set to <code>true</code> is
|
||||
* if using other filters that depend on a <code>HttpSession</code>
|
||||
* already existing, such as those which need to obtain a session ID. This
|
||||
* is only required in specialised cases, so leave it set to
|
||||
* <code>false</code> unless you have an actual requirement and are
|
||||
* conscious of the session creation overhead.
|
||||
*/
|
||||
private boolean forceEagerSessionCreation = false;
|
||||
/**
|
||||
* Indicates if this filter is required to create a <code>HttpSession</code>
|
||||
* for every request before proceeding through the filter chain, even if the
|
||||
* <code>HttpSession</code> would not ordinarily have been created. By
|
||||
* default this is <code>false</code>, which is entirely appropriate for
|
||||
* most circumstances as you do not want a <code>HttpSession</code>
|
||||
* created unless the filter actually needs one. It is envisaged the main
|
||||
* situation in which this property would be set to <code>true</code> is
|
||||
* if using other filters that depend on a <code>HttpSession</code>
|
||||
* already existing, such as those which need to obtain a session ID. This
|
||||
* is only required in specialised cases, so leave it set to
|
||||
* <code>false</code> unless you have an actual requirement and are
|
||||
* conscious of the session creation overhead.
|
||||
*/
|
||||
private boolean forceEagerSessionCreation = false;
|
||||
|
||||
/**
|
||||
* Indicates whether the <code>SecurityContext</code> will be cloned from
|
||||
* the <code>HttpSession</code>. The default is to simply reference (ie
|
||||
* the default is <code>false</code>). The default may cause issues if
|
||||
* concurrent threads need to have a different security identity from other
|
||||
* threads being concurrently processed that share the same
|
||||
* <code>HttpSession</code>. In most normal environments this does not
|
||||
* represent an issue, as changes to the security identity in one thread is
|
||||
* allowed to affect the security identitiy in other threads associated with
|
||||
* the same <code>HttpSession</code>. For unusual cases where this is not
|
||||
* permitted, change this value to <code>true</code> and ensure the
|
||||
* {@link #context} is set to a <code>SecurityContext</code> that
|
||||
* implements {@link Cloneable} and overrides the <code>clone()</code>
|
||||
* method.
|
||||
*/
|
||||
private boolean cloneFromHttpSession = false;
|
||||
/**
|
||||
* Indicates whether the <code>SecurityContext</code> will be cloned from
|
||||
* the <code>HttpSession</code>. The default is to simply reference (ie
|
||||
* the default is <code>false</code>). The default may cause issues if
|
||||
* concurrent threads need to have a different security identity from other
|
||||
* threads being concurrently processed that share the same
|
||||
* <code>HttpSession</code>. In most normal environments this does not
|
||||
* represent an issue, as changes to the security identity in one thread is
|
||||
* allowed to affect the security identitiy in other threads associated with
|
||||
* the same <code>HttpSession</code>. For unusual cases where this is not
|
||||
* permitted, change this value to <code>true</code> and ensure the
|
||||
* {@link #context} is set to a <code>SecurityContext</code> that
|
||||
* implements {@link Cloneable} and overrides the <code>clone()</code>
|
||||
* method.
|
||||
*/
|
||||
private boolean cloneFromHttpSession = false;
|
||||
|
||||
public boolean isCloneFromHttpSession() {
|
||||
return cloneFromHttpSession;
|
||||
}
|
||||
public boolean isCloneFromHttpSession() {
|
||||
return cloneFromHttpSession;
|
||||
}
|
||||
|
||||
public void setCloneFromHttpSession(boolean cloneFromHttpSession) {
|
||||
this.cloneFromHttpSession = cloneFromHttpSession;
|
||||
}
|
||||
public void setCloneFromHttpSession(boolean cloneFromHttpSession) {
|
||||
this.cloneFromHttpSession = cloneFromHttpSession;
|
||||
}
|
||||
|
||||
public HttpSessionContextIntegrationFilter() throws ServletException {
|
||||
this.contextObject = generateNewContext();
|
||||
}
|
||||
public HttpSessionContextIntegrationFilter() throws ServletException {
|
||||
this.contextObject = generateNewContext();
|
||||
}
|
||||
|
||||
// ~ Methods
|
||||
// ========================================================================================================
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if ((this.context == null) || (!SecurityContext.class.isAssignableFrom(this.context))) {
|
||||
throw new IllegalArgumentException("context must be defined and implement SecurityContext "
|
||||
+ "(typically use org.acegisecurity.context.SecurityContextImpl; existing class is " + this.context
|
||||
+ ")");
|
||||
}
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if ((this.context == null) || (!SecurityContext.class.isAssignableFrom(this.context))) {
|
||||
throw new IllegalArgumentException("context must be defined and implement SecurityContext "
|
||||
+ "(typically use org.acegisecurity.context.SecurityContextImpl; existing class is " + this.context
|
||||
+ ")");
|
||||
}
|
||||
|
||||
if ((forceEagerSessionCreation == true) && (allowSessionCreation == false)) {
|
||||
throw new IllegalArgumentException(
|
||||
"If using forceEagerSessionCreation, you must set allowSessionCreation to also be true");
|
||||
}
|
||||
}
|
||||
if (forceEagerSessionCreation && !allowSessionCreation) {
|
||||
throw new IllegalArgumentException(
|
||||
"If using forceEagerSessionCreation, you must set allowSessionCreation to also be true");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing. We use IoC container lifecycle services instead.
|
||||
*/
|
||||
public void destroy() {
|
||||
}
|
||||
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException,
|
||||
ServletException {
|
||||
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
|
||||
ServletException {
|
||||
boolean filterApplied = false;
|
||||
if ((request != null) && (request.getAttribute(FILTER_APPLIED) != null)) {
|
||||
// ensure that filter is only applied once per request
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
else {
|
||||
if (request != null) {
|
||||
filterApplied = true;
|
||||
request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
|
||||
}
|
||||
Assert.isInstanceOf(HttpServletRequest.class, req, "ServletRequest must be an instance of HttpServletRequest");
|
||||
Assert.isInstanceOf(HttpServletResponse.class, res, "ServletResponse must be an instance of HttpServletResponse");
|
||||
|
||||
HttpSession httpSession = null;
|
||||
boolean httpSessionExistedAtStartOfRequest = false;
|
||||
HttpServletRequest request = (HttpServletRequest) req;
|
||||
HttpServletResponse response = (HttpServletResponse) res;
|
||||
|
||||
try {
|
||||
httpSession = ((HttpServletRequest) request).getSession(forceEagerSessionCreation);
|
||||
}
|
||||
catch (IllegalStateException ignored) {
|
||||
}
|
||||
if (request.getAttribute(FILTER_APPLIED) != null) {
|
||||
// ensure that filter is only applied once per request
|
||||
chain.doFilter(request, response);
|
||||
|
||||
if (httpSession != null) {
|
||||
httpSessionExistedAtStartOfRequest = true;
|
||||
return;
|
||||
}
|
||||
|
||||
Object contextFromSessionObject = httpSession.getAttribute(ACEGI_SECURITY_CONTEXT_KEY);
|
||||
HttpSession httpSession = null;
|
||||
|
||||
if (contextFromSessionObject != null) {
|
||||
// Clone if required (see SEC-356)
|
||||
if (cloneFromHttpSession) {
|
||||
Assert.isInstanceOf(Cloneable.class, contextFromSessionObject,
|
||||
"Context must implement Clonable and provide a Object.clone() method");
|
||||
try {
|
||||
Method m = contextFromSessionObject.getClass().getMethod("clone", new Class[] {});
|
||||
if (!m.isAccessible()) {
|
||||
m.setAccessible(true);
|
||||
}
|
||||
contextFromSessionObject = m.invoke(contextFromSessionObject, new Object[] {});
|
||||
}
|
||||
catch (Exception ex) {
|
||||
ReflectionUtils.handleReflectionException(ex);
|
||||
}
|
||||
}
|
||||
try {
|
||||
httpSession = request.getSession(forceEagerSessionCreation);
|
||||
}
|
||||
catch (IllegalStateException ignored) {
|
||||
}
|
||||
|
||||
if (contextFromSessionObject instanceof SecurityContext) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Obtained from ACEGI_SECURITY_CONTEXT a valid SecurityContext and "
|
||||
+ "set to SecurityContextHolder: '" + contextFromSessionObject + "'");
|
||||
}
|
||||
boolean httpSessionExistedAtStartOfRequest = httpSession != null;
|
||||
|
||||
SecurityContextHolder.setContext((SecurityContext) contextFromSessionObject);
|
||||
}
|
||||
else {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger
|
||||
.warn("ACEGI_SECURITY_CONTEXT did not contain a SecurityContext but contained: '"
|
||||
+ contextFromSessionObject
|
||||
+ "'; are you improperly modifying the HttpSession directly "
|
||||
+ "(you should always use SecurityContextHolder) or using the HttpSession attribute "
|
||||
+ "reserved for this class? - new SecurityContext instance associated with "
|
||||
+ "SecurityContextHolder");
|
||||
}
|
||||
SecurityContext contextBeforeChainExecution = readSecurityContextFromSession(httpSession);
|
||||
|
||||
SecurityContextHolder.setContext(generateNewContext());
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("HttpSession returned null object for ACEGI_SECURITY_CONTEXT - new "
|
||||
+ "SecurityContext instance associated with SecurityContextHolder");
|
||||
}
|
||||
// Make the HttpSession null, as we don't want to keep a reference to it lying
|
||||
// around in case chain.doFilter() invalidates it.
|
||||
httpSession = null;
|
||||
|
||||
SecurityContextHolder.setContext(generateNewContext());
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("No HttpSession currently exists - new SecurityContext instance "
|
||||
+ "associated with SecurityContextHolder");
|
||||
}
|
||||
if (contextBeforeChainExecution == null) {
|
||||
contextBeforeChainExecution = generateNewContext();
|
||||
|
||||
SecurityContextHolder.setContext(generateNewContext());
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("New SecurityContext instance will be associated with SecurityContextHolder");
|
||||
}
|
||||
} else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Obtained a valid SecurityContext from ACEGI_SECURITY_CONTEXT to "
|
||||
+ "associate with SecurityContextHolder: '" + contextBeforeChainExecution + "'");
|
||||
}
|
||||
}
|
||||
|
||||
// Make the HttpSession null, as we want to ensure we don't keep
|
||||
// a reference to the HttpSession laying around in case the
|
||||
// chain.doFilter() invalidates it.
|
||||
httpSession = null;
|
||||
int contextHashBeforeChainExecution = contextBeforeChainExecution.hashCode();
|
||||
request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
|
||||
|
||||
// Proceed with chain
|
||||
int contextWhenChainProceeded = SecurityContextHolder.getContext().hashCode();
|
||||
// Create a wrapper that will eagerly update the session with the security context
|
||||
// if anything in the chain does a sendError() or sendRedirect().
|
||||
// See SEC-398
|
||||
|
||||
try {
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
catch (IOException ioe) {
|
||||
throw ioe;
|
||||
}
|
||||
catch (ServletException se) {
|
||||
throw se;
|
||||
}
|
||||
finally {
|
||||
// do clean up, even if there was an exception
|
||||
// Store context back to HttpSession
|
||||
try {
|
||||
httpSession = ((HttpServletRequest) request).getSession(false);
|
||||
}
|
||||
catch (IllegalStateException ignored) {
|
||||
}
|
||||
OnRedirectUpdateSessionResponseWrapper responseWrapper =
|
||||
new OnRedirectUpdateSessionResponseWrapper( response, request,
|
||||
httpSessionExistedAtStartOfRequest, contextHashBeforeChainExecution );
|
||||
|
||||
if ((httpSession == null) && httpSessionExistedAtStartOfRequest) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("HttpSession is now null, but was not null at start of request; "
|
||||
+ "session was invalidated, so do not create a new session");
|
||||
}
|
||||
}
|
||||
// Proceed with chain
|
||||
|
||||
// Generate a HttpSession only if we need to
|
||||
if ((httpSession == null) && !httpSessionExistedAtStartOfRequest) {
|
||||
if (!allowSessionCreation) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger
|
||||
.debug("The HttpSession is currently null, and the "
|
||||
+ "HttpSessionContextIntegrationFilter is prohibited from creating an HttpSession "
|
||||
+ "(because the allowSessionCreation property is false) - SecurityContext thus not "
|
||||
+ "stored for next request");
|
||||
}
|
||||
}
|
||||
else if (!contextObject.equals(SecurityContextHolder.getContext())) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("HttpSession being created as SecurityContextHolder contents are non-default");
|
||||
}
|
||||
try {
|
||||
// This is the only place in this class where SecurityContextHolder.setContext() is called
|
||||
SecurityContextHolder.setContext(contextBeforeChainExecution);
|
||||
|
||||
try {
|
||||
httpSession = ((HttpServletRequest) request).getSession(true);
|
||||
}
|
||||
catch (IllegalStateException ignored) {
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger
|
||||
.debug("HttpSession is null, but SecurityContextHolder has not changed from default: ' "
|
||||
+ SecurityContextHolder.getContext()
|
||||
+ "'; not creating HttpSession or storing SecurityContextHolder contents");
|
||||
}
|
||||
}
|
||||
}
|
||||
chain.doFilter(request, responseWrapper);
|
||||
}
|
||||
finally {
|
||||
// This is the only place in this class where SecurityContextHolder.getContext() is called
|
||||
SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();
|
||||
|
||||
// If HttpSession exists, store current
|
||||
// SecurityContextHolder contents but only if
|
||||
// SecurityContext has
|
||||
// actually changed (see JIRA SEC-37)
|
||||
if ((httpSession != null)
|
||||
&& (SecurityContextHolder.getContext().hashCode() != contextWhenChainProceeded)) {
|
||||
httpSession.setAttribute(ACEGI_SECURITY_CONTEXT_KEY, SecurityContextHolder.getContext());
|
||||
// Crucial removal of SecurityContextHolder contents - do this before anything else.
|
||||
SecurityContextHolder.clearContext();
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("SecurityContext stored to HttpSession: '" + SecurityContextHolder.getContext()
|
||||
+ "'");
|
||||
}
|
||||
}
|
||||
request.removeAttribute(FILTER_APPLIED);
|
||||
|
||||
if (filterApplied) {
|
||||
request.removeAttribute(FILTER_APPLIED);
|
||||
}
|
||||
|
||||
// Remove SecurityContextHolder contents
|
||||
SecurityContextHolder.clearContext();
|
||||
// storeSecurityContextInSession() might already be called by the response wrapper
|
||||
// if something in the chain called sendError() or sendRedirect(). This ensures we only call it
|
||||
// once per request.
|
||||
if ( !responseWrapper.isSessionUpdateDone() ) {
|
||||
storeSecurityContextInSession(contextAfterChainExecution, request,
|
||||
httpSessionExistedAtStartOfRequest, contextHashBeforeChainExecution);
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("SecurityContextHolder set to new context, as request processing completed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("SecurityContextHolder now cleared, as request processing completed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public SecurityContext generateNewContext() throws ServletException {
|
||||
try {
|
||||
return (SecurityContext) this.context.newInstance();
|
||||
}
|
||||
catch (InstantiationException ie) {
|
||||
throw new ServletException(ie);
|
||||
}
|
||||
catch (IllegalAccessException iae) {
|
||||
throw new ServletException(iae);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Gets the security context from the session (if available) and returns it.
|
||||
* <p/>
|
||||
* If the session is null, the context object is null or the context object stored in the session
|
||||
* is not an instance of SecurityContext it will return null.
|
||||
* <p/>
|
||||
* If <tt>cloneFromHttpSession</tt> is set to true, it will attempt to clone the context object
|
||||
* and return the cloned instance.
|
||||
*
|
||||
* @param httpSession the session obtained from the request.
|
||||
*/
|
||||
private SecurityContext readSecurityContextFromSession(HttpSession httpSession) {
|
||||
if (httpSession == null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("No HttpSession currently exists");
|
||||
}
|
||||
|
||||
public Class getContext() {
|
||||
return context;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing. We use IoC container lifecycle services instead.
|
||||
*
|
||||
* @param filterConfig ignored
|
||||
*
|
||||
* @throws ServletException ignored
|
||||
*/
|
||||
public void init(FilterConfig filterConfig) throws ServletException {
|
||||
}
|
||||
// Session exists, so try to obtain a context from it.
|
||||
|
||||
public boolean isAllowSessionCreation() {
|
||||
return allowSessionCreation;
|
||||
}
|
||||
Object contextFromSessionObject = httpSession.getAttribute(ACEGI_SECURITY_CONTEXT_KEY);
|
||||
|
||||
public boolean isForceEagerSessionCreation() {
|
||||
return forceEagerSessionCreation;
|
||||
}
|
||||
if (contextFromSessionObject == null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("HttpSession returned null object for ACEGI_SECURITY_CONTEXT");
|
||||
}
|
||||
|
||||
public void setAllowSessionCreation(boolean allowSessionCreation) {
|
||||
this.allowSessionCreation = allowSessionCreation;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setContext(Class secureContext) {
|
||||
this.context = secureContext;
|
||||
}
|
||||
// We now have the security context object from the session.
|
||||
|
||||
// Clone if required (see SEC-356)
|
||||
if (cloneFromHttpSession) {
|
||||
Assert.isInstanceOf(Cloneable.class, contextFromSessionObject,
|
||||
"Context must implement Clonable and provide a Object.clone() method");
|
||||
try {
|
||||
Method m = contextFromSessionObject.getClass().getMethod("clone", new Class[]{});
|
||||
if (!m.isAccessible()) {
|
||||
m.setAccessible(true);
|
||||
}
|
||||
contextFromSessionObject = m.invoke(contextFromSessionObject, new Object[]{});
|
||||
}
|
||||
catch (Exception ex) {
|
||||
ReflectionUtils.handleReflectionException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
if (!(contextFromSessionObject instanceof SecurityContext)) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("ACEGI_SECURITY_CONTEXT did not contain a SecurityContext but contained: '"
|
||||
+ contextFromSessionObject
|
||||
+ "'; are you improperly modifying the HttpSession directly "
|
||||
+ "(you should always use SecurityContextHolder) or using the HttpSession attribute "
|
||||
+ "reserved for this class?");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Everything OK. The only non-null return from this method.
|
||||
|
||||
return (SecurityContext) contextFromSessionObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the supplied security context in the session (if available) and if it has changed since it was
|
||||
* set at the start of the request.
|
||||
*
|
||||
* @param securityContext the context object obtained from the SecurityContextHolder after the request has
|
||||
* been processed by the filter chain. SecurityContextHolder.getContext() cannot be used to obtain
|
||||
* the context as it has already been cleared by the time this method is called.
|
||||
* @param request the request object (used to obtain the session, if one exists).
|
||||
* @param httpSessionExistedAtStartOfRequest indicates whether there was a session in place before the
|
||||
* filter chain executed. If this is true, and the session is found to be null, this indicates that it was
|
||||
* invalidated during the request and a new session will now be created.
|
||||
* @param contextHashBeforeChainExecution the hashcode of the context before the filter chain executed.
|
||||
* The context will only be stored if it has a different hashcode, indicating that the context changed
|
||||
* during the request.
|
||||
*
|
||||
*/
|
||||
private void storeSecurityContextInSession(SecurityContext securityContext,
|
||||
HttpServletRequest request,
|
||||
boolean httpSessionExistedAtStartOfRequest,
|
||||
int contextHashBeforeChainExecution) {
|
||||
HttpSession httpSession = null;
|
||||
|
||||
try {
|
||||
httpSession = request.getSession(false);
|
||||
}
|
||||
catch (IllegalStateException ignored) {
|
||||
}
|
||||
|
||||
if (httpSession == null) {
|
||||
if (httpSessionExistedAtStartOfRequest) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("HttpSession is now null, but was not null at start of request; "
|
||||
+ "session was invalidated, so do not create a new session");
|
||||
}
|
||||
} else {
|
||||
// Generate a HttpSession only if we need to
|
||||
|
||||
if (!allowSessionCreation) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("The HttpSession is currently null, and the "
|
||||
+ "HttpSessionContextIntegrationFilter is prohibited from creating an HttpSession "
|
||||
+ "(because the allowSessionCreation property is false) - SecurityContext thus not "
|
||||
+ "stored for next request");
|
||||
}
|
||||
} else if (!contextObject.equals(securityContext)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("HttpSession being created as SecurityContextHolder contents are non-default");
|
||||
}
|
||||
|
||||
try {
|
||||
httpSession = request.getSession(true);
|
||||
}
|
||||
catch (IllegalStateException ignored) {
|
||||
}
|
||||
} else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("HttpSession is null, but SecurityContextHolder has not changed from default: ' "
|
||||
+ securityContext
|
||||
+ "'; not creating HttpSession or storing SecurityContextHolder contents");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If HttpSession exists, store current SecurityContextHolder contents but only if
|
||||
// the SecurityContext has actually changed (see JIRA SEC-37)
|
||||
if (httpSession != null && securityContext.hashCode() != contextHashBeforeChainExecution) {
|
||||
httpSession.setAttribute(ACEGI_SECURITY_CONTEXT_KEY, securityContext);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("SecurityContext stored to HttpSession: '" + securityContext + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public SecurityContext generateNewContext() throws ServletException {
|
||||
try {
|
||||
return (SecurityContext) this.context.newInstance();
|
||||
}
|
||||
catch (InstantiationException ie) {
|
||||
throw new ServletException(ie);
|
||||
}
|
||||
catch (IllegalAccessException iae) {
|
||||
throw new ServletException(iae);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing. We use IoC container lifecycle services instead.
|
||||
*
|
||||
* @param filterConfig ignored
|
||||
* @throws ServletException ignored
|
||||
*/
|
||||
public void init(FilterConfig filterConfig) throws ServletException {
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing. We use IoC container lifecycle services instead.
|
||||
*/
|
||||
public void destroy() {
|
||||
}
|
||||
|
||||
public boolean isAllowSessionCreation() {
|
||||
return allowSessionCreation;
|
||||
}
|
||||
|
||||
public void setAllowSessionCreation(boolean allowSessionCreation) {
|
||||
this.allowSessionCreation = allowSessionCreation;
|
||||
}
|
||||
|
||||
public Class getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
public void setContext(Class secureContext) {
|
||||
this.context = secureContext;
|
||||
}
|
||||
|
||||
public boolean isForceEagerSessionCreation() {
|
||||
return forceEagerSessionCreation;
|
||||
}
|
||||
|
||||
public void setForceEagerSessionCreation(boolean forceEagerSessionCreation) {
|
||||
this.forceEagerSessionCreation = forceEagerSessionCreation;
|
||||
}
|
||||
|
||||
|
||||
//~ Inner Classes ==================================================================================================
|
||||
|
||||
/**
|
||||
* Wrapper that is applied to every request to update the <code>HttpSession<code> with
|
||||
* the <code>SecurityContext</code> when a <code>sendError()</code> or <code>sendRedirect</code>
|
||||
* happens. See SEC-398. The class contains the fields needed to call
|
||||
* <code>storeSecurityContextInSession()</code>
|
||||
*/
|
||||
private class OnRedirectUpdateSessionResponseWrapper extends HttpServletResponseWrapper {
|
||||
|
||||
HttpServletRequest request;
|
||||
boolean httpSessionExistedAtStartOfRequest;
|
||||
int contextHashBeforeChainExecution;
|
||||
|
||||
// Used to ensure storeSecurityContextInSession() is only
|
||||
// called once.
|
||||
boolean sessionUpdateDone = false;
|
||||
|
||||
/**
|
||||
* Takes the parameters required to call <code>storeSecurityContextInSession()</code> in
|
||||
* addition to the response object we are wrapping.
|
||||
* @see HttpSessionContextIntegrationFilter#storeSecurityContextInSession(SecurityContext, ServletRequest, boolean, int)
|
||||
*/
|
||||
public OnRedirectUpdateSessionResponseWrapper(HttpServletResponse response,
|
||||
HttpServletRequest request,
|
||||
boolean httpSessionExistedAtStartOfRequest,
|
||||
int contextHashBeforeChainExecution) {
|
||||
super(response);
|
||||
this.request = request;
|
||||
this.httpSessionExistedAtStartOfRequest = httpSessionExistedAtStartOfRequest;
|
||||
this.contextHashBeforeChainExecution = contextHashBeforeChainExecution;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure the session is updated before calling the
|
||||
* superclass <code>sendError()</code>
|
||||
*/
|
||||
public void sendError(int sc) throws IOException {
|
||||
doSessionUpdate();
|
||||
super.sendError(sc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure the session is updated before calling the
|
||||
* superclass <code>sendError()</code>
|
||||
*/
|
||||
public void sendError(int sc, String msg) throws IOException {
|
||||
doSessionUpdate();
|
||||
super.sendError(sc, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure the session is updated before calling the
|
||||
* superclass <code>sendRedirect()</code>
|
||||
*/
|
||||
public void sendRedirect(String location) throws IOException {
|
||||
doSessionUpdate();
|
||||
super.sendRedirect(location);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls <code>storeSecurityContextInSession()</code>
|
||||
*/
|
||||
private void doSessionUpdate() {
|
||||
if (sessionUpdateDone) {
|
||||
return;
|
||||
}
|
||||
SecurityContext securityContext = SecurityContextHolder.getContext();
|
||||
storeSecurityContextInSession(securityContext, request,
|
||||
httpSessionExistedAtStartOfRequest, contextHashBeforeChainExecution);
|
||||
sessionUpdateDone = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if the response wrapper has called
|
||||
* <code>storeSecurityContextInSession()</code>.
|
||||
*/
|
||||
public boolean isSessionUpdateDone() {
|
||||
return sessionUpdateDone;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void setForceEagerSessionCreation(boolean forceEagerSessionCreation) {
|
||||
this.forceEagerSessionCreation = forceEagerSessionCreation;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,397 +54,442 @@ import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
/**
|
||||
* Abstract class that implements security interception for secure objects.<p>The
|
||||
* <code>AbstractSecurityInterceptor</code> will ensure the proper startup configuration of the security interceptor.
|
||||
* It will also implement the proper handling of secure object invocations, being:
|
||||
* <ol>
|
||||
* <li>Obtain the {@link Authentication} object from the {@link SecurityContextHolder}.</li>
|
||||
* <li>Determine if the request relates to a secured or public invocation by looking up the secure object
|
||||
* request against the {@link ObjectDefinitionSource}.</li>
|
||||
* <li>For an invocation that is secured (there is a <code>ConfigAttributeDefinition</code> for the secure
|
||||
* object invocation):
|
||||
* <ol type="a">
|
||||
* <li>If either the {@link org.acegisecurity.Authentication#isAuthenticated()} returns
|
||||
* <code>false</code>, or the {@link #alwaysReauthenticate} is <code>true</code>, authenticate the request
|
||||
* against the configured {@link AuthenticationManager}. When authenticated, replace the
|
||||
* <code>Authentication</code> object on the <code>SecurityContextHolder</code> with the returned value.</li>
|
||||
* <li>Authorize the request against the configured {@link AccessDecisionManager}.</li>
|
||||
* <li>Perform any run-as replacement via the configured {@link RunAsManager}.</li>
|
||||
* <li>Pass control back to the concrete subclass, which will actually proceed with executing the
|
||||
* object. A {@link InterceptorStatusToken} is returned so that after the subclass has finished proceeding
|
||||
* with execution of the object, its finally clause can ensure the <code>AbstractSecurityInterceptor</code>
|
||||
* is re-called and tidies up correctly.</li>
|
||||
* <li>The concrete subclass will re-call the <code>AbstractSecurityInterceptor</code> via the
|
||||
* {@link #afterInvocation(InterceptorStatusToken, Object)} method.</li>
|
||||
* <li>If the <code>RunAsManager</code> replaced the <code>Authentication</code> object, return
|
||||
* the <code>SecurityContextHolder</code> to the object that existed after the call to
|
||||
* <code>AuthenticationManager</code>.</li>
|
||||
* <li>If an <code>AfterInvocationManager</code> is defined, invoke the invocation manager and
|
||||
* allow it to replace the object due to be returned to the caller.</li>
|
||||
* </ol>
|
||||
* </li>
|
||||
* <li>For an invocation that is public (there is no <code>ConfigAttributeDefinition</code> for the secure
|
||||
* object invocation):
|
||||
* <ol type="a">
|
||||
* <li>As described above, the concrete subclass will be returned an
|
||||
* <code>InterceptorStatusToken</code> which is subsequently re-presented to the
|
||||
* <code>AbstractSecurityInterceptor</code> after the secure object has been executed. The
|
||||
* <code>AbstractSecurityInterceptor</code> will take no further action when its {@link
|
||||
* #afterInvocation(InterceptorStatusToken, Object)} is called.</li>
|
||||
* </ol>
|
||||
* </li>
|
||||
* <li>Control again returns to the concrete subclass, along with the <code>Object</code> that should be
|
||||
* returned to the caller. The subclass will then return that result or exception to the original caller.</li>
|
||||
* </ol>
|
||||
* </p>
|
||||
*
|
||||
* Abstract class that implements security interception for secure objects.
|
||||
* <p>
|
||||
* The <code>AbstractSecurityInterceptor</code> will ensure the proper startup
|
||||
* configuration of the security interceptor. It will also implement the proper
|
||||
* handling of secure object invocations, being:
|
||||
* <ol>
|
||||
* <li>Obtain the {@link Authentication} object from the
|
||||
* {@link SecurityContextHolder}.</li>
|
||||
* <li>Determine if the request relates to a secured or public invocation by
|
||||
* looking up the secure object request against the
|
||||
* {@link ObjectDefinitionSource}.</li>
|
||||
* <li>For an invocation that is secured (there is a
|
||||
* <code>ConfigAttributeDefinition</code> for the secure object invocation):
|
||||
* <ol type="a">
|
||||
* <li>If either the {@link org.acegisecurity.Authentication#isAuthenticated()}
|
||||
* returns <code>false</code>, or the {@link #alwaysReauthenticate} is
|
||||
* <code>true</code>, authenticate the request against the configured
|
||||
* {@link AuthenticationManager}. When authenticated, replace the
|
||||
* <code>Authentication</code> object on the
|
||||
* <code>SecurityContextHolder</code> with the returned value.</li>
|
||||
* <li>Authorize the request against the configured
|
||||
* {@link AccessDecisionManager}.</li>
|
||||
* <li>Perform any run-as replacement via the configured {@link RunAsManager}.</li>
|
||||
* <li>Pass control back to the concrete subclass, which will actually proceed
|
||||
* with executing the object. A {@link InterceptorStatusToken} is returned so
|
||||
* that after the subclass has finished proceeding with execution of the object,
|
||||
* its finally clause can ensure the <code>AbstractSecurityInterceptor</code>
|
||||
* is re-called and tidies up correctly.</li>
|
||||
* <li>The concrete subclass will re-call the
|
||||
* <code>AbstractSecurityInterceptor</code> via the
|
||||
* {@link #afterInvocation(InterceptorStatusToken, Object)} method.</li>
|
||||
* <li>If the <code>RunAsManager</code> replaced the
|
||||
* <code>Authentication</code> object, return the
|
||||
* <code>SecurityContextHolder</code> to the object that existed after the
|
||||
* call to <code>AuthenticationManager</code>.</li>
|
||||
* <li>If an <code>AfterInvocationManager</code> is defined, invoke the
|
||||
* invocation manager and allow it to replace the object due to be returned to
|
||||
* the caller.</li>
|
||||
* </ol>
|
||||
* </li>
|
||||
* <li>For an invocation that is public (there is no
|
||||
* <code>ConfigAttributeDefinition</code> for the secure object invocation):
|
||||
* <ol type="a">
|
||||
* <li>As described above, the concrete subclass will be returned an
|
||||
* <code>InterceptorStatusToken</code> which is subsequently re-presented to
|
||||
* the <code>AbstractSecurityInterceptor</code> after the secure object has
|
||||
* been executed. The <code>AbstractSecurityInterceptor</code> will take no
|
||||
* further action when its {@link #afterInvocation(InterceptorStatusToken,
|
||||
* Object)} is called.</li>
|
||||
* </ol>
|
||||
* </li>
|
||||
* <li>Control again returns to the concrete subclass, along with the
|
||||
* <code>Object</code> that should be returned to the caller. The subclass
|
||||
* will then return that result or exception to the original caller.</li>
|
||||
* </ol>
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
* @version $Id: AbstractSecurityInterceptor.java 1790 2007-03-30 18:27:19Z
|
||||
* luke_t $
|
||||
*/
|
||||
public abstract class AbstractSecurityInterceptor implements InitializingBean, ApplicationEventPublisherAware,
|
||||
MessageSourceAware {
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
|
||||
protected static final Log logger = LogFactory.getLog(AbstractSecurityInterceptor.class);
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private AccessDecisionManager accessDecisionManager;
|
||||
private AfterInvocationManager afterInvocationManager;
|
||||
private ApplicationEventPublisher eventPublisher;
|
||||
private AuthenticationManager authenticationManager;
|
||||
protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor();
|
||||
private RunAsManager runAsManager = new NullRunAsManager();
|
||||
private boolean alwaysReauthenticate = false;
|
||||
private boolean rejectPublicInvocations = false;
|
||||
private boolean validateConfigAttributes = true;
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
/**
|
||||
* Completes the work of the <code>AbstractSecurityInterceptor</code> after the secure object invocation
|
||||
* has been complete
|
||||
*
|
||||
* @param token as returned by the {@link #beforeInvocation(Object)}} method
|
||||
* @param returnedObject any object returned from the secure object invocation (may be<code>null</code>)
|
||||
*
|
||||
* @return the object the secure object invocation should ultimately return to its caller (may be
|
||||
* <code>null</code>)
|
||||
*/
|
||||
protected Object afterInvocation(InterceptorStatusToken token, Object returnedObject) {
|
||||
if (token == null) {
|
||||
// public object
|
||||
return returnedObject;
|
||||
}
|
||||
|
||||
if (token.isContextHolderRefreshRequired()) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Reverting to original Authentication: " + token.getAuthentication().toString());
|
||||
}
|
||||
|
||||
SecurityContextHolder.getContext().setAuthentication(token.getAuthentication());
|
||||
}
|
||||
|
||||
if (afterInvocationManager != null) {
|
||||
// Attempt after invocation handling
|
||||
try {
|
||||
returnedObject = afterInvocationManager.decide(token.getAuthentication(), token.getSecureObject(),
|
||||
token.getAttr(), returnedObject);
|
||||
} catch (AccessDeniedException accessDeniedException) {
|
||||
AuthorizationFailureEvent event = new AuthorizationFailureEvent(token.getSecureObject(),
|
||||
token.getAttr(), token.getAuthentication(), accessDeniedException);
|
||||
publishEvent(event);
|
||||
|
||||
throw accessDeniedException;
|
||||
}
|
||||
}
|
||||
|
||||
return returnedObject;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(getSecureObjectClass(), "Subclass must provide a non-null response to getSecureObjectClass()");
|
||||
|
||||
Assert.notNull(this.messages, "A message source must be set");
|
||||
Assert.notNull(this.authenticationManager, "An AuthenticationManager is required");
|
||||
|
||||
Assert.notNull(this.accessDecisionManager, "An AccessDecisionManager is required");
|
||||
|
||||
Assert.notNull(this.runAsManager, "A RunAsManager is required");
|
||||
|
||||
Assert.notNull(this.obtainObjectDefinitionSource(), "An ObjectDefinitionSource is required");
|
||||
|
||||
Assert.isTrue(this.obtainObjectDefinitionSource().supports(getSecureObjectClass()),
|
||||
"ObjectDefinitionSource does not support secure object class: " + getSecureObjectClass());
|
||||
|
||||
Assert.isTrue(this.runAsManager.supports(getSecureObjectClass()),
|
||||
"RunAsManager does not support secure object class: " + getSecureObjectClass());
|
||||
|
||||
Assert.isTrue(this.accessDecisionManager.supports(getSecureObjectClass()),
|
||||
"AccessDecisionManager does not support secure object class: " + getSecureObjectClass());
|
||||
|
||||
if (this.afterInvocationManager != null) {
|
||||
Assert.isTrue(this.afterInvocationManager.supports(getSecureObjectClass()),
|
||||
"AfterInvocationManager does not support secure object class: " + getSecureObjectClass());
|
||||
}
|
||||
|
||||
if (this.validateConfigAttributes) {
|
||||
Iterator iter = this.obtainObjectDefinitionSource().getConfigAttributeDefinitions();
|
||||
|
||||
if (iter == null) {
|
||||
logger.warn("Could not validate configuration attributes as the MethodDefinitionSource did not return "
|
||||
+ "a ConfigAttributeDefinition Iterator");
|
||||
return;
|
||||
}
|
||||
|
||||
Set unsupportedAttrs = new HashSet();
|
||||
|
||||
while (iter.hasNext()) {
|
||||
ConfigAttributeDefinition def = (ConfigAttributeDefinition) iter.next();
|
||||
Iterator attributes = def.getConfigAttributes();
|
||||
|
||||
while (attributes.hasNext()) {
|
||||
ConfigAttribute attr = (ConfigAttribute) attributes.next();
|
||||
|
||||
if (!this.runAsManager.supports(attr) && !this.accessDecisionManager.supports(attr)
|
||||
&& ((this.afterInvocationManager == null) || !this.afterInvocationManager.supports(attr))) {
|
||||
unsupportedAttrs.add(attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (unsupportedAttrs.size() != 0) {
|
||||
throw new IllegalArgumentException("Unsupported configuration attributes: " + unsupportedAttrs);
|
||||
}
|
||||
|
||||
logger.info("Validated configuration attributes");
|
||||
}
|
||||
}
|
||||
|
||||
protected InterceptorStatusToken beforeInvocation(Object object) {
|
||||
Assert.notNull(object, "Object was null");
|
||||
|
||||
if (!getSecureObjectClass().isAssignableFrom(object.getClass())) {
|
||||
throw new IllegalArgumentException("Security invocation attempted for object "
|
||||
+ object.getClass().getName()
|
||||
+ " but AbstractSecurityInterceptor only configured to support secure objects of type: "
|
||||
+ getSecureObjectClass());
|
||||
}
|
||||
|
||||
ConfigAttributeDefinition attr = this.obtainObjectDefinitionSource().getAttributes(object);
|
||||
|
||||
if (attr == null) {
|
||||
if(rejectPublicInvocations) {
|
||||
throw new IllegalArgumentException(
|
||||
"No public invocations are allowed via this AbstractSecurityInterceptor. "
|
||||
+ "This indicates a configuration error because the "
|
||||
+ "AbstractSecurityInterceptor.rejectPublicInvocations property is set to 'true'");
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Public object - authentication not attempted");
|
||||
}
|
||||
|
||||
publishEvent(new PublicInvocationEvent(object));
|
||||
|
||||
return null; // no further work post-invocation
|
||||
}
|
||||
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Secure object: " + object.toString() + "; ConfigAttributes: " + attr.toString());
|
||||
}
|
||||
MessageSourceAware {
|
||||
// ~ Static fields/initializers
|
||||
// =====================================================================================
|
||||
|
||||
if (SecurityContextHolder.getContext().getAuthentication() == null) {
|
||||
credentialsNotFound(messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound",
|
||||
"An Authentication object was not found in the SecurityContext"), object, attr);
|
||||
}
|
||||
|
||||
// Attempt authentication if not already authenticated, or user always wants reauthentication
|
||||
Authentication authenticated;
|
||||
|
||||
if (!SecurityContextHolder.getContext().getAuthentication().isAuthenticated() || alwaysReauthenticate) {
|
||||
try {
|
||||
authenticated = this.authenticationManager.authenticate(SecurityContextHolder.getContext()
|
||||
.getAuthentication());
|
||||
} catch (AuthenticationException authenticationException) {
|
||||
throw authenticationException;
|
||||
}
|
||||
|
||||
// We don't authenticated.setAuthentication(true), because each provider should do that
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Successfully Authenticated: " + authenticated.toString());
|
||||
}
|
||||
|
||||
SecurityContextHolder.getContext().setAuthentication(authenticated);
|
||||
} else {
|
||||
authenticated = SecurityContextHolder.getContext().getAuthentication();
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Previously Authenticated: " + authenticated.toString());
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt authorization
|
||||
try {
|
||||
this.accessDecisionManager.decide(authenticated, object, attr);
|
||||
} catch (AccessDeniedException accessDeniedException) {
|
||||
AuthorizationFailureEvent event = new AuthorizationFailureEvent(object, attr, authenticated,
|
||||
accessDeniedException);
|
||||
publishEvent(event);
|
||||
|
||||
throw accessDeniedException;
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Authorization successful");
|
||||
}
|
||||
|
||||
AuthorizedEvent event = new AuthorizedEvent(object, attr, authenticated);
|
||||
publishEvent(event);
|
||||
|
||||
// Attempt to run as a different user
|
||||
Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attr);
|
||||
|
||||
if (runAs == null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("RunAsManager did not change Authentication object");
|
||||
}
|
||||
|
||||
// no further work post-invocation
|
||||
return new InterceptorStatusToken(authenticated, false, attr, object);
|
||||
} else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Switching to RunAs Authentication: " + runAs.toString());
|
||||
}
|
||||
|
||||
SecurityContextHolder.getContext().setAuthentication(runAs);
|
||||
|
||||
// revert to token.Authenticated post-invocation
|
||||
return new InterceptorStatusToken(authenticated, true, attr, object);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method which generates an exception containing the passed reason, and publishes an event to the
|
||||
* application context.<p>Always throws an exception.</p>
|
||||
*
|
||||
* @param reason to be provided in the exception detail
|
||||
* @param secureObject that was being called
|
||||
* @param configAttribs that were defined for the secureObject
|
||||
*/
|
||||
private void credentialsNotFound(String reason, Object secureObject, ConfigAttributeDefinition configAttribs) {
|
||||
AuthenticationCredentialsNotFoundException exception = new AuthenticationCredentialsNotFoundException(reason);
|
||||
|
||||
AuthenticationCredentialsNotFoundEvent event = new AuthenticationCredentialsNotFoundEvent(secureObject,
|
||||
configAttribs, exception);
|
||||
publishEvent(event);
|
||||
|
||||
throw exception;
|
||||
}
|
||||
|
||||
public AccessDecisionManager getAccessDecisionManager() {
|
||||
return accessDecisionManager;
|
||||
}
|
||||
|
||||
public AfterInvocationManager getAfterInvocationManager() {
|
||||
return afterInvocationManager;
|
||||
}
|
||||
|
||||
public AuthenticationManager getAuthenticationManager() {
|
||||
return this.authenticationManager;
|
||||
}
|
||||
|
||||
public RunAsManager getRunAsManager() {
|
||||
return runAsManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the type of secure objects the subclass will be presenting to the abstract parent for
|
||||
* processing. This is used to ensure collaborators wired to the <code>AbstractSecurityInterceptor</code> all
|
||||
* support the indicated secure object class.
|
||||
*
|
||||
* @return the type of secure object the subclass provides services for
|
||||
*/
|
||||
public abstract Class getSecureObjectClass();
|
||||
|
||||
public boolean isAlwaysReauthenticate() {
|
||||
return alwaysReauthenticate;
|
||||
}
|
||||
|
||||
public boolean isRejectPublicInvocations() {
|
||||
return rejectPublicInvocations;
|
||||
}
|
||||
|
||||
public boolean isValidateConfigAttributes() {
|
||||
return validateConfigAttributes;
|
||||
}
|
||||
|
||||
public abstract ObjectDefinitionSource obtainObjectDefinitionSource();
|
||||
|
||||
public void setAccessDecisionManager(AccessDecisionManager accessDecisionManager) {
|
||||
this.accessDecisionManager = accessDecisionManager;
|
||||
}
|
||||
|
||||
public void setAfterInvocationManager(AfterInvocationManager afterInvocationManager) {
|
||||
this.afterInvocationManager = afterInvocationManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the <code>AbstractSecurityInterceptor</code> should ignore the {@link
|
||||
* Authentication#isAuthenticated()} property. Defaults to <code>false</code>, meaning by default the
|
||||
* <code>Authentication.isAuthenticated()</code> property is trusted and re-authentication will not occur if the
|
||||
* principal has already been authenticated.
|
||||
*
|
||||
* @param alwaysReauthenticate <code>true</code> to force <code>AbstractSecurityInterceptor</code> to disregard the
|
||||
* value of <code>Authentication.isAuthenticated()</code> and always re-authenticate the request (defaults
|
||||
* to <code>false</code>).
|
||||
*/
|
||||
public void setAlwaysReauthenticate(boolean alwaysReauthenticate) {
|
||||
this.alwaysReauthenticate = alwaysReauthenticate;
|
||||
}
|
||||
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
|
||||
this.eventPublisher = applicationEventPublisher;
|
||||
}
|
||||
|
||||
public void setAuthenticationManager(AuthenticationManager newManager) {
|
||||
this.authenticationManager = newManager;
|
||||
}
|
||||
|
||||
public void setMessageSource(MessageSource messageSource) {
|
||||
this.messages = new MessageSourceAccessor(messageSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* By rejecting public invocations (and setting this property to <code>true</code>), essentially you are
|
||||
* ensuring that every secure object invocation advised by <code>AbstractSecurityInterceptor</code> has a
|
||||
* configuration attribute defined. This is useful to ensure a "fail safe" mode where undeclared secure objects
|
||||
* will be rejected and configuration omissions detected early. An <code>IllegalArgumentException</code> will be
|
||||
* thrown by the <code>AbstractSecurityInterceptor</code> if you set this property to <code>true</code> and an
|
||||
* attempt is made to invoke a secure object that has no configuration attributes.
|
||||
*
|
||||
* @param rejectPublicInvocations set to <code>true</code> to reject invocations of secure objects that have no
|
||||
* configuration attributes (by default it is <code>false</code> which treats undeclared secure objects as
|
||||
* "public" or unauthorized)
|
||||
*/
|
||||
public void setRejectPublicInvocations(boolean rejectPublicInvocations) {
|
||||
this.rejectPublicInvocations = rejectPublicInvocations;
|
||||
}
|
||||
|
||||
public void setRunAsManager(RunAsManager runAsManager) {
|
||||
this.runAsManager = runAsManager;
|
||||
}
|
||||
|
||||
public void setValidateConfigAttributes(boolean validateConfigAttributes) {
|
||||
this.validateConfigAttributes = validateConfigAttributes;
|
||||
}
|
||||
|
||||
private void publishEvent(ApplicationEvent event) {
|
||||
if (this.eventPublisher != null) {
|
||||
this.eventPublisher.publishEvent(event);
|
||||
}
|
||||
}
|
||||
protected static final Log logger = LogFactory.getLog(AbstractSecurityInterceptor.class);
|
||||
|
||||
// ~ Instance fields
|
||||
// ================================================================================================
|
||||
|
||||
private AccessDecisionManager accessDecisionManager;
|
||||
|
||||
private AfterInvocationManager afterInvocationManager;
|
||||
|
||||
private ApplicationEventPublisher eventPublisher;
|
||||
|
||||
private AuthenticationManager authenticationManager;
|
||||
|
||||
protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor();
|
||||
|
||||
private RunAsManager runAsManager = new NullRunAsManager();
|
||||
|
||||
private boolean alwaysReauthenticate = false;
|
||||
|
||||
private boolean rejectPublicInvocations = false;
|
||||
|
||||
private boolean validateConfigAttributes = true;
|
||||
|
||||
// ~ Methods
|
||||
// ========================================================================================================
|
||||
|
||||
/**
|
||||
* Completes the work of the <code>AbstractSecurityInterceptor</code>
|
||||
* after the secure object invocation has been complete
|
||||
*
|
||||
* @param token as returned by the {@link #beforeInvocation(Object)}}
|
||||
* method
|
||||
* @param returnedObject any object returned from the secure object
|
||||
* invocation (may be<code>null</code>)
|
||||
*
|
||||
* @return the object the secure object invocation should ultimately return
|
||||
* to its caller (may be <code>null</code>)
|
||||
*/
|
||||
protected Object afterInvocation(InterceptorStatusToken token, Object returnedObject) {
|
||||
if (token == null) {
|
||||
// public object
|
||||
return returnedObject;
|
||||
}
|
||||
|
||||
if (token.isContextHolderRefreshRequired()) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Reverting to original Authentication: " + token.getAuthentication().toString());
|
||||
}
|
||||
|
||||
SecurityContextHolder.getContext().setAuthentication(token.getAuthentication());
|
||||
}
|
||||
|
||||
if (afterInvocationManager != null) {
|
||||
// Attempt after invocation handling
|
||||
try {
|
||||
returnedObject = afterInvocationManager.decide(token.getAuthentication(), token.getSecureObject(),
|
||||
token.getAttr(), returnedObject);
|
||||
}
|
||||
catch (AccessDeniedException accessDeniedException) {
|
||||
AuthorizationFailureEvent event = new AuthorizationFailureEvent(token.getSecureObject(), token
|
||||
.getAttr(), token.getAuthentication(), accessDeniedException);
|
||||
publishEvent(event);
|
||||
|
||||
throw accessDeniedException;
|
||||
}
|
||||
}
|
||||
|
||||
return returnedObject;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(getSecureObjectClass(), "Subclass must provide a non-null response to getSecureObjectClass()");
|
||||
|
||||
Assert.notNull(this.messages, "A message source must be set");
|
||||
|
||||
Assert.notNull(this.authenticationManager, "An AuthenticationManager is required");
|
||||
|
||||
Assert.notNull(this.accessDecisionManager, "An AccessDecisionManager is required");
|
||||
|
||||
Assert.notNull(this.runAsManager, "A RunAsManager is required");
|
||||
|
||||
Assert.notNull(this.obtainObjectDefinitionSource(), "An ObjectDefinitionSource is required");
|
||||
|
||||
Assert.isTrue(this.obtainObjectDefinitionSource().supports(getSecureObjectClass()),
|
||||
"ObjectDefinitionSource does not support secure object class: " + getSecureObjectClass());
|
||||
|
||||
Assert.isTrue(this.runAsManager.supports(getSecureObjectClass()),
|
||||
"RunAsManager does not support secure object class: " + getSecureObjectClass());
|
||||
|
||||
Assert.isTrue(this.accessDecisionManager.supports(getSecureObjectClass()),
|
||||
"AccessDecisionManager does not support secure object class: " + getSecureObjectClass());
|
||||
|
||||
if (this.afterInvocationManager != null) {
|
||||
Assert.isTrue(this.afterInvocationManager.supports(getSecureObjectClass()),
|
||||
"AfterInvocationManager does not support secure object class: " + getSecureObjectClass());
|
||||
}
|
||||
|
||||
if (this.validateConfigAttributes) {
|
||||
Iterator iter = this.obtainObjectDefinitionSource().getConfigAttributeDefinitions();
|
||||
|
||||
if (iter == null) {
|
||||
logger.warn("Could not validate configuration attributes as the MethodDefinitionSource did not return "
|
||||
+ "a ConfigAttributeDefinition Iterator");
|
||||
return;
|
||||
}
|
||||
|
||||
Set unsupportedAttrs = new HashSet();
|
||||
|
||||
while (iter.hasNext()) {
|
||||
ConfigAttributeDefinition def = (ConfigAttributeDefinition) iter.next();
|
||||
Iterator attributes = def.getConfigAttributes();
|
||||
|
||||
while (attributes.hasNext()) {
|
||||
ConfigAttribute attr = (ConfigAttribute) attributes.next();
|
||||
|
||||
if (!this.runAsManager.supports(attr) && !this.accessDecisionManager.supports(attr)
|
||||
&& ((this.afterInvocationManager == null) || !this.afterInvocationManager.supports(attr))) {
|
||||
unsupportedAttrs.add(attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (unsupportedAttrs.size() != 0) {
|
||||
throw new IllegalArgumentException("Unsupported configuration attributes: " + unsupportedAttrs);
|
||||
}
|
||||
|
||||
logger.info("Validated configuration attributes");
|
||||
}
|
||||
}
|
||||
|
||||
protected InterceptorStatusToken beforeInvocation(Object object) {
|
||||
Assert.notNull(object, "Object was null");
|
||||
|
||||
if (!getSecureObjectClass().isAssignableFrom(object.getClass())) {
|
||||
throw new IllegalArgumentException("Security invocation attempted for object "
|
||||
+ object.getClass().getName()
|
||||
+ " but AbstractSecurityInterceptor only configured to support secure objects of type: "
|
||||
+ getSecureObjectClass());
|
||||
}
|
||||
|
||||
ConfigAttributeDefinition attr = this.obtainObjectDefinitionSource().getAttributes(object);
|
||||
|
||||
if (attr == null) {
|
||||
if (rejectPublicInvocations) {
|
||||
throw new IllegalArgumentException(
|
||||
"No public invocations are allowed via this AbstractSecurityInterceptor. "
|
||||
+ "This indicates a configuration error because the "
|
||||
+ "AbstractSecurityInterceptor.rejectPublicInvocations property is set to 'true'");
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Public object - authentication not attempted");
|
||||
}
|
||||
|
||||
publishEvent(new PublicInvocationEvent(object));
|
||||
|
||||
return null; // no further work post-invocation
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Secure object: " + object.toString() + "; ConfigAttributes: " + attr.toString());
|
||||
}
|
||||
|
||||
if (SecurityContextHolder.getContext().getAuthentication() == null) {
|
||||
credentialsNotFound(messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound",
|
||||
"An Authentication object was not found in the SecurityContext"), object, attr);
|
||||
}
|
||||
|
||||
// Attempt authentication if not already authenticated, or user always
|
||||
// wants reauthentication
|
||||
Authentication authenticated;
|
||||
|
||||
if (!SecurityContextHolder.getContext().getAuthentication().isAuthenticated() || alwaysReauthenticate) {
|
||||
try {
|
||||
authenticated = this.authenticationManager.authenticate(SecurityContextHolder.getContext()
|
||||
.getAuthentication());
|
||||
}
|
||||
catch (AuthenticationException authenticationException) {
|
||||
throw authenticationException;
|
||||
}
|
||||
|
||||
// We don't authenticated.setAuthentication(true), because each
|
||||
// provider should do that
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Successfully Authenticated: " + authenticated.toString());
|
||||
}
|
||||
|
||||
SecurityContextHolder.getContext().setAuthentication(authenticated);
|
||||
}
|
||||
else {
|
||||
authenticated = SecurityContextHolder.getContext().getAuthentication();
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Previously Authenticated: " + authenticated.toString());
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt authorization
|
||||
try {
|
||||
this.accessDecisionManager.decide(authenticated, object, attr);
|
||||
}
|
||||
catch (AccessDeniedException accessDeniedException) {
|
||||
AuthorizationFailureEvent event = new AuthorizationFailureEvent(object, attr, authenticated,
|
||||
accessDeniedException);
|
||||
publishEvent(event);
|
||||
|
||||
throw accessDeniedException;
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Authorization successful");
|
||||
}
|
||||
|
||||
AuthorizedEvent event = new AuthorizedEvent(object, attr, authenticated);
|
||||
publishEvent(event);
|
||||
|
||||
// Attempt to run as a different user
|
||||
Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attr);
|
||||
|
||||
if (runAs == null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("RunAsManager did not change Authentication object");
|
||||
}
|
||||
|
||||
// no further work post-invocation
|
||||
return new InterceptorStatusToken(authenticated, false, attr, object);
|
||||
}
|
||||
else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Switching to RunAs Authentication: " + runAs.toString());
|
||||
}
|
||||
|
||||
SecurityContextHolder.getContext().setAuthentication(runAs);
|
||||
|
||||
// revert to token.Authenticated post-invocation
|
||||
return new InterceptorStatusToken(authenticated, true, attr, object);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method which generates an exception containing the passed reason,
|
||||
* and publishes an event to the application context.
|
||||
* <p>
|
||||
* Always throws an exception.
|
||||
* </p>
|
||||
*
|
||||
* @param reason to be provided in the exception detail
|
||||
* @param secureObject that was being called
|
||||
* @param configAttribs that were defined for the secureObject
|
||||
*/
|
||||
private void credentialsNotFound(String reason, Object secureObject, ConfigAttributeDefinition configAttribs) {
|
||||
AuthenticationCredentialsNotFoundException exception = new AuthenticationCredentialsNotFoundException(reason);
|
||||
|
||||
AuthenticationCredentialsNotFoundEvent event = new AuthenticationCredentialsNotFoundEvent(secureObject,
|
||||
configAttribs, exception);
|
||||
publishEvent(event);
|
||||
|
||||
throw exception;
|
||||
}
|
||||
|
||||
public AccessDecisionManager getAccessDecisionManager() {
|
||||
return accessDecisionManager;
|
||||
}
|
||||
|
||||
public AfterInvocationManager getAfterInvocationManager() {
|
||||
return afterInvocationManager;
|
||||
}
|
||||
|
||||
public AuthenticationManager getAuthenticationManager() {
|
||||
return this.authenticationManager;
|
||||
}
|
||||
|
||||
public RunAsManager getRunAsManager() {
|
||||
return runAsManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the type of secure objects the subclass will be presenting to
|
||||
* the abstract parent for processing. This is used to ensure collaborators
|
||||
* wired to the <code>AbstractSecurityInterceptor</code> all support the
|
||||
* indicated secure object class.
|
||||
*
|
||||
* @return the type of secure object the subclass provides services for
|
||||
*/
|
||||
public abstract Class getSecureObjectClass();
|
||||
|
||||
public boolean isAlwaysReauthenticate() {
|
||||
return alwaysReauthenticate;
|
||||
}
|
||||
|
||||
public boolean isRejectPublicInvocations() {
|
||||
return rejectPublicInvocations;
|
||||
}
|
||||
|
||||
public boolean isValidateConfigAttributes() {
|
||||
return validateConfigAttributes;
|
||||
}
|
||||
|
||||
public abstract ObjectDefinitionSource obtainObjectDefinitionSource();
|
||||
|
||||
public void setAccessDecisionManager(AccessDecisionManager accessDecisionManager) {
|
||||
this.accessDecisionManager = accessDecisionManager;
|
||||
}
|
||||
|
||||
public void setAfterInvocationManager(AfterInvocationManager afterInvocationManager) {
|
||||
this.afterInvocationManager = afterInvocationManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the <code>AbstractSecurityInterceptor</code> should
|
||||
* ignore the {@link Authentication#isAuthenticated()} property. Defaults to
|
||||
* <code>false</code>, meaning by default the
|
||||
* <code>Authentication.isAuthenticated()</code> property is trusted and
|
||||
* re-authentication will not occur if the principal has already been
|
||||
* authenticated.
|
||||
*
|
||||
* @param alwaysReauthenticate <code>true</code> to force
|
||||
* <code>AbstractSecurityInterceptor</code> to disregard the value of
|
||||
* <code>Authentication.isAuthenticated()</code> and always
|
||||
* re-authenticate the request (defaults to <code>false</code>).
|
||||
*/
|
||||
public void setAlwaysReauthenticate(boolean alwaysReauthenticate) {
|
||||
this.alwaysReauthenticate = alwaysReauthenticate;
|
||||
}
|
||||
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
|
||||
this.eventPublisher = applicationEventPublisher;
|
||||
}
|
||||
|
||||
public void setAuthenticationManager(AuthenticationManager newManager) {
|
||||
this.authenticationManager = newManager;
|
||||
}
|
||||
|
||||
public void setMessageSource(MessageSource messageSource) {
|
||||
this.messages = new MessageSourceAccessor(messageSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* By rejecting public invocations (and setting this property to
|
||||
* <code>true</code>), essentially you are ensuring that every secure
|
||||
* object invocation advised by <code>AbstractSecurityInterceptor</code>
|
||||
* has a configuration attribute defined. This is useful to ensure a "fail
|
||||
* safe" mode where undeclared secure objects will be rejected and
|
||||
* configuration omissions detected early. An
|
||||
* <code>IllegalArgumentException</code> will be thrown by the
|
||||
* <code>AbstractSecurityInterceptor</code> if you set this property to
|
||||
* <code>true</code> and an attempt is made to invoke a secure object that
|
||||
* has no configuration attributes.
|
||||
*
|
||||
* @param rejectPublicInvocations set to <code>true</code> to reject
|
||||
* invocations of secure objects that have no configuration attributes (by
|
||||
* default it is <code>false</code> which treats undeclared secure objects
|
||||
* as "public" or unauthorized)
|
||||
*/
|
||||
public void setRejectPublicInvocations(boolean rejectPublicInvocations) {
|
||||
this.rejectPublicInvocations = rejectPublicInvocations;
|
||||
}
|
||||
|
||||
public void setRunAsManager(RunAsManager runAsManager) {
|
||||
this.runAsManager = runAsManager;
|
||||
}
|
||||
|
||||
public void setValidateConfigAttributes(boolean validateConfigAttributes) {
|
||||
this.validateConfigAttributes = validateConfigAttributes;
|
||||
}
|
||||
|
||||
private void publishEvent(ApplicationEvent event) {
|
||||
if (this.eventPublisher != null) {
|
||||
this.eventPublisher.publishEvent(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-4
@@ -34,17 +34,17 @@ import java.util.Vector;
|
||||
* Maintains a <code>List</code> of <code>ConfigAttributeDefinition</code>s associated with different HTTP request
|
||||
* URL Apache Ant path-based patterns.<p>Apache Ant path expressions are used to match a HTTP request URL against a
|
||||
* <code>ConfigAttributeDefinition</code>.</p>
|
||||
* <p>The order of registering the Ant paths using the {@link #addSecureUrl(String, ConfigAttributeDefinition)} is
|
||||
* <p>The order of registering the Ant paths using the {@link #addSecureUrl(String,ConfigAttributeDefinition)} is
|
||||
* very important. The system will identify the <b>first</b> matching path for a given HTTP URL. It will not proceed
|
||||
* to evaluate later paths if a match has already been found. Accordingly, the most specific paths should be
|
||||
* registered first, with the most general paths registered last.</p>
|
||||
* <p>If no registered paths match the HTTP URL, <code>null</code> is returned.</p>
|
||||
* <p>If no registered paths match the HTTP URL, <code>null</code> is returned.</p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class PathBasedFilterInvocationDefinitionMap extends AbstractFilterInvocationDefinitionSource
|
||||
implements FilterInvocationDefinition {
|
||||
implements FilterInvocationDefinition {
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
|
||||
private static final Log logger = LogFactory.getLog(PathBasedFilterInvocationDefinitionMap.class);
|
||||
@@ -58,6 +58,12 @@ public class PathBasedFilterInvocationDefinitionMap extends AbstractFilterInvoca
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public void addSecureUrl(String antPath, ConfigAttributeDefinition attr) {
|
||||
// SEC-501: If using lower case comparison, we should convert the paths to lower case
|
||||
// as any upper case characters included by mistake will prevent the URL from ever being matched.
|
||||
if (convertUrlToLowercaseBeforeComparison) {
|
||||
antPath = antPath.toLowerCase();
|
||||
}
|
||||
|
||||
requestMap.add(new EntryHolder(antPath, attr));
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
@@ -110,7 +116,7 @@ public class PathBasedFilterInvocationDefinitionMap extends AbstractFilterInvoca
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Candidate is: '" + url + "'; pattern is " + entryHolder.getAntPath() + "; matched="
|
||||
+ matched);
|
||||
+ matched);
|
||||
}
|
||||
|
||||
if (matched) {
|
||||
|
||||
@@ -79,7 +79,7 @@ public class FilterBasedLdapUserSearch implements LdapUserSearch {
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
public FilterBasedLdapUserSearch(String searchBase, String searchFilter,
|
||||
InitialDirContextFactory initialDirContextFactory) {
|
||||
InitialDirContextFactory initialDirContextFactory) {
|
||||
Assert.notNull(initialDirContextFactory, "initialDirContextFactory must not be null");
|
||||
Assert.notNull(searchFilter, "searchFilter must not be null.");
|
||||
Assert.notNull(searchBase, "searchBase must not be null (an empty string is acceptable).");
|
||||
@@ -106,10 +106,8 @@ public class FilterBasedLdapUserSearch implements LdapUserSearch {
|
||||
* @throws UsernameNotFoundException if no matching entry is found.
|
||||
*/
|
||||
public LdapUserDetails searchForUser(String username) {
|
||||
DirContext ctx = initialDirContextFactory.newInitialDirContext();
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Searching for user '" + username + "', in context " + ctx + ", with user search "
|
||||
logger.debug("Searching for user '" + username + "', with user search "
|
||||
+ this.toString());
|
||||
}
|
||||
|
||||
@@ -167,7 +165,7 @@ public class FilterBasedLdapUserSearch implements LdapUserSearch {
|
||||
sb.append("[ searchFilter: '").append(searchFilter).append("', ");
|
||||
sb.append("searchBase: '").append(searchBase).append("'");
|
||||
sb.append(", scope: ")
|
||||
.append((searchControls.getSearchScope() == SearchControls.SUBTREE_SCOPE) ? "subtree" : "single-level, ");
|
||||
.append(searchControls.getSearchScope() == SearchControls.SUBTREE_SCOPE ? "subtree" : "single-level, ");
|
||||
sb.append("searchTimeLimit: ").append(searchControls.getTimeLimit());
|
||||
sb.append("derefLinkFlag: ").append(searchControls.getDerefLinkFlag()).append(" ]");
|
||||
|
||||
|
||||
@@ -193,6 +193,7 @@ public class ProviderManager extends AbstractAuthenticationManager implements In
|
||||
|
||||
try {
|
||||
result = provider.authenticate(authentication);
|
||||
copyDetails(authentication, result);
|
||||
sessionController.checkAuthenticationAllowed(result);
|
||||
} catch (AuthenticationException ae) {
|
||||
lastException = ae;
|
||||
@@ -245,6 +246,21 @@ public class ProviderManager extends AbstractAuthenticationManager implements In
|
||||
throw lastException;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the authentication details from a source Authentication object to a destination one, provided the
|
||||
* latter does not already have one set.
|
||||
*
|
||||
* @param source source authentication
|
||||
* @param dest the destination authentication object
|
||||
*/
|
||||
private void copyDetails(Authentication source, Authentication dest) {
|
||||
if ((dest instanceof AbstractAuthenticationToken) && (dest.getDetails() == null)) {
|
||||
AbstractAuthenticationToken token = (AbstractAuthenticationToken) dest;
|
||||
|
||||
token.setDetails(source.getDetails());
|
||||
}
|
||||
}
|
||||
|
||||
public List getProviders() {
|
||||
return this.providers;
|
||||
}
|
||||
|
||||
+1
-11
@@ -26,7 +26,6 @@ import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.MessageSourceAware;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
@@ -36,7 +35,7 @@ import org.springframework.util.Assert;
|
||||
* {@link org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken#getKeyHash()} must match this class'
|
||||
* {@link #getKey()}.</p>
|
||||
*/
|
||||
public class AnonymousAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware, Ordered {
|
||||
public class AnonymousAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware {
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
|
||||
private static final Log logger = LogFactory.getLog(AnonymousAuthenticationProvider.class);
|
||||
@@ -45,18 +44,9 @@ public class AnonymousAuthenticationProvider implements AuthenticationProvider,
|
||||
|
||||
protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor();
|
||||
private String key;
|
||||
private int order = -1; // default: same as non-Ordered
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.hasLength(key, "A Key is required");
|
||||
Assert.notNull(this.messages, "A message source must be set");
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.acegisecurity.BadCredentialsException;
|
||||
|
||||
import org.acegisecurity.providers.AuthenticationProvider;
|
||||
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
|
||||
import org.acegisecurity.providers.cas.cache.NullStatelessTicketCache;
|
||||
|
||||
import org.acegisecurity.ui.cas.CasProcessingFilter;
|
||||
|
||||
@@ -35,7 +36,6 @@ import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.MessageSourceAware;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -50,7 +50,7 @@ import org.springframework.util.Assert;
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class CasAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware, Ordered {
|
||||
public class CasAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware {
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
|
||||
private static final Log logger = LogFactory.getLog(CasAuthenticationProvider.class);
|
||||
@@ -60,28 +60,18 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
|
||||
private CasAuthoritiesPopulator casAuthoritiesPopulator;
|
||||
private CasProxyDecider casProxyDecider;
|
||||
protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor();
|
||||
private StatelessTicketCache statelessTicketCache;
|
||||
private StatelessTicketCache statelessTicketCache = new NullStatelessTicketCache();
|
||||
private String key;
|
||||
private TicketValidator ticketValidator;
|
||||
private int order = -1; // default: same as non-Ordered
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(this.casAuthoritiesPopulator, "A casAuthoritiesPopulator must be set");
|
||||
Assert.notNull(this.ticketValidator, "A ticketValidator must be set");
|
||||
Assert.notNull(this.casProxyDecider, "A casProxyDecider must be set");
|
||||
Assert.notNull(this.statelessTicketCache, "A statelessTicketCache must be set");
|
||||
Assert.notNull(key,
|
||||
"A Key is required so CasAuthenticationProvider can identify tokens it previously authenticated");
|
||||
Assert.hasText(this.key, "A Key is required so CasAuthenticationProvider can identify tokens it previously authenticated");
|
||||
Assert.notNull(this.messages, "A message source must be set");
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -15,9 +15,9 @@
|
||||
|
||||
package org.acegisecurity.providers.cas.cache;
|
||||
|
||||
import net.sf.ehcache.Cache;
|
||||
import net.sf.ehcache.CacheException;
|
||||
import net.sf.ehcache.Element;
|
||||
import net.sf.ehcache.Ehcache;
|
||||
|
||||
import org.acegisecurity.providers.cas.CasAuthenticationToken;
|
||||
import org.acegisecurity.providers.cas.StatelessTicketCache;
|
||||
@@ -45,7 +45,7 @@ public class EhCacheBasedTicketCache implements StatelessTicketCache, Initializi
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private Cache cache;
|
||||
private Ehcache cache;
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
@@ -73,7 +73,7 @@ public class EhCacheBasedTicketCache implements StatelessTicketCache, Initializi
|
||||
}
|
||||
}
|
||||
|
||||
public Cache getCache() {
|
||||
public Ehcache getCache() {
|
||||
return cache;
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ public class EhCacheBasedTicketCache implements StatelessTicketCache, Initializi
|
||||
cache.remove(serviceTicket);
|
||||
}
|
||||
|
||||
public void setCache(Cache cache) {
|
||||
public void setCache(Ehcache cache) {
|
||||
this.cache = cache;
|
||||
}
|
||||
}
|
||||
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
/* 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.acegisecurity.providers.cas.cache;
|
||||
|
||||
import org.acegisecurity.providers.cas.CasAuthenticationProvider;
|
||||
import org.acegisecurity.providers.cas.CasAuthenticationToken;
|
||||
import org.acegisecurity.providers.cas.StatelessTicketCache;
|
||||
|
||||
/**
|
||||
* Implementation of @link {@link StatelessTicketCache} that has no backing cache. Useful
|
||||
* in instances where storing of tickets for stateless session management is not required.
|
||||
* <p>
|
||||
* This is the default StatelessTicketCache of the @link {@link CasAuthenticationProvider} to
|
||||
* eliminate the unnecessary dependency on EhCache that applications have even if they are not using
|
||||
* the stateless session management.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @version $Id$
|
||||
*
|
||||
*@see CasAuthenticationProvider
|
||||
*/
|
||||
public final class NullStatelessTicketCache implements StatelessTicketCache {
|
||||
|
||||
/**
|
||||
* @return null since we are not storing any tickets.
|
||||
*/
|
||||
public CasAuthenticationToken getByTicketId(final String serviceTicket) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a no-op since we are not storing tickets.
|
||||
*/
|
||||
public void putTicketInCache(final CasAuthenticationToken token) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a no-op since we are not storing tickets.
|
||||
*/
|
||||
public void removeTicketFromCache(final CasAuthenticationToken token) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a no-op since we are not storing tickets.
|
||||
*/
|
||||
public void removeTicketFromCache(final String serviceTicket) {
|
||||
// nothing to do
|
||||
}
|
||||
}
|
||||
+129
-107
@@ -15,140 +15,162 @@
|
||||
|
||||
package org.acegisecurity.providers.dao;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.acegisecurity.AuthenticationException;
|
||||
import org.acegisecurity.AuthenticationServiceException;
|
||||
import org.acegisecurity.BadCredentialsException;
|
||||
|
||||
import org.acegisecurity.providers.AuthenticationProvider;
|
||||
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
|
||||
import org.acegisecurity.providers.encoding.PasswordEncoder;
|
||||
import org.acegisecurity.providers.encoding.PlaintextPasswordEncoder;
|
||||
|
||||
import org.acegisecurity.userdetails.UserDetails;
|
||||
import org.acegisecurity.userdetails.UserDetailsService;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
/**
|
||||
* An {@link AuthenticationProvider} implementation that retrieves user details from an {@link UserDetailsService}.
|
||||
*
|
||||
* An {@link AuthenticationProvider} implementation that retrieves user details
|
||||
* from an {@link UserDetailsService}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
* @version $Id: DaoAuthenticationProvider.java 1857 2007-05-24 00:47:12Z
|
||||
* benalex $
|
||||
*/
|
||||
public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider implements Ordered {
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
|
||||
|
||||
private PasswordEncoder passwordEncoder = new PlaintextPasswordEncoder();
|
||||
private SaltSource saltSource;
|
||||
private UserDetailsService userDetailsService;
|
||||
private boolean includeDetailsObject = true;
|
||||
private int order = -1; // default: same as non-Ordered
|
||||
// ~ Instance fields
|
||||
// ================================================================================================
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
private PasswordEncoder passwordEncoder = new PlaintextPasswordEncoder();
|
||||
|
||||
protected void additionalAuthenticationChecks(UserDetails userDetails,
|
||||
UsernamePasswordAuthenticationToken authentication)
|
||||
throws AuthenticationException {
|
||||
Object salt = null;
|
||||
private SaltSource saltSource;
|
||||
|
||||
if (this.saltSource != null) {
|
||||
salt = this.saltSource.getSalt(userDetails);
|
||||
}
|
||||
|
||||
if (authentication.getCredentials() == null) {
|
||||
throw new BadCredentialsException(messages.getMessage(
|
||||
"AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"),
|
||||
includeDetailsObject ? userDetails : null);
|
||||
}
|
||||
|
||||
String presentedPassword = authentication.getCredentials() == null ? "" : authentication.getCredentials().toString();
|
||||
private UserDetailsService userDetailsService;
|
||||
|
||||
if (!passwordEncoder.isPasswordValid(
|
||||
userDetails.getPassword(), presentedPassword, salt)) {
|
||||
throw new BadCredentialsException(messages.getMessage(
|
||||
"AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"),
|
||||
includeDetailsObject ? userDetails : null);
|
||||
}
|
||||
}
|
||||
private boolean includeDetailsObject = true;
|
||||
|
||||
protected void doAfterPropertiesSet() throws Exception {
|
||||
Assert.notNull(this.userDetailsService, "A UserDetailsService must be set");
|
||||
}
|
||||
// ~ Methods
|
||||
// ========================================================================================================
|
||||
|
||||
public PasswordEncoder getPasswordEncoder() {
|
||||
return passwordEncoder;
|
||||
}
|
||||
protected void additionalAuthenticationChecks(UserDetails userDetails,
|
||||
UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
|
||||
Object salt = null;
|
||||
|
||||
public SaltSource getSaltSource() {
|
||||
return saltSource;
|
||||
}
|
||||
if (this.saltSource != null) {
|
||||
salt = this.saltSource.getSalt(userDetails);
|
||||
}
|
||||
|
||||
public UserDetailsService getUserDetailsService() {
|
||||
return userDetailsService;
|
||||
}
|
||||
if (authentication.getCredentials() == null) {
|
||||
throw new BadCredentialsException(messages.getMessage(
|
||||
"AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"),
|
||||
includeDetailsObject ? userDetails : null);
|
||||
}
|
||||
|
||||
protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
|
||||
throws AuthenticationException {
|
||||
UserDetails loadedUser;
|
||||
String presentedPassword = authentication.getCredentials() == null ? "" : authentication.getCredentials()
|
||||
.toString();
|
||||
|
||||
try {
|
||||
loadedUser = this.getUserDetailsService().loadUserByUsername(username);
|
||||
} catch (DataAccessException repositoryProblem) {
|
||||
throw new AuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem);
|
||||
}
|
||||
|
||||
if (loadedUser == null) {
|
||||
throw new AuthenticationServiceException(
|
||||
"UserDetailsService returned null, which is an interface contract violation");
|
||||
}
|
||||
return loadedUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the PasswordEncoder instance to be used to encode and validate passwords. If not set, {@link
|
||||
* PlaintextPasswordEncoder} will be used by default.
|
||||
*
|
||||
* @param passwordEncoder The passwordEncoder to use
|
||||
*/
|
||||
public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
}
|
||||
|
||||
/**
|
||||
* The source of salts to use when decoding passwords. <code>null</code> is a valid value, meaning the
|
||||
* <code>DaoAuthenticationProvider</code> will present <code>null</code> to the relevant
|
||||
* <code>PasswordEncoder</code>.
|
||||
*
|
||||
* @param saltSource to use when attempting to decode passwords via the <code>PasswordEncoder</code>
|
||||
*/
|
||||
public void setSaltSource(SaltSource saltSource) {
|
||||
this.saltSource = saltSource;
|
||||
}
|
||||
|
||||
public void setUserDetailsService(UserDetailsService userDetailsService) {
|
||||
this.userDetailsService = userDetailsService;
|
||||
}
|
||||
|
||||
public boolean isIncludeDetailsObject() {
|
||||
return includeDetailsObject;
|
||||
}
|
||||
|
||||
public void setIncludeDetailsObject(boolean includeDetailsObject) {
|
||||
this.includeDetailsObject = includeDetailsObject;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
if (!passwordEncoder.isPasswordValid(userDetails.getPassword(), presentedPassword, salt)) {
|
||||
throw new BadCredentialsException(messages.getMessage(
|
||||
"AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"),
|
||||
includeDetailsObject ? userDetails : null);
|
||||
}
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return order ;
|
||||
protected void doAfterPropertiesSet() throws Exception {
|
||||
Assert.notNull(this.userDetailsService, "A UserDetailsService must be set");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Introspects the <code>Applicationcontext</code> for the single instance
|
||||
* of {@link AccessDeniedHandler}. If found invoke
|
||||
* setAccessDeniedHandler(AccessDeniedHandler accessDeniedHandler) method by
|
||||
* providing the found instance of accessDeniedHandler as a method
|
||||
* parameter. If more than one instance of <code>AccessDeniedHandler</code>
|
||||
* is found, the method throws <code>IllegalStateException</code>.
|
||||
*
|
||||
* @param applicationContext to locate the instance
|
||||
*/
|
||||
private void autoDetectAnyUserDetailsServiceAndUseIt(ApplicationContext applicationContext) {
|
||||
if (applicationContext != null) {
|
||||
Map map = applicationContext.getBeansOfType(UserDetailsService.class);
|
||||
|
||||
if (map.size() > 1) {
|
||||
throw new IllegalArgumentException(
|
||||
"More than one UserDetailsService beans detected please refer to the one using "
|
||||
+ " [ principalRepositoryBeanRef ] " + "attribute");
|
||||
}
|
||||
else if (map.size() == 1) {
|
||||
setUserDetailsService((UserDetailsService) map.values().iterator().next());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public PasswordEncoder getPasswordEncoder() {
|
||||
return passwordEncoder;
|
||||
}
|
||||
|
||||
public SaltSource getSaltSource() {
|
||||
return saltSource;
|
||||
}
|
||||
|
||||
public UserDetailsService getUserDetailsService() {
|
||||
return userDetailsService;
|
||||
}
|
||||
|
||||
protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
|
||||
throws AuthenticationException {
|
||||
UserDetails loadedUser;
|
||||
|
||||
try {
|
||||
loadedUser = this.getUserDetailsService().loadUserByUsername(username);
|
||||
}
|
||||
catch (DataAccessException repositoryProblem) {
|
||||
throw new AuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem);
|
||||
}
|
||||
|
||||
if (loadedUser == null) {
|
||||
throw new AuthenticationServiceException(
|
||||
"UserDetailsService returned null, which is an interface contract violation");
|
||||
}
|
||||
return loadedUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the PasswordEncoder instance to be used to encode and validate
|
||||
* passwords. If not set, {@link PlaintextPasswordEncoder} will be used by
|
||||
* default.
|
||||
*
|
||||
* @param passwordEncoder The passwordEncoder to use
|
||||
*/
|
||||
public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
}
|
||||
|
||||
/**
|
||||
* The source of salts to use when decoding passwords. <code>null</code>
|
||||
* is a valid value, meaning the <code>DaoAuthenticationProvider</code>
|
||||
* will present <code>null</code> to the relevant
|
||||
* <code>PasswordEncoder</code>.
|
||||
*
|
||||
* @param saltSource to use when attempting to decode passwords via the
|
||||
* <code>PasswordEncoder</code>
|
||||
*/
|
||||
public void setSaltSource(SaltSource saltSource) {
|
||||
this.saltSource = saltSource;
|
||||
}
|
||||
|
||||
public void setUserDetailsService(UserDetailsService userDetailsService) {
|
||||
this.userDetailsService = userDetailsService;
|
||||
}
|
||||
|
||||
public boolean isIncludeDetailsObject() {
|
||||
return includeDetailsObject;
|
||||
}
|
||||
|
||||
public void setIncludeDetailsObject(boolean includeDetailsObject) {
|
||||
this.includeDetailsObject = includeDetailsObject;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+4
-4
@@ -15,9 +15,9 @@
|
||||
|
||||
package org.acegisecurity.providers.dao.cache;
|
||||
|
||||
import net.sf.ehcache.Cache;
|
||||
import net.sf.ehcache.CacheException;
|
||||
import net.sf.ehcache.Element;
|
||||
import net.sf.ehcache.Ehcache;
|
||||
|
||||
import org.acegisecurity.providers.dao.UserCache;
|
||||
|
||||
@@ -47,7 +47,7 @@ public class EhCacheBasedUserCache implements UserCache, InitializingBean {
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private Cache cache;
|
||||
private Ehcache cache;
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
@@ -55,7 +55,7 @@ public class EhCacheBasedUserCache implements UserCache, InitializingBean {
|
||||
Assert.notNull(cache, "cache mandatory");
|
||||
}
|
||||
|
||||
public Cache getCache() {
|
||||
public Ehcache getCache() {
|
||||
return cache;
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ public class EhCacheBasedUserCache implements UserCache, InitializingBean {
|
||||
cache.remove(username);
|
||||
}
|
||||
|
||||
public void setCache(Cache cache) {
|
||||
public void setCache(Ehcache cache) {
|
||||
this.cache = cache;
|
||||
}
|
||||
}
|
||||
|
||||
+16
-32
@@ -36,12 +36,8 @@ import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.*;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
@@ -141,32 +137,23 @@ import javax.security.auth.login.LoginException;
|
||||
* @author Ray Krueger
|
||||
* @version $Id$
|
||||
*/
|
||||
public class JaasAuthenticationProvider implements AuthenticationProvider, InitializingBean, ApplicationContextAware,
|
||||
ApplicationListener, Ordered {
|
||||
public class JaasAuthenticationProvider implements AuthenticationProvider, ApplicationEventPublisherAware,
|
||||
InitializingBean, ApplicationListener {
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
|
||||
protected static final Log log = LogFactory.getLog(JaasAuthenticationProvider.class);
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private ApplicationContext context;
|
||||
private LoginExceptionResolver loginExceptionResolver = new DefaultLoginExceptionResolver();
|
||||
private Resource loginConfig;
|
||||
private String loginContextName = "ACEGI";
|
||||
private AuthorityGranter[] authorityGranters;
|
||||
private JaasAuthenticationCallbackHandler[] callbackHandlers;
|
||||
private int order = -1; // default: same as non-Ordered
|
||||
private ApplicationEventPublisher applicationEventPublisher;
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(loginConfig, "loginConfig must be set on " + getClass());
|
||||
Assert.hasLength(loginContextName, "loginContextName must be set on " + getClass());
|
||||
@@ -256,10 +243,9 @@ public class JaasAuthenticationProvider implements AuthenticationProvider, Initi
|
||||
*
|
||||
* @param loginConfig URL to Jaas login configuration
|
||||
*
|
||||
* @throws IOException DOCUMENT ME!
|
||||
* @throws IOException if there is a problem reading the config resource.
|
||||
*/
|
||||
protected void configureJaas(Resource loginConfig)
|
||||
throws IOException {
|
||||
protected void configureJaas(Resource loginConfig) throws IOException {
|
||||
configureJaasUsingLoop();
|
||||
}
|
||||
|
||||
@@ -267,7 +253,6 @@ public class JaasAuthenticationProvider implements AuthenticationProvider, Initi
|
||||
* Loops through the login.config.url.1,login.config.url.2 properties looking for the login configuration.
|
||||
* If it is not set, it will be set to the last available login.config.url.X property.
|
||||
*
|
||||
* @throws IOException DOCUMENT ME!
|
||||
*/
|
||||
private void configureJaasUsingLoop() throws IOException {
|
||||
String loginConfigUrl = loginConfig.getURL().toString();
|
||||
@@ -294,10 +279,6 @@ public class JaasAuthenticationProvider implements AuthenticationProvider, Initi
|
||||
}
|
||||
}
|
||||
|
||||
public ApplicationContext getApplicationContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the AuthorityGrannter array that was passed to the {@link
|
||||
* #setAuthorityGranters(AuthorityGranter[])} method, or null if it none were ever set.
|
||||
@@ -385,7 +366,7 @@ public class JaasAuthenticationProvider implements AuthenticationProvider, Initi
|
||||
* @param ase The {@link AcegiSecurityException} that caused the failure
|
||||
*/
|
||||
protected void publishFailureEvent(UsernamePasswordAuthenticationToken token, AcegiSecurityException ase) {
|
||||
getApplicationContext().publishEvent(new JaasAuthenticationFailedEvent(token, ase));
|
||||
applicationEventPublisher.publishEvent(new JaasAuthenticationFailedEvent(token, ase));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -395,12 +376,7 @@ public class JaasAuthenticationProvider implements AuthenticationProvider, Initi
|
||||
* @param token The {@link UsernamePasswordAuthenticationToken} being processed
|
||||
*/
|
||||
protected void publishSuccessEvent(UsernamePasswordAuthenticationToken token) {
|
||||
getApplicationContext().publishEvent(new JaasAuthenticationSuccessEvent(token));
|
||||
}
|
||||
|
||||
public void setApplicationContext(ApplicationContext applicationContext)
|
||||
throws BeansException {
|
||||
this.context = applicationContext;
|
||||
applicationEventPublisher.publishEvent(new JaasAuthenticationSuccessEvent(token));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -455,6 +431,14 @@ public class JaasAuthenticationProvider implements AuthenticationProvider, Initi
|
||||
return UsernamePasswordAuthenticationToken.class.isAssignableFrom(aClass);
|
||||
}
|
||||
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
|
||||
this.applicationEventPublisher = applicationEventPublisher;
|
||||
}
|
||||
|
||||
protected ApplicationEventPublisher getApplicationEventPublisher() {
|
||||
return applicationEventPublisher;
|
||||
}
|
||||
|
||||
//~ Inner Classes ==================================================================================================
|
||||
|
||||
/**
|
||||
|
||||
+24
-13
@@ -19,6 +19,7 @@ import org.acegisecurity.AuthenticationException;
|
||||
import org.acegisecurity.BadCredentialsException;
|
||||
import org.acegisecurity.GrantedAuthority;
|
||||
import org.acegisecurity.AuthenticationServiceException;
|
||||
import org.acegisecurity.ldap.LdapDataAccessException;
|
||||
|
||||
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
|
||||
import org.acegisecurity.providers.dao.AbstractUserDetailsAuthenticationProvider;
|
||||
@@ -32,7 +33,6 @@ import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
|
||||
|
||||
@@ -114,7 +114,7 @@ import org.springframework.dao.DataAccessException;
|
||||
* @see org.acegisecurity.providers.ldap.authenticator.BindAuthenticator
|
||||
* @see org.acegisecurity.providers.ldap.populator.DefaultLdapAuthoritiesPopulator
|
||||
*/
|
||||
public class LdapAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider implements Ordered {
|
||||
public class LdapAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
|
||||
private static final Log logger = LogFactory.getLog(LdapAuthenticationProvider.class);
|
||||
@@ -124,20 +124,11 @@ public class LdapAuthenticationProvider extends AbstractUserDetailsAuthenticatio
|
||||
private LdapAuthenticator authenticator;
|
||||
private LdapAuthoritiesPopulator authoritiesPopulator;
|
||||
private boolean includeDetailsObject = true;
|
||||
private int order = -1; // default: same as non-Ordered
|
||||
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an initialized instance to the values passed as arguments
|
||||
/**
|
||||
* Create an instance with the supplied authenticator and authorities populator implementations.
|
||||
*
|
||||
* @param authenticator the authentication strategy (bind, password comparison, etc)
|
||||
* to be used by this provider for authenticating users.
|
||||
@@ -149,6 +140,17 @@ public class LdapAuthenticationProvider extends AbstractUserDetailsAuthenticatio
|
||||
this.setAuthoritiesPopulator(authoritiesPopulator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance with the supplied authenticator and a null authorities populator.
|
||||
* In this case, the authorities must be mapped from the user context.
|
||||
*
|
||||
* @param authenticator the authenticator strategy.
|
||||
*/
|
||||
public LdapAuthenticationProvider(LdapAuthenticator authenticator) {
|
||||
this.setAuthenticator(authenticator);
|
||||
this.setAuthoritiesPopulator(new NullAuthoritiesPopulator());
|
||||
}
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
private void setAuthenticator(LdapAuthenticator authenticator) {
|
||||
@@ -244,4 +246,13 @@ public class LdapAuthenticationProvider extends AbstractUserDetailsAuthenticatio
|
||||
public void setIncludeDetailsObject(boolean includeDetailsObject) {
|
||||
this.includeDetailsObject = includeDetailsObject;
|
||||
}
|
||||
|
||||
//~ Inner Classes ==================================================================================================
|
||||
|
||||
private static class NullAuthoritiesPopulator implements LdapAuthoritiesPopulator {
|
||||
public GrantedAuthority[] getGrantedAuthorities(LdapUserDetails userDetails) throws LdapDataAccessException {
|
||||
return new GrantedAuthority[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -87,6 +87,7 @@ public class BindAuthenticator extends AbstractLdapAuthenticator {
|
||||
LdapUserDetailsImpl.Essence user = (LdapUserDetailsImpl.Essence) template.retrieveEntry(userDn,
|
||||
getUserDetailsMapper(), getUserAttributes());
|
||||
user.setUsername(username);
|
||||
user.setPassword(password);
|
||||
|
||||
return user.createUserDetails();
|
||||
} catch (BadCredentialsException e) {
|
||||
|
||||
+30
-7
@@ -29,7 +29,8 @@ import java.security.MessageDigest;
|
||||
|
||||
/**
|
||||
* A version of {@link ShaPasswordEncoder} which supports Ldap SHA and SSHA (salted-SHA) encodings. The values are
|
||||
* base-64 encoded and have the label "{SHA}" (or "{SSHA}") prepended to the encoded hash.
|
||||
* base-64 encoded and have the label "{SHA}" (or "{SSHA}") prepended to the encoded hash. These can be made lower-case
|
||||
* in the encoded password, if required, by setting the <tt>forceLowerCasePrefix</tt> property to true.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
@@ -40,7 +41,12 @@ public class LdapShaPasswordEncoder implements PasswordEncoder {
|
||||
/** The number of bytes in a SHA hash */
|
||||
private static final int SHA_LENGTH = 20;
|
||||
private static final String SSHA_PREFIX = "{SSHA}";
|
||||
private static final String SSHA_PREFIX_LC = SSHA_PREFIX.toLowerCase();
|
||||
private static final String SHA_PREFIX = "{SHA}";
|
||||
private static final String SHA_PREFIX_LC = SHA_PREFIX.toLowerCase();
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
private boolean forceLowerCasePrefix;
|
||||
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
@@ -76,7 +82,7 @@ public class LdapShaPasswordEncoder implements PasswordEncoder {
|
||||
try {
|
||||
sha = MessageDigest.getInstance("SHA");
|
||||
} catch (java.security.NoSuchAlgorithmException e) {
|
||||
throw new LdapDataAccessException("No SHA implementation available!");
|
||||
throw new LdapDataAccessException("No SHA implementation available!", e);
|
||||
}
|
||||
|
||||
sha.update(rawPass.getBytes());
|
||||
@@ -88,7 +94,15 @@ public class LdapShaPasswordEncoder implements PasswordEncoder {
|
||||
|
||||
byte[] hash = combineHashAndSalt(sha.digest(), (byte[]) salt);
|
||||
|
||||
return ((salt == null) ? SHA_PREFIX : SSHA_PREFIX) + new String(Base64.encodeBase64(hash));
|
||||
String prefix;
|
||||
|
||||
if (salt == null) {
|
||||
prefix = forceLowerCasePrefix ? SHA_PREFIX_LC : SHA_PREFIX;
|
||||
} else {
|
||||
prefix = forceLowerCasePrefix ? SSHA_PREFIX_LC : SSHA_PREFIX;
|
||||
}
|
||||
|
||||
return prefix + new String(Base64.encodeBase64(hash));
|
||||
}
|
||||
|
||||
private byte[] extractSalt(String encPass) {
|
||||
@@ -106,19 +120,28 @@ public class LdapShaPasswordEncoder implements PasswordEncoder {
|
||||
* Checks the validity of an unencoded password against an encoded one in the form
|
||||
* "{SSHA}sQuQF8vj8Eg2Y1hPdh3bkQhCKQBgjhQI".
|
||||
*
|
||||
* @param encPass the SSHA or SHA encoded password
|
||||
* @param encPass the actual SSHA or SHA encoded password
|
||||
* @param rawPass unencoded password to be verified.
|
||||
* @param salt ignored. If the format is SSHA the salt bytes will be extracted from the encoded password.
|
||||
*
|
||||
* @return true if they match.
|
||||
* @return true if they match (independent of the case of the prefix).
|
||||
*/
|
||||
public boolean isPasswordValid(String encPass, String rawPass, Object salt) {
|
||||
if (encPass.startsWith(SSHA_PREFIX)) {
|
||||
String encPassWithoutPrefix;
|
||||
|
||||
if (encPass.startsWith(SSHA_PREFIX) || encPass.startsWith(SSHA_PREFIX_LC)) {
|
||||
encPassWithoutPrefix = encPass.substring(6);
|
||||
salt = extractSalt(encPass);
|
||||
} else {
|
||||
encPassWithoutPrefix = encPass.substring(5);
|
||||
salt = null;
|
||||
}
|
||||
|
||||
return encPass.equals(encodePassword(rawPass, salt));
|
||||
// Compare the encoded passwords without the prefix
|
||||
return encodePassword(rawPass, salt).endsWith(encPassWithoutPrefix);
|
||||
}
|
||||
|
||||
public void setForceLowerCasePrefix(boolean forceLowerCasePrefix) {
|
||||
this.forceLowerCasePrefix = forceLowerCasePrefix;
|
||||
}
|
||||
}
|
||||
|
||||
+46
-24
@@ -40,9 +40,11 @@ import javax.naming.directory.SearchControls;
|
||||
|
||||
/**
|
||||
* The default strategy for obtaining user role information from the directory.
|
||||
* <p>It obtains roles by performing a search for "groups" the user is a member of.
|
||||
* </p>
|
||||
* <p>A typical group search scenario would be where each group/role is specified using the <tt>groupOfNames</tt>
|
||||
* <p/>
|
||||
* <p>It obtains roles by performing a search for "groups" the user is a member of.</p>
|
||||
* <p/>
|
||||
* <p/>
|
||||
* A typical group search scenario would be where each group/role is specified using the <tt>groupOfNames</tt>
|
||||
* (or <tt>groupOfUniqueNames</tt>) LDAP objectClass and the user's DN is listed in the <tt>member</tt> (or
|
||||
* <tt>uniqueMember</tt>) attribute to indicate that they should be assigned that role. The following LDIF sample has
|
||||
* the groups stored under the DN <tt>ou=groups,dc=acegisecurity,dc=org</tt> and a group called "developers" with
|
||||
@@ -54,11 +56,14 @@ import javax.naming.directory.SearchControls;
|
||||
* member: uid=ben,ou=people,dc=acegisecurity,dc=orgmember: uid=marissa,ou=people,dc=acegisecurity,dc=orgou: developer
|
||||
* </pre>
|
||||
* </p>
|
||||
* <p>The group search is performed within a DN specified by the <tt>groupSearchBase</tt> property, which should
|
||||
* <p/>
|
||||
* The group search is performed within a DN specified by the <tt>groupSearchBase</tt> property, which should
|
||||
* be relative to the root DN of its <tt>InitialDirContextFactory</tt>. If the search base is null, group searching is
|
||||
* disabled. The filter used in the search is defined by the <tt>groupSearchFilter</tt> property, with the filter
|
||||
* argument {0} being the full DN of the user. You can also specify which attribute defines the role name by setting
|
||||
* argument {0} being the full DN of the user. You can also optionally use the parameter {1}, which will be substituted
|
||||
* with the username. You can also specify which attribute defines the role name by setting
|
||||
* the <tt>groupRoleAttribute</tt> property (the default is "cn").</p>
|
||||
* <p/>
|
||||
* <p>The configuration below shows how the group search might be performed with the above schema.
|
||||
* <pre>
|
||||
* <bean id="ldapAuthoritiesPopulator"
|
||||
@@ -73,7 +78,11 @@ import javax.naming.directory.SearchControls;
|
||||
* </bean>
|
||||
* </pre>
|
||||
* A search for roles for user "uid=ben,ou=people,dc=acegisecurity,dc=org" would return the single granted authority
|
||||
* "ROLE_DEVELOPER".</p>
|
||||
* "ROLE_DEVELOPER".
|
||||
* </p>
|
||||
* <p/>
|
||||
* The single-level search is performed by default. Setting the <tt>searchSubTree</tt> property to true will enable
|
||||
* a search of the entire subtree under <tt>groupSearchBase</tt>.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
@@ -85,10 +94,14 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
/** A default role which will be assigned to all authenticated users if set */
|
||||
/**
|
||||
* A default role which will be assigned to all authenticated users if set
|
||||
*/
|
||||
private GrantedAuthority defaultRole = null;
|
||||
|
||||
/** An initial context factory is only required if searching for groups is required. */
|
||||
/**
|
||||
* An initial context factory is only required if searching for groups is required.
|
||||
*/
|
||||
private InitialDirContextFactory initialDirContextFactory = null;
|
||||
private LdapTemplate ldapTemplate;
|
||||
|
||||
@@ -98,16 +111,24 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
|
||||
*/
|
||||
private SearchControls searchControls = new SearchControls();
|
||||
|
||||
/** The ID of the attribute which contains the role name for a group */
|
||||
/**
|
||||
* The ID of the attribute which contains the role name for a group
|
||||
*/
|
||||
private String groupRoleAttribute = "cn";
|
||||
|
||||
/** The base DN from which the search for group membership should be performed */
|
||||
/**
|
||||
* The base DN from which the search for group membership should be performed
|
||||
*/
|
||||
private String groupSearchBase = null;
|
||||
|
||||
/** The pattern to be used for the user search. {0} is the user's DN */
|
||||
/**
|
||||
* The pattern to be used for the user search. {0} is the user's DN
|
||||
*/
|
||||
private String groupSearchFilter = "(member={0})";
|
||||
|
||||
/** Attributes of the User's LDAP Object that contain role name information. */
|
||||
/**
|
||||
* Attributes of the User's LDAP Object that contain role name information.
|
||||
*/
|
||||
|
||||
// private String[] userRoleAttributes = null;
|
||||
private String rolePrefix = "ROLE_";
|
||||
@@ -120,8 +141,8 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
|
||||
* set as a property.
|
||||
*
|
||||
* @param initialDirContextFactory supplies the contexts used to search for user roles.
|
||||
* @param groupSearchBase if this is an empty string the search will be performed from the root DN of the
|
||||
* context factory.
|
||||
* @param groupSearchBase if this is an empty string the search will be performed from the root DN of the
|
||||
* context factory.
|
||||
*/
|
||||
public DefaultLdapAuthoritiesPopulator(InitialDirContextFactory initialDirContextFactory, String groupSearchBase) {
|
||||
this.setInitialDirContextFactory(initialDirContextFactory);
|
||||
@@ -135,7 +156,6 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
|
||||
* roles for the given user (on top of those obtained from the standard
|
||||
* search implemented by this class).
|
||||
*
|
||||
*
|
||||
* @param ldapUser the user who's roles are required
|
||||
* @return the extra roles which will be merged with those returned by the group search
|
||||
*/
|
||||
@@ -149,7 +169,6 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
|
||||
* the supplied LdapUserDetails object.
|
||||
*
|
||||
* @param userDetails the user who's authorities are required
|
||||
*
|
||||
* @return the set of roles granted to the user.
|
||||
*/
|
||||
public final GrantedAuthority[] getGrantedAuthorities(LdapUserDetails userDetails) {
|
||||
@@ -203,11 +222,11 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Searching for roles for user '" + username + "', DN = " + "'" + userDn + "', with filter "
|
||||
+ groupSearchFilter + " in search base '" + getGroupSearchBase() + "'");
|
||||
+ groupSearchFilter + " in search base '" + getGroupSearchBase() + "'");
|
||||
}
|
||||
|
||||
Set userRoles = ldapTemplate.searchForSingleAttributeValues(getGroupSearchBase(), groupSearchFilter,
|
||||
new String[] {userDn, username}, groupRoleAttribute);
|
||||
new String[]{userDn, username}, groupRoleAttribute);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Roles from search: " + userRoles);
|
||||
@@ -231,12 +250,10 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
|
||||
/**
|
||||
* Searches for groups the user is a member of.
|
||||
*
|
||||
* @param userDn the user's distinguished name.
|
||||
* @param userDn the user's distinguished name.
|
||||
* @param userAttributes the retrieved user's attributes (unused by default).
|
||||
*
|
||||
* @return the set of roles obtained from a group membership search, or null if <tt>groupSearchBase</tt> has been
|
||||
* set.
|
||||
*
|
||||
* @deprecated Subclasses should implement <tt>getAdditionalRoles</tt> instead.
|
||||
*/
|
||||
protected Set getGroupMembershipRoles(String userDn, Attributes userAttributes) {
|
||||
@@ -264,18 +281,18 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
|
||||
* Set the group search base (name to search under)
|
||||
*
|
||||
* @param groupSearchBase if this is an empty string the search will be performed from the root DN of the context
|
||||
* factory.
|
||||
* factory.
|
||||
*/
|
||||
private void setGroupSearchBase(String groupSearchBase) {
|
||||
Assert.notNull(groupSearchBase, "The groupSearchBase (name to search under), must not be null.");
|
||||
this.groupSearchBase = groupSearchBase;
|
||||
if (groupSearchBase.length() == 0) {
|
||||
logger.info("groupSearchBase is empty. Searches will be performed from the root: "
|
||||
+ getInitialDirContextFactory().getRootDn());
|
||||
+ getInitialDirContextFactory().getRootDn());
|
||||
}
|
||||
}
|
||||
|
||||
private String getGroupSearchBase() {
|
||||
protected String getGroupSearchBase() {
|
||||
return groupSearchBase;
|
||||
}
|
||||
|
||||
@@ -308,6 +325,11 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
|
||||
this.rolePrefix = rolePrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to true, a subtree scope search will be performed. If false a single-level search is used.
|
||||
*
|
||||
* @param searchSubtree set to true to enable searching of the entire tree below the <tt>groupSearchBase</tt>.
|
||||
*/
|
||||
public void setSearchSubtree(boolean searchSubtree) {
|
||||
int searchScope = searchSubtree ? SearchControls.SUBTREE_SCOPE : SearchControls.ONELEVEL_SCOPE;
|
||||
searchControls.setSearchScope(searchScope);
|
||||
|
||||
+1
-11
@@ -23,7 +23,6 @@ import org.acegisecurity.providers.AuthenticationProvider;
|
||||
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -44,22 +43,13 @@ import org.springframework.util.Assert;
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class RemoteAuthenticationProvider implements AuthenticationProvider, InitializingBean, Ordered {
|
||||
public class RemoteAuthenticationProvider implements AuthenticationProvider, InitializingBean {
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private RemoteAuthenticationManager remoteAuthenticationManager;
|
||||
private int order = -1; // default: same as non-Ordered
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(this.remoteAuthenticationManager, "remoteAuthenticationManager is mandatory");
|
||||
}
|
||||
|
||||
+1
-11
@@ -30,7 +30,6 @@ import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.MessageSourceAware;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -41,7 +40,7 @@ import org.springframework.util.Assert;
|
||||
* {@link org.acegisecurity.providers.rememberme.RememberMeAuthenticationToken#getKeyHash()} must match this class'
|
||||
* {@link #getKey()}.</p>
|
||||
*/
|
||||
public class RememberMeAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware, Ordered {
|
||||
public class RememberMeAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware {
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
|
||||
private static final Log logger = LogFactory.getLog(RememberMeAuthenticationProvider.class);
|
||||
@@ -50,18 +49,9 @@ public class RememberMeAuthenticationProvider implements AuthenticationProvider,
|
||||
|
||||
protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor();
|
||||
private String key;
|
||||
private int order = -1; // default: same as non-Ordered
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.hasLength(key);
|
||||
Assert.notNull(this.messages, "A message source must be set");
|
||||
|
||||
+1
-12
@@ -28,7 +28,6 @@ import org.acegisecurity.userdetails.UserDetails;
|
||||
import org.acegisecurity.userdetails.UserDetailsService;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -38,7 +37,7 @@ import org.springframework.util.Assert;
|
||||
* @author Scott McCrory
|
||||
* @version $Id: SiteminderAuthenticationProvider.java 1582 2006-07-15 15:18:51Z smccrory $
|
||||
*/
|
||||
public class SiteminderAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider implements Ordered {
|
||||
public class SiteminderAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
|
||||
|
||||
|
||||
/**
|
||||
@@ -53,8 +52,6 @@ public class SiteminderAuthenticationProvider extends AbstractUserDetailsAuthent
|
||||
*/
|
||||
private UserDetailsService userDetailsService;
|
||||
|
||||
private int order = -1; // default: same as non-Ordered
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
/**
|
||||
@@ -133,12 +130,4 @@ public class SiteminderAuthenticationProvider extends AbstractUserDetailsAuthent
|
||||
this.userDetailsService = userDetailsService;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-11
@@ -33,7 +33,6 @@ import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.MessageSourceAware;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -47,7 +46,7 @@ import java.security.cert.X509Certificate;
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public class X509AuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware, Ordered {
|
||||
public class X509AuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware {
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
|
||||
private static final Log logger = LogFactory.getLog(X509AuthenticationProvider.class);
|
||||
@@ -57,18 +56,9 @@ public class X509AuthenticationProvider implements AuthenticationProvider, Initi
|
||||
protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor();
|
||||
private X509AuthoritiesPopulator x509AuthoritiesPopulator;
|
||||
private X509UserCache userCache = new NullX509UserCache();
|
||||
private int order = -1; // default: same as non-Ordered
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(userCache, "An x509UserCache must be set");
|
||||
Assert.notNull(x509AuthoritiesPopulator, "An X509AuthoritiesPopulator must be set");
|
||||
|
||||
+3
-3
@@ -15,9 +15,9 @@
|
||||
|
||||
package org.acegisecurity.providers.x509.cache;
|
||||
|
||||
import net.sf.ehcache.Cache;
|
||||
import net.sf.ehcache.CacheException;
|
||||
import net.sf.ehcache.Element;
|
||||
import net.sf.ehcache.Ehcache;
|
||||
|
||||
import org.acegisecurity.providers.x509.X509UserCache;
|
||||
|
||||
@@ -50,7 +50,7 @@ public class EhCacheBasedX509UserCache implements X509UserCache, InitializingBea
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private Cache cache;
|
||||
private Ehcache cache;
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
@@ -102,7 +102,7 @@ public class EhCacheBasedX509UserCache implements X509UserCache, InitializingBea
|
||||
cache.remove(userCert);
|
||||
}
|
||||
|
||||
public void setCache(Cache cache) {
|
||||
public void setCache(Ehcache cache) {
|
||||
this.cache = cache;
|
||||
}
|
||||
}
|
||||
|
||||
+13
-5
@@ -18,6 +18,7 @@ package org.acegisecurity.providers.x509.populator;
|
||||
import org.acegisecurity.AcegiMessageSource;
|
||||
import org.acegisecurity.AuthenticationException;
|
||||
import org.acegisecurity.BadCredentialsException;
|
||||
import org.acegisecurity.AuthenticationServiceException;
|
||||
|
||||
import org.acegisecurity.providers.x509.X509AuthoritiesPopulator;
|
||||
|
||||
@@ -79,8 +80,7 @@ public class DaoX509AuthoritiesPopulator implements X509AuthoritiesPopulator, In
|
||||
}
|
||||
}
|
||||
|
||||
public UserDetails getUserDetails(X509Certificate clientCert)
|
||||
throws AuthenticationException {
|
||||
public UserDetails getUserDetails(X509Certificate clientCert) throws AuthenticationException {
|
||||
String subjectDN = clientCert.getSubjectDN().getName();
|
||||
PatternMatcher matcher = new Perl5Matcher();
|
||||
|
||||
@@ -97,7 +97,14 @@ public class DaoX509AuthoritiesPopulator implements X509AuthoritiesPopulator, In
|
||||
|
||||
String userName = match.group(1);
|
||||
|
||||
return this.userDetailsService.loadUserByUsername(userName);
|
||||
UserDetails user = this.userDetailsService.loadUserByUsername(userName);
|
||||
|
||||
if (user == null) {
|
||||
throw new AuthenticationServiceException(
|
||||
"UserDetailsService returned null, which is an interface contract violation");
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setMessageSource(MessageSource messageSource) {
|
||||
@@ -106,9 +113,10 @@ public class DaoX509AuthoritiesPopulator implements X509AuthoritiesPopulator, In
|
||||
|
||||
/**
|
||||
* Sets the regular expression which will by used to extract the user name from the certificate's Subject
|
||||
* DN.<p>It should contain a single group; for example the default expression "CN=(.?)," matches the common
|
||||
* DN.
|
||||
* <p>It should contain a single group; for example the default expression "CN=(.?)," matches the common
|
||||
* name field. So "CN=Jimi Hendrix, OU=..." will give a user name of "Jimi Hendrix".</p>
|
||||
* <p>The matches are case insensitive. So "emailAddress=(.?)," will match "EMAILADDRESS=jimi@hendrix.org,
|
||||
* <p>The matches are case insensitive. So "emailAddress=(.?)," will match "EMAILADDRESS=jimi@hendrix.org,
|
||||
* CN=..." giving a user name "jimi@hendrix.org"</p>
|
||||
*
|
||||
* @param subjectDNRegex the regular expression to find in the subject
|
||||
|
||||
@@ -27,7 +27,6 @@ import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.MessageSourceAware;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -39,23 +38,14 @@ import org.springframework.util.Assert;
|
||||
* <code>RunAsImplAuthenticationProvider</code>-configured key.</p>
|
||||
* <P>If the key does not match, a <code>BadCredentialsException</code> is thrown.</p>
|
||||
*/
|
||||
public class RunAsImplAuthenticationProvider implements InitializingBean, AuthenticationProvider, MessageSourceAware, Ordered {
|
||||
public class RunAsImplAuthenticationProvider implements InitializingBean, AuthenticationProvider, MessageSourceAware {
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor();
|
||||
private String key;
|
||||
private int order = -1; // default: same as non-Ordered
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(key, "A Key is required and should match that configured for the RunAsManagerImpl");
|
||||
}
|
||||
|
||||
@@ -54,424 +54,494 @@ import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
|
||||
/**
|
||||
* Abstract processor of browser-based HTTP-based authentication requests.<p>This filter is responsible for
|
||||
* processing authentication requests. If authentication is successful, the resulting {@link Authentication} object
|
||||
* will be placed into the <code>SecurityContext</code>, which is guaranteed to have already been created by an
|
||||
* earlier filter.</p>
|
||||
* <p>If authentication fails, the <code>AuthenticationException</code> will be placed into the
|
||||
* <code>HttpSession</code> with the attribute defined by {@link #ACEGI_SECURITY_LAST_EXCEPTION_KEY}.</p>
|
||||
* <p>To use this filter, it is necessary to specify the following properties:</p>
|
||||
* <ul>
|
||||
* <li><code>defaultTargetUrl</code> indicates the URL that should be used for redirection if the
|
||||
* <code>HttpSession</code> attribute named {@link #ACEGI_SAVED_REQUEST_KEY} does not indicate the target URL once
|
||||
* authentication is completed successfully. eg: <code>/</code>. The <code>defaultTargetUrl</code> will be treated
|
||||
* as relative to the web-app's context path, and should include the leading <code>/</code>. Alternatively,
|
||||
* inclusion of a scheme name (eg http:// or https://) as the prefix will denote a fully-qualified URL and this is
|
||||
* also supported.</li>
|
||||
* <li><code>authenticationFailureUrl</code> indicates the URL that should be used for redirection if the
|
||||
* authentication request fails. eg: <code>/login.jsp?login_error=1</code>.</li>
|
||||
* <li><code>filterProcessesUrl</code> indicates the URL that this filter will respond to. This parameter
|
||||
* varies by subclass.</li>
|
||||
* <li><code>alwaysUseDefaultTargetUrl</code> causes successful authentication to always redirect to the
|
||||
* <code>defaultTargetUrl</code>, even if the <code>HttpSession</code> attribute named {@link
|
||||
* #ACEGI_SAVED_REQUEST_KEY} defines the intended target URL.</li>
|
||||
* </ul>
|
||||
* <p>To configure this filter to redirect to specific pages as the result of specific {@link
|
||||
* AuthenticationException}s you can do the following. Configure the <code>exceptionMappings</code> property in your
|
||||
* application xml. This property is a java.util.Properties object that maps a fully-qualified exception class name to
|
||||
* a redirection url target.
|
||||
* For example:
|
||||
* Abstract processor of browser-based HTTP-based authentication requests.
|
||||
* <p>
|
||||
* This filter is responsible for processing authentication requests. If
|
||||
* authentication is successful, the resulting {@link Authentication} object
|
||||
* will be placed into the <code>SecurityContext</code>, which is guaranteed
|
||||
* to have already been created by an earlier filter.
|
||||
* </p>
|
||||
* <p>
|
||||
* If authentication fails, the <code>AuthenticationException</code> will be
|
||||
* placed into the <code>HttpSession</code> with the attribute defined by
|
||||
* {@link #ACEGI_SECURITY_LAST_EXCEPTION_KEY}.
|
||||
* </p>
|
||||
* <p>
|
||||
* To use this filter, it is necessary to specify the following properties:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li><code>defaultTargetUrl</code> indicates the URL that should be used
|
||||
* for redirection if the <code>HttpSession</code> attribute named
|
||||
* {@link #ACEGI_SAVED_REQUEST_KEY} does not indicate the target URL once
|
||||
* authentication is completed successfully. eg: <code>/</code>. The
|
||||
* <code>defaultTargetUrl</code> will be treated as relative to the web-app's
|
||||
* context path, and should include the leading <code>/</code>.
|
||||
* Alternatively, inclusion of a scheme name (eg http:// or https://) as the
|
||||
* prefix will denote a fully-qualified URL and this is also supported.</li>
|
||||
* <li><code>authenticationFailureUrl</code> indicates the URL that should be
|
||||
* used for redirection if the authentication request fails. eg:
|
||||
* <code>/login.jsp?login_error=1</code>.</li>
|
||||
* <li><code>filterProcessesUrl</code> indicates the URL that this filter
|
||||
* will respond to. This parameter varies by subclass.</li>
|
||||
* <li><code>alwaysUseDefaultTargetUrl</code> causes successful
|
||||
* authentication to always redirect to the <code>defaultTargetUrl</code>,
|
||||
* even if the <code>HttpSession</code> attribute named {@link
|
||||
* #ACEGI_SAVED_REQUEST_KEY} defines the intended target URL.</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* To configure this filter to redirect to specific pages as the result of
|
||||
* specific {@link AuthenticationException}s you can do the following.
|
||||
* Configure the <code>exceptionMappings</code> property in your application
|
||||
* xml. This property is a java.util.Properties object that maps a
|
||||
* fully-qualified exception class name to a redirection url target. For
|
||||
* example:
|
||||
*
|
||||
* <pre>
|
||||
* <property name="exceptionMappings">
|
||||
* <property name="exceptionMappings">
|
||||
* <props>
|
||||
* <prop> key="org.acegisecurity.BadCredentialsException">/bad_credentials.jsp</prop>
|
||||
* <prop> key="org.acegisecurity.BadCredentialsException">/bad_credentials.jsp</prop>
|
||||
* </props>
|
||||
* </property>
|
||||
* </pre>
|
||||
* The example above would redirect all {@link org.acegisecurity.BadCredentialsException}s thrown, to a page in the
|
||||
* web-application called /bad_credentials.jsp.</p>
|
||||
* <p>Any {@link AuthenticationException} thrown that cannot be matched in the <code>exceptionMappings</code> will
|
||||
* be redirected to the <code>authenticationFailureUrl</code></p>
|
||||
* <p>If authentication is successful, an {@link
|
||||
* org.acegisecurity.event.authentication.InteractiveAuthenticationSuccessEvent} will be published to the application
|
||||
* context. No events will be published if authentication was unsuccessful, because this would generally be recorded
|
||||
* via an <code>AuthenticationManager</code>-specific application event.</p>
|
||||
*
|
||||
*
|
||||
* The example above would redirect all
|
||||
* {@link org.acegisecurity.BadCredentialsException}s thrown, to a page in the
|
||||
* web-application called /bad_credentials.jsp.
|
||||
* </p>
|
||||
* <p>
|
||||
* Any {@link AuthenticationException} thrown that cannot be matched in the
|
||||
* <code>exceptionMappings</code> will be redirected to the
|
||||
* <code>authenticationFailureUrl</code>
|
||||
* </p>
|
||||
* <p>
|
||||
* If authentication is successful, an {@link
|
||||
* org.acegisecurity.event.authentication.InteractiveAuthenticationSuccessEvent}
|
||||
* will be published to the application context. No events will be published if
|
||||
* authentication was unsuccessful, because this would generally be recorded via
|
||||
* an <code>AuthenticationManager</code>-specific application event.
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
* @version $Id: AbstractProcessingFilter.java 1909 2007-06-19 04:08:19Z
|
||||
* vishalpuri $
|
||||
*/
|
||||
public abstract class AbstractProcessingFilter implements Filter, InitializingBean, ApplicationEventPublisherAware,
|
||||
MessageSourceAware {
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
|
||||
public static final String ACEGI_SAVED_REQUEST_KEY = "ACEGI_SAVED_REQUEST_KEY";
|
||||
public static final String ACEGI_SECURITY_LAST_EXCEPTION_KEY = "ACEGI_SECURITY_LAST_EXCEPTION";
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
protected ApplicationEventPublisher eventPublisher;
|
||||
protected AuthenticationDetailsSource authenticationDetailsSource = new AuthenticationDetailsSourceImpl();
|
||||
private AuthenticationManager authenticationManager;
|
||||
protected final Log logger = LogFactory.getLog(this.getClass());
|
||||
protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor();
|
||||
private Properties exceptionMappings = new Properties();
|
||||
private RememberMeServices rememberMeServices = new NullRememberMeServices();
|
||||
|
||||
/** Where to redirect the browser to if authentication fails */
|
||||
private String authenticationFailureUrl;
|
||||
|
||||
/**
|
||||
* Where to redirect the browser to if authentication is successful but ACEGI_SAVED_REQUEST_KEY is
|
||||
* <code>null</code>
|
||||
*/
|
||||
private String defaultTargetUrl;
|
||||
|
||||
/**
|
||||
* The URL destination that this filter intercepts and processes (usually something like
|
||||
* <code>/j_acegi_security_check</code>)
|
||||
*/
|
||||
private String filterProcessesUrl = getDefaultFilterProcessesUrl();
|
||||
|
||||
/**
|
||||
* If <code>true</code>, will always redirect to the value of {@link #getDefaultTargetUrl} upon successful
|
||||
* authentication, irrespective of the page that caused the authentication request (defaults to <code>false</code>).
|
||||
*/
|
||||
private boolean alwaysUseDefaultTargetUrl = false;
|
||||
|
||||
/**
|
||||
* Indicates if the filter chain should be continued prior to delegation to {@link
|
||||
* #successfulAuthentication(HttpServletRequest, HttpServletResponse, Authentication)}, which may be useful in
|
||||
* certain environment (eg Tapestry). Defaults to <code>false</code>.
|
||||
*/
|
||||
private boolean continueChainBeforeSuccessfulAuthentication = false;
|
||||
|
||||
/**
|
||||
* Specifies the buffer size to use in the event of a directory. A buffer size is used to ensure the
|
||||
* response is not written back to the client immediately. This provides a way for the <code>HttpSession</code>
|
||||
* to be updated before the browser redirect will be sent. Defaults to an 8 Kb buffer.
|
||||
*/
|
||||
private int bufferSize = 8 * 1024;
|
||||
|
||||
/**
|
||||
* If true, causes any redirection URLs to be calculated minus the protocol and context path (defaults to false).
|
||||
*/
|
||||
private boolean useRelativeContext = false;
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.hasLength(filterProcessesUrl, "filterProcessesUrl must be specified");
|
||||
Assert.hasLength(defaultTargetUrl, "defaultTargetUrl must be specified");
|
||||
Assert.hasLength(authenticationFailureUrl, "authenticationFailureUrl must be specified");
|
||||
Assert.notNull(authenticationManager, "authenticationManager must be specified");
|
||||
Assert.notNull(this.rememberMeServices);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs actual authentication.
|
||||
*
|
||||
* @param request from which to extract parameters and perform the authentication
|
||||
*
|
||||
* @return the authenticated user
|
||||
*
|
||||
* @throws AuthenticationException if authentication fails
|
||||
*/
|
||||
public abstract Authentication attemptAuthentication(HttpServletRequest request)
|
||||
throws AuthenticationException;
|
||||
|
||||
/**
|
||||
* Does nothing. We use IoC container lifecycle services instead.
|
||||
*/
|
||||
public void destroy() {}
|
||||
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
||||
throws IOException, ServletException {
|
||||
if (!(request instanceof HttpServletRequest)) {
|
||||
throw new ServletException("Can only process HttpServletRequest");
|
||||
}
|
||||
|
||||
if (!(response instanceof HttpServletResponse)) {
|
||||
throw new ServletException("Can only process HttpServletResponse");
|
||||
}
|
||||
|
||||
HttpServletRequest httpRequest = (HttpServletRequest) request;
|
||||
HttpServletResponse httpResponse = (HttpServletResponse) response;
|
||||
|
||||
if (requiresAuthentication(httpRequest, httpResponse)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Request is to process authentication");
|
||||
}
|
||||
|
||||
Authentication authResult;
|
||||
|
||||
try {
|
||||
onPreAuthentication(httpRequest, httpResponse);
|
||||
authResult = attemptAuthentication(httpRequest);
|
||||
} catch (AuthenticationException failed) {
|
||||
// Authentication failed
|
||||
unsuccessfulAuthentication(httpRequest, httpResponse, failed);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Authentication success
|
||||
if (continueChainBeforeSuccessfulAuthentication) {
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
successfulAuthentication(httpRequest, httpResponse, authResult);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
public String getAuthenticationFailureUrl() {
|
||||
return authenticationFailureUrl;
|
||||
}
|
||||
|
||||
public AuthenticationManager getAuthenticationManager() {
|
||||
return authenticationManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the default <code>filterProcessesUrl</code> for the implementation.
|
||||
*
|
||||
* @return the default <code>filterProcessesUrl</code>
|
||||
*/
|
||||
public abstract String getDefaultFilterProcessesUrl();
|
||||
|
||||
/**
|
||||
* Supplies the default target Url that will be used if no saved request is found or the
|
||||
* <tt>alwaysUseDefaultTargetUrl</tt> propert is set to true.
|
||||
* Override this method of you want to provide a customized default Url (for example if you want different Urls
|
||||
* depending on the authorities of the user who has just logged in).
|
||||
*
|
||||
* @return the defaultTargetUrl property
|
||||
*/
|
||||
public String getDefaultTargetUrl() {
|
||||
return defaultTargetUrl;
|
||||
}
|
||||
|
||||
public Properties getExceptionMappings() {
|
||||
return new Properties(exceptionMappings);
|
||||
}
|
||||
|
||||
public String getFilterProcessesUrl() {
|
||||
return filterProcessesUrl;
|
||||
}
|
||||
|
||||
public RememberMeServices getRememberMeServices() {
|
||||
return rememberMeServices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing. We use IoC container lifecycle services instead.
|
||||
*
|
||||
* @param arg0 ignored
|
||||
*
|
||||
* @throws ServletException ignored
|
||||
*/
|
||||
public void init(FilterConfig arg0) throws ServletException {}
|
||||
|
||||
public boolean isAlwaysUseDefaultTargetUrl() {
|
||||
return alwaysUseDefaultTargetUrl;
|
||||
}
|
||||
|
||||
public boolean isContinueChainBeforeSuccessfulAuthentication() {
|
||||
return continueChainBeforeSuccessfulAuthentication;
|
||||
}
|
||||
|
||||
public static String obtainFullRequestUrl(HttpServletRequest request) {
|
||||
SavedRequest savedRequest = (SavedRequest) request.getSession()
|
||||
.getAttribute(AbstractProcessingFilter.ACEGI_SAVED_REQUEST_KEY);
|
||||
|
||||
return (savedRequest == null) ? null : savedRequest.getFullRequestUrl();
|
||||
}
|
||||
|
||||
protected void onPreAuthentication(HttpServletRequest request, HttpServletResponse response)
|
||||
throws AuthenticationException, IOException {}
|
||||
|
||||
protected void onSuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
|
||||
Authentication authResult) throws IOException {}
|
||||
|
||||
protected void onUnsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
|
||||
AuthenticationException failed) throws IOException {}
|
||||
|
||||
/**
|
||||
* <p>Indicates whether this filter should attempt to process a login request for the current invocation.</p>
|
||||
* <p>It strips any parameters from the "path" section of the request URL (such as the jsessionid
|
||||
* parameter in <em>http://host/myapp/index.html;jsessionid=blah</em>) before matching against the
|
||||
* <code>filterProcessesUrl</code> property.</p>
|
||||
* <p>Subclasses may override for special requirements, such as Tapestry integration.</p>
|
||||
*
|
||||
* @param request as received from the filter chain
|
||||
* @param response as received from the filter chain
|
||||
*
|
||||
* @return <code>true</code> if the filter should attempt authentication, <code>false</code> otherwise
|
||||
*/
|
||||
protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
|
||||
String uri = request.getRequestURI();
|
||||
int pathParamIndex = uri.indexOf(';');
|
||||
|
||||
if (pathParamIndex > 0) {
|
||||
// strip everything after the first semi-colon
|
||||
uri = uri.substring(0, pathParamIndex);
|
||||
}
|
||||
|
||||
if ("".equals(request.getContextPath())) {
|
||||
return uri.endsWith(filterProcessesUrl);
|
||||
}
|
||||
|
||||
return uri.endsWith(request.getContextPath() + filterProcessesUrl);
|
||||
}
|
||||
|
||||
protected void sendRedirect(HttpServletRequest request, HttpServletResponse response, String url)
|
||||
throws IOException {
|
||||
String finalUrl;
|
||||
if (!url.startsWith("http://") && !url.startsWith("https://")) {
|
||||
if (useRelativeContext) {
|
||||
finalUrl = url;
|
||||
} else {
|
||||
finalUrl = request.getContextPath() + url;
|
||||
}
|
||||
} else if (useRelativeContext) {
|
||||
// Calculate the relative URL from the fully qualifed URL, minus the protocol and base context.
|
||||
int len = request.getContextPath().length();
|
||||
int index = url.indexOf(request.getContextPath()) + len;
|
||||
finalUrl = url.substring(index);
|
||||
if (finalUrl.length() > 1 && finalUrl.charAt(0) == '/') {
|
||||
finalUrl = finalUrl.substring( 1 );
|
||||
}
|
||||
} else {
|
||||
finalUrl = url;
|
||||
}
|
||||
|
||||
Assert.isTrue(!response.isCommitted(), "Response already committed; the authentication mechanism must be able to modify buffer size");
|
||||
response.setBufferSize(bufferSize);
|
||||
response.sendRedirect(response.encodeRedirectURL(finalUrl));
|
||||
}
|
||||
|
||||
public void setAlwaysUseDefaultTargetUrl(boolean alwaysUseDefaultTargetUrl) {
|
||||
this.alwaysUseDefaultTargetUrl = alwaysUseDefaultTargetUrl;
|
||||
}
|
||||
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher eventPublisher) {
|
||||
this.eventPublisher = eventPublisher;
|
||||
}
|
||||
|
||||
public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) {
|
||||
Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource required");
|
||||
this.authenticationDetailsSource = authenticationDetailsSource;
|
||||
}
|
||||
|
||||
public void setAuthenticationFailureUrl(String authenticationFailureUrl) {
|
||||
this.authenticationFailureUrl = authenticationFailureUrl;
|
||||
}
|
||||
|
||||
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
|
||||
this.authenticationManager = authenticationManager;
|
||||
}
|
||||
|
||||
public void setContinueChainBeforeSuccessfulAuthentication(boolean continueChainBeforeSuccessfulAuthentication) {
|
||||
this.continueChainBeforeSuccessfulAuthentication = continueChainBeforeSuccessfulAuthentication;
|
||||
}
|
||||
|
||||
public void setDefaultTargetUrl(String defaultTargetUrl) {
|
||||
Assert.isTrue(defaultTargetUrl.startsWith("/") | defaultTargetUrl.startsWith("http"),
|
||||
"defaultTarget must start with '/' or with 'http(s)'");
|
||||
this.defaultTargetUrl = defaultTargetUrl;
|
||||
}
|
||||
|
||||
public void setExceptionMappings(Properties exceptionMappings) {
|
||||
this.exceptionMappings = exceptionMappings;
|
||||
}
|
||||
|
||||
public void setFilterProcessesUrl(String filterProcessesUrl) {
|
||||
this.filterProcessesUrl = filterProcessesUrl;
|
||||
}
|
||||
|
||||
public void setMessageSource(MessageSource messageSource) {
|
||||
this.messages = new MessageSourceAccessor(messageSource);
|
||||
}
|
||||
|
||||
public void setRememberMeServices(RememberMeServices rememberMeServices) {
|
||||
this.rememberMeServices = rememberMeServices;
|
||||
}
|
||||
|
||||
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
|
||||
Authentication authResult) throws IOException {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Authentication success: " + authResult.toString());
|
||||
}
|
||||
|
||||
SecurityContextHolder.getContext().setAuthentication(authResult);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Updated SecurityContextHolder to contain the following Authentication: '" + authResult + "'");
|
||||
}
|
||||
|
||||
String targetUrl = determineTargetUrl(request);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Redirecting to target URL from HTTP Session (or default): " + targetUrl);
|
||||
}
|
||||
|
||||
onSuccessfulAuthentication(request, response, authResult);
|
||||
|
||||
rememberMeServices.loginSuccess(request, response, authResult);
|
||||
|
||||
// Fire event
|
||||
if (this.eventPublisher != null) {
|
||||
eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
|
||||
}
|
||||
|
||||
sendRedirect(request, response, targetUrl);
|
||||
}
|
||||
|
||||
protected String determineTargetUrl(HttpServletRequest request) {
|
||||
// Don't attempt to obtain the url from the saved request if alwaysUsedefaultTargetUrl is set
|
||||
String targetUrl = alwaysUseDefaultTargetUrl ? null : obtainFullRequestUrl(request);
|
||||
|
||||
if (targetUrl == null) {
|
||||
targetUrl = getDefaultTargetUrl();
|
||||
}
|
||||
|
||||
return targetUrl;
|
||||
}
|
||||
|
||||
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
|
||||
AuthenticationException failed) throws IOException {
|
||||
SecurityContextHolder.getContext().setAuthentication(null);
|
||||
MessageSourceAware {
|
||||
// ~ Static fields/initializers
|
||||
// =====================================================================================
|
||||
|
||||
public static final String ACEGI_SAVED_REQUEST_KEY = "ACEGI_SAVED_REQUEST_KEY";
|
||||
|
||||
public static final String ACEGI_SECURITY_LAST_EXCEPTION_KEY = "ACEGI_SECURITY_LAST_EXCEPTION";
|
||||
|
||||
// ~ Instance fields
|
||||
// ================================================================================================
|
||||
|
||||
protected ApplicationEventPublisher eventPublisher;
|
||||
|
||||
protected AuthenticationDetailsSource authenticationDetailsSource = new AuthenticationDetailsSourceImpl();
|
||||
|
||||
private AuthenticationManager authenticationManager;
|
||||
|
||||
protected final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor();
|
||||
|
||||
private Properties exceptionMappings = new Properties();
|
||||
|
||||
private RememberMeServices rememberMeServices = new NullRememberMeServices();
|
||||
|
||||
/** Where to redirect the browser to if authentication fails */
|
||||
private String authenticationFailureUrl;
|
||||
|
||||
/**
|
||||
* Where to redirect the browser to if authentication is successful but
|
||||
* ACEGI_SAVED_REQUEST_KEY is <code>null</code>
|
||||
*/
|
||||
private String defaultTargetUrl;
|
||||
|
||||
/**
|
||||
* The URL destination that this filter intercepts and processes (usually
|
||||
* something like <code>/j_acegi_security_check</code>)
|
||||
*/
|
||||
private String filterProcessesUrl = getDefaultFilterProcessesUrl();
|
||||
|
||||
/**
|
||||
* If <code>true</code>, will always redirect to the value of
|
||||
* {@link #getDefaultTargetUrl} upon successful authentication, irrespective
|
||||
* of the page that caused the authentication request (defaults to
|
||||
* <code>false</code>).
|
||||
*/
|
||||
private boolean alwaysUseDefaultTargetUrl = false;
|
||||
|
||||
/**
|
||||
* Indicates if the filter chain should be continued prior to delegation to
|
||||
* {@link #successfulAuthentication(HttpServletRequest, HttpServletResponse,
|
||||
* Authentication)}, which may be useful in certain environment (eg
|
||||
* Tapestry). Defaults to <code>false</code>.
|
||||
*/
|
||||
private boolean continueChainBeforeSuccessfulAuthentication = false;
|
||||
|
||||
/**
|
||||
* Specifies the buffer size to use in the event of a directory. A buffer
|
||||
* size is used to ensure the response is not written back to the client
|
||||
* immediately. This provides a way for the <code>HttpSession</code> to be
|
||||
* updated before the browser redirect will be sent. Defaults to an 8 Kb
|
||||
* buffer.
|
||||
*/
|
||||
private int bufferSize = 8 * 1024;
|
||||
|
||||
/**
|
||||
* If true, causes any redirection URLs to be calculated minus the protocol
|
||||
* and context path (defaults to false).
|
||||
*/
|
||||
private boolean useRelativeContext = false;
|
||||
|
||||
// ~ Methods
|
||||
// ========================================================================================================
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.hasLength(filterProcessesUrl, "filterProcessesUrl must be specified");
|
||||
Assert.hasLength(defaultTargetUrl, "defaultTargetUrl must be specified");
|
||||
Assert.hasLength(authenticationFailureUrl, "authenticationFailureUrl must be specified");
|
||||
Assert.notNull(authenticationManager, "authenticationManager must be specified");
|
||||
Assert.notNull(this.rememberMeServices);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs actual authentication.
|
||||
*
|
||||
* @param request from which to extract parameters and perform the
|
||||
* authentication
|
||||
*
|
||||
* @return the authenticated user
|
||||
*
|
||||
* @throws AuthenticationException if authentication fails
|
||||
*/
|
||||
public abstract Authentication attemptAuthentication(HttpServletRequest request) throws AuthenticationException;
|
||||
|
||||
/**
|
||||
* Does nothing. We use IoC container lifecycle services instead.
|
||||
*/
|
||||
public void destroy() {
|
||||
}
|
||||
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
|
||||
ServletException {
|
||||
if (!(request instanceof HttpServletRequest)) {
|
||||
throw new ServletException("Can only process HttpServletRequest");
|
||||
}
|
||||
|
||||
if (!(response instanceof HttpServletResponse)) {
|
||||
throw new ServletException("Can only process HttpServletResponse");
|
||||
}
|
||||
|
||||
HttpServletRequest httpRequest = (HttpServletRequest) request;
|
||||
HttpServletResponse httpResponse = (HttpServletResponse) response;
|
||||
|
||||
if (requiresAuthentication(httpRequest, httpResponse)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Request is to process authentication");
|
||||
}
|
||||
|
||||
Authentication authResult;
|
||||
|
||||
try {
|
||||
onPreAuthentication(httpRequest, httpResponse);
|
||||
authResult = attemptAuthentication(httpRequest);
|
||||
}
|
||||
catch (AuthenticationException failed) {
|
||||
// Authentication failed
|
||||
unsuccessfulAuthentication(httpRequest, httpResponse, failed);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Authentication success
|
||||
if (continueChainBeforeSuccessfulAuthentication) {
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
successfulAuthentication(httpRequest, httpResponse, authResult);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
public String getAuthenticationFailureUrl() {
|
||||
return authenticationFailureUrl;
|
||||
}
|
||||
|
||||
public AuthenticationManager getAuthenticationManager() {
|
||||
return authenticationManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the default <code>filterProcessesUrl</code> for the
|
||||
* implementation.
|
||||
*
|
||||
* @return the default <code>filterProcessesUrl</code>
|
||||
*/
|
||||
public abstract String getDefaultFilterProcessesUrl();
|
||||
|
||||
/**
|
||||
* Supplies the default target Url that will be used if no saved request is
|
||||
* found or the <tt>alwaysUseDefaultTargetUrl</tt> propert is set to true.
|
||||
* Override this method of you want to provide a customized default Url (for
|
||||
* example if you want different Urls depending on the authorities of the
|
||||
* user who has just logged in).
|
||||
*
|
||||
* @return the defaultTargetUrl property
|
||||
*/
|
||||
public String getDefaultTargetUrl() {
|
||||
return defaultTargetUrl;
|
||||
}
|
||||
|
||||
public Properties getExceptionMappings() {
|
||||
return new Properties(exceptionMappings);
|
||||
}
|
||||
|
||||
public String getFilterProcessesUrl() {
|
||||
return filterProcessesUrl;
|
||||
}
|
||||
|
||||
public RememberMeServices getRememberMeServices() {
|
||||
return rememberMeServices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing. We use IoC container lifecycle services instead.
|
||||
*
|
||||
* @param arg0 ignored
|
||||
*
|
||||
* @throws ServletException ignored
|
||||
*/
|
||||
public void init(FilterConfig arg0) throws ServletException {
|
||||
}
|
||||
|
||||
public boolean isAlwaysUseDefaultTargetUrl() {
|
||||
return alwaysUseDefaultTargetUrl;
|
||||
}
|
||||
|
||||
public boolean isContinueChainBeforeSuccessfulAuthentication() {
|
||||
return continueChainBeforeSuccessfulAuthentication;
|
||||
}
|
||||
|
||||
public static String obtainFullRequestUrl(HttpServletRequest request) {
|
||||
SavedRequest savedRequest = (SavedRequest) request.getSession().getAttribute(
|
||||
AbstractProcessingFilter.ACEGI_SAVED_REQUEST_KEY);
|
||||
|
||||
return (savedRequest == null) ? null : savedRequest.getFullRequestUrl();
|
||||
}
|
||||
|
||||
protected void onPreAuthentication(HttpServletRequest request, HttpServletResponse response)
|
||||
throws AuthenticationException, IOException {
|
||||
}
|
||||
|
||||
protected void onSuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
|
||||
Authentication authResult) throws IOException {
|
||||
}
|
||||
|
||||
protected void onUnsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
|
||||
AuthenticationException failed) throws IOException {
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Indicates whether this filter should attempt to process a login request
|
||||
* for the current invocation.
|
||||
* </p>
|
||||
* <p>
|
||||
* It strips any parameters from the "path" section of the request URL (such
|
||||
* as the jsessionid parameter in
|
||||
* <em>http://host/myapp/index.html;jsessionid=blah</em>) before matching
|
||||
* against the <code>filterProcessesUrl</code> property.
|
||||
* </p>
|
||||
* <p>
|
||||
* Subclasses may override for special requirements, such as Tapestry
|
||||
* integration.
|
||||
* </p>
|
||||
*
|
||||
* @param request as received from the filter chain
|
||||
* @param response as received from the filter chain
|
||||
*
|
||||
* @return <code>true</code> if the filter should attempt authentication,
|
||||
* <code>false</code> otherwise
|
||||
*/
|
||||
protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
|
||||
String uri = request.getRequestURI();
|
||||
int pathParamIndex = uri.indexOf(';');
|
||||
|
||||
if (pathParamIndex > 0) {
|
||||
// strip everything after the first semi-colon
|
||||
uri = uri.substring(0, pathParamIndex);
|
||||
}
|
||||
|
||||
if ("".equals(request.getContextPath())) {
|
||||
return uri.endsWith(filterProcessesUrl);
|
||||
}
|
||||
|
||||
return uri.endsWith(request.getContextPath() + filterProcessesUrl);
|
||||
}
|
||||
|
||||
protected void sendRedirect(HttpServletRequest request, HttpServletResponse response, String url)
|
||||
throws IOException {
|
||||
String finalUrl;
|
||||
if (!url.startsWith("http://") && !url.startsWith("https://")) {
|
||||
if (useRelativeContext) {
|
||||
finalUrl = url;
|
||||
}
|
||||
else {
|
||||
finalUrl = request.getContextPath() + url;
|
||||
}
|
||||
}
|
||||
else if (useRelativeContext) {
|
||||
// Calculate the relative URL from the fully qualifed URL, minus the
|
||||
// protocol and base context.
|
||||
int len = request.getContextPath().length();
|
||||
int index = url.indexOf(request.getContextPath()) + len;
|
||||
finalUrl = url.substring(index);
|
||||
if (finalUrl.length() > 1 && finalUrl.charAt(0) == '/') {
|
||||
finalUrl = finalUrl.substring(1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
finalUrl = url;
|
||||
}
|
||||
|
||||
Assert.isTrue(!response.isCommitted(),
|
||||
"Response already committed; the authentication mechanism must be able to modify buffer size");
|
||||
response.setBufferSize(bufferSize);
|
||||
response.sendRedirect(response.encodeRedirectURL(finalUrl));
|
||||
}
|
||||
|
||||
public void setAlwaysUseDefaultTargetUrl(boolean alwaysUseDefaultTargetUrl) {
|
||||
this.alwaysUseDefaultTargetUrl = alwaysUseDefaultTargetUrl;
|
||||
}
|
||||
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher eventPublisher) {
|
||||
this.eventPublisher = eventPublisher;
|
||||
}
|
||||
|
||||
public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) {
|
||||
Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource required");
|
||||
this.authenticationDetailsSource = authenticationDetailsSource;
|
||||
}
|
||||
|
||||
public void setAuthenticationFailureUrl(String authenticationFailureUrl) {
|
||||
this.authenticationFailureUrl = authenticationFailureUrl;
|
||||
}
|
||||
|
||||
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
|
||||
this.authenticationManager = authenticationManager;
|
||||
}
|
||||
|
||||
public void setContinueChainBeforeSuccessfulAuthentication(boolean continueChainBeforeSuccessfulAuthentication) {
|
||||
this.continueChainBeforeSuccessfulAuthentication = continueChainBeforeSuccessfulAuthentication;
|
||||
}
|
||||
|
||||
public void setDefaultTargetUrl(String defaultTargetUrl) {
|
||||
Assert.isTrue(defaultTargetUrl.startsWith("/") | defaultTargetUrl.startsWith("http"),
|
||||
"defaultTarget must start with '/' or with 'http(s)'");
|
||||
this.defaultTargetUrl = defaultTargetUrl;
|
||||
}
|
||||
|
||||
public void setExceptionMappings(Properties exceptionMappings) {
|
||||
this.exceptionMappings = exceptionMappings;
|
||||
}
|
||||
|
||||
public void setFilterProcessesUrl(String filterProcessesUrl) {
|
||||
this.filterProcessesUrl = filterProcessesUrl;
|
||||
}
|
||||
|
||||
public void setMessageSource(MessageSource messageSource) {
|
||||
this.messages = new MessageSourceAccessor(messageSource);
|
||||
}
|
||||
|
||||
public void setRememberMeServices(RememberMeServices rememberMeServices) {
|
||||
this.rememberMeServices = rememberMeServices;
|
||||
}
|
||||
|
||||
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
|
||||
Authentication authResult) throws IOException {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Authentication success: " + authResult.toString());
|
||||
}
|
||||
|
||||
SecurityContextHolder.getContext().setAuthentication(authResult);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Updated SecurityContextHolder to contain the following Authentication: '" + authResult + "'");
|
||||
}
|
||||
|
||||
String targetUrl = determineTargetUrl(request);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Redirecting to target URL from HTTP Session (or default): " + targetUrl);
|
||||
}
|
||||
|
||||
onSuccessfulAuthentication(request, response, authResult);
|
||||
|
||||
rememberMeServices.loginSuccess(request, response, authResult);
|
||||
|
||||
// Fire event
|
||||
if (this.eventPublisher != null) {
|
||||
eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
|
||||
}
|
||||
|
||||
sendRedirect(request, response, targetUrl);
|
||||
}
|
||||
|
||||
protected String determineTargetUrl(HttpServletRequest request) {
|
||||
// Don't attempt to obtain the url from the saved request if
|
||||
// alwaysUsedefaultTargetUrl is set
|
||||
String targetUrl = alwaysUseDefaultTargetUrl ? null : obtainFullRequestUrl(request);
|
||||
|
||||
if (targetUrl == null) {
|
||||
targetUrl = getDefaultTargetUrl();
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Updated SecurityContextHolder to contain null Authentication");
|
||||
}
|
||||
return targetUrl;
|
||||
}
|
||||
|
||||
String failureUrl = exceptionMappings.getProperty(failed.getClass().getName(), authenticationFailureUrl);
|
||||
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
|
||||
AuthenticationException failed) throws IOException {
|
||||
SecurityContextHolder.getContext().setAuthentication(null);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Authentication request failed: " + failed.toString());
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Updated SecurityContextHolder to contain null Authentication");
|
||||
}
|
||||
|
||||
try {
|
||||
request.getSession().setAttribute(ACEGI_SECURITY_LAST_EXCEPTION_KEY, failed);
|
||||
} catch (Exception ignored) {}
|
||||
String failureUrl = determineFailureUrl(request, failed);
|
||||
|
||||
onUnsuccessfulAuthentication(request, response, failed);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Authentication request failed: " + failed.toString());
|
||||
}
|
||||
|
||||
rememberMeServices.loginFail(request, response);
|
||||
try {
|
||||
request.getSession().setAttribute(ACEGI_SECURITY_LAST_EXCEPTION_KEY, failed);
|
||||
}
|
||||
catch (Exception ignored) {
|
||||
}
|
||||
|
||||
sendRedirect(request, response, failureUrl);
|
||||
onUnsuccessfulAuthentication(request, response, failed);
|
||||
|
||||
rememberMeServices.loginFail(request, response);
|
||||
|
||||
sendRedirect(request, response, failureUrl);
|
||||
}
|
||||
|
||||
protected String determineFailureUrl(HttpServletRequest request, AuthenticationException failed) {
|
||||
return exceptionMappings.getProperty(failed.getClass().getName(), authenticationFailureUrl);
|
||||
}
|
||||
|
||||
public AuthenticationDetailsSource getAuthenticationDetailsSource() {
|
||||
// Required due to SEC-310
|
||||
return authenticationDetailsSource;
|
||||
}
|
||||
// Required due to SEC-310
|
||||
return authenticationDetailsSource;
|
||||
}
|
||||
|
||||
public void setBufferSize(int bufferSize) {
|
||||
this.bufferSize = bufferSize;
|
||||
@@ -480,4 +550,5 @@ public abstract class AbstractProcessingFilter implements Filter, InitializingBe
|
||||
public void setUseRelativeContext(boolean useRelativeContext) {
|
||||
this.useRelativeContext = useRelativeContext;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,10 +33,12 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
@@ -47,185 +49,210 @@ import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
|
||||
/**
|
||||
* Handles any <code>AccessDeniedException</code> and <code>AuthenticationException</code> thrown within the filter
|
||||
* chain.<p>This filter is necessary because it provides the bridge between Java exceptions and HTTP responses. It
|
||||
* is solely concerned with maintaining the user interface. This filter does not do any actual security enforcement.</p>
|
||||
* <p>If an {@link AuthenticationException} is detected, the filter will launch the
|
||||
* <code>authenticationEntryPoint</code>. This allows common handling of authentication failures originating from any
|
||||
* subclass of {@link org.acegisecurity.intercept.AbstractSecurityInterceptor}.</p>
|
||||
* <p>If an {@link AccessDeniedException} is detected, the filter will determine whether or not the user is an
|
||||
* anonymous user. If they are an anonymous user, the <code>authenticationEntryPoint</code> will be launched. If they
|
||||
* are not an anonymous user, the filter will delegate to the {@link org.acegisecurity.ui.AccessDeniedHandler}. By
|
||||
* default the filter will use {@link org.acegisecurity.ui.AccessDeniedHandlerImpl}.</p>
|
||||
* <p>To use this filter, it is necessary to specify the following properties:</p>
|
||||
* <ul>
|
||||
* <li><code>authenticationEntryPoint</code> indicates the handler that should commence the authentication
|
||||
* process if an <code>AuthenticationException</code> is detected. Note that this may also switch the current
|
||||
* protocol from http to https for an SSL login.</li>
|
||||
* <li><code>portResolver</code> is used to determine the "real" port that a request was received on.</li>
|
||||
* </ul>
|
||||
* <P><B>Do not use this class directly.</B> Instead configure <code>web.xml</code> to use the {@link
|
||||
* org.acegisecurity.util.FilterToBeanProxy}.</p>
|
||||
* Handles any <code>AccessDeniedException</code> and <code>AuthenticationException</code> thrown within the
|
||||
* filter chain.
|
||||
* <p>
|
||||
* This filter is necessary because it provides the bridge between Java exceptions and HTTP responses.
|
||||
* It is solely concerned with maintaining the user interface. This filter does not do any actual security enforcement.
|
||||
* </p>
|
||||
* <p>
|
||||
* If an {@link AuthenticationException} is detected, the filter will launch the <code>authenticationEntryPoint</code>.
|
||||
* This allows common handling of authentication failures originating from any subclass of
|
||||
* {@link org.acegisecurity.intercept.AbstractSecurityInterceptor}.
|
||||
* </p>
|
||||
* <p>
|
||||
* If an {@link AccessDeniedException} is detected, the filter will determine whether or not the user is an anonymous
|
||||
* user. If they are an anonymous user, the <code>authenticationEntryPoint</code> will be launched. If they are not
|
||||
* an anonymous user, the filter will delegate to the {@link org.acegisecurity.ui.AccessDeniedHandler}.
|
||||
* By default the filter will use {@link org.acegisecurity.ui.AccessDeniedHandlerImpl}.
|
||||
* </p>
|
||||
* <p>
|
||||
* To use this filter, it is necessary to specify the following properties:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li><code>authenticationEntryPoint</code> indicates the handler that
|
||||
* should commence the authentication process if an
|
||||
* <code>AuthenticationException</code> is detected. Note that this may also
|
||||
* switch the current protocol from http to https for an SSL login.</li>
|
||||
* <li><code>portResolver</code> is used to determine the "real" port that a
|
||||
* request was received on.</li>
|
||||
* </ul>
|
||||
* <P>
|
||||
* <B>Do not use this class directly.</B> Instead configure
|
||||
* <code>web.xml</code> to use the {@link
|
||||
* org.acegisecurity.util.FilterToBeanProxy}.
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author colin sampaleanu
|
||||
* @version $Id$
|
||||
*/
|
||||
public class ExceptionTranslationFilter implements Filter, InitializingBean {
|
||||
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
|
||||
private static final Log logger = LogFactory.getLog(ExceptionTranslationFilter.class);
|
||||
private static final Log logger = LogFactory.getLog(ExceptionTranslationFilter.class);
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private AccessDeniedHandler accessDeniedHandler = new AccessDeniedHandlerImpl();
|
||||
private AuthenticationEntryPoint authenticationEntryPoint;
|
||||
private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
|
||||
private PortResolver portResolver = new PortResolverImpl();
|
||||
private boolean createSessionAllowed = true;
|
||||
private AuthenticationEntryPoint authenticationEntryPoint;
|
||||
private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
|
||||
private PortResolver portResolver = new PortResolverImpl();
|
||||
private boolean createSessionAllowed = true;
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(authenticationEntryPoint, "authenticationEntryPoint must be specified");
|
||||
Assert.notNull(portResolver, "portResolver must be specified");
|
||||
Assert.notNull(authenticationTrustResolver, "authenticationTrustResolver must be specified");
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(authenticationEntryPoint, "authenticationEntryPoint must be specified");
|
||||
Assert.notNull(portResolver, "portResolver must be specified");
|
||||
Assert.notNull(authenticationTrustResolver, "authenticationTrustResolver must be specified");
|
||||
}
|
||||
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
|
||||
ServletException {
|
||||
if (!(request instanceof HttpServletRequest)) {
|
||||
throw new ServletException("HttpServletRequest required");
|
||||
}
|
||||
|
||||
if (!(response instanceof HttpServletResponse)) {
|
||||
throw new ServletException("HttpServletResponse required");
|
||||
}
|
||||
|
||||
try {
|
||||
chain.doFilter(request, response);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Chain processed normally");
|
||||
}
|
||||
}
|
||||
catch (AuthenticationException ex) {
|
||||
handleException(request, response, chain, ex);
|
||||
}
|
||||
catch (AccessDeniedException ex) {
|
||||
handleException(request, response, chain, ex);
|
||||
}
|
||||
catch (ServletException ex) {
|
||||
if (ex.getRootCause() instanceof AuthenticationException
|
||||
|| ex.getRootCause() instanceof AccessDeniedException) {
|
||||
handleException(request, response, chain, (AcegiSecurityException) ex.getRootCause());
|
||||
}
|
||||
else {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
public AuthenticationEntryPoint getAuthenticationEntryPoint() {
|
||||
return authenticationEntryPoint;
|
||||
}
|
||||
|
||||
public AuthenticationTrustResolver getAuthenticationTrustResolver() {
|
||||
return authenticationTrustResolver;
|
||||
}
|
||||
|
||||
public PortResolver getPortResolver() {
|
||||
return portResolver;
|
||||
}
|
||||
|
||||
private void handleException(ServletRequest request, ServletResponse response, FilterChain chain,
|
||||
AcegiSecurityException exception) throws IOException, ServletException {
|
||||
if (exception instanceof AuthenticationException) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Authentication exception occurred; redirecting to authentication entry point", exception);
|
||||
}
|
||||
|
||||
sendStartAuthentication(request, response, chain, (AuthenticationException) exception);
|
||||
}
|
||||
else if (exception instanceof AccessDeniedException) {
|
||||
if (authenticationTrustResolver.isAnonymous(SecurityContextHolder.getContext().getAuthentication())) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Access is denied (user is anonymous); redirecting to authentication entry point",
|
||||
exception);
|
||||
}
|
||||
|
||||
sendStartAuthentication(request, response, chain, new InsufficientAuthenticationException(
|
||||
"Full authentication is required to access this resource"));
|
||||
}
|
||||
else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Access is denied (user is not anonymous); delegating to AccessDeniedHandler",
|
||||
exception);
|
||||
}
|
||||
|
||||
accessDeniedHandler.handle(request, response, (AccessDeniedException) exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If <code>true</code>, indicates that <code>SecurityEnforcementFilter</code> is permitted to store the target
|
||||
* URL and exception information in the <code>HttpSession</code> (the default).
|
||||
* In situations where you do not wish to unnecessarily create <code>HttpSession</code>s - because the user agent
|
||||
* will know the failed URL, such as with BASIC or Digest authentication - you may wish to
|
||||
* set this property to <code>false</code>. Remember to also set the
|
||||
* {@link org.acegisecurity.context.HttpSessionContextIntegrationFilter#allowSessionCreation}
|
||||
* to <code>false</code> if you set this property to <code>false</code>.
|
||||
*
|
||||
* @return <code>true</code> if the <code>HttpSession</code> will be
|
||||
* used to store information about the failed request, <code>false</code>
|
||||
* if the <code>HttpSession</code> will not be used
|
||||
*/
|
||||
public boolean isCreateSessionAllowed() {
|
||||
return createSessionAllowed;
|
||||
}
|
||||
|
||||
protected void sendStartAuthentication(ServletRequest request, ServletResponse response, FilterChain chain,
|
||||
AuthenticationException reason) throws ServletException, IOException {
|
||||
HttpServletRequest httpRequest = (HttpServletRequest) request;
|
||||
|
||||
SavedRequest savedRequest = new SavedRequest(httpRequest, portResolver);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Authentication entry point being called; SavedRequest added to Session: " + savedRequest);
|
||||
}
|
||||
|
||||
if (createSessionAllowed) {
|
||||
// Store the HTTP request itself. Used by AbstractProcessingFilter
|
||||
// for redirection after successful authentication (SEC-29)
|
||||
httpRequest.getSession().setAttribute(AbstractProcessingFilter.ACEGI_SAVED_REQUEST_KEY, savedRequest);
|
||||
}
|
||||
|
||||
// SEC-112: Clear the SecurityContextHolder's Authentication, as the
|
||||
// existing Authentication is no longer considered valid
|
||||
SecurityContextHolder.getContext().setAuthentication(null);
|
||||
|
||||
authenticationEntryPoint.commence(httpRequest, (HttpServletResponse) response, reason);
|
||||
}
|
||||
|
||||
public void setAccessDeniedHandler(AccessDeniedHandler accessDeniedHandler) {
|
||||
Assert.notNull(accessDeniedHandler, "AccessDeniedHandler required");
|
||||
this.accessDeniedHandler = accessDeniedHandler;
|
||||
}
|
||||
|
||||
public void setAuthenticationEntryPoint(AuthenticationEntryPoint authenticationEntryPoint) {
|
||||
this.authenticationEntryPoint = authenticationEntryPoint;
|
||||
}
|
||||
|
||||
public void setAuthenticationTrustResolver(AuthenticationTrustResolver authenticationTrustResolver) {
|
||||
this.authenticationTrustResolver = authenticationTrustResolver;
|
||||
}
|
||||
|
||||
public void setCreateSessionAllowed(boolean createSessionAllowed) {
|
||||
this.createSessionAllowed = createSessionAllowed;
|
||||
}
|
||||
|
||||
public void setPortResolver(PortResolver portResolver) {
|
||||
this.portResolver = portResolver;
|
||||
}
|
||||
|
||||
public void init(FilterConfig filterConfig) throws ServletException {
|
||||
}
|
||||
|
||||
public void destroy() {}
|
||||
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
||||
throws IOException, ServletException {
|
||||
if (!(request instanceof HttpServletRequest)) {
|
||||
throw new ServletException("HttpServletRequest required");
|
||||
}
|
||||
|
||||
if (!(response instanceof HttpServletResponse)) {
|
||||
throw new ServletException("HttpServletResponse required");
|
||||
}
|
||||
|
||||
try {
|
||||
chain.doFilter(request, response);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Chain processed normally");
|
||||
}
|
||||
} catch (AuthenticationException ex) {
|
||||
handleException(request, response, chain, ex);
|
||||
} catch (AccessDeniedException ex) {
|
||||
handleException(request, response, chain, ex);
|
||||
} catch (ServletException ex) {
|
||||
if (ex.getRootCause() instanceof AuthenticationException
|
||||
|| ex.getRootCause() instanceof AccessDeniedException) {
|
||||
handleException(request, response, chain, (AcegiSecurityException) ex.getRootCause());
|
||||
} else {
|
||||
throw ex;
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
public AuthenticationEntryPoint getAuthenticationEntryPoint() {
|
||||
return authenticationEntryPoint;
|
||||
}
|
||||
|
||||
public AuthenticationTrustResolver getAuthenticationTrustResolver() {
|
||||
return authenticationTrustResolver;
|
||||
}
|
||||
|
||||
public PortResolver getPortResolver() {
|
||||
return portResolver;
|
||||
}
|
||||
|
||||
private void handleException(ServletRequest request, ServletResponse response, FilterChain chain,
|
||||
AcegiSecurityException exception) throws IOException, ServletException {
|
||||
if (exception instanceof AuthenticationException) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Authentication exception occurred; redirecting to authentication entry point", exception);
|
||||
}
|
||||
|
||||
sendStartAuthentication(request, response, chain, (AuthenticationException) exception);
|
||||
} else if (exception instanceof AccessDeniedException) {
|
||||
if (authenticationTrustResolver.isAnonymous(SecurityContextHolder.getContext().getAuthentication())) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Access is denied (user is anonymous); redirecting to authentication entry point",
|
||||
exception);
|
||||
}
|
||||
|
||||
sendStartAuthentication(request, response, chain,
|
||||
new InsufficientAuthenticationException("Full authentication is required to access this resource"));
|
||||
} else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Access is denied (user is not anonymous); delegating to AccessDeniedHandler",
|
||||
exception);
|
||||
}
|
||||
|
||||
accessDeniedHandler.handle(request, response, (AccessDeniedException) exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void init(FilterConfig filterConfig) throws ServletException {}
|
||||
|
||||
/**
|
||||
* If <code>true</code>, indicates that <code>SecurityEnforcementFilter</code> is permitted to store the
|
||||
* target URL and exception information in the <code>HttpSession</code> (the default). In situations where you do
|
||||
* not wish to unnecessarily create <code>HttpSession</code>s - because the user agent will know the failed URL,
|
||||
* such as with BASIC or Digest authentication - you may wish to set this property to <code>false</code>. Remember
|
||||
* to also set the {@link org.acegisecurity.context.HttpSessionContextIntegrationFilter#allowSessionCreation} to
|
||||
* <code>false</code> if you set this property to <code>false</code>.
|
||||
*
|
||||
* @return <code>true</code> if the <code>HttpSession</code> will be used to store information about the failed
|
||||
* request, <code>false</code> if the <code>HttpSession</code> will not be used
|
||||
*/
|
||||
public boolean isCreateSessionAllowed() {
|
||||
return createSessionAllowed;
|
||||
}
|
||||
|
||||
protected void sendStartAuthentication(ServletRequest request, ServletResponse response, FilterChain chain,
|
||||
AuthenticationException reason) throws ServletException, IOException {
|
||||
HttpServletRequest httpRequest = (HttpServletRequest) request;
|
||||
|
||||
SavedRequest savedRequest = new SavedRequest(httpRequest, portResolver);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Authentication entry point being called; SavedRequest added to Session: " + savedRequest);
|
||||
}
|
||||
|
||||
if (createSessionAllowed) {
|
||||
// Store the HTTP request itself. Used by AbstractProcessingFilter
|
||||
// for redirection after successful authentication (SEC-29)
|
||||
httpRequest.getSession().setAttribute(AbstractProcessingFilter.ACEGI_SAVED_REQUEST_KEY, savedRequest);
|
||||
}
|
||||
|
||||
// SEC-112: Clear the SecurityContextHolder's Authentication, as the
|
||||
// existing Authentication is no longer considered valid
|
||||
SecurityContextHolder.getContext().setAuthentication(null);
|
||||
|
||||
authenticationEntryPoint.commence(httpRequest, (HttpServletResponse) response, reason);
|
||||
}
|
||||
|
||||
public void setAccessDeniedHandler(AccessDeniedHandler accessDeniedHandler) {
|
||||
Assert.notNull(accessDeniedHandler, "AccessDeniedHandler required");
|
||||
this.accessDeniedHandler = accessDeniedHandler;
|
||||
}
|
||||
|
||||
public void setAuthenticationEntryPoint(AuthenticationEntryPoint authenticationEntryPoint) {
|
||||
this.authenticationEntryPoint = authenticationEntryPoint;
|
||||
}
|
||||
|
||||
public void setAuthenticationTrustResolver(AuthenticationTrustResolver authenticationTrustResolver) {
|
||||
this.authenticationTrustResolver = authenticationTrustResolver;
|
||||
}
|
||||
|
||||
public void setCreateSessionAllowed(boolean createSessionAllowed) {
|
||||
this.createSessionAllowed = createSessionAllowed;
|
||||
}
|
||||
|
||||
public void setPortResolver(PortResolver portResolver) {
|
||||
this.portResolver = portResolver;
|
||||
}
|
||||
public void destroy() {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.acegisecurity.AuthenticationException;
|
||||
import org.acegisecurity.AuthenticationManager;
|
||||
import org.acegisecurity.context.SecurityContextHolder;
|
||||
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
|
||||
import org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken;
|
||||
import org.acegisecurity.ui.AuthenticationDetailsSource;
|
||||
import org.acegisecurity.ui.AuthenticationDetailsSourceImpl;
|
||||
import org.acegisecurity.ui.AuthenticationEntryPoint;
|
||||
@@ -39,7 +40,6 @@ import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ import org.springframework.util.Assert;
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class BasicProcessingFilter implements Filter, InitializingBean, Ordered {
|
||||
public class BasicProcessingFilter implements Filter, InitializingBean {
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
|
||||
private static final Log logger = LogFactory.getLog(BasicProcessingFilter.class);
|
||||
@@ -85,7 +85,6 @@ public class BasicProcessingFilter implements Filter, InitializingBean, Ordered
|
||||
private AuthenticationManager authenticationManager;
|
||||
private RememberMeServices rememberMeServices;
|
||||
private boolean ignoreFailure = false;
|
||||
private int order;
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
@@ -190,6 +189,17 @@ public class BasicProcessingFilter implements Filter, InitializingBean, Ordered
|
||||
if (existingAuth instanceof UsernamePasswordAuthenticationToken && !existingAuth.getName().equals(username)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle unusual condition where an AnonymousAuthenticationToken is already present
|
||||
// This shouldn't happen very often, as BasicProcessingFitler is meant to be earlier in the filter
|
||||
// chain than AnonymousProcessingFilter. Nevertheless, presence of both an AnonymousAuthenticationToken
|
||||
// together with a BASIC authentication request header should indicate reauthentication using the
|
||||
// BASIC protocol is desirable. This behaviour is also consistent with that provided by form and digest,
|
||||
// both of which force re-authentication if the respective header is detected (and in doing so replace
|
||||
// any existing AnonymousAuthenticationToken). See SEC-610.
|
||||
if (existingAuth instanceof AnonymousAuthenticationToken) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -229,11 +239,4 @@ public class BasicProcessingFilter implements Filter, InitializingBean, Ordered
|
||||
this.rememberMeServices = rememberMeServices;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-22
@@ -24,11 +24,7 @@ import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.acegisecurity.AuthenticationException;
|
||||
import org.acegisecurity.ui.AuthenticationEntryPoint;
|
||||
import org.acegisecurity.util.OrderedUtils;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
@@ -42,29 +38,15 @@ import org.springframework.util.Assert;
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class BasicProcessingFilterEntryPoint implements AuthenticationEntryPoint, InitializingBean, Ordered, ApplicationContextAware {
|
||||
public class BasicProcessingFilterEntryPoint implements AuthenticationEntryPoint, InitializingBean {
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private static final int DEFAULT_ORDER = Integer.MAX_VALUE;
|
||||
private String realmName;
|
||||
private int order = DEFAULT_ORDER;
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.hasText(realmName, "realmName must be specified");
|
||||
if (order == DEFAULT_ORDER) {
|
||||
OrderedUtils.copyOrderFromOtherClass(BasicProcessingFilter.class, applicationContext, this, true);
|
||||
}
|
||||
}
|
||||
|
||||
public void commence(ServletRequest request, ServletResponse response, AuthenticationException authException)
|
||||
@@ -82,7 +64,4 @@ public class BasicProcessingFilterEntryPoint implements AuthenticationEntryPoint
|
||||
this.realmName = realmName;
|
||||
}
|
||||
|
||||
public void setApplicationContext(ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ import javax.servlet.http.HttpServletResponse;
|
||||
import org.acegisecurity.AuthenticationException;
|
||||
import org.acegisecurity.ui.AuthenticationEntryPoint;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
@@ -42,23 +41,14 @@ import org.springframework.util.Assert;
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class CasProcessingFilterEntryPoint implements AuthenticationEntryPoint, InitializingBean, Ordered{
|
||||
public class CasProcessingFilterEntryPoint implements AuthenticationEntryPoint, InitializingBean {
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private ServiceProperties serviceProperties;
|
||||
private String loginUrl;
|
||||
private int order = Integer.MAX_VALUE; // ~ default
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.hasLength(this.loginUrl, "loginUrl must be specified");
|
||||
Assert.notNull(this.serviceProperties, "serviceProperties must be specified");
|
||||
|
||||
@@ -68,20 +68,20 @@ import javax.servlet.http.HttpServletResponse;
|
||||
* <code>SecurityContextHolder</code>.<p>For a detailed background on what this filter is designed to process,
|
||||
* refer to <a href="http://www.ietf.org/rfc/rfc2617.txt">RFC 2617</a> (which superseded RFC 2069, although this
|
||||
* filter support clients that implement either RFC 2617 or RFC 2069).</p>
|
||||
* <p>This filter can be used to provide Digest authentication services to both remoting protocol clients (such as
|
||||
* <p>This filter can be used to provide Digest authentication services to both remoting protocol clients (such as
|
||||
* Hessian and SOAP) as well as standard user agents (such as Internet Explorer and FireFox).</p>
|
||||
* <p>This Digest implementation has been designed to avoid needing to store session state between invocations.
|
||||
* <p>This Digest implementation has been designed to avoid needing to store session state between invocations.
|
||||
* All session management information is stored in the "nonce" that is sent to the client by the {@link
|
||||
* DigestProcessingFilterEntryPoint}.</p>
|
||||
* <P>If authentication is successful, the resulting {@link org.acegisecurity.Authentication Authentication}
|
||||
* <P>If authentication is successful, the resulting {@link org.acegisecurity.Authentication Authentication}
|
||||
* object will be placed into the <code>SecurityContextHolder</code>.</p>
|
||||
* <p>If authentication fails, an {@link org.acegisecurity.ui.AuthenticationEntryPoint AuthenticationEntryPoint}
|
||||
* <p>If authentication fails, an {@link org.acegisecurity.ui.AuthenticationEntryPoint AuthenticationEntryPoint}
|
||||
* implementation is called. This must always be {@link DigestProcessingFilterEntryPoint}, which will prompt the user
|
||||
* to authenticate again via Digest authentication.</p>
|
||||
* <p>Note there are limitations to Digest authentication, although it is a more comprehensive and secure solution
|
||||
* <p>Note there are limitations to Digest authentication, although it is a more comprehensive and secure solution
|
||||
* than Basic authentication. Please see RFC 2617 section 4 for a full discussion on the advantages of Digest
|
||||
* authentication over Basic authentication, including commentary on the limitations that it still imposes.</p>
|
||||
* <p><b>Do not use this class directly.</b> Instead configure <code>web.xml</code> to use the {@link
|
||||
* <p><b>Do not use this class directly.</b> Instead configure <code>web.xml</code> to use the {@link
|
||||
* org.acegisecurity.util.FilterToBeanProxy}.</p>
|
||||
*/
|
||||
public class DigestProcessingFilter implements Filter, InitializingBean, MessageSourceAware {
|
||||
@@ -105,10 +105,11 @@ public class DigestProcessingFilter implements Filter, InitializingBean, Message
|
||||
Assert.notNull(authenticationEntryPoint, "A DigestProcessingFilterEntryPoint is required");
|
||||
}
|
||||
|
||||
public void destroy() {}
|
||||
public void destroy() {
|
||||
}
|
||||
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
||||
throws IOException, ServletException {
|
||||
throws IOException, ServletException {
|
||||
if (!(request instanceof HttpServletRequest)) {
|
||||
throw new ServletException("Can only process HttpServletRequest");
|
||||
}
|
||||
@@ -128,7 +129,7 @@ public class DigestProcessingFilter implements Filter, InitializingBean, Message
|
||||
if ((header != null) && header.startsWith("Digest ")) {
|
||||
String section212response = header.substring(7);
|
||||
|
||||
String[] headerEntries = StringUtils.commaDelimitedListToStringArray(section212response);
|
||||
String[] headerEntries = StringSplitUtils.splitIgnoringQuotes(section212response, ',');
|
||||
Map headerMap = StringSplitUtils.splitEachArrayElementAndCreateMap(headerEntries, "=", "\"");
|
||||
|
||||
String username = (String) headerMap.get("username");
|
||||
@@ -144,12 +145,12 @@ public class DigestProcessingFilter implements Filter, InitializingBean, Message
|
||||
if ((username == null) || (realm == null) || (nonce == null) || (uri == null) || (response == null)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("extracted username: '" + username + "'; realm: '" + username + "'; nonce: '"
|
||||
+ username + "'; uri: '" + username + "'; response: '" + username + "'");
|
||||
+ username + "'; uri: '" + username + "'; response: '" + username + "'");
|
||||
}
|
||||
|
||||
fail(request, response,
|
||||
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.missingMandatory",
|
||||
new Object[] {section212response}, "Missing mandatory digest value; received header {0}")));
|
||||
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.missingMandatory",
|
||||
new Object[]{section212response}, "Missing mandatory digest value; received header {0}")));
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -162,8 +163,8 @@ public class DigestProcessingFilter implements Filter, InitializingBean, Message
|
||||
}
|
||||
|
||||
fail(request, response,
|
||||
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.missingAuth",
|
||||
new Object[] {section212response}, "Missing mandatory digest value; received header {0}")));
|
||||
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.missingAuth",
|
||||
new Object[]{section212response}, "Missing mandatory digest value; received header {0}")));
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -172,9 +173,9 @@ public class DigestProcessingFilter implements Filter, InitializingBean, Message
|
||||
// Check realm name equals what we expected
|
||||
if (!this.getAuthenticationEntryPoint().getRealmName().equals(realm)) {
|
||||
fail(request, response,
|
||||
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.incorrectRealm",
|
||||
new Object[] {realm, this.getAuthenticationEntryPoint().getRealmName()},
|
||||
"Response realm name '{0}' does not match system realm name of '{1}'")));
|
||||
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.incorrectRealm",
|
||||
new Object[]{realm, this.getAuthenticationEntryPoint().getRealmName()},
|
||||
"Response realm name '{0}' does not match system realm name of '{1}'")));
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -182,22 +183,22 @@ public class DigestProcessingFilter implements Filter, InitializingBean, Message
|
||||
// Check nonce was a Base64 encoded (as sent by DigestProcessingFilterEntryPoint)
|
||||
if (!Base64.isArrayByteBase64(nonce.getBytes())) {
|
||||
fail(request, response,
|
||||
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.nonceEncoding",
|
||||
new Object[] {nonce}, "Nonce is not encoded in Base64; received nonce {0}")));
|
||||
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.nonceEncoding",
|
||||
new Object[]{nonce}, "Nonce is not encoded in Base64; received nonce {0}")));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Decode nonce from Base64
|
||||
// format of nonce is:
|
||||
// format of nonce is:
|
||||
// base64(expirationTime + ":" + md5Hex(expirationTime + ":" + key))
|
||||
String nonceAsPlainText = new String(Base64.decodeBase64(nonce.getBytes()));
|
||||
String[] nonceTokens = StringUtils.delimitedListToStringArray(nonceAsPlainText, ":");
|
||||
|
||||
if (nonceTokens.length != 2) {
|
||||
fail(request, response,
|
||||
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.nonceNotTwoTokens",
|
||||
new Object[] {nonceAsPlainText}, "Nonce should have yielded two tokens but was {0}")));
|
||||
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.nonceNotTwoTokens",
|
||||
new Object[]{nonceAsPlainText}, "Nonce should have yielded two tokens but was {0}")));
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -209,9 +210,9 @@ public class DigestProcessingFilter implements Filter, InitializingBean, Message
|
||||
nonceExpiryTime = new Long(nonceTokens[0]).longValue();
|
||||
} catch (NumberFormatException nfe) {
|
||||
fail(request, response,
|
||||
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.nonceNotNumeric",
|
||||
new Object[] {nonceAsPlainText},
|
||||
"Nonce token should have yielded a numeric first token, but was {0}")));
|
||||
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.nonceNotNumeric",
|
||||
new Object[]{nonceAsPlainText},
|
||||
"Nonce token should have yielded a numeric first token, but was {0}")));
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -222,8 +223,8 @@ public class DigestProcessingFilter implements Filter, InitializingBean, Message
|
||||
|
||||
if (!expectedNonceSignature.equals(nonceTokens[1])) {
|
||||
fail(request, response,
|
||||
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.nonceCompromised",
|
||||
new Object[] {nonceAsPlainText}, "Nonce token compromised {0}")));
|
||||
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.nonceCompromised",
|
||||
new Object[]{nonceAsPlainText}, "Nonce token compromised {0}")));
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -241,15 +242,15 @@ public class DigestProcessingFilter implements Filter, InitializingBean, Message
|
||||
user = userDetailsService.loadUserByUsername(username);
|
||||
} catch (UsernameNotFoundException notFound) {
|
||||
fail(request, response,
|
||||
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.usernameNotFound",
|
||||
new Object[] {username}, "Username {0} not found")));
|
||||
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.usernameNotFound",
|
||||
new Object[]{username}, "Username {0} not found")));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (user == null) {
|
||||
throw new AuthenticationServiceException(
|
||||
"AuthenticationDao returned null, which is an interface contract violation");
|
||||
"AuthenticationDao returned null, which is an interface contract violation");
|
||||
}
|
||||
|
||||
userCache.putUserInCache(user);
|
||||
@@ -266,7 +267,7 @@ public class DigestProcessingFilter implements Filter, InitializingBean, Message
|
||||
if (!serverDigestMd5.equals(responseDigest) && !loadedFromDao) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(
|
||||
"Digest comparison failure; trying to refresh user from DAO in case password had changed");
|
||||
"Digest comparison failure; trying to refresh user from DAO in case password had changed");
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -274,8 +275,8 @@ public class DigestProcessingFilter implements Filter, InitializingBean, Message
|
||||
} catch (UsernameNotFoundException notFound) {
|
||||
// Would very rarely happen, as user existed earlier
|
||||
fail(request, response,
|
||||
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.usernameNotFound",
|
||||
new Object[] {username}, "Username {0} not found")));
|
||||
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.usernameNotFound",
|
||||
new Object[]{username}, "Username {0} not found")));
|
||||
}
|
||||
|
||||
userCache.putUserInCache(user);
|
||||
@@ -289,12 +290,12 @@ public class DigestProcessingFilter implements Filter, InitializingBean, Message
|
||||
if (!serverDigestMd5.equals(responseDigest)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Expected response: '" + serverDigestMd5 + "' but received: '" + responseDigest
|
||||
+ "'; is AuthenticationDao returning clear text passwords?");
|
||||
+ "'; is AuthenticationDao returning clear text passwords?");
|
||||
}
|
||||
|
||||
fail(request, response,
|
||||
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.incorrectResponse",
|
||||
"Incorrect response")));
|
||||
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.incorrectResponse",
|
||||
"Incorrect response")));
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -305,15 +306,15 @@ public class DigestProcessingFilter implements Filter, InitializingBean, Message
|
||||
// but the request was otherwise appearing to be valid
|
||||
if (nonceExpiryTime < System.currentTimeMillis()) {
|
||||
fail(request, response,
|
||||
new NonceExpiredException(messages.getMessage("DigestProcessingFilter.nonceExpired",
|
||||
"Nonce has expired/timed out")));
|
||||
new NonceExpiredException(messages.getMessage("DigestProcessingFilter.nonceExpired",
|
||||
"Nonce has expired/timed out")));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Authentication success for user: '" + username + "' with response: '" + responseDigest
|
||||
+ "'");
|
||||
+ "'");
|
||||
}
|
||||
|
||||
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(user,
|
||||
@@ -335,7 +336,7 @@ public class DigestProcessingFilter implements Filter, InitializingBean, Message
|
||||
}
|
||||
|
||||
private void fail(ServletRequest request, ServletResponse response, AuthenticationException failed)
|
||||
throws IOException, ServletException {
|
||||
throws IOException, ServletException {
|
||||
SecurityContextHolder.getContext().setAuthentication(null);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
@@ -351,24 +352,22 @@ public class DigestProcessingFilter implements Filter, InitializingBean, Message
|
||||
* coding of user agents.
|
||||
*
|
||||
* @param passwordAlreadyEncoded true if the password argument is already encoded in the correct format. False if
|
||||
* it is plain text.
|
||||
* @param username the user's login name.
|
||||
* @param realm the name of the realm.
|
||||
* @param password the user's password in plaintext or ready-encoded.
|
||||
* @param httpMethod the HTTP request method (GET, POST etc.)
|
||||
* @param uri the request URI.
|
||||
* @param qop the qop directive, or null if not set.
|
||||
* @param nonce the nonce supplied by the server
|
||||
* @param nc the "nonce-count" as defined in RFC 2617.
|
||||
* @param cnonce opaque string supplied by the client when qop is set.
|
||||
*
|
||||
* it is plain text.
|
||||
* @param username the user's login name.
|
||||
* @param realm the name of the realm.
|
||||
* @param password the user's password in plaintext or ready-encoded.
|
||||
* @param httpMethod the HTTP request method (GET, POST etc.)
|
||||
* @param uri the request URI.
|
||||
* @param qop the qop directive, or null if not set.
|
||||
* @param nonce the nonce supplied by the server
|
||||
* @param nc the "nonce-count" as defined in RFC 2617.
|
||||
* @param cnonce opaque string supplied by the client when qop is set.
|
||||
* @return the MD5 of the digest authentication response, encoded in hex
|
||||
*
|
||||
* @throws IllegalArgumentException if the supplied qop value is unsupported.
|
||||
*/
|
||||
public static String generateDigest(boolean passwordAlreadyEncoded, String username, String realm, String password,
|
||||
String httpMethod, String uri, String qop, String nonce, String nc, String cnonce)
|
||||
throws IllegalArgumentException {
|
||||
String httpMethod, String uri, String qop, String nonce, String nc, String cnonce)
|
||||
throws IllegalArgumentException {
|
||||
String a1Md5 = null;
|
||||
String a2 = httpMethod + ":" + uri;
|
||||
String a2Md5 = new String(DigestUtils.md5Hex(a2));
|
||||
@@ -408,7 +407,8 @@ public class DigestProcessingFilter implements Filter, InitializingBean, Message
|
||||
return userDetailsService;
|
||||
}
|
||||
|
||||
public void init(FilterConfig ignored) throws ServletException {}
|
||||
public void init(FilterConfig ignored) throws ServletException {
|
||||
}
|
||||
|
||||
public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) {
|
||||
Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource required");
|
||||
|
||||
@@ -15,15 +15,6 @@
|
||||
|
||||
package org.acegisecurity.ui.logout;
|
||||
|
||||
import org.acegisecurity.Authentication;
|
||||
|
||||
import org.acegisecurity.context.SecurityContextHolder;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
@@ -35,14 +26,26 @@ import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.acegisecurity.Authentication;
|
||||
import org.acegisecurity.context.SecurityContextHolder;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Logs a principal out.<p>Polls a series of {@link LogoutHandler}s. The handlers should be specified in the order
|
||||
* they are required. Generally you will want to call logout handlers <code>TokenBasedRememberMeServices</code> and
|
||||
* <code>SecurityContextLogoutHandler</code> (in that order).</p>
|
||||
* <p>After logout, the URL specified by {@link #logoutSuccessUrl} will be shown.</p>
|
||||
* <p><b>Do not use this class directly.</b> Instead configure <code>web.xml</code> to use the {@link
|
||||
* org.acegisecurity.util.FilterToBeanProxy}.</p>
|
||||
* Logs a principal out.
|
||||
* <p>
|
||||
* Polls a series of {@link LogoutHandler}s. The handlers should be specified in the order they are required.
|
||||
* Generally you will want to call logout handlers <code>TokenBasedRememberMeServices</code> and
|
||||
* <code>SecurityContextLogoutHandler</code> (in that order).
|
||||
* </p>
|
||||
* <p>
|
||||
* After logout, the URL specified by {@link #logoutSuccessUrl} will be shown.
|
||||
* </p>
|
||||
* <p>
|
||||
* <b>Do not use this class directly.</b> Instead configure <code>web.xml</code> to use the
|
||||
* {@link org.acegisecurity.util.FilterToBeanProxy}.
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
@@ -72,10 +75,11 @@ public class LogoutFilter implements Filter {
|
||||
/**
|
||||
* Not used. Use IoC container lifecycle methods instead.
|
||||
*/
|
||||
public void destroy() {}
|
||||
public void destroy() {
|
||||
}
|
||||
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
||||
throws IOException, ServletException {
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
|
||||
ServletException {
|
||||
if (!(request instanceof HttpServletRequest)) {
|
||||
throw new ServletException("Can only process HttpServletRequest");
|
||||
}
|
||||
@@ -113,10 +117,11 @@ public class LogoutFilter implements Filter {
|
||||
*
|
||||
* @throws ServletException ignored
|
||||
*/
|
||||
public void init(FilterConfig arg0) throws ServletException {}
|
||||
public void init(FilterConfig arg0) throws ServletException {
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow subclasses to modify when a logout should tak eplace.
|
||||
* Allow subclasses to modify when a logout should take place.
|
||||
*
|
||||
* @param request the request
|
||||
* @param response the response
|
||||
@@ -128,28 +133,35 @@ public class LogoutFilter implements Filter {
|
||||
int pathParamIndex = uri.indexOf(';');
|
||||
|
||||
if (pathParamIndex > 0) {
|
||||
// strip everything after the first semi-colon
|
||||
// strip everything from the first semi-colon
|
||||
uri = uri.substring(0, pathParamIndex);
|
||||
}
|
||||
|
||||
if ("".equals(request.getContextPath())) {
|
||||
return uri.endsWith(filterProcessesUrl);
|
||||
int queryParamIndex = uri.indexOf('?');
|
||||
|
||||
if (queryParamIndex > 0) {
|
||||
// strip everything from the first question mark
|
||||
uri = uri.substring(0, queryParamIndex);
|
||||
}
|
||||
|
||||
|
||||
if ("".equals(request.getContextPath())) {
|
||||
return uri.endsWith(filterProcessesUrl);
|
||||
}
|
||||
|
||||
return uri.endsWith(request.getContextPath() + filterProcessesUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow subclasses to modify the redirection message.
|
||||
*
|
||||
* @param request the request
|
||||
* @param request the request
|
||||
* @param response the response
|
||||
* @param url the URL to redirect to
|
||||
* @param url the URL to redirect to
|
||||
*
|
||||
* @throws IOException in the event of any failure
|
||||
*/
|
||||
protected void sendRedirect(HttpServletRequest request, HttpServletResponse response, String url)
|
||||
throws IOException {
|
||||
throws IOException {
|
||||
if (!url.startsWith("http://") && !url.startsWith("https://")) {
|
||||
url = request.getContextPath() + url;
|
||||
}
|
||||
@@ -161,4 +173,8 @@ public class LogoutFilter implements Filter {
|
||||
Assert.hasText(filterProcessesUrl, "FilterProcessesUrl required");
|
||||
this.filterProcessesUrl = filterProcessesUrl;
|
||||
}
|
||||
|
||||
protected String getFilterProcessesUrl() {
|
||||
return filterProcessesUrl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ package org.acegisecurity.ui.logout;
|
||||
import org.acegisecurity.Authentication;
|
||||
|
||||
import org.acegisecurity.context.SecurityContextHolder;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@@ -38,14 +37,12 @@ import javax.servlet.http.HttpSession;
|
||||
* @version $Id: SecurityContextLogoutHandler.java 1784 2007-02-24 21:00:24Z
|
||||
* luke_t $
|
||||
*/
|
||||
public class SecurityContextLogoutHandler implements LogoutHandler, Ordered {
|
||||
public class SecurityContextLogoutHandler implements LogoutHandler {
|
||||
// ~ Methods
|
||||
// ========================================================================================================
|
||||
|
||||
private boolean invalidateHttpSession = true;
|
||||
|
||||
private int order = Integer.MAX_VALUE; //~ default
|
||||
|
||||
/**
|
||||
* Requires the request to be passed in.
|
||||
*
|
||||
@@ -80,12 +77,4 @@ public class SecurityContextLogoutHandler implements LogoutHandler, Ordered {
|
||||
this.invalidateHttpSession = invalidateHttpSession;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -78,9 +78,9 @@ public class RememberMeProcessingFilter implements Filter, InitializingBean, App
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(rememberMeServices, "RememberMeServices required");
|
||||
Assert.notNull(authenticationManager, "AuthenticationManager required");
|
||||
}
|
||||
Assert.notNull(authenticationManager, "authenticationManager must be specified");
|
||||
Assert.notNull(this.rememberMeServices);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing - we rely on IoC lifecycle services instead.
|
||||
@@ -167,4 +167,5 @@ public class RememberMeProcessingFilter implements Filter, InitializingBean, App
|
||||
public void setRememberMeServices(RememberMeServices rememberMeServices) {
|
||||
this.rememberMeServices = rememberMeServices;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+367
-257
@@ -16,6 +16,7 @@
|
||||
package org.acegisecurity.ui.rememberme;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@@ -23,6 +24,7 @@ import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.acegisecurity.Authentication;
|
||||
import org.acegisecurity.providers.rememberme.RememberMeAuthenticationToken;
|
||||
import org.acegisecurity.ui.AccessDeniedHandler;
|
||||
import org.acegisecurity.ui.AuthenticationDetailsSource;
|
||||
import org.acegisecurity.ui.AuthenticationDetailsSourceImpl;
|
||||
import org.acegisecurity.ui.logout.LogoutHandler;
|
||||
@@ -34,343 +36,451 @@ import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.RequestUtils;
|
||||
|
||||
|
||||
/**
|
||||
* Identifies previously remembered users by a Base-64 encoded cookie.
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* This implementation does not rely on an external database, so is attractive for simple applications.
|
||||
* The cookie will be valid for a specific period from the date of the last
|
||||
* {@link #loginSuccess(HttpServletRequest, HttpServletResponse, Authentication)}. As per the
|
||||
* interface contract, this method will only be called when the principal completes a successful interactive
|
||||
* authentication. As such the time period commences from the last authentication attempt where they furnished
|
||||
* credentials - not the time period they last logged in via remember-me. The implementation will only send a
|
||||
* remember-me token if the parameter defined by {@link #setParameter(String)} is present.</p>
|
||||
*
|
||||
* <p>An {@link org.acegisecurity.userdetails.UserDetailsService} is required by this implementation, so that it
|
||||
* can construct a valid <code>Authentication</code> from the returned {@link
|
||||
* org.acegisecurity.userdetails.UserDetails}. This is also necessary so that the user's password is available and can
|
||||
* be checked as part of the encoded cookie.</p>
|
||||
*
|
||||
* This implementation does not rely on an external database, so is attractive
|
||||
* for simple applications. The cookie will be valid for a specific period from
|
||||
* the date of the last
|
||||
* {@link #loginSuccess(HttpServletRequest, HttpServletResponse, Authentication)}.
|
||||
* As per the interface contract, this method will only be called when the
|
||||
* principal completes a successful interactive authentication. As such the time
|
||||
* period commences from the last authentication attempt where they furnished
|
||||
* credentials - not the time period they last logged in via remember-me. The
|
||||
* implementation will only send a remember-me token if the parameter defined by
|
||||
* {@link #setParameter(String)} is present.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* An {@link org.acegisecurity.userdetails.UserDetailsService} is required by
|
||||
* this implementation, so that it can construct a valid
|
||||
* <code>Authentication</code> from the returned {@link
|
||||
* org.acegisecurity.userdetails.UserDetails}. This is also necessary so that
|
||||
* the user's password is available and can be checked as part of the encoded
|
||||
* cookie.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* The cookie encoded by this implementation adopts the following form:
|
||||
* <pre>username + ":" + expiryTime + ":" + Md5Hex(username + ":" + expiryTime + ":" + password + ":" + key)</pre>
|
||||
*
|
||||
* <pre>
|
||||
* username + ":" + expiryTime + ":" + Md5Hex(username + ":" + expiryTime + ":" + password + ":" + key)
|
||||
* </pre>
|
||||
*
|
||||
* </p>
|
||||
* <p>
|
||||
* As such, if the user changes their password any remember-me token will be invalidated. Equally, the system
|
||||
* administrator may invalidate every remember-me token on issue by changing the key. This provides some reasonable
|
||||
* approaches to recovering from a remember-me token being left on a public machine (eg kiosk system, Internet cafe
|
||||
* etc). Most importantly, at no time is the user's password ever sent to the user agent, providing an important
|
||||
* security safeguard. Unfortunately the username is necessary in this implementation (as we do not want to rely on a
|
||||
* database for remember-me services) and as such high security applications should be aware of this occasionally
|
||||
* undesired disclosure of a valid username.
|
||||
* As such, if the user changes their password any remember-me token will be
|
||||
* invalidated. Equally, the system administrator may invalidate every
|
||||
* remember-me token on issue by changing the key. This provides some reasonable
|
||||
* approaches to recovering from a remember-me token being left on a public
|
||||
* machine (eg kiosk system, Internet cafe etc). Most importantly, at no time is
|
||||
* the user's password ever sent to the user agent, providing an important
|
||||
* security safeguard. Unfortunately the username is necessary in this
|
||||
* implementation (as we do not want to rely on a database for remember-me
|
||||
* services) and as such high security applications should be aware of this
|
||||
* occasionally undesired disclosure of a valid username.
|
||||
* </p>
|
||||
* <p>
|
||||
* This is a basic remember-me implementation which is suitable for many applications. However, we recommend a
|
||||
* database-based implementation if you require a more secure remember-me approach.
|
||||
* This is a basic remember-me implementation which is suitable for many
|
||||
* applications. However, we recommend a database-based implementation if you
|
||||
* require a more secure remember-me approach.
|
||||
* </p>
|
||||
* <p>
|
||||
* By default the tokens will be valid for 14 days from the last successful authentication attempt. This can be
|
||||
* changed using {@link #setTokenValiditySeconds(long)}.
|
||||
* By default the tokens will be valid for 14 days from the last successful
|
||||
* authentication attempt. This can be changed using
|
||||
* {@link #setTokenValiditySeconds(long)}.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
* @version $Id: TokenBasedRememberMeServices.java 1871 2007-05-25 03:12:49Z
|
||||
* benalex $
|
||||
*/
|
||||
public class TokenBasedRememberMeServices implements RememberMeServices, InitializingBean, LogoutHandler, Ordered {
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
public class TokenBasedRememberMeServices implements RememberMeServices, InitializingBean, LogoutHandler {
|
||||
// ~ Static fields/initializers
|
||||
// =====================================================================================
|
||||
|
||||
public static final String ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY = "ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE";
|
||||
public static final String DEFAULT_PARAMETER = "_acegi_security_remember_me";
|
||||
protected static final Log logger = LogFactory.getLog(TokenBasedRememberMeServices.class);
|
||||
public static final String ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY = "ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE";
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
public static final String DEFAULT_PARAMETER = "_acegi_security_remember_me";
|
||||
|
||||
private AuthenticationDetailsSource authenticationDetailsSource = new AuthenticationDetailsSourceImpl();
|
||||
private String key;
|
||||
private String parameter = DEFAULT_PARAMETER;
|
||||
private UserDetailsService userDetailsService;
|
||||
private long tokenValiditySeconds = 1209600; // 14 days
|
||||
private boolean alwaysRemember = false;
|
||||
private int order = Integer.MAX_VALUE; //~ default
|
||||
private String cookieName = ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY;
|
||||
protected static final Log logger = LogFactory.getLog(TokenBasedRememberMeServices.class);
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
// ~ Instance fields
|
||||
// ================================================================================================
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.hasLength(key);
|
||||
Assert.hasLength(parameter);
|
||||
Assert.hasLength(cookieName);
|
||||
Assert.notNull(userDetailsService);
|
||||
}
|
||||
protected AuthenticationDetailsSource authenticationDetailsSource = new AuthenticationDetailsSourceImpl();
|
||||
|
||||
public Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) {
|
||||
Cookie[] cookies = request.getCookies();
|
||||
private String key;
|
||||
|
||||
if ((cookies == null) || (cookies.length == 0)) {
|
||||
return null;
|
||||
}
|
||||
private String parameter = DEFAULT_PARAMETER;
|
||||
|
||||
for (int i = 0; i < cookies.length; i++) {
|
||||
if (cookieName.equals(cookies[i].getName())) {
|
||||
String cookieValue = cookies[i].getValue();
|
||||
private UserDetailsService userDetailsService;
|
||||
|
||||
for (int j = 0; j < cookieValue.length() % 4; j++) {
|
||||
cookieValue = cookieValue + "=";
|
||||
}
|
||||
|
||||
if (Base64.isArrayByteBase64(cookieValue.getBytes())) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Remember-me cookie detected");
|
||||
}
|
||||
protected long tokenValiditySeconds = 1209600; // 14 days
|
||||
|
||||
// Decode token from Base64
|
||||
// format of token is:
|
||||
// username + ":" + expiryTime + ":" +
|
||||
// Md5Hex(username + ":" + expiryTime + ":" + password + ":" + key)
|
||||
String cookieAsPlainText = new String(Base64.decodeBase64(cookieValue.getBytes()));
|
||||
String[] cookieTokens = StringUtils.delimitedListToStringArray(cookieAsPlainText, ":");
|
||||
private boolean alwaysRemember = false;
|
||||
|
||||
if (cookieTokens.length == 3) {
|
||||
long tokenExpiryTime;
|
||||
private static final int DEFAULT_ORDER = Integer.MAX_VALUE; // ~ default
|
||||
|
||||
try {
|
||||
tokenExpiryTime = new Long(cookieTokens[1]).longValue();
|
||||
} catch (NumberFormatException nfe) {
|
||||
cancelCookie(request, response,
|
||||
"Cookie token[1] did not contain a valid number (contained '" + cookieTokens[1] + "')");
|
||||
private String cookieName = ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY;
|
||||
|
||||
return null;
|
||||
}
|
||||
// ~ Methods
|
||||
// ========================================================================================================
|
||||
|
||||
// Check it has not expired
|
||||
if (tokenExpiryTime < System.currentTimeMillis()) {
|
||||
cancelCookie(request, response,
|
||||
"Cookie token[1] has expired (expired on '" + new Date(tokenExpiryTime)
|
||||
+ "'; current time is '" + new Date() + "')");
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.hasLength(key);
|
||||
Assert.hasLength(parameter);
|
||||
Assert.hasLength(cookieName);
|
||||
Assert.notNull(userDetailsService);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Introspects the <code>Applicationcontext</code> for the single instance
|
||||
* of {@link AccessDeniedHandler}. If found invoke
|
||||
* setAccessDeniedHandler(AccessDeniedHandler accessDeniedHandler) method by
|
||||
* providing the found instance of accessDeniedHandler as a method
|
||||
* parameter. If more than one instance of <code>AccessDeniedHandler</code>
|
||||
* is found, the method throws <code>IllegalStateException</code>.
|
||||
*
|
||||
* @param applicationContext to locate the instance
|
||||
*/
|
||||
private void autoDetectAndUseAnyUserDetailsService(ApplicationContext applicationContext) {
|
||||
Map map = applicationContext.getBeansOfType(UserDetailsService.class);
|
||||
if (map.size() > 1) {
|
||||
throw new IllegalArgumentException(
|
||||
"More than one UserDetailsService beans detected please refer to the one using "
|
||||
+ " [ principalRepositoryBeanRef ] " + "attribute");
|
||||
}
|
||||
else if (map.size() == 1) {
|
||||
setUserDetailsService((UserDetailsService) map.values().iterator().next());
|
||||
}
|
||||
}
|
||||
|
||||
// Check the user exists
|
||||
// Defer lookup until after expiry time checked, to possibly avoid expensive lookup
|
||||
UserDetails userDetails;
|
||||
public Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) {
|
||||
Cookie[] cookies = request.getCookies();
|
||||
|
||||
try {
|
||||
userDetails = this.userDetailsService.loadUserByUsername(cookieTokens[0]);
|
||||
} catch (UsernameNotFoundException notFound) {
|
||||
cancelCookie(request, response,
|
||||
"Cookie token[0] contained username '" + cookieTokens[0] + "' but was not found");
|
||||
if ((cookies == null) || (cookies.length == 0)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
for (int i = 0; i < cookies.length; i++) {
|
||||
if (cookieName.equals(cookies[i].getName())) {
|
||||
String cookieValue = cookies[i].getValue();
|
||||
|
||||
// Immediately reject if the user is not allowed to login
|
||||
if (!userDetails.isAccountNonExpired() || !userDetails.isCredentialsNonExpired()
|
||||
|| !userDetails.isEnabled()) {
|
||||
cancelCookie(request, response,
|
||||
"Cookie token[0] contained username '" + cookieTokens[0]
|
||||
+ "' but account has expired, credentials have expired, or user is disabled");
|
||||
for (int j = 0; j < cookieValue.length() % 4; j++) {
|
||||
cookieValue = cookieValue + "=";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
if (Base64.isArrayByteBase64(cookieValue.getBytes())) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Remember-me cookie detected");
|
||||
}
|
||||
|
||||
// Check signature of token matches remaining details
|
||||
// Must do this after user lookup, as we need the DAO-derived password
|
||||
// If efficiency was a major issue, just add in a UserCache implementation,
|
||||
// but recall this method is usually only called one per HttpSession
|
||||
// (as if the token is valid, it will cause SecurityContextHolder population, whilst
|
||||
// if invalid, will cause the cookie to be cancelled)
|
||||
String expectedTokenSignature = DigestUtils.md5Hex(userDetails.getUsername() + ":"
|
||||
+ tokenExpiryTime + ":" + userDetails.getPassword() + ":" + this.key);
|
||||
// Decode token from Base64
|
||||
// format of token is:
|
||||
// username + ":" + expiryTime + ":" +
|
||||
// Md5Hex(username + ":" + expiryTime + ":" + password + ":"
|
||||
// + key)
|
||||
String cookieAsPlainText = new String(Base64.decodeBase64(cookieValue.getBytes()));
|
||||
String[] cookieTokens = StringUtils.delimitedListToStringArray(cookieAsPlainText, ":");
|
||||
|
||||
if (!expectedTokenSignature.equals(cookieTokens[2])) {
|
||||
cancelCookie(request, response,
|
||||
"Cookie token[2] contained signature '" + cookieTokens[2] + "' but expected '"
|
||||
+ expectedTokenSignature + "'");
|
||||
if (cookieTokens.length == 3) {
|
||||
|
||||
return null;
|
||||
}
|
||||
long tokenExpiryTime;
|
||||
|
||||
// By this stage we have a valid token
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Remember-me cookie accepted");
|
||||
}
|
||||
try {
|
||||
tokenExpiryTime = new Long(cookieTokens[1]).longValue();
|
||||
}
|
||||
catch (NumberFormatException nfe) {
|
||||
cancelCookie(request, response,
|
||||
"Cookie token[1] did not contain a valid number (contained '" + cookieTokens[1]
|
||||
+ "')");
|
||||
|
||||
RememberMeAuthenticationToken auth = new RememberMeAuthenticationToken(this.key, userDetails,
|
||||
userDetails.getAuthorities());
|
||||
auth.setDetails(authenticationDetailsSource.buildDetails((HttpServletRequest) request));
|
||||
return null;
|
||||
}
|
||||
|
||||
return auth;
|
||||
} else {
|
||||
cancelCookie(request, response,
|
||||
"Cookie token did not contain 3 tokens; decoded value was '" + cookieAsPlainText + "'");
|
||||
if (isTokenExpired(tokenExpiryTime)) {
|
||||
cancelCookie(request, response, "Cookie token[1] has expired (expired on '"
|
||||
+ new Date(tokenExpiryTime) + "'; current time is '" + new Date() + "')");
|
||||
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
cancelCookie(request, response,
|
||||
"Cookie token was not Base64 encoded; value was '" + cookieValue + "'");
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check the user exists
|
||||
// Defer lookup until after expiry time checked, to
|
||||
// possibly avoid expensive lookup
|
||||
UserDetails userDetails = loadUserDetails(request, response, cookieTokens);
|
||||
|
||||
return null;
|
||||
}
|
||||
if (userDetails == null) {
|
||||
cancelCookie(request, response, "Cookie token[0] contained username '" + cookieTokens[0]
|
||||
+ "' but was not found");
|
||||
return null;
|
||||
}
|
||||
|
||||
private void cancelCookie(HttpServletRequest request, HttpServletResponse response, String reasonForLog) {
|
||||
if ((reasonForLog != null) && logger.isDebugEnabled()) {
|
||||
logger.debug("Cancelling cookie for reason: " + reasonForLog);
|
||||
}
|
||||
if (!isValidUserDetails(request, response, userDetails, cookieTokens)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
response.addCookie(makeCancelCookie(request));
|
||||
}
|
||||
// Check signature of token matches remaining details
|
||||
// Must do this after user lookup, as we need the
|
||||
// DAO-derived password
|
||||
// If efficiency was a major issue, just add in a
|
||||
// UserCache implementation,
|
||||
// but recall this method is usually only called one per
|
||||
// HttpSession
|
||||
// (as if the token is valid, it will cause
|
||||
// SecurityContextHolder population, whilst
|
||||
// if invalid, will cause the cookie to be cancelled)
|
||||
String expectedTokenSignature = makeTokenSignature(tokenExpiryTime, userDetails);
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
if (!expectedTokenSignature.equals(cookieTokens[2])) {
|
||||
cancelCookie(request, response, "Cookie token[2] contained signature '" + cookieTokens[2]
|
||||
+ "' but expected '" + expectedTokenSignature + "'");
|
||||
|
||||
public String getParameter() {
|
||||
return parameter;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public long getTokenValiditySeconds() {
|
||||
return tokenValiditySeconds;
|
||||
}
|
||||
// By this stage we have a valid token
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Remember-me cookie accepted");
|
||||
}
|
||||
|
||||
public UserDetailsService getUserDetailsService() {
|
||||
return userDetailsService;
|
||||
}
|
||||
RememberMeAuthenticationToken auth = new RememberMeAuthenticationToken(this.key, userDetails,
|
||||
userDetails.getAuthorities());
|
||||
auth.setDetails(authenticationDetailsSource.buildDetails((HttpServletRequest) request));
|
||||
|
||||
public void loginFail(HttpServletRequest request, HttpServletResponse response) {
|
||||
cancelCookie(request, response, "Interactive authentication attempt was unsuccessful");
|
||||
}
|
||||
return auth;
|
||||
}
|
||||
else {
|
||||
cancelCookie(request, response, "Cookie token did not contain 3 tokens; decoded value was '"
|
||||
+ cookieAsPlainText + "'");
|
||||
|
||||
protected boolean rememberMeRequested(HttpServletRequest request, String parameter) {
|
||||
if (alwaysRemember) {
|
||||
return true;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
cancelCookie(request, response, "Cookie token was not Base64 encoded; value was '" + cookieValue
|
||||
+ "'");
|
||||
|
||||
return RequestUtils.getBooleanParameter(request, parameter, false);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void loginSuccess(HttpServletRequest request, HttpServletResponse response,
|
||||
Authentication successfulAuthentication) {
|
||||
// Exit if the principal hasn't asked to be remembered
|
||||
if (!rememberMeRequested(request, parameter)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Did not send remember-me cookie (principal did not set parameter '" + this.parameter
|
||||
+ "')");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* @param tokenExpiryTime
|
||||
* @param userDetails
|
||||
* @return
|
||||
*/
|
||||
protected String makeTokenSignature(long tokenExpiryTime, UserDetails userDetails) {
|
||||
String expectedTokenSignature = DigestUtils.md5Hex(userDetails.getUsername() + ":" + tokenExpiryTime + ":"
|
||||
+ userDetails.getPassword() + ":" + this.key);
|
||||
return expectedTokenSignature;
|
||||
}
|
||||
|
||||
// Determine username and password, ensuring empty strings
|
||||
Assert.notNull(successfulAuthentication.getPrincipal());
|
||||
Assert.notNull(successfulAuthentication.getCredentials());
|
||||
protected boolean isValidUserDetails(HttpServletRequest request, HttpServletResponse response,
|
||||
UserDetails userDetails, String[] cookieTokens) {
|
||||
// Immediately reject if the user is not allowed to
|
||||
// login
|
||||
if (!userDetails.isAccountNonExpired() || !userDetails.isCredentialsNonExpired() || !userDetails.isEnabled()) {
|
||||
cancelCookie(request, response, "Cookie token[0] contained username '" + cookieTokens[0]
|
||||
+ "' but account has expired, credentials have expired, or user is disabled");
|
||||
|
||||
String username;
|
||||
String password;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (successfulAuthentication.getPrincipal() instanceof UserDetails) {
|
||||
username = ((UserDetails) successfulAuthentication.getPrincipal()).getUsername();
|
||||
password = ((UserDetails) successfulAuthentication.getPrincipal()).getPassword();
|
||||
} else {
|
||||
username = successfulAuthentication.getPrincipal().toString();
|
||||
password = successfulAuthentication.getCredentials().toString();
|
||||
}
|
||||
|
||||
// If unable to find a username and password, just abort as TokenBasedRememberMeServices unable to construct a valid token in this case
|
||||
if (!StringUtils.hasLength(username) || !StringUtils.hasLength(password)) {
|
||||
return;
|
||||
}
|
||||
protected UserDetails loadUserDetails(HttpServletRequest request, HttpServletResponse response,
|
||||
String[] cookieTokens) {
|
||||
UserDetails userDetails = null;
|
||||
|
||||
Assert.hasLength(username);
|
||||
Assert.hasLength(password);
|
||||
try {
|
||||
userDetails = this.userDetailsService.loadUserByUsername(cookieTokens[0]);
|
||||
}
|
||||
catch (UsernameNotFoundException notFound) {
|
||||
cancelCookie(request, response, "Cookie token[0] contained username '" + cookieTokens[0]
|
||||
+ "' but was not found");
|
||||
|
||||
long expiryTime = System.currentTimeMillis() + (tokenValiditySeconds * 1000);
|
||||
return null;
|
||||
}
|
||||
return userDetails;
|
||||
}
|
||||
|
||||
// construct token to put in cookie; format is:
|
||||
// username + ":" + expiryTime + ":" + Md5Hex(username + ":" + expiryTime + ":" + password + ":" + key)
|
||||
String signatureValue = DigestUtils.md5Hex(username + ":" + expiryTime + ":" + password + ":" + key);
|
||||
String tokenValue = username + ":" + expiryTime + ":" + signatureValue;
|
||||
String tokenValueBase64 = new String(Base64.encodeBase64(tokenValue.getBytes()));
|
||||
response.addCookie(makeValidCookie(tokenValueBase64, request, tokenValiditySeconds));
|
||||
protected boolean isTokenExpired(long tokenExpiryTime) {
|
||||
// Check it has not expired
|
||||
if (tokenExpiryTime < System.currentTimeMillis()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Added remember-me cookie for user '" + username
|
||||
+ "', expiry: '" + new Date(expiryTime) + "'");
|
||||
}
|
||||
}
|
||||
protected void cancelCookie(HttpServletRequest request, HttpServletResponse response, String reasonForLog) {
|
||||
if ((reasonForLog != null) && logger.isDebugEnabled()) {
|
||||
logger.debug("Cancelling cookie for reason: " + reasonForLog);
|
||||
}
|
||||
|
||||
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
|
||||
cancelCookie(request, response, "Logout of user "
|
||||
+ (authentication == null ? "Unknown" : authentication.getName()));
|
||||
}
|
||||
response.addCookie(makeCancelCookie(request));
|
||||
}
|
||||
|
||||
protected Cookie makeCancelCookie(HttpServletRequest request) {
|
||||
Cookie cookie = new Cookie(cookieName, null);
|
||||
cookie.setMaxAge(0);
|
||||
cookie.setPath(StringUtils.hasLength(request.getContextPath()) ? request.getContextPath() : "/");
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
return cookie;
|
||||
}
|
||||
public String getParameter() {
|
||||
return parameter;
|
||||
}
|
||||
|
||||
protected Cookie makeValidCookie(String tokenValueBase64, HttpServletRequest request, long maxAge) {
|
||||
Cookie cookie = new Cookie(cookieName, tokenValueBase64);
|
||||
cookie.setMaxAge(new Long(maxAge).intValue());
|
||||
cookie.setPath(StringUtils.hasLength(request.getContextPath()) ? request.getContextPath() : "/");
|
||||
public long getTokenValiditySeconds() {
|
||||
return tokenValiditySeconds;
|
||||
}
|
||||
|
||||
return cookie;
|
||||
}
|
||||
public UserDetailsService getUserDetailsService() {
|
||||
return userDetailsService;
|
||||
}
|
||||
|
||||
public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) {
|
||||
Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource required");
|
||||
this.authenticationDetailsSource = authenticationDetailsSource;
|
||||
}
|
||||
public void loginFail(HttpServletRequest request, HttpServletResponse response) {
|
||||
cancelCookie(request, response, "Interactive authentication attempt was unsuccessful");
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
protected boolean rememberMeRequested(HttpServletRequest request, String parameter) {
|
||||
if (alwaysRemember) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setParameter(String parameter) {
|
||||
this.parameter = parameter;
|
||||
}
|
||||
return RequestUtils.getBooleanParameter(request, parameter, false);
|
||||
}
|
||||
|
||||
public void setCookieName(String cookieName) {
|
||||
public void loginSuccess(HttpServletRequest request, HttpServletResponse response,
|
||||
Authentication successfulAuthentication) {
|
||||
// Exit if the principal hasn't asked to be remembered
|
||||
if (!rememberMeRequested(request, parameter)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Did not send remember-me cookie (principal did not set parameter '" + this.parameter
|
||||
+ "')");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine username and password, ensuring empty strings
|
||||
Assert.notNull(successfulAuthentication.getPrincipal());
|
||||
Assert.notNull(successfulAuthentication.getCredentials());
|
||||
|
||||
String username = retrieveUserName(successfulAuthentication);
|
||||
String password = retrievePassword(successfulAuthentication);
|
||||
|
||||
// If unable to find a username and password, just abort as
|
||||
// TokenBasedRememberMeServices unable to construct a valid token in
|
||||
// this case
|
||||
if (!StringUtils.hasLength(username) || !StringUtils.hasLength(password)) {
|
||||
return;
|
||||
}
|
||||
|
||||
long expiryTime = System.currentTimeMillis() + (tokenValiditySeconds * 1000);
|
||||
|
||||
// construct token to put in cookie; format is:
|
||||
// username + ":" + expiryTime + ":" + Md5Hex(username + ":" +
|
||||
// expiryTime + ":" + password + ":" + key)
|
||||
String signatureValue = DigestUtils.md5Hex(username + ":" + expiryTime + ":" + password + ":" + key);
|
||||
String tokenValue = username + ":" + expiryTime + ":" + signatureValue;
|
||||
String tokenValueBase64 = new String(Base64.encodeBase64(tokenValue.getBytes()));
|
||||
response.addCookie(makeValidCookie(tokenValueBase64, request, tokenValiditySeconds));
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger
|
||||
.debug("Added remember-me cookie for user '" + username + "', expiry: '" + new Date(expiryTime)
|
||||
+ "'");
|
||||
}
|
||||
}
|
||||
|
||||
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
|
||||
cancelCookie(request, response, "Logout of user "
|
||||
+ (authentication == null ? "Unknown" : authentication.getName()));
|
||||
}
|
||||
|
||||
protected String retrieveUserName(Authentication successfulAuthentication) {
|
||||
if (isInstanceOfUserDetails(successfulAuthentication)) {
|
||||
return ((UserDetails) successfulAuthentication.getPrincipal()).getUsername();
|
||||
}
|
||||
else {
|
||||
return successfulAuthentication.getPrincipal().toString();
|
||||
}
|
||||
}
|
||||
|
||||
protected String retrievePassword(Authentication successfulAuthentication) {
|
||||
if (isInstanceOfUserDetails(successfulAuthentication)) {
|
||||
return ((UserDetails) successfulAuthentication.getPrincipal()).getPassword();
|
||||
}
|
||||
else {
|
||||
return successfulAuthentication.getCredentials().toString();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isInstanceOfUserDetails(Authentication authentication) {
|
||||
return authentication.getPrincipal() instanceof UserDetails;
|
||||
}
|
||||
|
||||
protected Cookie makeCancelCookie(HttpServletRequest request) {
|
||||
Cookie cookie = new Cookie(cookieName, null);
|
||||
cookie.setMaxAge(0);
|
||||
cookie.setPath(StringUtils.hasLength(request.getContextPath()) ? request.getContextPath() : "/");
|
||||
|
||||
return cookie;
|
||||
}
|
||||
|
||||
protected Cookie makeValidCookie(String tokenValueBase64, HttpServletRequest request, long maxAge) {
|
||||
Cookie cookie = new Cookie(cookieName, tokenValueBase64);
|
||||
cookie.setMaxAge(new Long(maxAge).intValue());
|
||||
cookie.setPath(StringUtils.hasLength(request.getContextPath()) ? request.getContextPath() : "/");
|
||||
|
||||
return cookie;
|
||||
}
|
||||
|
||||
public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) {
|
||||
Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource required");
|
||||
this.authenticationDetailsSource = authenticationDetailsSource;
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public void setParameter(String parameter) {
|
||||
this.parameter = parameter;
|
||||
}
|
||||
|
||||
public void setCookieName(String cookieName) {
|
||||
this.cookieName = cookieName;
|
||||
}
|
||||
|
||||
public void setTokenValiditySeconds(long tokenValiditySeconds) {
|
||||
this.tokenValiditySeconds = tokenValiditySeconds;
|
||||
}
|
||||
|
||||
public void setUserDetailsService(UserDetailsService userDetailsService) {
|
||||
this.userDetailsService = userDetailsService;
|
||||
}
|
||||
|
||||
public boolean isAlwaysRemember() {
|
||||
return alwaysRemember;
|
||||
}
|
||||
|
||||
public void setAlwaysRemember(boolean alwaysRemember) {
|
||||
this.alwaysRemember = alwaysRemember;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return order;
|
||||
this.tokenValiditySeconds = tokenValiditySeconds;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
public void setUserDetailsService(UserDetailsService userDetailsService) {
|
||||
this.userDetailsService = userDetailsService;
|
||||
}
|
||||
|
||||
public boolean isAlwaysRemember() {
|
||||
return alwaysRemember;
|
||||
}
|
||||
|
||||
public void setAlwaysRemember(boolean alwaysRemember) {
|
||||
this.alwaysRemember = alwaysRemember;
|
||||
}
|
||||
|
||||
public String getCookieName() {
|
||||
return cookieName;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,10 +28,12 @@ import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
|
||||
/**
|
||||
* Processes an authentication form.<p>Login forms must present two parameters to this filter: a username and
|
||||
* Processes an authentication form.
|
||||
* <p>Login forms must present two parameters to this filter: a username and
|
||||
* password. The parameter names to use are contained in the static fields {@link #ACEGI_SECURITY_FORM_USERNAME_KEY}
|
||||
* and {@link #ACEGI_SECURITY_FORM_PASSWORD_KEY}.</p>
|
||||
* <P><B>Do not use this class directly.</B> Instead configure <code>web.xml</code> to use the {@link
|
||||
*
|
||||
* <p><b>Do not use this class directly.</b> Instead configure <code>web.xml</code> to use the {@link
|
||||
* org.acegisecurity.util.FilterToBeanProxy}.</p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
@@ -47,8 +49,7 @@ public class AuthenticationProcessingFilter extends AbstractProcessingFilter {
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public Authentication attemptAuthentication(HttpServletRequest request)
|
||||
throws AuthenticationException {
|
||||
public Authentication attemptAuthentication(HttpServletRequest request) throws AuthenticationException {
|
||||
String username = obtainUsername(request);
|
||||
String password = obtainPassword(request);
|
||||
|
||||
@@ -60,6 +61,8 @@ public class AuthenticationProcessingFilter extends AbstractProcessingFilter {
|
||||
password = "";
|
||||
}
|
||||
|
||||
username = username.trim();
|
||||
|
||||
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
|
||||
|
||||
// Place the last username attempted into HttpSession for views
|
||||
|
||||
+170
-156
@@ -28,7 +28,6 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -41,211 +40,226 @@ import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
|
||||
/**
|
||||
* <p>Used by the <code>SecurityEnforcementFilter</code> to commence authentication via the {@link
|
||||
* AuthenticationProcessingFilter}. This object holds the location of the login form, relative to the web app context
|
||||
* path, and is used to commence a redirect to that form.</p>
|
||||
* <p>By setting the <em>forceHttps</em> property to true, you may configure the class to force the protocol used
|
||||
* for the login form to be <code>HTTPS</code>, even if the original intercepted request for a resource used the
|
||||
* <code>HTTP</code> protocol. When this happens, after a successful login (via HTTPS), the original resource will
|
||||
* still be accessed as HTTP, via the original request URL. For the forced HTTPS feature to work, the {@link
|
||||
* PortMapper} is consulted to determine the HTTP:HTTPS pairs.</p>
|
||||
*
|
||||
* <p>
|
||||
* Used by the <code>SecurityEnforcementFilter</code> to commence
|
||||
* authentication via the {@link AuthenticationProcessingFilter}. This object
|
||||
* holds the location of the login form, relative to the web app context path,
|
||||
* and is used to commence a redirect to that form.
|
||||
* </p>
|
||||
* <p>
|
||||
* By setting the <em>forceHttps</em> property to true, you may configure the
|
||||
* class to force the protocol used for the login form to be <code>HTTPS</code>,
|
||||
* even if the original intercepted request for a resource used the
|
||||
* <code>HTTP</code> protocol. When this happens, after a successful login
|
||||
* (via HTTPS), the original resource will still be accessed as HTTP, via the
|
||||
* original request URL. For the forced HTTPS feature to work, the {@link
|
||||
* PortMapper} is consulted to determine the HTTP:HTTPS pairs.
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author colin sampaleanu
|
||||
* @author Omri Spector
|
||||
* @version $Id$
|
||||
* @version $Id: AuthenticationProcessingFilterEntryPoint.java 1873 2007-05-25
|
||||
* 03:21:17Z benalex $
|
||||
*/
|
||||
public class AuthenticationProcessingFilterEntryPoint implements AuthenticationEntryPoint, InitializingBean, Ordered {
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
public class AuthenticationProcessingFilterEntryPoint implements AuthenticationEntryPoint, InitializingBean {
|
||||
// ~ Static fields/initializers
|
||||
// =====================================================================================
|
||||
|
||||
private static final Log logger = LogFactory.getLog(AuthenticationProcessingFilterEntryPoint.class);
|
||||
private static final Log logger = LogFactory.getLog(AuthenticationProcessingFilterEntryPoint.class);
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
// ~ Instance fields
|
||||
// ================================================================================================
|
||||
|
||||
private PortMapper portMapper = new PortMapperImpl();
|
||||
private PortResolver portResolver = new PortResolverImpl();
|
||||
private String loginFormUrl;
|
||||
private boolean forceHttps = false;
|
||||
private boolean serverSideRedirect = false;
|
||||
private int order = Integer.MAX_VALUE; // ~ default
|
||||
private PortMapper portMapper = new PortMapperImpl();
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
private PortResolver portResolver = new PortResolverImpl();
|
||||
|
||||
private String loginFormUrl;
|
||||
|
||||
private boolean forceHttps = false;
|
||||
|
||||
private boolean serverSideRedirect = false;
|
||||
|
||||
// ~ Methods
|
||||
// ========================================================================================================
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.hasLength(loginFormUrl, "loginFormUrl must be specified");
|
||||
Assert.notNull(portMapper, "portMapper must be specified");
|
||||
Assert.notNull(portResolver, "portResolver must be specified");
|
||||
}
|
||||
Assert.hasLength(loginFormUrl, "loginFormUrl must be specified");
|
||||
Assert.notNull(portMapper, "portMapper must be specified");
|
||||
Assert.notNull(portResolver, "portResolver must be specified");
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows subclasses to modify the login form URL that should be applicable for a given request.
|
||||
* Allows subclasses to modify the login form URL that should be applicable
|
||||
* for a given request.
|
||||
*
|
||||
* @param request the request
|
||||
* @param response the response
|
||||
* @param exception the exception
|
||||
* @return the URL (cannot be null or empty; defaults to {@link #getLoginFormUrl()})
|
||||
* @return the URL (cannot be null or empty; defaults to
|
||||
* {@link #getLoginFormUrl()})
|
||||
*/
|
||||
protected String determineUrlToUseForThisRequest(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) {
|
||||
protected String determineUrlToUseForThisRequest(HttpServletRequest request, HttpServletResponse response,
|
||||
AuthenticationException exception) {
|
||||
return getLoginFormUrl();
|
||||
}
|
||||
|
||||
public void commence(ServletRequest request, ServletResponse response, AuthenticationException authException)
|
||||
throws IOException, ServletException {
|
||||
HttpServletRequest req = (HttpServletRequest) request;
|
||||
HttpServletResponse resp = (HttpServletResponse) response;
|
||||
String scheme = request.getScheme();
|
||||
String serverName = request.getServerName();
|
||||
int serverPort = portResolver.getServerPort(request);
|
||||
String contextPath = req.getContextPath();
|
||||
|
||||
boolean inHttp = "http".equals(scheme.toLowerCase());
|
||||
boolean inHttps = "https".equals(scheme.toLowerCase());
|
||||
public void commence(ServletRequest request, ServletResponse response, AuthenticationException authException)
|
||||
throws IOException, ServletException {
|
||||
HttpServletRequest req = (HttpServletRequest) request;
|
||||
HttpServletResponse resp = (HttpServletResponse) response;
|
||||
String scheme = request.getScheme();
|
||||
String serverName = request.getServerName();
|
||||
int serverPort = portResolver.getServerPort(request);
|
||||
String contextPath = req.getContextPath();
|
||||
|
||||
boolean includePort = true;
|
||||
boolean inHttp = "http".equals(scheme.toLowerCase());
|
||||
boolean inHttps = "https".equals(scheme.toLowerCase());
|
||||
|
||||
String redirectUrl = null;
|
||||
boolean doForceHttps = false;
|
||||
Integer httpsPort = null;
|
||||
boolean includePort = true;
|
||||
|
||||
if (inHttp && (serverPort == 80)) {
|
||||
includePort = false;
|
||||
} else if (inHttps && (serverPort == 443)) {
|
||||
includePort = false;
|
||||
}
|
||||
String redirectUrl = null;
|
||||
boolean doForceHttps = false;
|
||||
Integer httpsPort = null;
|
||||
|
||||
if (forceHttps && inHttp) {
|
||||
httpsPort = (Integer) portMapper.lookupHttpsPort(new Integer(serverPort));
|
||||
|
||||
if (httpsPort != null) {
|
||||
doForceHttps = true;
|
||||
if (httpsPort.intValue() == 443) {
|
||||
includePort = false;
|
||||
} else {
|
||||
includePort = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
String loginForm = determineUrlToUseForThisRequest(req, resp, authException);
|
||||
|
||||
if ( serverSideRedirect ) {
|
||||
if (inHttp && (serverPort == 80)) {
|
||||
includePort = false;
|
||||
}
|
||||
else if (inHttps && (serverPort == 443)) {
|
||||
includePort = false;
|
||||
}
|
||||
|
||||
if ( doForceHttps ) {
|
||||
|
||||
// before doing server side redirect, we need to do client redirect to https.
|
||||
|
||||
String servletPath = req.getServletPath();
|
||||
String pathInfo = req.getPathInfo();
|
||||
String query = req.getQueryString();
|
||||
if (forceHttps && inHttp) {
|
||||
httpsPort = (Integer) portMapper.lookupHttpsPort(new Integer(serverPort));
|
||||
|
||||
redirectUrl = "https://" + serverName + ((includePort) ? (":" + httpsPort) : "") + contextPath
|
||||
+ servletPath + (pathInfo == null ? "" : pathInfo ) + (query == null ? "" : "?"+query );
|
||||
if (httpsPort != null) {
|
||||
doForceHttps = true;
|
||||
if (httpsPort.intValue() == 443) {
|
||||
includePort = false;
|
||||
}
|
||||
else {
|
||||
includePort = true;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Server side forward to: " + loginForm);
|
||||
}
|
||||
String loginForm = determineUrlToUseForThisRequest(req, resp, authException);
|
||||
|
||||
RequestDispatcher dispatcher = req.getRequestDispatcher(loginForm);
|
||||
if (serverSideRedirect) {
|
||||
|
||||
dispatcher.forward(request, response);
|
||||
|
||||
return;
|
||||
if (doForceHttps) {
|
||||
|
||||
}
|
||||
// before doing server side redirect, we need to do client
|
||||
// redirect to https.
|
||||
|
||||
} else {
|
||||
String servletPath = req.getServletPath();
|
||||
String pathInfo = req.getPathInfo();
|
||||
String query = req.getQueryString();
|
||||
|
||||
if ( doForceHttps ) {
|
||||
redirectUrl = "https://" + serverName + ((includePort) ? (":" + httpsPort) : "") + contextPath
|
||||
+ servletPath + (pathInfo == null ? "" : pathInfo) + (query == null ? "" : "?" + query);
|
||||
|
||||
redirectUrl = "https://" + serverName + ((includePort) ? (":" + httpsPort) : "") + contextPath
|
||||
+ loginForm;
|
||||
}
|
||||
else {
|
||||
|
||||
} else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Server side forward to: " + loginForm);
|
||||
}
|
||||
|
||||
redirectUrl = scheme + "://" + serverName + ((includePort) ? (":" + serverPort) : "") + contextPath
|
||||
+ loginForm;
|
||||
RequestDispatcher dispatcher = req.getRequestDispatcher(loginForm);
|
||||
|
||||
}
|
||||
}
|
||||
dispatcher.forward(request, response);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Redirecting to: " + redirectUrl);
|
||||
}
|
||||
return;
|
||||
|
||||
((HttpServletResponse) response).sendRedirect(((HttpServletResponse) response).encodeRedirectURL(redirectUrl));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getForceHttps() {
|
||||
return forceHttps;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
public String getLoginFormUrl() {
|
||||
return loginFormUrl;
|
||||
}
|
||||
if (doForceHttps) {
|
||||
|
||||
public PortMapper getPortMapper() {
|
||||
return portMapper;
|
||||
}
|
||||
redirectUrl = "https://" + serverName + ((includePort) ? (":" + httpsPort) : "") + contextPath
|
||||
+ loginForm;
|
||||
|
||||
public PortResolver getPortResolver() {
|
||||
return portResolver;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
public boolean isServerSideRedirect() {
|
||||
return serverSideRedirect;
|
||||
}
|
||||
redirectUrl = scheme + "://" + serverName + ((includePort) ? (":" + serverPort) : "") + contextPath
|
||||
+ loginForm;
|
||||
|
||||
/**
|
||||
* Set to true to force login form access to be via https. If this value is ture (the default is false),
|
||||
* and the incoming request for the protected resource which triggered the interceptor was not already
|
||||
* <code>https</code>, then
|
||||
*
|
||||
* @param forceHttps
|
||||
*/
|
||||
public void setForceHttps(boolean forceHttps) {
|
||||
this.forceHttps = forceHttps;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The URL where the <code>AuthenticationProcessingFilter</code> login page can be found. Should be
|
||||
* relative to the web-app context path, and include a leading <code>/</code>
|
||||
*
|
||||
* @param loginFormUrl
|
||||
*/
|
||||
public void setLoginFormUrl(String loginFormUrl) {
|
||||
this.loginFormUrl = loginFormUrl;
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Redirecting to: " + redirectUrl);
|
||||
}
|
||||
|
||||
public void setPortMapper(PortMapper portMapper) {
|
||||
this.portMapper = portMapper;
|
||||
}
|
||||
|
||||
public void setPortResolver(PortResolver portResolver) {
|
||||
this.portResolver = portResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if we are to do a server side include of the <code>loginFormUrl</code> instead of a 302
|
||||
* redirect.
|
||||
*
|
||||
* @param serverSideRedirect
|
||||
*/
|
||||
public void setServerSideRedirect(boolean serverSideRedirect) {
|
||||
this.serverSideRedirect = serverSideRedirect;
|
||||
}
|
||||
|
||||
|
||||
public int getOrder() {
|
||||
return order;
|
||||
((HttpServletResponse) response).sendRedirect(((HttpServletResponse) response).encodeRedirectURL(redirectUrl));
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
public boolean getForceHttps() {
|
||||
return forceHttps;
|
||||
}
|
||||
|
||||
public String getLoginFormUrl() {
|
||||
return loginFormUrl;
|
||||
}
|
||||
|
||||
public PortMapper getPortMapper() {
|
||||
return portMapper;
|
||||
}
|
||||
|
||||
public PortResolver getPortResolver() {
|
||||
return portResolver;
|
||||
}
|
||||
|
||||
public boolean isServerSideRedirect() {
|
||||
return serverSideRedirect;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true to force login form access to be via https. If this value is
|
||||
* ture (the default is false), and the incoming request for the protected
|
||||
* resource which triggered the interceptor was not already
|
||||
* <code>https</code>, then
|
||||
*
|
||||
* @param forceHttps
|
||||
*/
|
||||
public void setForceHttps(boolean forceHttps) {
|
||||
this.forceHttps = forceHttps;
|
||||
}
|
||||
|
||||
/**
|
||||
* The URL where the <code>AuthenticationProcessingFilter</code> login
|
||||
* page can be found. Should be relative to the web-app context path, and
|
||||
* include a leading <code>/</code>
|
||||
*
|
||||
* @param loginFormUrl
|
||||
*/
|
||||
public void setLoginFormUrl(String loginFormUrl) {
|
||||
this.loginFormUrl = loginFormUrl;
|
||||
}
|
||||
|
||||
public void setPortMapper(PortMapper portMapper) {
|
||||
this.portMapper = portMapper;
|
||||
}
|
||||
|
||||
public void setPortResolver(PortResolver portResolver) {
|
||||
this.portResolver = portResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if we are to do a server side include of the
|
||||
* <code>loginFormUrl</code> instead of a 302 redirect.
|
||||
*
|
||||
* @param serverSideRedirect
|
||||
*/
|
||||
public void setServerSideRedirect(boolean serverSideRedirect) {
|
||||
this.serverSideRedirect = serverSideRedirect;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ import org.acegisecurity.AuthenticationException;
|
||||
import org.acegisecurity.ui.AuthenticationEntryPoint;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
/**
|
||||
* In the X.509 authentication case (unlike CAS, for example) the certificate
|
||||
@@ -47,28 +46,15 @@ import org.springframework.core.Ordered;
|
||||
*
|
||||
* @see org.acegisecurity.ui.ExceptionTranslationFilter
|
||||
*/
|
||||
public class X509ProcessingFilterEntryPoint implements AuthenticationEntryPoint, Ordered {
|
||||
public class X509ProcessingFilterEntryPoint implements AuthenticationEntryPoint {
|
||||
// ~ Static fields/initializers
|
||||
// =====================================================================================
|
||||
|
||||
private static final Log logger = LogFactory.getLog(X509ProcessingFilterEntryPoint.class);
|
||||
|
||||
// ~ instance fields
|
||||
// =====================================================================================
|
||||
|
||||
private int order = Integer.MAX_VALUE; // ~ default
|
||||
|
||||
// ~ Methods
|
||||
// ========================================================================================================
|
||||
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a 403 error code to the client.
|
||||
*
|
||||
|
||||
@@ -27,7 +27,7 @@ import org.acegisecurity.BadCredentialsException;
|
||||
public class UsernameNotFoundException extends BadCredentialsException {
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
/**
|
||||
/**
|
||||
* Constructs a <code>UsernameNotFoundException</code> with the specified
|
||||
* message.
|
||||
*
|
||||
@@ -37,7 +37,18 @@ public class UsernameNotFoundException extends BadCredentialsException {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Constructs a <code>UsernameNotFoundException</code>, making use of the <tt>extraInformation</tt>
|
||||
* property of the superclass.
|
||||
*
|
||||
* @param msg the detail message
|
||||
* @param extraInformation additional information such as the username.
|
||||
*/
|
||||
public UsernameNotFoundException(String msg, Object extraInformation) {
|
||||
super(msg, extraInformation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a <code>UsernameNotFoundException</code> with the specified
|
||||
* message and root cause.
|
||||
*
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
package org.acegisecurity.util;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* Proivdes common logic for manipulating classes implementing the Spring
|
||||
* {@link Ordered} interface.
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
public abstract class OrderedUtils {
|
||||
/**
|
||||
* Introspects the application context for a single instance of <code>sourceClass</code>. If found, the order from the source
|
||||
* class instance is copied into the <code>destinationObject</code>. If more than one instance of <code>sourceClass</code>
|
||||
* is found, the method throws <code>IllegalStateException</code>.
|
||||
*
|
||||
* <p>The <code>destinationObject</code> is required to provide a public <code>setOrder(int)</code> method to permit
|
||||
* mutation of the order property.
|
||||
*
|
||||
* @param sourceClass to locate in the application context (must be assignable to Ordered)
|
||||
* @param applicationContext to locate the class
|
||||
* @param destinationObject to copy the order into (must provide public setOrder(int) method)
|
||||
* @param skipIfMoreThanOneCandidateSourceClassInstance if the application context provides more than one potential source, skip modifications (if false, the first located matching source will be used)
|
||||
* @return whether or not the destination class was updated
|
||||
*/
|
||||
public static boolean copyOrderFromOtherClass(Class sourceClass, ApplicationContext applicationContext, Object destinationObject, boolean skipIfMoreThanOneCandidateSourceClassInstance) {
|
||||
Assert.notNull(sourceClass, "Source class required");
|
||||
Assert.notNull(applicationContext, "ApplicationContext required");
|
||||
Assert.notNull(destinationObject, "Destination object required");
|
||||
Assert.isTrue(Ordered.class.isAssignableFrom(sourceClass), "Source class " + sourceClass + " must be assignable to Ordered");
|
||||
Map map = applicationContext.getBeansOfType(sourceClass);
|
||||
if (map.size() == 0) {
|
||||
return false;
|
||||
} else if (map.size() > 1 && skipIfMoreThanOneCandidateSourceClassInstance) {
|
||||
return false;
|
||||
} else {
|
||||
copyOrderFromOtherObject((Ordered)map.values().iterator().next(), destinationObject);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the order property from the <code>sourceObject</code> into the <code>destinationObject</code>.
|
||||
*
|
||||
* <p>The <code>destinationObject</code> is required to provide a public <code>setOrder(int)</code> method to permit
|
||||
* mutation of the order property.
|
||||
*
|
||||
* @param sourceObject to copy the order from
|
||||
* @param destinationObject to copy the order into (must provide public setOrder(int) method)
|
||||
*/
|
||||
public static void copyOrderFromOtherObject(Ordered sourceObject, Object destinationObject) {
|
||||
Assert.notNull(sourceObject, "Source object required");
|
||||
Assert.notNull(destinationObject, "Destination object required");
|
||||
Method m = ReflectionUtils.findMethod(destinationObject.getClass(), "setOrder", new Class[] {int.class});
|
||||
Assert.notNull(m, "Method setOrder(int) not found on " + destinationObject.getClass());
|
||||
ReflectionUtils.invokeMethod(m, destinationObject, new Object[] {new Integer(sourceObject.getOrder())});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,6 +20,8 @@ import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
@@ -29,6 +31,9 @@ import java.util.Map;
|
||||
* @version $Id$
|
||||
*/
|
||||
public final class StringSplitUtils {
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
private static final String[] EMPTY_STRING_ARRAY = new String[0];
|
||||
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
private StringSplitUtils() {
|
||||
@@ -40,12 +45,10 @@ public final class StringSplitUtils {
|
||||
* Splits a <code>String</code> at the first instance of the delimiter.<p>Does not include the delimiter in
|
||||
* the response.</p>
|
||||
*
|
||||
* @param toSplit the string to split
|
||||
* @param toSplit the string to split
|
||||
* @param delimiter to split the string up with
|
||||
*
|
||||
* @return a two element array with index 0 being before the delimiter, and index 1 being after the delimiter
|
||||
* (neither element includes the delimiter)
|
||||
*
|
||||
* @throws IllegalArgumentException if an argument was invalid
|
||||
*/
|
||||
public static String[] split(String toSplit, String delimiter) {
|
||||
@@ -65,7 +68,7 @@ public final class StringSplitUtils {
|
||||
String beforeDelimiter = toSplit.substring(0, offset);
|
||||
String afterDelimiter = toSplit.substring(offset + 1);
|
||||
|
||||
return new String[] {beforeDelimiter, afterDelimiter};
|
||||
return new String[]{beforeDelimiter, afterDelimiter};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,11 +77,10 @@ public final class StringSplitUtils {
|
||||
* then generated, with the left of the delimiter providing the key, and the right of the delimiter providing the
|
||||
* value.<p>Will trim both the key and value before adding to the <code>Map</code>.</p>
|
||||
*
|
||||
* @param array the array to process
|
||||
* @param delimiter to split each element using (typically the equals symbol)
|
||||
* @param array the array to process
|
||||
* @param delimiter to split each element using (typically the equals symbol)
|
||||
* @param removeCharacters one or more characters to remove from each element prior to attempting the split
|
||||
* operation (typically the quotation mark symbol) or <code>null</code> if no removal should occur
|
||||
*
|
||||
* operation (typically the quotation mark symbol) or <code>null</code> if no removal should occur
|
||||
* @return a <code>Map</code> representing the array contents, or <code>null</code> if the array to process was
|
||||
* null or empty
|
||||
*/
|
||||
@@ -135,4 +137,58 @@ public final class StringSplitUtils {
|
||||
return str.substring(pos + separator.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits a given string on the given separator character, skips the contents of quoted substrings
|
||||
* when looking for separators.
|
||||
* Introduced for use in DigestProcessingFilter (see SEC-506).
|
||||
* <p/>
|
||||
* This was copied and modified from commons-lang StringUtils
|
||||
*/
|
||||
public static String[] splitIgnoringQuotes(String str, char separatorChar) {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int len = str.length();
|
||||
|
||||
if (len == 0) {
|
||||
return EMPTY_STRING_ARRAY;
|
||||
}
|
||||
|
||||
List list = new ArrayList();
|
||||
int i = 0;
|
||||
int start = 0;
|
||||
boolean match = false;
|
||||
|
||||
while (i < len) {
|
||||
if (str.charAt(i) == '"') {
|
||||
i++;
|
||||
while (i < len) {
|
||||
if (str.charAt(i) == '"') {
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
match = true;
|
||||
continue;
|
||||
}
|
||||
if (str.charAt(i) == separatorChar) {
|
||||
if (match) {
|
||||
list.add(str.substring(start, i));
|
||||
match = false;
|
||||
}
|
||||
start = ++i;
|
||||
continue;
|
||||
}
|
||||
match = true;
|
||||
i++;
|
||||
}
|
||||
if (match) {
|
||||
list.add(str.substring(start, i));
|
||||
}
|
||||
|
||||
return (String[]) list.toArray(new String[list.size()]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,40 +15,43 @@
|
||||
|
||||
package org.acegisecurity.vote;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.acegisecurity.AccessDecisionManager;
|
||||
import org.acegisecurity.AccessDeniedException;
|
||||
import org.acegisecurity.AcegiMessageSource;
|
||||
import org.acegisecurity.ConfigAttribute;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.MessageSourceAware;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* Abstract implementation of {@link AccessDecisionManager}.<p>Handles configuration of a bean context defined list
|
||||
* of {@link AccessDecisionVoter}s and the access control behaviour if all voters abstain from voting (defaults to
|
||||
* deny access).</p>
|
||||
* Abstract implementation of {@link AccessDecisionManager}.
|
||||
* <p/>
|
||||
* Handles configuration of a bean context defined list of
|
||||
* {@link AccessDecisionVoter}s and the access control behaviour if all voters
|
||||
* abstain from voting (defaults to deny access).
|
||||
* </p>
|
||||
*/
|
||||
public abstract class AbstractAccessDecisionManager implements AccessDecisionManager, InitializingBean,
|
||||
MessageSourceAware {
|
||||
//~ Instance fields ================================================================================================
|
||||
MessageSourceAware {
|
||||
// ~ Instance fields
|
||||
// ================================================================================================
|
||||
|
||||
private List decisionVoters;
|
||||
|
||||
protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor();
|
||||
|
||||
private boolean allowIfAllAbstainDecisions = false;
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
// ~ Methods
|
||||
// ========================================================================================================
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
checkIfValidList(this.decisionVoters);
|
||||
Assert.notEmpty(this.decisionVoters, "A list of AccessDecisionVoters is required");
|
||||
Assert.notNull(this.messages, "A message source must be set");
|
||||
}
|
||||
|
||||
@@ -59,12 +62,6 @@ public abstract class AbstractAccessDecisionManager implements AccessDecisionMan
|
||||
}
|
||||
}
|
||||
|
||||
private void checkIfValidList(List listToCheck) {
|
||||
if ((listToCheck == null) || (listToCheck.size() == 0)) {
|
||||
throw new IllegalArgumentException("A list of AccessDecisionVoters is required");
|
||||
}
|
||||
}
|
||||
|
||||
public List getDecisionVoters() {
|
||||
return this.decisionVoters;
|
||||
}
|
||||
@@ -78,21 +75,14 @@ public abstract class AbstractAccessDecisionManager implements AccessDecisionMan
|
||||
}
|
||||
|
||||
public void setDecisionVoters(List newList) {
|
||||
checkIfValidList(newList);
|
||||
Assert.notEmpty(newList);
|
||||
|
||||
Iterator iter = newList.iterator();
|
||||
|
||||
while (iter.hasNext()) {
|
||||
Object currentObject = null;
|
||||
|
||||
try {
|
||||
currentObject = iter.next();
|
||||
|
||||
AccessDecisionVoter attemptToCast = (AccessDecisionVoter) currentObject;
|
||||
} catch (ClassCastException cce) {
|
||||
throw new IllegalArgumentException("AccessDecisionVoter " + currentObject.getClass().getName()
|
||||
Object currentObject = iter.next();
|
||||
Assert.isInstanceOf(AccessDecisionVoter.class, currentObject, "AccessDecisionVoter " + currentObject.getClass().getName()
|
||||
+ " must implement AccessDecisionVoter");
|
||||
}
|
||||
}
|
||||
|
||||
this.decisionVoters = newList;
|
||||
@@ -117,11 +107,14 @@ public abstract class AbstractAccessDecisionManager implements AccessDecisionMan
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates through all <code>AccessDecisionVoter</code>s and ensures each can support the presented class.<p>If
|
||||
* one or more voters cannot support the presented class, <code>false</code> is returned.</p>
|
||||
* Iterates through all <code>AccessDecisionVoter</code>s and ensures
|
||||
* each can support the presented class.
|
||||
* <p/>
|
||||
* If one or more voters cannot support the presented class,
|
||||
* <code>false</code> is returned.
|
||||
* </p>
|
||||
*
|
||||
* @param clazz DOCUMENT ME!
|
||||
*
|
||||
* @return DOCUMENT ME!
|
||||
*/
|
||||
public boolean supports(Class clazz) {
|
||||
|
||||
@@ -47,11 +47,10 @@ public class AuthenticatedVoter implements AccessDecisionVoter {
|
||||
public static final String IS_AUTHENTICATED_FULLY = "IS_AUTHENTICATED_FULLY";
|
||||
public static final String IS_AUTHENTICATED_REMEMBERED = "IS_AUTHENTICATED_REMEMBERED";
|
||||
public static final String IS_AUTHENTICATED_ANONYMOUSLY = "IS_AUTHENTICATED_ANONYMOUSLY";
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
|
||||
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
private boolean isFullyAuthenticated(Authentication authentication) {
|
||||
|
||||
@@ -25,10 +25,10 @@ import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
@@ -57,7 +57,7 @@ public class LabelBasedAclVoter extends AbstractAclVoter {
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private HashMap labelMap = null;
|
||||
private Map labelMap = null;
|
||||
private String attributeIndicatingLabeledOperation = null;
|
||||
private boolean allowAccessIfNoAttributesAreLabeled = true;
|
||||
|
||||
@@ -136,7 +136,7 @@ public class LabelBasedAclVoter extends AbstractAclVoter {
|
||||
* @param labelMap a map structured as in the above example.
|
||||
*
|
||||
*/
|
||||
public void setLabelMap(HashMap labelMap) {
|
||||
public void setLabelMap(Map labelMap) {
|
||||
this.labelMap = labelMap;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,92 +15,108 @@
|
||||
|
||||
package org.acegisecurity.vote;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.acegisecurity.Authentication;
|
||||
import org.acegisecurity.ConfigAttribute;
|
||||
import org.acegisecurity.ConfigAttributeDefinition;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
|
||||
/**
|
||||
* <p>Votes if any {@link ConfigAttribute#getAttribute()} starts with a prefix indicating that it is a role. The
|
||||
* default prefix string is <Code>ROLE_</code>, but this may be overriden to any value. It may also be set to empty,
|
||||
* which means that essentially any attribute will be voted on. As described further below, the effect of an empty
|
||||
* prefix may not be quite desireable.</p>
|
||||
* <p>Abstains from voting if no configuration attribute commences with the role prefix. Votes to grant access if
|
||||
* there is an exact matching {@link org.acegisecurity.GrantedAuthority} to a <code>ConfigAttribute</code> starting
|
||||
* with the role prefix. Votes to deny access if there is no exact matching <code>GrantedAuthority</code> to a
|
||||
* <code>ConfigAttribute</code> starting with the role prefix.</p>
|
||||
* <p>An empty role prefix means that the voter will vote for every ConfigAttribute. When there are different
|
||||
* categories of ConfigAttributes used, this will not be optimal since the voter will be voting for attributes which
|
||||
* do not represent roles. However, this option may be of some use when using preexisting role names without a prefix,
|
||||
* and no ability exists to prefix them with a role prefix on reading them in, such as provided for example in {@link
|
||||
* org.acegisecurity.userdetails.jdbc.JdbcDaoImpl}.</p>
|
||||
* <p>All comparisons and prefixes are case sensitive.</p>
|
||||
*
|
||||
* <p>
|
||||
* Votes if any {@link ConfigAttribute#getAttribute()} starts with a prefix
|
||||
* indicating that it is a role. The default prefix string is <Code>ROLE_</code>,
|
||||
* but this may be overriden to any value. It may also be set to empty, which
|
||||
* means that essentially any attribute will be voted on. As described further
|
||||
* below, the effect of an empty prefix may not be quite desireable.
|
||||
* </p>
|
||||
* <p>
|
||||
* Abstains from voting if no configuration attribute commences with the role
|
||||
* prefix. Votes to grant access if there is an exact matching
|
||||
* {@link org.acegisecurity.GrantedAuthority} to a <code>ConfigAttribute</code>
|
||||
* starting with the role prefix. Votes to deny access if there is no exact
|
||||
* matching <code>GrantedAuthority</code> to a <code>ConfigAttribute</code>
|
||||
* starting with the role prefix.
|
||||
* </p>
|
||||
* <p>
|
||||
* An empty role prefix means that the voter will vote for every
|
||||
* ConfigAttribute. When there are different categories of ConfigAttributes
|
||||
* used, this will not be optimal since the voter will be voting for attributes
|
||||
* which do not represent roles. However, this option may be of some use when
|
||||
* using preexisting role names without a prefix, and no ability exists to
|
||||
* prefix them with a role prefix on reading them in, such as provided for
|
||||
* example in {@link org.acegisecurity.userdetails.jdbc.JdbcDaoImpl}.
|
||||
* </p>
|
||||
* <p>
|
||||
* All comparisons and prefixes are case sensitive.
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author colin sampaleanu
|
||||
* @version $Id$
|
||||
*/
|
||||
public class RoleVoter implements AccessDecisionVoter {
|
||||
//~ Instance fields ================================================================================================
|
||||
// ~ Instance fields
|
||||
// ================================================================================================
|
||||
|
||||
private String rolePrefix = "ROLE_";
|
||||
private String rolePrefix = "ROLE_";
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
// ~ Methods
|
||||
// ========================================================================================================
|
||||
|
||||
public String getRolePrefix() {
|
||||
return rolePrefix;
|
||||
}
|
||||
public String getRolePrefix() {
|
||||
return rolePrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows the default role prefix of <code>ROLE_</code> to be overriden. May be set to an empty value,
|
||||
* although this is usually not desireable.
|
||||
*
|
||||
* @param rolePrefix the new prefix
|
||||
*/
|
||||
public void setRolePrefix(String rolePrefix) {
|
||||
this.rolePrefix = rolePrefix;
|
||||
}
|
||||
/**
|
||||
* Allows the default role prefix of <code>ROLE_</code> to be overriden.
|
||||
* May be set to an empty value, although this is usually not desireable.
|
||||
*
|
||||
* @param rolePrefix the new prefix
|
||||
*/
|
||||
public void setRolePrefix(String rolePrefix) {
|
||||
this.rolePrefix = rolePrefix;
|
||||
}
|
||||
|
||||
public boolean supports(ConfigAttribute attribute) {
|
||||
if ((attribute.getAttribute() != null) && attribute.getAttribute().startsWith(getRolePrefix())) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public boolean supports(ConfigAttribute attribute) {
|
||||
if ((attribute.getAttribute() != null) && attribute.getAttribute().startsWith(getRolePrefix())) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation supports any type of class, because it does not query the presented secure object.
|
||||
*
|
||||
* @param clazz the secure object
|
||||
*
|
||||
* @return always <code>true</code>
|
||||
*/
|
||||
public boolean supports(Class clazz) {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* This implementation supports any type of class, because it does not query
|
||||
* the presented secure object.
|
||||
*
|
||||
* @param clazz the secure object
|
||||
*
|
||||
* @return always <code>true</code>
|
||||
*/
|
||||
public boolean supports(Class clazz) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config) {
|
||||
int result = ACCESS_ABSTAIN;
|
||||
Iterator iter = config.getConfigAttributes();
|
||||
public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config) {
|
||||
int result = ACCESS_ABSTAIN;
|
||||
Iterator iter = config.getConfigAttributes();
|
||||
|
||||
while (iter.hasNext()) {
|
||||
ConfigAttribute attribute = (ConfigAttribute) iter.next();
|
||||
while (iter.hasNext()) {
|
||||
ConfigAttribute attribute = (ConfigAttribute) iter.next();
|
||||
|
||||
if (this.supports(attribute)) {
|
||||
result = ACCESS_DENIED;
|
||||
if (this.supports(attribute)) {
|
||||
result = ACCESS_DENIED;
|
||||
|
||||
// Attempt to find a matching granted authority
|
||||
for (int i = 0; i < authentication.getAuthorities().length; i++) {
|
||||
if (attribute.getAttribute().equals(authentication.getAuthorities()[i].getAuthority())) {
|
||||
return ACCESS_GRANTED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Attempt to find a matching granted authority
|
||||
for (int i = 0; i < authentication.getAuthorities().length; i++) {
|
||||
if (attribute.getAttribute().equals(authentication.getAuthorities()[i].getAuthority())) {
|
||||
return ACCESS_GRANTED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
http\://www.springframework.org/schema/security=org.acegisecurity.config.SecurityNamespaceHandler
|
||||
@@ -1,2 +0,0 @@
|
||||
http\://www.springframework.org/schema/security/spring-security-2.0.xsd=org/acegisecurity/config/spring-security-2.0.xsd
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
AuthByAdapterProvider.incorrectKey=Pou\u017eit\u00e1 implementace AuthByAdapter neobsahuje o\u010dek\u00e1van\u00fd kl\u00ed\u010d
|
||||
BasicAclEntryAfterInvocationProvider.noPermission=Autentizovan\u00fd u\u017eivatel {0} nem\u00e1 \u017d\u00c1DN\u00c1 pr\u00e1va k objektu {1}
|
||||
BasicAclEntryAfterInvocationProvider.insufficientPermission=Autentizovan\u00fd u\u017eivatel {0} nem\u00e1 po\u017eadovan\u00e1 opr\u00e1vn\u011bn\u00ed k objektu {1}
|
||||
ConcurrentSessionControllerImpl.exceededAllowed=Maxim\u00e1ln\u00ed po\u010det sou\u010dasn\u00fdch p\u0159ihl\u00e1\u0161en\u00ed {0} tohoto u\u017eivatele je p\u0159ekro\u010den.
|
||||
ProviderManager.providerNotFound=Nebyl nalezen \u017e\u00e1dn\u00fd AuthenticationProvider pro {0}
|
||||
AnonymousAuthenticationProvider.incorrectKey=Pou\u017eit\u00fd AnonymousAuthenticationToken neobsahuje o\u010dek\u00e1van\u00fd kl\u00ed\u010d
|
||||
CasAuthenticationProvider.incorrectKey=Pou\u017eit\u00fd CasAuthenticationToken neobsahuje o\u010dek\u00e1van\u00fd kl\u00ed\u010d
|
||||
CasAuthenticationProvider.noServiceTicket=Nepoda\u0159ilo se z\u00edskat otisk CAS (centr\u00e1ln\u00ed autentiza\u010dn\u00ed autority) k ov\u011b\u0159en\u00ed autenticity u\u017eivatele.
|
||||
NamedCasProxyDecider.untrusted=Nelze v\u011b\u0159it nejbli\u017e\u0161\u00ed proxy {0}
|
||||
RejectProxyTickets.reject=Otisky proxy jsou odm\u00edtnuty
|
||||
AbstractSecurityInterceptor.authenticationNotFound=Nebyl nalezen \u017e\u00e1dn\u00fd Authentication objekt v SecurityContext
|
||||
AbstractUserDetailsAuthenticationProvider.onlySupports=Je podporov\u00e1n pouze UsernamePasswordAuthenticationToken
|
||||
AbstractUserDetailsAuthenticationProvider.locked=U\u017eivatelsk\u00fd \u00fa\u010det je uzam\u010den
|
||||
AbstractUserDetailsAuthenticationProvider.disabled=U\u017eivatelsk\u00fd \u00fa\u010det nen\u00ed aktivn\u00ed
|
||||
AbstractUserDetailsAuthenticationProvider.expired=Platnost u\u017eivatelsk\u00e9ho \u00fa\u010dtu vypr\u0161ela
|
||||
AbstractUserDetailsAuthenticationProvider.credentialsExpired=Platnost u\u017eivatelsk\u00e9ho hesla vypr\u0161ela
|
||||
AbstractUserDetailsAuthenticationProvider.badCredentials=\u0160patn\u00e9 p\u0159ihla\u0161ovac\u00ed \u00fadaje
|
||||
X509AuthenticationProvider.certificateNull=Certifik\u00e1t je pr\u00e1zdn\u00fd
|
||||
DaoX509AuthoritiesPopulator.noMatching=V subjectDN nebyl nalezen \u017e\u00e1dn\u00fd \u0159et\u011bzec odpov\u00eddaj\u00edc\u00ed vy\u017eadovan\u00e9 masce: {0}
|
||||
RememberMeAuthenticationProvider.incorrectKey=Pou\u017eit\u00fd RememberMeAuthenticationToken neobsahuje o\u010dek\u00e1van\u00fd kl\u00ed\u010d
|
||||
RunAsImplAuthenticationProvider.incorrectKey=Pou\u017eit\u00fd RunAsUserToken neobsahuje o\u010dek\u00e1van\u00fd kl\u00ed\u010d
|
||||
DigestProcessingFilter.missingMandatory=Chyb\u00ed povinn\u00e1 kl\u00ed\u010dov\u00e1 polo\u017eka; p\u0159ijat\u00e1 hlavi\u010dka {0}
|
||||
DigestProcessingFilter.missingAuth=Chyb\u00ed povinn\u00e1 kl\u00ed\u010dov\u00e1 polo\u017eka 'auth' QOP (\u00farove\u0148 bezpe\u010dnosti RFC 2617); p\u0159ijat\u00e1 hlavi\u010dka {0}
|
||||
DigestProcessingFilter.incorrectRealm=Oblast odpov\u011bdi {0} neodpov\u00edd\u00e1 syst\u00e9mov\u00e9 oblasti {1}
|
||||
DigestProcessingFilter.nonceExpired=Kryptovan\u00fd kl\u00ed\u010d (nonce) vypr\u0161el
|
||||
DigestProcessingFilter.nonceEncoding=Kryptovan\u00fd kl\u00ed\u010d (nonce) nen\u00ed p\u0159ek\u00e9dov\u00e1n do Base60; p\u0159ijat\u00fd kl\u00ed\u010d {0}
|
||||
DigestProcessingFilter.nonceNotTwoTokens=Kryptovan\u00fd kl\u00ed\u010d (nonce) by m\u011bl b\u00fdt slo\u017een ze dvou \u010d\u00e1st\u00ed {0}
|
||||
DigestProcessingFilter.nonceNotNumeric=Kryptovan\u00fd kl\u00ed\u010d (nonce) by m\u011bl m\u00edt prvn\u00ed \u010d\u00e1st \u010d\u00edselnou, ale je {0}
|
||||
DigestProcessingFilter.nonceCompromised=Kryptovan\u00fd kl\u00ed\u010d (nonce) je znehodnocen\u00fd {0}
|
||||
DigestProcessingFilter.usernameNotFound=U\u017eivatelsk\u00e9 jm\u00e9no {0} nebylo nalezeno
|
||||
DigestProcessingFilter.incorrectResponse=Vadn\u00e1 odpov\u011b\u010f
|
||||
SwitchUserProcessingFilter.noCurrentUser=\u017d\u00e1dn\u00fd u\u017eivatel nen\u00ed asociov\u00e1n s t\u00edmto po\u017eadavkem
|
||||
SwitchUserProcessingFilter.noOriginalAuthentication=Nepoda\u0159ilo se nal\u00e9zt p\u016fvodn\u00ed Authentication objekt
|
||||
SwitchUserProcessingFilter.usernameNotFound=U\u017eivatelsk\u00e9 jm\u00e9no {0} nebylo nalezeno
|
||||
SwitchUserProcessingFilter.locked=U\u017eivatelsk\u00fd \u00fa\u010det je uzam\u010den
|
||||
SwitchUserProcessingFilter.disabled=U\u017eivatelsk\u00fd \u00fa\u010det nen\u00ed aktivn\u00ed
|
||||
SwitchUserProcessingFilter.expired=Platnost u\u017eivatelsk\u00e9ho \u00fa\u010dtu vypr\u0161ela
|
||||
SwitchUserProcessingFilter.credentialsExpired=Platnost u\u017eivatelsk\u00e9ho hesla vypr\u0161ela
|
||||
AbstractAccessDecisionManager.accessDenied=P\u0159\u00edstup odep\u0159en
|
||||
LdapAuthenticationProvider.emptyUsername=Nen\u00ed povoleno pr\u00e1zdn\u00e9 u\u017eivatelsk\u00e9 jm\u00e9no
|
||||
LdapAuthenticationProvider.emptyPassword=\u0160patn\u00e9 p\u0159ihla\u0161ovac\u00ed \u00fadaje
|
||||
DefaultIntitalDirContextFactory.communicationFailure=Nen\u00ed mo\u017en\u00e9 se p\u0159ipojit k LDAP serveru
|
||||
DefaultIntitalDirContextFactory.badCredentials=\u0160patn\u00e9 p\u0159ihla\u0161ovac\u00ed \u00fadaje
|
||||
DefaultIntitalDirContextFactory.unexpectedException=Nepoda\u0159ilo se z\u00edskat InitialDirContext d\u00edky neo\u010dek\u00e1van\u00e9 vyj\u00edmce
|
||||
PasswordComparisonAuthenticator.badCredentials=\u0160patn\u00e9 p\u0159ihla\u0161ovac\u00ed \u00fadaje
|
||||
BindAuthenticator.badCredentials=\u0160patn\u00e9 p\u0159ihla\u0161ovac\u00ed \u00fadaje
|
||||
BindAuthenticator.failedToLoadAttributes=\u0160patn\u00e9 p\u0159ihla\u0161ovac\u00ed \u00fadaje
|
||||
@@ -0,0 +1,48 @@
|
||||
AuthByAdapterProvider.incorrectKey=Die angegebene AuthByAdapter-Implementierung enthält nicht den erwarteten Schlüssel
|
||||
BasicAclEntryAfterInvocationProvider.noPermission=Authentifikation {0} hat KEINE Berechtigungen bezüglich des Domänen-Objekts {1}
|
||||
BasicAclEntryAfterInvocationProvider.insufficientPermission=Authentifikation {0} hat keine ausreichenden ACL-Berechtigungen bezüglich des Domänen-Objekts {1}
|
||||
ConcurrentSessionControllerImpl.exceededAllowed=Die maximale Sitzungs-Anzahl von {0} für diesen Nutzer wurde überschritten
|
||||
ProviderManager.providerNotFound=Für {0} wurde kein AuthenticationProvider gefunden
|
||||
AnonymousAuthenticationProvider.incorrectKey=Das angegebene AnonymousAuthenticationToken enthält nicht den erwarteten Schlüssel
|
||||
CasAuthenticationProvider.incorrectKey=Das angegebene CasAuthenticationToken enthält nicht den erwarteten Schlüssel
|
||||
CasAuthenticationProvider.noServiceTicket=Es konnte kein CAS Service-Ticket zur Prüfung geliefert werden
|
||||
NamedCasProxyDecider.untrusted=Der nächste Proxy {0} ist nicht vertrauenswürdig
|
||||
RejectProxyTickets.reject=Proxy-Tickets sind abgelehnt
|
||||
AbstractSecurityInterceptor.authenticationNotFound=Im SecurityContext wurde keine Authentifikation gefunden
|
||||
AbstractUserDetailsAuthenticationProvider.onlySupports=Nur UsernamePasswordAuthenticationToken wird unterstützt
|
||||
AbstractUserDetailsAuthenticationProvider.locked=Das Benutzerkonto ist gesperrt
|
||||
AbstractUserDetailsAuthenticationProvider.disabled=Der Benutzer ist deaktiviert
|
||||
AbstractUserDetailsAuthenticationProvider.expired=Die Gültigkeit des Benutzerkontos ist abgelaufen
|
||||
AbstractUserDetailsAuthenticationProvider.credentialsExpired=Die Gültigkeit der Benutzerberechtigungen ist abgelaufen
|
||||
AbstractUserDetailsAuthenticationProvider.badCredentials=Ungültige Benutzerberechtigungen
|
||||
X509AuthenticationProvider.certificateNull=Zertifikat ist nicht gesetzt
|
||||
DaoX509AuthoritiesPopulator.noMatching=Kein passendes Muster gefunden in subjectDN: {0}
|
||||
RememberMeAuthenticationProvider.incorrectKey=Das angegebene RememberMeAuthenticationToken enthält nicht den erwarteten Schlüssel
|
||||
RunAsImplAuthenticationProvider.incorrectKey=Das angegebene RunAsUserToken enthält nicht den erwarteten Schlüssel
|
||||
DigestProcessingFilter.missingMandatory=Erforderlicher Digest-Wert fehlt; Empfangener Header {0}
|
||||
DigestProcessingFilter.missingAuth=Erforderlicher Digest-Wert fehlt für 'auth' QOP; Empfangener Header {0}
|
||||
DigestProcessingFilter.incorrectRealm=Realm-Name in Antwort {0} entspricht nicht dem Namen des System-Realms {1}
|
||||
DigestProcessingFilter.nonceExpired=Die Nonce ist nicht mehr gültig
|
||||
DigestProcessingFilter.nonceEncoding=Die Nonce ist nicht in Base64 kodiert; Empfangene Nonce {0}
|
||||
DigestProcessingFilter.nonceNotTwoTokens=Nonce sollte zwei Elemente beinhalten. Gefundener Inhalt: {0}
|
||||
DigestProcessingFilter.nonceNotNumeric=Das erste Element der Nonce sollte numerisch sein. Gefundener Inhalt: {0}
|
||||
DigestProcessingFilter.nonceCompromised=Das Nonce-Element ist kompromittiert {0}
|
||||
DigestProcessingFilter.usernameNotFound=Benutzername {0} wurde nicht gefunden
|
||||
DigestProcessingFilter.incorrectResponse=Fehlerhafte Antwort
|
||||
SwitchUserProcessingFilter.noCurrentUser=Mit dieser Anfrage ist kein aktueller Benutzer assoziiert
|
||||
SwitchUserProcessingFilter.noOriginalAuthentication=Kann das ursprüngliche Authentifikationsobjekt nicht finden
|
||||
SwitchUserProcessingFilter.usernameNotFound=Benutzername {0} wurde nicht gefunden
|
||||
SwitchUserProcessingFilter.locked=Das Benutzerkonto ist gesperrt
|
||||
SwitchUserProcessingFilter.disabled=Der Benutzer ist deaktiviert
|
||||
SwitchUserProcessingFilter.expired=Die Gültigkeit des Benutzerkontos ist abgelaufen
|
||||
SwitchUserProcessingFilter.credentialsExpired=Die Gültigkeit der Benutzerberechtigungen ist abgelaufen
|
||||
AbstractAccessDecisionManager.accessDenied=Zugriff verweigert
|
||||
LdapAuthenticationProvider.emptyUsername=Ein leerer Benutzername ist nicht erlaubt
|
||||
LdapAuthenticationProvider.emptyPassword=Ungültige Benutzerberechtigungen
|
||||
DefaultIntitalDirContextFactory.communicationFailure=Kann keine Verbindung zum LDAP-Server herstellen
|
||||
DefaultIntitalDirContextFactory.badCredentials=Ungültige Benutzerberechtigungen
|
||||
DefaultIntitalDirContextFactory.unexpectedException=Auf den InitialDirContext kann aufgrund eines unerwarteten Fehlers nicht zugegriffen werden
|
||||
PasswordComparisonAuthenticator.badCredentials=Ungültige Benutzerberechtigungen
|
||||
BindAuthenticator.badCredentials=Ungültige Benutzerberechtigungen
|
||||
BindAuthenticator.failedToLoadAttributes=Ungültige Benutzerberechtigungen
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
# Acegi security
|
||||
# Messages in French
|
||||
# Translation by Laurent Pireyn (laurent.pireyn@pisolutions.eu)
|
||||
|
||||
AuthByAdapterProvider.incorrectKey = L'implémentation de AuthByAdapter présentée ne contient pas la clé attendue
|
||||
BasicAclEntryAfterInvocationProvider.noPermission = L'authentification {0} n'a AUCUNE permission pour l'objet domaine {1}
|
||||
BasicAclEntryAfterInvocationProvider.insufficientPermission = L'authentification {0} a des permissions ACL pour l'objet domaine, mais pas la permission ACL requise pour l'objet domaine {1}
|
||||
ConcurrentSessionControllerImpl.exceededAllowed = Le maximum de {0} sessions a été dépassé pour cet utilisateur
|
||||
ProviderManager.providerNotFound = Aucun AuthenticationProvider n'a été trouvé pour {0}
|
||||
AnonymousAuthenticationProvider.incorrectKey = L'AnonymousAuthenticationToken présenté ne contient pas la clé attendue
|
||||
CasAuthenticationProvider.incorrectKey = Le CasAuthenticationToken présenté ne contient pas la clé attendue
|
||||
CasAuthenticationProvider.noServiceTicket = Echec d'obtention d'un ticket CAS à valider
|
||||
NamedCasProxyDecider.untrusted = Le proxy {0} le plus proche n'est pas fiable
|
||||
RejectProxyTickets.reject = Des tickets proxy ont été rejetés
|
||||
AbstractSecurityInterceptor.authenticationNotFound = Aucun objet Authentication n'a été trouvé dans le SecurityContext
|
||||
AbstractUserDetailsAuthenticationProvider.onlySupports = Seul UsernamePasswordAuthenticationToken est pris en charge
|
||||
AbstractUserDetailsAuthenticationProvider.locked = Le compte utilisateur est bloqué
|
||||
AbstractUserDetailsAuthenticationProvider.disabled = Le compte utilisateur est désactivé
|
||||
AbstractUserDetailsAuthenticationProvider.expired = Le compte utilisateur a expiré
|
||||
AbstractUserDetailsAuthenticationProvider.credentialsExpired = Les créances de l'utilisateur ont expiré
|
||||
AbstractUserDetailsAuthenticationProvider.badCredentials = Les créances sont erronées
|
||||
X509AuthenticationProvider.certificateNull = Le certificat est null
|
||||
DaoX509AuthoritiesPopulator.noMatching = Aucun motif concordant n'a été trouvé dans le subjectDN: {0}
|
||||
RememberMeAuthenticationProvider.incorrectKey = Le RememberMeAuthenticationToken présenté ne contient pas la clé attendue
|
||||
RunAsImplAuthenticationProvider.incorrectKey = Le RunAsUserToken présenté ne contient pas la clé attendue
|
||||
DigestProcessingFilter.missingMandatory = Une valeur obligatoire manque au condensé; reçu l'entête {0}
|
||||
DigestProcessingFilter.missingAuth = Une valeur obligatoire manque au condensé pour 'auth' QOP; reçu l'entête {0}
|
||||
DigestProcessingFilter.incorrectRealm = Le nom de domaine de la réponse {0} ne correspond pas au nom de domaine du système {1}
|
||||
DigestProcessingFilter.nonceExpired = Le nonce a expiré
|
||||
DigestProcessingFilter.nonceEncoding = Le nonce n'est pas encodé en Base64; reçu le nonce {0}
|
||||
DigestProcessingFilter.nonceNotTwoTokens = Le nonce aurait dû générer deux jetons, mais était {0}
|
||||
DigestProcessingFilter.nonceNotNumeric = Le jeton nonce aurait dû générer d'abord un jeton numérique, mais était {0}
|
||||
DigestProcessingFilter.nonceCompromised = Le jeton nonce est compromis {0}
|
||||
DigestProcessingFilter.usernameNotFound = Le nom d'utilisateur {0} n'a pas été trouvé
|
||||
DigestProcessingFilter.incorrectResponse = Réponse incorrecte
|
||||
SwitchUserProcessingFilter.noCurrentUser = Aucun utilisateur n'est associé à la requête en cours
|
||||
SwitchUserProcessingFilter.noOriginalAuthentication = L'objet Authentication original n'a pas été trouvé
|
||||
SwitchUserProcessingFilter.usernameNotFound = Le nom d'utilisateur {0} n'a pas été trouvé
|
||||
SwitchUserProcessingFilter.locked = Le compte utilisateur est bloqué
|
||||
SwitchUserProcessingFilter.disabled = Le compte utilisateur est désactivé
|
||||
SwitchUserProcessingFilter.expired = Le compte utilisateur a expiré
|
||||
SwitchUserProcessingFilter.credentialsExpired = Les créances de l'utilisateur ont expiré
|
||||
AbstractAccessDecisionManager.accessDenied = Accès refusé
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
|
||||
<project name="Acegi Security Core">
|
||||
|
||||
<body>
|
||||
<menu ref="parent"/>
|
||||
<menu ref="reports"/>
|
||||
</body>
|
||||
|
||||
</project>
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.acegisecurity.ui.AuthenticationEntryPoint;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
|
||||
/**
|
||||
@@ -33,22 +32,13 @@ import org.springframework.core.Ordered;
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class MockAuthenticationEntryPoint implements AuthenticationEntryPoint, Ordered {
|
||||
public class MockAuthenticationEntryPoint implements AuthenticationEntryPoint {
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private String url;
|
||||
private int order = Integer.MAX_VALUE; // ~ default
|
||||
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
public MockAuthenticationEntryPoint(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
+4
-8
@@ -17,7 +17,7 @@ package org.acegisecurity.acl.basic.cache;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import net.sf.ehcache.Cache;
|
||||
import net.sf.ehcache.Ehcache;
|
||||
|
||||
import org.acegisecurity.MockApplicationContext;
|
||||
|
||||
@@ -56,14 +56,10 @@ public class EhCacheBasedAclEntryCacheTests extends TestCase {
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
private Cache getCache() {
|
||||
private Ehcache getCache() {
|
||||
ApplicationContext ctx = MockApplicationContext.getContext();
|
||||
|
||||
return (Cache) ctx.getBean("eHCacheBackend");
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
junit.textui.TestRunner.run(EhCacheBasedAclEntryCacheTests.class);
|
||||
return (Ehcache) ctx.getBean("eHCacheBackend");
|
||||
}
|
||||
|
||||
public final void setUp() throws Exception {
|
||||
@@ -99,7 +95,7 @@ public class EhCacheBasedAclEntryCacheTests extends TestCase {
|
||||
assertTrue(true);
|
||||
}
|
||||
|
||||
Cache myCache = getCache();
|
||||
Ehcache myCache = getCache();
|
||||
cache.setCache(myCache);
|
||||
assertEquals(myCache, cache.getCache());
|
||||
}
|
||||
|
||||
+4
-2
@@ -51,7 +51,9 @@ public class AlwaysTestAfterTimeInMillisCaptchaChannelProcessorTests extends Tes
|
||||
context.setHuman();
|
||||
assertFalse(alwaysTestAfterTimeInMillisCaptchaChannelProcessor.isContextValidConcerningHumanity(context));
|
||||
}
|
||||
|
||||
/* Commented out as it makes assumptions about the speed of the build server and fails intermittently on
|
||||
build.springframework.org - L.T.
|
||||
|
||||
public void testIsContextValidConcerningHumanity()
|
||||
throws Exception {
|
||||
CaptchaSecurityContext context = new CaptchaSecurityContextImpl();
|
||||
@@ -72,7 +74,7 @@ public class AlwaysTestAfterTimeInMillisCaptchaChannelProcessorTests extends Tes
|
||||
|
||||
assertFalse(alwaysTestAfterTimeInMillisCaptchaChannelProcessor.isContextValidConcerningHumanity(context));
|
||||
}
|
||||
|
||||
*/
|
||||
public void testNewContext() {
|
||||
CaptchaSecurityContext context = new CaptchaSecurityContextImpl();
|
||||
|
||||
|
||||
+4
-2
@@ -43,7 +43,7 @@ public class AlwaysTestBelowAverageTimeInMillisBetweenRequestsChannelProcessorTe
|
||||
context.setHuman();
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
/*
|
||||
while ((System.currentTimeMillis() - now) <= 100) {
|
||||
assertTrue(alwaysTestBelowAverageTimeInMillisBetweenRequestsChannelProcessor
|
||||
.isContextValidConcerningHumanity(context));
|
||||
@@ -64,8 +64,9 @@ public class AlwaysTestBelowAverageTimeInMillisBetweenRequestsChannelProcessorTe
|
||||
assertFalse(alwaysTestBelowAverageTimeInMillisBetweenRequestsChannelProcessor.isContextValidConcerningHumanity(
|
||||
context));
|
||||
alwaysTestBelowAverageTimeInMillisBetweenRequestsChannelProcessor.setThresold(0);
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
public void testIsContextValidConcerningHumanity()
|
||||
throws Exception {
|
||||
CaptchaSecurityContext context = new CaptchaSecurityContextImpl();
|
||||
@@ -112,4 +113,5 @@ public class AlwaysTestBelowAverageTimeInMillisBetweenRequestsChannelProcessorTe
|
||||
.isContextValidConcerningHumanity(context));
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
+213
@@ -0,0 +1,213 @@
|
||||
/* 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.acegisecurity.concurrent;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Tests concurrency access to SessionRegistryImpl.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public class SessionRegistryImplMultithreadedTests extends TestCase {
|
||||
private static final Random rnd = new Random();
|
||||
private static boolean errorOccurred;
|
||||
|
||||
protected void setUp() throws Exception {
|
||||
errorOccurred = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reproduces the NPE mentioned in SEC-484 where a sessionId is removed from
|
||||
* the set of sessions before it is removed from the list of sessions for a principal.
|
||||
* getAllSessions(principal, false) then finds the sessionId in the principal's session list
|
||||
* but reads null for the SessionInformation with the same Id.
|
||||
* Note that this is not guaranteed to produce the error but is a good testing point. Increasing the number
|
||||
* of sessions makes a failure more likely, but slows the test considerably.
|
||||
* Inserting temporary sleep statements in SessionRegistryClassImpl will also help.
|
||||
*/
|
||||
public void testConcurrencyOfReadAndRemoveIsSafe() {
|
||||
Object principal = "Joe Principal";
|
||||
SessionRegistryImpl sessionregistry = new SessionRegistryImpl();
|
||||
Set sessions = Collections.synchronizedSet(new HashSet());
|
||||
// Register some sessions
|
||||
for (int i = 0; i < 50; i++) {
|
||||
String sessionId = Integer.toString(i);
|
||||
sessions.add(sessionId);
|
||||
sessionregistry.registerNewSession(sessionId, principal);
|
||||
}
|
||||
|
||||
// Pile of readers to hammer the getAllSessions method.
|
||||
for (int i=0; i < 10; i++) {
|
||||
Thread reader = new Thread(new SessionRegistryReader(principal, sessionregistry));
|
||||
reader.start();
|
||||
}
|
||||
|
||||
Thread remover = new Thread(new SessionRemover("remover", sessionregistry, sessions));
|
||||
|
||||
remover.start();
|
||||
|
||||
while(remover.isAlive()) {
|
||||
pause(250);
|
||||
}
|
||||
|
||||
assertFalse("Thread errors detected; review log output for details", errorOccurred);
|
||||
}
|
||||
|
||||
public void testConcurrentRemovalIsSafe() {
|
||||
Object principal = "Some principal object";
|
||||
SessionRegistryImpl sessionregistry = new SessionRegistryImpl();
|
||||
// The session list (effectivelly the containers sessions).
|
||||
Set sessions = Collections.synchronizedSet(new HashSet());
|
||||
Thread registerer = new Thread(new SessionRegisterer(principal, sessionregistry, 100, sessions));
|
||||
|
||||
registerer.start();
|
||||
|
||||
int nRemovers = 4;
|
||||
|
||||
SessionRemover[] removers = new SessionRemover[nRemovers];
|
||||
Thread[] removerThreads = new Thread[nRemovers];
|
||||
|
||||
for (int i = 0; i < removers.length; i++) {
|
||||
removers[i] = new SessionRemover("remover" + i, sessionregistry, sessions);
|
||||
removerThreads[i] = new Thread(removers[i], "remover" + i);
|
||||
removerThreads[i].start();
|
||||
}
|
||||
|
||||
while (stillRunning(removerThreads)) {
|
||||
pause(500);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean stillRunning(Thread[] threads) {
|
||||
for (int i = 0; i < threads.length; i++) {
|
||||
if (threads[i].isAlive()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static class SessionRegisterer implements Runnable {
|
||||
private SessionRegistry sessionregistry;
|
||||
private int nIterations;
|
||||
private Set sessionList;
|
||||
private Object principal;
|
||||
|
||||
public SessionRegisterer(Object principal, SessionRegistry sessionregistry, int nIterations, Set sessionList) {
|
||||
this.sessionregistry = sessionregistry;
|
||||
this.nIterations = nIterations;
|
||||
this.sessionList = sessionList;
|
||||
this.principal = principal;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
for (int i=0; i < nIterations && !errorOccurred; i++) {
|
||||
String sessionId = Integer.toString(i);
|
||||
sessionList.add(sessionId);
|
||||
try {
|
||||
sessionregistry.registerNewSession(sessionId,principal);
|
||||
pause(20);
|
||||
Thread.yield();
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
errorOccurred = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class SessionRegistryReader implements Runnable {
|
||||
private SessionRegistry sessionRegistry;
|
||||
private Object principal;
|
||||
|
||||
public SessionRegistryReader(Object principal, SessionRegistry sessionregistry) {
|
||||
this.sessionRegistry = sessionregistry;
|
||||
this.principal = principal;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
while (!errorOccurred) {
|
||||
try {
|
||||
sessionRegistry.getAllSessions(principal, false);
|
||||
sessionRegistry.getAllPrincipals();
|
||||
sessionRegistry.getAllSessions(principal, true);
|
||||
Thread.yield();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
errorOccurred = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class SessionRemover implements Runnable {
|
||||
private SessionRegistry sessionregistry;
|
||||
private Set sessionList;
|
||||
private String name;
|
||||
|
||||
public SessionRemover(String name, SessionRegistry sessionregistry, Set sessionList) {
|
||||
this.name = name;
|
||||
this.sessionregistry = sessionregistry;
|
||||
this.sessionList = sessionList;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
boolean finished = false;
|
||||
|
||||
while (!finished && !errorOccurred) {
|
||||
if (sessionList.isEmpty()) {
|
||||
finished = true;
|
||||
// List of sessions appears to be empty but give it a chance to fill up again
|
||||
System.out.println(name + ": Session list empty. Waiting.");
|
||||
pause(500);
|
||||
}
|
||||
|
||||
Object[] sessions = sessionList.toArray();
|
||||
|
||||
if (sessions.length > 0) {
|
||||
finished = false;
|
||||
String sessionId = (String) sessions[0];
|
||||
// System.out.println(name + ": removing " + sessionId);
|
||||
try {
|
||||
sessionregistry.removeSessionInformation(sessionId);
|
||||
|
||||
pause(rnd.nextInt(100));
|
||||
|
||||
sessionList.remove(sessionId);
|
||||
Thread.yield();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
errorOccurred = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void pause(int length) {
|
||||
try {
|
||||
Thread.sleep(length);
|
||||
} catch (InterruptedException ignore) {}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@ import java.util.Date;
|
||||
/**
|
||||
* Tests {@link SessionRegistryImpl}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class SessionRegistryImplTests extends TestCase {
|
||||
|
||||
+289
-209
@@ -23,6 +23,7 @@ import org.acegisecurity.GrantedAuthorityImpl;
|
||||
import org.acegisecurity.MockFilterConfig;
|
||||
|
||||
import org.acegisecurity.adapters.PrincipalAcegiUserToken;
|
||||
import org.jmock.Mock;
|
||||
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
@@ -36,271 +37,350 @@ import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
|
||||
|
||||
/**
|
||||
* Tests {@link HttpSessionContextIntegrationFilter}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
* @version $Id: HttpSessionContextIntegrationFilterTests.java 1858 2007-05-24
|
||||
* 02:04:47Z benalex $
|
||||
*/
|
||||
public class HttpSessionContextIntegrationFilterTests extends TestCase {
|
||||
//~ Constructors ===================================================================================================
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
public HttpSessionContextIntegrationFilterTests() {
|
||||
super();
|
||||
}
|
||||
public HttpSessionContextIntegrationFilterTests() {
|
||||
}
|
||||
|
||||
public HttpSessionContextIntegrationFilterTests(String arg0) {
|
||||
super(arg0);
|
||||
}
|
||||
public HttpSessionContextIntegrationFilterTests(String arg0) {
|
||||
super(arg0);
|
||||
}
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
private void executeFilterInContainerSimulator(FilterConfig filterConfig, Filter filter, ServletRequest request,
|
||||
ServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
filter.init(filterConfig);
|
||||
filter.doFilter(request, response, filterChain);
|
||||
filter.destroy();
|
||||
}
|
||||
private static void executeFilterInContainerSimulator(
|
||||
FilterConfig filterConfig, Filter filter, ServletRequest request,
|
||||
ServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
filter.init(filterConfig);
|
||||
filter.doFilter(request, response, filterChain);
|
||||
filter.destroy();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
junit.textui.TestRunner.run(HttpSessionContextIntegrationFilterTests.class);
|
||||
}
|
||||
public void testDetectsIncompatibleSessionProperties() throws Exception {
|
||||
HttpSessionContextIntegrationFilter filter = new HttpSessionContextIntegrationFilter();
|
||||
|
||||
public void testDetectsIncompatibleSessionProperties()
|
||||
throws Exception {
|
||||
HttpSessionContextIntegrationFilter filter = new HttpSessionContextIntegrationFilter();
|
||||
try {
|
||||
filter.setAllowSessionCreation(false);
|
||||
filter.setForceEagerSessionCreation(true);
|
||||
filter.afterPropertiesSet();
|
||||
fail("Shown have thrown IllegalArgumentException");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertTrue(true);
|
||||
}
|
||||
|
||||
try {
|
||||
filter.setAllowSessionCreation(false);
|
||||
filter.setForceEagerSessionCreation(true);
|
||||
filter.afterPropertiesSet();
|
||||
fail("Shown have thrown IllegalArgumentException");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertTrue(true);
|
||||
}
|
||||
filter.setAllowSessionCreation(true);
|
||||
filter.afterPropertiesSet();
|
||||
assertTrue(true);
|
||||
}
|
||||
|
||||
filter.setAllowSessionCreation(true);
|
||||
filter.afterPropertiesSet();
|
||||
assertTrue(true);
|
||||
}
|
||||
public void testDetectsMissingOrInvalidContext() throws Exception {
|
||||
HttpSessionContextIntegrationFilter filter = new HttpSessionContextIntegrationFilter();
|
||||
|
||||
public void testDetectsMissingOrInvalidContext() throws Exception {
|
||||
HttpSessionContextIntegrationFilter filter = new HttpSessionContextIntegrationFilter();
|
||||
try {
|
||||
filter.setContext(null);
|
||||
filter.afterPropertiesSet();
|
||||
fail("Shown have thrown IllegalArgumentException");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertTrue(true);
|
||||
}
|
||||
|
||||
try {
|
||||
filter.setContext(null);
|
||||
filter.afterPropertiesSet();
|
||||
fail("Shown have thrown IllegalArgumentException");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertTrue(true);
|
||||
}
|
||||
try {
|
||||
filter.setContext(Integer.class);
|
||||
assertEquals(Integer.class, filter.getContext());
|
||||
filter.afterPropertiesSet();
|
||||
fail("Shown have thrown IllegalArgumentException");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertTrue(true);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
filter.setContext(Integer.class);
|
||||
assertEquals(Integer.class, filter.getContext());
|
||||
filter.afterPropertiesSet();
|
||||
fail("Shown have thrown IllegalArgumentException");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertTrue(true);
|
||||
}
|
||||
}
|
||||
public void testExceptionWithinFilterChainStillClearsSecurityContextHolder() throws Exception {
|
||||
// Build an Authentication object we simulate came from HttpSession
|
||||
PrincipalAcegiUserToken sessionPrincipal = new PrincipalAcegiUserToken(
|
||||
"key",
|
||||
"someone",
|
||||
"password",
|
||||
new GrantedAuthority[] { new GrantedAuthorityImpl("SOME_ROLE") },
|
||||
null);
|
||||
|
||||
public void testExceptionWithinFilterChainStillClearsSecurityContextHolder()
|
||||
throws Exception {
|
||||
// Build an Authentication object we simulate came from HttpSession
|
||||
PrincipalAcegiUserToken sessionPrincipal = new PrincipalAcegiUserToken("key", "someone", "password",
|
||||
new GrantedAuthority[] {new GrantedAuthorityImpl("SOME_ROLE")}, null);
|
||||
// Build a Context to store in HttpSession (simulating prior request)
|
||||
SecurityContext sc = new SecurityContextImpl();
|
||||
sc.setAuthentication(sessionPrincipal);
|
||||
|
||||
// Build a Context to store in HttpSession (simulating prior request)
|
||||
SecurityContext sc = new SecurityContextImpl();
|
||||
sc.setAuthentication(sessionPrincipal);
|
||||
// Build a mock request
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.getSession().setAttribute(
|
||||
HttpSessionContextIntegrationFilter.ACEGI_SECURITY_CONTEXT_KEY,
|
||||
sc);
|
||||
|
||||
// Build a mock request
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.getSession().setAttribute(HttpSessionContextIntegrationFilter.ACEGI_SECURITY_CONTEXT_KEY, sc);
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
FilterChain chain = new MockFilterChain(sessionPrincipal, null,
|
||||
new IOException());
|
||||
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
FilterChain chain = new MockFilterChain(sessionPrincipal, null, new IOException());
|
||||
// Prepare filter
|
||||
HttpSessionContextIntegrationFilter filter = new HttpSessionContextIntegrationFilter();
|
||||
filter.setContext(SecurityContextImpl.class);
|
||||
filter.afterPropertiesSet();
|
||||
|
||||
// Prepare filter
|
||||
HttpSessionContextIntegrationFilter filter = new HttpSessionContextIntegrationFilter();
|
||||
filter.setContext(SecurityContextImpl.class);
|
||||
filter.afterPropertiesSet();
|
||||
// Execute filter
|
||||
try {
|
||||
executeFilterInContainerSimulator(new MockFilterConfig(), filter,
|
||||
request, response, chain);
|
||||
fail("We should have received the IOException thrown inside the filter chain here");
|
||||
} catch (IOException ioe) {
|
||||
assertTrue(true);
|
||||
}
|
||||
|
||||
// Execute filter
|
||||
try {
|
||||
executeFilterInContainerSimulator(new MockFilterConfig(), filter, request, response, chain);
|
||||
fail("We should have received the IOException thrown inside the filter chain here");
|
||||
} catch (IOException ioe) {
|
||||
assertTrue(true);
|
||||
}
|
||||
// Check the SecurityContextHolder is null, even though an exception was
|
||||
// thrown during chain
|
||||
assertEquals(new SecurityContextImpl(), SecurityContextHolder.getContext());
|
||||
assertNull("Should have cleared FILTER_APPLIED",
|
||||
request.getAttribute(HttpSessionContextIntegrationFilter.FILTER_APPLIED));
|
||||
}
|
||||
|
||||
// Check the SecurityContextHolder is null, even though an exception was thrown during chain
|
||||
assertEquals(new SecurityContextImpl(), SecurityContextHolder.getContext());
|
||||
assertNull("Should have cleared FILTER_APPLIED", request.getAttribute(HttpSessionContextIntegrationFilter.FILTER_APPLIED));
|
||||
}
|
||||
public void testExistingContextContentsCopiedIntoContextHolderFromSessionAndChangesToContextCopiedBackToSession()
|
||||
throws Exception {
|
||||
// Build an Authentication object we simulate came from HttpSession
|
||||
PrincipalAcegiUserToken sessionPrincipal = new PrincipalAcegiUserToken(
|
||||
"key",
|
||||
"someone",
|
||||
"password",
|
||||
new GrantedAuthority[] { new GrantedAuthorityImpl("SOME_ROLE") },
|
||||
null);
|
||||
|
||||
public void testExistingContextContentsCopiedIntoContextHolderFromSessionAndChangesToContextCopiedBackToSession()
|
||||
throws Exception {
|
||||
// Build an Authentication object we simulate came from HttpSession
|
||||
PrincipalAcegiUserToken sessionPrincipal = new PrincipalAcegiUserToken("key", "someone", "password",
|
||||
new GrantedAuthority[] {new GrantedAuthorityImpl("SOME_ROLE")}, null);
|
||||
// Build an Authentication object we simulate our Authentication changed
|
||||
// it to
|
||||
PrincipalAcegiUserToken updatedPrincipal = new PrincipalAcegiUserToken(
|
||||
"key", "someone", "password",
|
||||
new GrantedAuthority[] { new GrantedAuthorityImpl(
|
||||
"SOME_DIFFERENT_ROLE") }, null);
|
||||
|
||||
// Build an Authentication object we simulate our Authentication changed it to
|
||||
PrincipalAcegiUserToken updatedPrincipal = new PrincipalAcegiUserToken("key", "someone", "password",
|
||||
new GrantedAuthority[] {new GrantedAuthorityImpl("SOME_DIFFERENT_ROLE")}, null);
|
||||
// Build a Context to store in HttpSession (simulating prior request)
|
||||
SecurityContext sc = new SecurityContextImpl();
|
||||
sc.setAuthentication(sessionPrincipal);
|
||||
|
||||
// Build a Context to store in HttpSession (simulating prior request)
|
||||
SecurityContext sc = new SecurityContextImpl();
|
||||
sc.setAuthentication(sessionPrincipal);
|
||||
// Build a mock request
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.getSession().setAttribute(
|
||||
HttpSessionContextIntegrationFilter.ACEGI_SECURITY_CONTEXT_KEY,
|
||||
sc);
|
||||
|
||||
// Build a mock request
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.getSession().setAttribute(HttpSessionContextIntegrationFilter.ACEGI_SECURITY_CONTEXT_KEY, sc);
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
FilterChain chain = new MockFilterChain(sessionPrincipal,
|
||||
updatedPrincipal, null);
|
||||
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
FilterChain chain = new MockFilterChain(sessionPrincipal, updatedPrincipal, null);
|
||||
// Prepare filter
|
||||
HttpSessionContextIntegrationFilter filter = new HttpSessionContextIntegrationFilter();
|
||||
filter.setContext(SecurityContextImpl.class);
|
||||
filter.afterPropertiesSet();
|
||||
|
||||
// Prepare filter
|
||||
HttpSessionContextIntegrationFilter filter = new HttpSessionContextIntegrationFilter();
|
||||
filter.setContext(SecurityContextImpl.class);
|
||||
filter.afterPropertiesSet();
|
||||
// Execute filter
|
||||
executeFilterInContainerSimulator(new MockFilterConfig(), filter,
|
||||
request, response, chain);
|
||||
|
||||
// Execute filter
|
||||
executeFilterInContainerSimulator(new MockFilterConfig(), filter, request, response, chain);
|
||||
// Obtain new/update Authentication from HttpSession
|
||||
SecurityContext context = (SecurityContext) request.getSession().getAttribute(
|
||||
HttpSessionContextIntegrationFilter.ACEGI_SECURITY_CONTEXT_KEY);
|
||||
assertEquals(updatedPrincipal, ((SecurityContext) context).getAuthentication());
|
||||
}
|
||||
|
||||
// Obtain new/update Authentication from HttpSession
|
||||
SecurityContext context = (SecurityContext) request.getSession()
|
||||
.getAttribute(HttpSessionContextIntegrationFilter.ACEGI_SECURITY_CONTEXT_KEY);
|
||||
assertEquals(updatedPrincipal, ((SecurityContext) context).getAuthentication());
|
||||
}
|
||||
public void testHttpSessionCreatedWhenContextHolderChanges() throws Exception {
|
||||
// Build an Authentication object we simulate our Authentication changed it to
|
||||
PrincipalAcegiUserToken updatedPrincipal = new PrincipalAcegiUserToken(
|
||||
"key", "someone", "password",
|
||||
new GrantedAuthority[] { new GrantedAuthorityImpl(
|
||||
"SOME_DIFFERENT_ROLE") }, null);
|
||||
|
||||
public void testHttpSessionCreatedWhenContextHolderChanges()
|
||||
throws Exception {
|
||||
// Build an Authentication object we simulate our Authentication changed it to
|
||||
PrincipalAcegiUserToken updatedPrincipal = new PrincipalAcegiUserToken("key", "someone", "password",
|
||||
new GrantedAuthority[] {new GrantedAuthorityImpl("SOME_DIFFERENT_ROLE")}, null);
|
||||
// Build a mock request
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
FilterChain chain = new MockFilterChain(null, updatedPrincipal, null);
|
||||
|
||||
// Build a mock request
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
FilterChain chain = new MockFilterChain(null, updatedPrincipal, null);
|
||||
// Prepare filter
|
||||
HttpSessionContextIntegrationFilter filter = new HttpSessionContextIntegrationFilter();
|
||||
filter.setContext(SecurityContextImpl.class);
|
||||
// don't call afterPropertiesSet to test case when Spring filter.afterPropertiesSet(); isn't called
|
||||
|
||||
// Prepare filter
|
||||
HttpSessionContextIntegrationFilter filter = new HttpSessionContextIntegrationFilter();
|
||||
filter.setContext(SecurityContextImpl.class);
|
||||
// don't call afterPropertiesSet to test case when not instantiated by Spring
|
||||
//filter.afterPropertiesSet();
|
||||
// Execute filter
|
||||
executeFilterInContainerSimulator(new MockFilterConfig(), filter, request, response, chain);
|
||||
|
||||
// Execute filter
|
||||
executeFilterInContainerSimulator(new MockFilterConfig(), filter, request, response, chain);
|
||||
// Obtain new/updated Authentication from HttpSession
|
||||
SecurityContext context = (SecurityContext) request.getSession(false).getAttribute(
|
||||
HttpSessionContextIntegrationFilter.ACEGI_SECURITY_CONTEXT_KEY);
|
||||
assertEquals(updatedPrincipal, ((SecurityContext) context).getAuthentication());
|
||||
}
|
||||
|
||||
// Obtain new/updated Authentication from HttpSession
|
||||
SecurityContext context = (SecurityContext) request.getSession(false)
|
||||
.getAttribute(HttpSessionContextIntegrationFilter.ACEGI_SECURITY_CONTEXT_KEY);
|
||||
assertEquals(updatedPrincipal, ((SecurityContext) context).getAuthentication());
|
||||
}
|
||||
public void testHttpSessionEagerlyCreatedWhenDirected() throws Exception {
|
||||
// Build a mock request
|
||||
MockHttpServletRequest request = new MockHttpServletRequest(null, null);
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
FilterChain chain = new MockFilterChain(null, null, null);
|
||||
|
||||
public void testHttpSessionEagerlyCreatedWhenDirected()
|
||||
throws Exception {
|
||||
// Build a mock request
|
||||
MockHttpServletRequest request = new MockHttpServletRequest(null, null);
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
FilterChain chain = new MockFilterChain(null, null, null);
|
||||
// Prepare filter
|
||||
HttpSessionContextIntegrationFilter filter = new HttpSessionContextIntegrationFilter();
|
||||
filter.setContext(SecurityContextImpl.class);
|
||||
filter.setForceEagerSessionCreation(true); // non-default
|
||||
filter.afterPropertiesSet();
|
||||
|
||||
// Prepare filter
|
||||
HttpSessionContextIntegrationFilter filter = new HttpSessionContextIntegrationFilter();
|
||||
filter.setContext(SecurityContextImpl.class);
|
||||
filter.setForceEagerSessionCreation(true); // non-default
|
||||
filter.afterPropertiesSet();
|
||||
// Execute filter
|
||||
executeFilterInContainerSimulator(new MockFilterConfig(), filter,
|
||||
request, response, chain);
|
||||
|
||||
// Execute filter
|
||||
executeFilterInContainerSimulator(new MockFilterConfig(), filter, request, response, chain);
|
||||
// Check the session is not null
|
||||
assertNotNull(request.getSession(false));
|
||||
}
|
||||
|
||||
// Check the session is not null
|
||||
assertNotNull(request.getSession(false));
|
||||
}
|
||||
public void testHttpSessionNotCreatedUnlessContextHolderChanges() throws Exception {
|
||||
// Build a mock request
|
||||
MockHttpServletRequest request = new MockHttpServletRequest(null, null);
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
FilterChain chain = new MockFilterChain(null, null, null);
|
||||
|
||||
public void testHttpSessionNotCreatedUnlessContextHolderChanges()
|
||||
throws Exception {
|
||||
// Build a mock request
|
||||
MockHttpServletRequest request = new MockHttpServletRequest(null, null);
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
FilterChain chain = new MockFilterChain(null, null, null);
|
||||
// Prepare filter
|
||||
HttpSessionContextIntegrationFilter filter = new HttpSessionContextIntegrationFilter();
|
||||
filter.setContext(SecurityContextImpl.class);
|
||||
filter.afterPropertiesSet();
|
||||
|
||||
// Prepare filter
|
||||
HttpSessionContextIntegrationFilter filter = new HttpSessionContextIntegrationFilter();
|
||||
filter.setContext(SecurityContextImpl.class);
|
||||
filter.afterPropertiesSet();
|
||||
// Execute filter
|
||||
executeFilterInContainerSimulator(new MockFilterConfig(), filter,
|
||||
request, response, chain);
|
||||
|
||||
// Execute filter
|
||||
executeFilterInContainerSimulator(new MockFilterConfig(), filter, request, response, chain);
|
||||
// Check the session is null
|
||||
assertNull(request.getSession(false));
|
||||
}
|
||||
|
||||
// Check the session is null
|
||||
assertNull(request.getSession(false));
|
||||
}
|
||||
public void testHttpSessionWithNonContextInWellKnownLocationIsOverwritten() throws Exception {
|
||||
// Build an Authentication object we simulate our Authentication changed
|
||||
// it to
|
||||
PrincipalAcegiUserToken updatedPrincipal = new PrincipalAcegiUserToken(
|
||||
"key", "someone", "password",
|
||||
new GrantedAuthority[] { new GrantedAuthorityImpl(
|
||||
"SOME_DIFFERENT_ROLE") }, null);
|
||||
|
||||
public void testHttpSessionWithNonContextInWellKnownLocationIsOverwritten()
|
||||
throws Exception {
|
||||
// Build an Authentication object we simulate our Authentication changed it to
|
||||
PrincipalAcegiUserToken updatedPrincipal = new PrincipalAcegiUserToken("key", "someone", "password",
|
||||
new GrantedAuthority[] {new GrantedAuthorityImpl("SOME_DIFFERENT_ROLE")}, null);
|
||||
// Build a mock request
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.getSession().setAttribute(
|
||||
HttpSessionContextIntegrationFilter.ACEGI_SECURITY_CONTEXT_KEY,
|
||||
"NOT_A_CONTEXT_OBJECT");
|
||||
|
||||
// Build a mock request
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.getSession()
|
||||
.setAttribute(HttpSessionContextIntegrationFilter.ACEGI_SECURITY_CONTEXT_KEY, "NOT_A_CONTEXT_OBJECT");
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
FilterChain chain = new MockFilterChain(null, updatedPrincipal, null);
|
||||
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
FilterChain chain = new MockFilterChain(null, updatedPrincipal, null);
|
||||
// Prepare filter
|
||||
HttpSessionContextIntegrationFilter filter = new HttpSessionContextIntegrationFilter();
|
||||
filter.setContext(SecurityContextImpl.class);
|
||||
filter.afterPropertiesSet();
|
||||
|
||||
// Prepare filter
|
||||
HttpSessionContextIntegrationFilter filter = new HttpSessionContextIntegrationFilter();
|
||||
filter.setContext(SecurityContextImpl.class);
|
||||
filter.afterPropertiesSet();
|
||||
// Execute filter
|
||||
executeFilterInContainerSimulator(new MockFilterConfig(), filter, request, response, chain);
|
||||
|
||||
// Execute filter
|
||||
executeFilterInContainerSimulator(new MockFilterConfig(), filter, request, response, chain);
|
||||
// Obtain new/update Authentication from HttpSession
|
||||
SecurityContext context = (SecurityContext) request.getSession().getAttribute(
|
||||
HttpSessionContextIntegrationFilter.ACEGI_SECURITY_CONTEXT_KEY);
|
||||
assertEquals(updatedPrincipal, ((SecurityContext) context).getAuthentication());
|
||||
}
|
||||
|
||||
// Obtain new/update Authentication from HttpSession
|
||||
SecurityContext context = (SecurityContext) request.getSession()
|
||||
.getAttribute(HttpSessionContextIntegrationFilter.ACEGI_SECURITY_CONTEXT_KEY);
|
||||
assertEquals(updatedPrincipal, ((SecurityContext) context).getAuthentication());
|
||||
}
|
||||
public void testConcurrentThreadsLazilyChangeFilterAppliedValueToTrue() throws Exception {
|
||||
PrincipalAcegiUserToken sessionPrincipal = new PrincipalAcegiUserToken(
|
||||
"key",
|
||||
"someone",
|
||||
"password",
|
||||
new GrantedAuthority[] { new GrantedAuthorityImpl("SOME_ROLE") },
|
||||
null);
|
||||
|
||||
//~ Inner Classes ==================================================================================================
|
||||
// Build a Context to store in HttpSession (simulating prior request)
|
||||
SecurityContext sc = new SecurityContextImpl();
|
||||
sc.setAuthentication(sessionPrincipal);
|
||||
|
||||
private class MockFilterChain extends TestCase implements FilterChain {
|
||||
private Authentication changeContextHolder;
|
||||
private Authentication expectedOnContextHolder;
|
||||
private IOException toThrowDuringChain;
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.getSession().setAttribute(
|
||||
HttpSessionContextIntegrationFilter.ACEGI_SECURITY_CONTEXT_KEY,
|
||||
sc);
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
|
||||
public MockFilterChain(Authentication expectedOnContextHolder, Authentication changeContextHolder,
|
||||
IOException toThrowDuringChain) {
|
||||
this.expectedOnContextHolder = expectedOnContextHolder;
|
||||
this.changeContextHolder = changeContextHolder;
|
||||
this.toThrowDuringChain = toThrowDuringChain;
|
||||
}
|
||||
// Prepare filter
|
||||
HttpSessionContextIntegrationFilter filter = new HttpSessionContextIntegrationFilter();
|
||||
filter.setContext(SecurityContextImpl.class);
|
||||
filter.afterPropertiesSet();
|
||||
|
||||
private MockFilterChain() {}
|
||||
for (int i = 0; i < 3; i++) {
|
||||
ThreadRunner runner = new ThreadRunner(request, response, filter,
|
||||
new MockFilterChain(sessionPrincipal, null, null));
|
||||
runner.start();
|
||||
}
|
||||
|
||||
public void doFilter(ServletRequest arg0, ServletResponse arg1)
|
||||
throws IOException, ServletException {
|
||||
if (expectedOnContextHolder != null) {
|
||||
assertEquals(expectedOnContextHolder, SecurityContextHolder.getContext().getAuthentication());
|
||||
}
|
||||
}
|
||||
|
||||
if (changeContextHolder != null) {
|
||||
SecurityContext sc = SecurityContextHolder.getContext();
|
||||
sc.setAuthentication(changeContextHolder);
|
||||
SecurityContextHolder.setContext(sc);
|
||||
}
|
||||
// ~ Inner Classes
|
||||
// ==================================================================================================
|
||||
|
||||
if (toThrowDuringChain != null) {
|
||||
throw toThrowDuringChain;
|
||||
}
|
||||
}
|
||||
}
|
||||
private class MockFilterChain extends TestCase implements FilterChain {
|
||||
private Authentication changeContextHolder;
|
||||
private Authentication expectedOnContextHolder;
|
||||
private IOException toThrowDuringChain;
|
||||
|
||||
public MockFilterChain(Authentication expectedOnContextHolder,
|
||||
Authentication changeContextHolder,
|
||||
IOException toThrowDuringChain) {
|
||||
this.expectedOnContextHolder = expectedOnContextHolder;
|
||||
this.changeContextHolder = changeContextHolder;
|
||||
this.toThrowDuringChain = toThrowDuringChain;
|
||||
}
|
||||
|
||||
public void doFilter(ServletRequest arg0, ServletResponse arg1) throws IOException, ServletException {
|
||||
if (expectedOnContextHolder != null) {
|
||||
assertEquals(expectedOnContextHolder, SecurityContextHolder.getContext().getAuthentication());
|
||||
}
|
||||
|
||||
if (changeContextHolder != null) {
|
||||
SecurityContext sc = SecurityContextHolder.getContext();
|
||||
sc.setAuthentication(changeContextHolder);
|
||||
SecurityContextHolder.setContext(sc);
|
||||
}
|
||||
|
||||
if (toThrowDuringChain != null) {
|
||||
throw toThrowDuringChain;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static class ThreadRunner extends Thread {
|
||||
private MockHttpServletRequest request;
|
||||
private MockHttpServletResponse response;
|
||||
private HttpSessionContextIntegrationFilter filter;
|
||||
private MockFilterChain chain;
|
||||
|
||||
public ThreadRunner(MockHttpServletRequest request,
|
||||
MockHttpServletResponse response,
|
||||
HttpSessionContextIntegrationFilter filter,
|
||||
MockFilterChain chain) {
|
||||
this.request = request;
|
||||
this.response = response;
|
||||
this.filter = filter;
|
||||
this.chain = chain;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
// Execute filter
|
||||
executeFilterInContainerSimulator(new MockFilterConfig(), filter, request, response, chain);
|
||||
|
||||
// Check the session is not null
|
||||
assertNotNull(request.getSession(false));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+23
-13
@@ -36,7 +36,6 @@ public class PathBasedFilterDefinitionMapTests extends TestCase {
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
public PathBasedFilterDefinitionMapTests() {
|
||||
super();
|
||||
}
|
||||
|
||||
public PathBasedFilterDefinitionMapTests(String arg0) {
|
||||
@@ -59,7 +58,6 @@ public class PathBasedFilterDefinitionMapTests extends TestCase {
|
||||
public void testLookupNotRequiringExactMatchSuccessIfNotMatching() {
|
||||
PathBasedFilterInvocationDefinitionMap map = new PathBasedFilterInvocationDefinitionMap();
|
||||
map.setConvertUrlToLowercaseBeforeComparison(true);
|
||||
assertTrue(map.isConvertUrlToLowercaseBeforeComparison());
|
||||
|
||||
ConfigAttributeDefinition def = new ConfigAttributeDefinition();
|
||||
def.addConfigAttribute(new SecurityConfig("ROLE_ONE"));
|
||||
@@ -71,10 +69,26 @@ public class PathBasedFilterDefinitionMapTests extends TestCase {
|
||||
assertEquals(def, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* SEC-501
|
||||
*/
|
||||
public void testLookupNotRequiringExactMatchSucceedsIfSecureUrlPathContainsUpperCase() {
|
||||
PathBasedFilterInvocationDefinitionMap map = new PathBasedFilterInvocationDefinitionMap();
|
||||
map.setConvertUrlToLowercaseBeforeComparison(true);
|
||||
|
||||
ConfigAttributeDefinition def = new ConfigAttributeDefinition();
|
||||
def.addConfigAttribute(new SecurityConfig("ROLE_ONE"));
|
||||
map.addSecureUrl("/SeCuRE/super/**", def);
|
||||
|
||||
FilterInvocation fi = createFilterinvocation("/secure/super/somefile.html");
|
||||
|
||||
ConfigAttributeDefinition response = map.lookupAttributes(fi.getRequestUrl());
|
||||
assertEquals(def, response);
|
||||
}
|
||||
|
||||
|
||||
public void testLookupRequiringExactMatchFailsIfNotMatching() {
|
||||
PathBasedFilterInvocationDefinitionMap map = new PathBasedFilterInvocationDefinitionMap();
|
||||
assertFalse(map.isConvertUrlToLowercaseBeforeComparison());
|
||||
|
||||
ConfigAttributeDefinition def = new ConfigAttributeDefinition();
|
||||
def.addConfigAttribute(new SecurityConfig("ROLE_ONE"));
|
||||
map.addSecureUrl("/secure/super/**", def);
|
||||
@@ -87,13 +101,11 @@ public class PathBasedFilterDefinitionMapTests extends TestCase {
|
||||
|
||||
public void testLookupRequiringExactMatchIsSuccessful() {
|
||||
PathBasedFilterInvocationDefinitionMap map = new PathBasedFilterInvocationDefinitionMap();
|
||||
assertFalse(map.isConvertUrlToLowercaseBeforeComparison());
|
||||
|
||||
ConfigAttributeDefinition def = new ConfigAttributeDefinition();
|
||||
def.addConfigAttribute(new SecurityConfig("ROLE_ONE"));
|
||||
map.addSecureUrl("/secure/super/**", def);
|
||||
map.addSecureUrl("/SeCurE/super/**", def);
|
||||
|
||||
FilterInvocation fi = createFilterinvocation("/secure/super/somefile.html");
|
||||
FilterInvocation fi = createFilterinvocation("/SeCurE/super/somefile.html");
|
||||
|
||||
ConfigAttributeDefinition response = map.lookupAttributes(fi.getRequestUrl());
|
||||
assertEquals(def, response);
|
||||
@@ -101,8 +113,6 @@ public class PathBasedFilterDefinitionMapTests extends TestCase {
|
||||
|
||||
public void testLookupRequiringExactMatchWithAdditionalSlashesIsSuccessful() {
|
||||
PathBasedFilterInvocationDefinitionMap map = new PathBasedFilterInvocationDefinitionMap();
|
||||
assertFalse(map.isConvertUrlToLowercaseBeforeComparison());
|
||||
|
||||
ConfigAttributeDefinition def = new ConfigAttributeDefinition();
|
||||
def.addConfigAttribute(new SecurityConfig("ROLE_ONE"));
|
||||
map.addSecureUrl("/someAdminPage.html**", def);
|
||||
@@ -113,11 +123,11 @@ public class PathBasedFilterDefinitionMapTests extends TestCase {
|
||||
assertEquals(def, response); // see SEC-161 (it should truncate after ? sign)
|
||||
}
|
||||
|
||||
/** Check fixes for SEC-321 */
|
||||
/**
|
||||
* Check fixes for SEC-321
|
||||
*/
|
||||
public void testExtraQuestionMarkStillMatches() {
|
||||
PathBasedFilterInvocationDefinitionMap map = new PathBasedFilterInvocationDefinitionMap();
|
||||
assertFalse(map.isConvertUrlToLowercaseBeforeComparison());
|
||||
|
||||
ConfigAttributeDefinition def = new ConfigAttributeDefinition();
|
||||
def.addConfigAttribute(new SecurityConfig("ROLE_ONE"));
|
||||
map.addSecureUrl("/someAdminPage.html*", def);
|
||||
|
||||
@@ -23,8 +23,6 @@ import java.util.Hashtable;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
@@ -36,7 +34,7 @@ public abstract class AbstractLdapServerTestCase extends TestCase {
|
||||
protected static final String MANAGER_PASSWORD = "acegisecurity";
|
||||
|
||||
// External server config
|
||||
// private static final String PROVIDER_URL = "ldap://monkeymachine:389/"+ROOT_DN;
|
||||
// private static final String PROVIDER_URL = "ldap://gorille:389/"+ROOT_DN;
|
||||
// private static final String CONTEXT_FACTORY = "com.sun.jndi.ldap.LdapCtxFactory";
|
||||
// private static final Hashtable EXTRA_ENV = new Hashtable();
|
||||
|
||||
@@ -52,7 +50,8 @@ public abstract class AbstractLdapServerTestCase extends TestCase {
|
||||
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
protected AbstractLdapServerTestCase() {}
|
||||
protected AbstractLdapServerTestCase() {
|
||||
}
|
||||
|
||||
protected AbstractLdapServerTestCase(String string) {
|
||||
super(string);
|
||||
@@ -64,7 +63,8 @@ public abstract class AbstractLdapServerTestCase extends TestCase {
|
||||
return idf;
|
||||
}
|
||||
|
||||
protected void onSetUp() {}
|
||||
protected void onSetUp() {
|
||||
}
|
||||
|
||||
public final void setUp() {
|
||||
idf = new DefaultInitialDirContextFactory(PROVIDER_URL);
|
||||
|
||||
@@ -22,8 +22,6 @@ import javax.naming.directory.DirContext;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
@@ -67,22 +65,24 @@ public class LdapTemplateTests extends AbstractLdapServerTestCase {
|
||||
public void testNamingExceptionIsTranslatedCorrectly() {
|
||||
try {
|
||||
template.execute(new LdapCallback() {
|
||||
public Object doInDirContext(DirContext dirContext)
|
||||
public Object doInDirContext(DirContext dirContext)
|
||||
throws NamingException {
|
||||
throw new NamingException();
|
||||
}
|
||||
});
|
||||
throw new NamingException();
|
||||
}
|
||||
});
|
||||
fail("Expected LdapDataAccessException on NamingException");
|
||||
} catch (LdapDataAccessException expected) {}
|
||||
} catch (LdapDataAccessException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
public void testSearchForSingleAttributeValues() {
|
||||
String param = "uid=ben,ou=people,dc=acegisecurity,dc=org";
|
||||
|
||||
Set values = template.searchForSingleAttributeValues("ou=groups", "(member={0})", new String[] {param}, "ou");
|
||||
Set values = template.searchForSingleAttributeValues("ou=groups", "(member={0})", new String[]{param}, "ou");
|
||||
|
||||
assertEquals("Expected 2 results from search", 2, values.size());
|
||||
assertEquals("Expected 3 results from search", 3, values.size());
|
||||
assertTrue(values.contains("developer"));
|
||||
assertTrue(values.contains("manager"));
|
||||
assertTrue(values.contains("submanager"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ public class LdapTestServer {
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public void createGroup(String cn, String ou, String[] memberDns) {
|
||||
public void createGroup(String cn, String groupContext, String ou, String[] memberDns) {
|
||||
Attributes group = new BasicAttributes("cn", cn);
|
||||
Attribute members = new BasicAttribute("member");
|
||||
Attribute orgUnit = new BasicAttribute("ou", ou);
|
||||
@@ -82,7 +82,8 @@ public class LdapTestServer {
|
||||
group.put(orgUnit);
|
||||
|
||||
try {
|
||||
serverContext.createSubcontext("cn=" + cn + ",ou=groups", group);
|
||||
DirContext ctx = serverContext.createSubcontext("cn=" + cn + "," + groupContext, group);
|
||||
System.out.println("Created group " + ctx.getNameInNamespace());
|
||||
} catch (NameAlreadyBoundException ignore) {
|
||||
// System.out.println(" group " + cn + " already exists.");
|
||||
} catch (NamingException ne) {
|
||||
@@ -122,7 +123,7 @@ public class LdapTestServer {
|
||||
ou.put(objectClass);
|
||||
|
||||
try {
|
||||
serverContext.createSubcontext("ou=" + name, ou);
|
||||
serverContext.createSubcontext(name, ou);
|
||||
} catch (NameAlreadyBoundException ignore) {
|
||||
// System.out.println(" ou " + name + " already exists.");
|
||||
} catch (NamingException ne) {
|
||||
@@ -188,16 +189,19 @@ public class LdapTestServer {
|
||||
}
|
||||
|
||||
private void initTestData() {
|
||||
createOu("people");
|
||||
createOu("groups");
|
||||
createOu("ou=people");
|
||||
createOu("ou=groups");
|
||||
createOu("ou=subgroups,ou=groups");
|
||||
|
||||
createUser("bob", "Bob Hamilton", "bobspassword");
|
||||
createUser("ben", "Ben Alex", "{SHA}nFCebWjxfaLbHHG1Qk5UU4trbvQ=");
|
||||
|
||||
String[] developers = new String[] {
|
||||
String[] developers = new String[]{
|
||||
"uid=ben,ou=people,dc=acegisecurity,dc=org", "uid=bob,ou=people,dc=acegisecurity,dc=org"
|
||||
};
|
||||
createGroup("developers", "developer", developers);
|
||||
createGroup("managers", "manager", new String[] {developers[0]});
|
||||
};
|
||||
createGroup("developers", "ou=groups", "developer", developers);
|
||||
createGroup("managers", "ou=groups", "manager", new String[]{developers[0]});
|
||||
createGroup("submanagers", "ou=subgroups,ou=groups", "submanager", new String[]{developers[0]});
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
@@ -243,11 +247,13 @@ public class LdapTestServer {
|
||||
}
|
||||
}
|
||||
|
||||
/** Recursively deletes a directory */
|
||||
/**
|
||||
* Recursively deletes a directory
|
||||
*/
|
||||
private boolean deleteDir(File dir) {
|
||||
if (dir.isDirectory()) {
|
||||
String[] children = dir.list();
|
||||
for (int i=0; i<children.length; i++) {
|
||||
for (int i = 0; i < children.length; i++) {
|
||||
boolean success = deleteDir(new File(dir, children[i]));
|
||||
if (!success) {
|
||||
return false;
|
||||
|
||||
@@ -42,7 +42,6 @@ public class FilterBasedLdapUserSearchTests extends AbstractLdapServerTestCase {
|
||||
}
|
||||
|
||||
public FilterBasedLdapUserSearchTests() {
|
||||
super();
|
||||
}
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
@@ -246,6 +246,8 @@ public class CasAuthenticationProviderTests extends TestCase {
|
||||
public void testDetectsMissingStatelessTicketCache()
|
||||
throws Exception {
|
||||
CasAuthenticationProvider cap = new CasAuthenticationProvider();
|
||||
// set this explicitly to null to test failure
|
||||
cap.setStatelessTicketCache(null);
|
||||
cap.setCasAuthoritiesPopulator(new MockAuthoritiesPopulator());
|
||||
cap.setCasProxyDecider(new MockProxyDecider());
|
||||
cap.setKey("qwerty");
|
||||
|
||||
+4
-9
@@ -17,7 +17,7 @@ package org.acegisecurity.providers.cas.cache;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import net.sf.ehcache.Cache;
|
||||
import net.sf.ehcache.Ehcache;
|
||||
|
||||
import org.acegisecurity.GrantedAuthority;
|
||||
import org.acegisecurity.GrantedAuthorityImpl;
|
||||
@@ -43,7 +43,6 @@ public class EhCacheBasedTicketCacheTests extends TestCase {
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
public EhCacheBasedTicketCacheTests() {
|
||||
super();
|
||||
}
|
||||
|
||||
public EhCacheBasedTicketCacheTests(String arg0) {
|
||||
@@ -52,10 +51,10 @@ public class EhCacheBasedTicketCacheTests extends TestCase {
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
private Cache getCache() {
|
||||
private Ehcache getCache() {
|
||||
ApplicationContext ctx = MockApplicationContext.getContext();
|
||||
|
||||
return (Cache) ctx.getBean("eHCacheBackend");
|
||||
return (Ehcache) ctx.getBean("eHCacheBackend");
|
||||
}
|
||||
|
||||
private CasAuthenticationToken getToken() {
|
||||
@@ -70,10 +69,6 @@ public class EhCacheBasedTicketCacheTests extends TestCase {
|
||||
proxyList, "PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt");
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
junit.textui.TestRunner.run(EhCacheBasedTicketCacheTests.class);
|
||||
}
|
||||
|
||||
public final void setUp() throws Exception {
|
||||
super.setUp();
|
||||
}
|
||||
@@ -106,7 +101,7 @@ public class EhCacheBasedTicketCacheTests extends TestCase {
|
||||
assertTrue(true);
|
||||
}
|
||||
|
||||
Cache myCache = getCache();
|
||||
Ehcache myCache = getCache();
|
||||
cache.setCache(myCache);
|
||||
assertEquals(myCache, cache.getCache());
|
||||
}
|
||||
|
||||
Vendored
+61
@@ -0,0 +1,61 @@
|
||||
/* 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.acegisecurity.providers.cas.cache;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.acegisecurity.GrantedAuthority;
|
||||
import org.acegisecurity.GrantedAuthorityImpl;
|
||||
import org.acegisecurity.providers.cas.CasAuthenticationToken;
|
||||
import org.acegisecurity.providers.cas.StatelessTicketCache;
|
||||
import org.acegisecurity.userdetails.User;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Test cases for the @link {@link NullStatelessTicketCache}
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @version $Id$
|
||||
*
|
||||
*/
|
||||
public class NullStatelessTicketCacheTests extends TestCase {
|
||||
|
||||
private StatelessTicketCache cache = new NullStatelessTicketCache();
|
||||
|
||||
public void testGetter() {
|
||||
assertNull(cache.getByTicketId(null));
|
||||
assertNull(cache.getByTicketId("test"));
|
||||
}
|
||||
|
||||
public void testInsertAndGet() {
|
||||
final CasAuthenticationToken token = getToken();
|
||||
cache.putTicketInCache(token);
|
||||
assertNull(cache.getByTicketId((String) token.getCredentials()));
|
||||
}
|
||||
|
||||
private CasAuthenticationToken getToken() {
|
||||
List proxyList = new ArrayList();
|
||||
proxyList.add("https://localhost/newPortal/j_spring_cas_security_check");
|
||||
|
||||
User user = new User("marissa", "password", true, true, true, true,
|
||||
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl("ROLE_TWO")});
|
||||
|
||||
return new CasAuthenticationToken("key", user, "ST-0-ER94xMJmn6pha35CQRoZ",
|
||||
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl("ROLE_TWO")}, user,
|
||||
proxyList, "PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt");
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user