Compare commits
205 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0a48b38179 | |||
| 3074dd8193 | |||
| 8d312a6365 | |||
| 4ef184d520 | |||
| 0f6c3affda | |||
| cf9a392b0a | |||
| 39918b4a01 | |||
| 8f21d03832 | |||
| e9d12d2023 | |||
| 6a61d07019 | |||
| 915b2acf73 | |||
| c3f5f4686e | |||
| 4c9a13a755 | |||
| dc201b7989 | |||
| fb227b5433 | |||
| aa489f7ff6 | |||
| 70d5ba536e | |||
| d50184deda | |||
| 5c4f4cbe4d | |||
| b192680df3 | |||
| 95d0e08059 | |||
| 4f993d95b5 | |||
| 4c832fc946 | |||
| 5945abb10a | |||
| 8c224f39dc | |||
| f0a6b7ca27 | |||
| 0350c2833e | |||
| 7406e03306 | |||
| ca3c1979b8 | |||
| c9facdd993 | |||
| d2e6343295 | |||
| 31338a7bdb | |||
| 7714c5cd02 | |||
| 5ed5590268 | |||
| 5dd6b4a77a | |||
| 5118e0b86e | |||
| 21f2991ab4 | |||
| 3679227b11 | |||
| 25e17c1568 | |||
| 9847366d5e | |||
| 7cb472f105 | |||
| 863b36962b | |||
| bbfb3da9c7 | |||
| b1af3d00ee | |||
| daa7f3f64e | |||
| 7a3135f0f9 | |||
| 82163e2546 | |||
| 2d27b28199 | |||
| 714ee3e960 | |||
| ee74c4ced2 | |||
| 102027a44c | |||
| 799a43d72e | |||
| 3dc4158f7d | |||
| 62f70f17ff | |||
| 4b0fbe1606 | |||
| a8bce41876 | |||
| cea1f4499f | |||
| c19a5ffd73 | |||
| 594ee9515e | |||
| a087e828a6 | |||
| 5238ba0e26 | |||
| 887e3361d2 | |||
| a24570ae06 | |||
| ba719dc0e1 | |||
| 28e70db8f2 | |||
| 84031c6001 | |||
| ca2af8bc59 | |||
| 6f59805ef3 | |||
| f359bed596 | |||
| 0f1ae574ab | |||
| cb7a94af88 | |||
| 9b8d2719a6 | |||
| 73b67da3a8 | |||
| b5546d1d29 | |||
| 70ca0d1a39 | |||
| 7a5a062cd0 | |||
| 977da0da1f | |||
| dfbc938e99 | |||
| d5f1f6cbff | |||
| a2cdbab50c | |||
| 1833b234a5 | |||
| 6c97fccc91 | |||
| 2888f2b86f | |||
| 04d42211b1 | |||
| 6a87a5f1a1 | |||
| f6b21880a2 | |||
| 198d5d0482 | |||
| acee3e2593 | |||
| b87dabe1ac | |||
| f509193604 | |||
| 11a091f051 | |||
| 8e48658efb | |||
| afd556412e | |||
| 187a530760 | |||
| 1b6587a5d4 | |||
| ece824fca2 | |||
| e3644e2d27 | |||
| b3943ac268 | |||
| 537d8f108a | |||
| d0bada2bad | |||
| 952af853ac | |||
| 4dea140331 | |||
| bb3a973fcb | |||
| 522e8db5da | |||
| 8f71f6febf | |||
| 69a1fb76d3 | |||
| 156a6924fa | |||
| d53db3ba13 | |||
| 90304f64c6 | |||
| 6349359b77 | |||
| e80853b698 | |||
| 82d105cbc3 | |||
| e88f47a96a | |||
| 979ea63980 | |||
| 0bdc9c176b | |||
| 80fd238c3a | |||
| 5c597c8cde | |||
| ec7b9703a6 | |||
| 71b2af31ee | |||
| fc75b69ab8 | |||
| 6141ef79b3 | |||
| 3cfe23f60d | |||
| 82d140ffb1 | |||
| 1563491322 | |||
| b688bb69ee | |||
| 36f008643d | |||
| cbdf77e991 | |||
| 399e921d14 | |||
| c458311d2d | |||
| d6f408e8bf | |||
| 1739628e6a | |||
| 8e68fa1334 | |||
| 82cd72768d | |||
| 161710cc87 | |||
| dc1b652512 | |||
| ed9411c660 | |||
| e58f982351 | |||
| 072b73354f | |||
| 443231d1e8 | |||
| 45674a16ea | |||
| a1b124def5 | |||
| f6abc24ed6 | |||
| 1a9b7e1b6f | |||
| 8b5c70951d | |||
| c891ab45ec | |||
| 657a69b906 | |||
| 3b8fbe8bee | |||
| 4ad85cdfdf | |||
| 845c50a1c3 | |||
| 25d222208d | |||
| 9b0c21dfef | |||
| 8301bd6276 | |||
| 1872d94aa1 | |||
| 46611872db | |||
| b6cbdde0cb | |||
| 71e1702224 | |||
| 80ccd2b285 | |||
| 02c1f02f2a | |||
| 21a664b2eb | |||
| 9a2d0c2cb5 | |||
| 73b62497a3 | |||
| 09aba3906c | |||
| 57cfff6f5c | |||
| b7b6b2bac7 | |||
| 8602ae3863 | |||
| 8737fe3acb | |||
| 27faad3402 | |||
| aaa7bd90b2 | |||
| 295e0ded18 | |||
| 304f12fb63 | |||
| 8cbe232fbf | |||
| 5ac106808e | |||
| 8c605516b3 | |||
| e6e168f127 | |||
| 6d6c2d31ef | |||
| 8f6aecac9b | |||
| 0760bb947b | |||
| 9d2e2ca11d | |||
| 6354c7e052 | |||
| 42cdaa0ce2 | |||
| eda60b72b1 | |||
| 0d198d42ae | |||
| f000aaa7e8 | |||
| 634e340d80 | |||
| 4c8e9e2d7e | |||
| e518adbef1 | |||
| 59b69f6f48 | |||
| b8e50c0933 | |||
| 677576ea8b | |||
| 91153df78d | |||
| 1b0ac9c785 | |||
| 8c9159f273 | |||
| 4c8b0faa88 | |||
| 5a5b62e2cb | |||
| 6ac8588144 | |||
| 5690f1c581 | |||
| 87cf27ab7c | |||
| 41e06152b3 | |||
| a7e21318bf | |||
| bc6aae132b | |||
| b46ae6ac62 | |||
| 317da55cd0 | |||
| 9e751e22c8 | |||
| 4d65b35827 | |||
| 9831980bc2 |
-186
@@ -1,186 +0,0 @@
|
||||
_Have something you'd like to contribute to the framework? We welcome pull requests, but ask that you carefully read this document first to understand how best to submit them; what kind of changes are likely to be accepted; and what to expect from the Spring Security team when evaluating your submission._
|
||||
|
||||
_Please refer back to this document as a checklist before issuing any pull request; this will save time for everyone!_
|
||||
|
||||
# Similar but different
|
||||
|
||||
Each Spring module is slightly different than another in terms of team size, number of issues, etc. Therefore each project is managed slightly different. You will notice that this document is very similar to the [Spring Framework Contributor guidelines](https://github.com/SpringSource/spring-framework/wiki/Contributor-guidelines). However, there are some subtle differences between the two documents, so please be sure to read this document thoroughly.
|
||||
|
||||
# Understand the basics
|
||||
Not sure what a pull request is, or how to submit one? Take a look at GitHub's excellent [help documentation first](https://help.github.com/articles/using-pull-requests).
|
||||
|
||||
# Search JIRA first; create an issue if necessary
|
||||
Is there already an issue that addresses your concern? Do a bit of searching in our [JIRA issue tracker](https://jira.springsource.org/browse/SEC) to see if you can find something similar. If not, please create a new issue before submitting a pull request unless the change is not a user facing issue.
|
||||
|
||||
# Discuss non-trivial contribution ideas with committers
|
||||
If you're considering anything more than correcting a typo or fixing a minor bug , please discuss it on the [Spring Security forums](http://forum.springsource.org/forumdisplay.php?33-Security) before submitting a pull request. We're happy to provide guidance but please spend an hour or two researching the subject on your own including searching the forums for prior discussions.
|
||||
|
||||
# Sign the Contributor License Agreement
|
||||
If you have not previously done so, please fill out and submit the SpringSource CLA form. You'll receive a token when this process is complete. Keep track of this, you may be asked for it later!
|
||||
|
||||
* For **Project** select _Spring Security_
|
||||
* For **Project Lead** enter _Rob Winch_
|
||||
* Note that emailing/postal mailing a signed copy is not necessary. Submission of the web form is all that is required.
|
||||
|
||||
When you've completed the web form, simply add the following in a comment on your pull request:
|
||||
|
||||
> I have signed and agree to the terms of the SpringSource Individual Contributor License Agreement.
|
||||
|
||||
You do not need to include your token/id. Please add the statement above to all future pull requests as well, simply so the Spring Security team knows immediately that this process is complete.
|
||||
|
||||
# Create your branch from master
|
||||
Create your topic branch to be submitted as a pull request from master. The Spring team will consider your pull request for backporting on a case-by-case basis; you don't need to worry about submitting anything for backporting.
|
||||
|
||||
# Use short branch names
|
||||
Branches used when submitting pull requests should preferably be named according to JIRA issues, e.g. 'SEC-1234'. Otherwise, use succinct, lower-case, dash (-) delimited names, such as 'fix-warnings', 'fix-typo', etc. This is important, because branch names show up in the merge commits that result from accepting pull requests, and should be as expressive and concise as possible.
|
||||
|
||||
#Keep commits focused
|
||||
|
||||
Remember each JIRA should be focused on a single item of interest since the JIRA tickets are used to produce the changelog. Since each commit should be tied to a JIRA, ensure that your commits are focused. For example, do not include an update to a transitive library in your commit unless the JIRA is to update the library. Reviewing your commits is essential before sending a pull request.
|
||||
|
||||
# Mind the whitespace
|
||||
Please carefully follow the whitespace and formatting conventions already present in the framework.
|
||||
|
||||
1. Spaces, not tabs
|
||||
1. Unix (LF), not dos (CRLF) line endings
|
||||
1. Eliminate all trailing whitespace
|
||||
1. Aim to wrap code at 120 characters, but favor readability over wrapping
|
||||
1. Preserve existing formatting; i.e. do not reformat code for its own sake
|
||||
1. Search the codebase using git grep and other tools to discover common naming conventions, etc.
|
||||
1. Latin-1 (ISO-8859-1) encoding for Java sources; use native2ascii to convert if necessary
|
||||
|
||||
Whitespace management tips
|
||||
|
||||
1. You can use the [AnyEdit Eclipse plugin](http://marketplace.eclipse.org/content/anyedit-tools) to ensure spaces are used and to clean up trailing whitespaces.
|
||||
1. Use git's pre-commit.sample hook to prevent invalid whitespace from being pushed out. You can enable it by moving ~/spring-security/.git/hooks/pre-commit.sample to ~/spring-security/.git/hooks/pre-commit and ensuring it is executable. For more information on hooks refer to [Pro Git's Pre-Commit Hook's section](http://git-scm.com/book/cs/ch7-3.html)
|
||||
|
||||
# Add Apache license header to all new classes
|
||||
|
||||
<pre>
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package ...;
|
||||
</pre>
|
||||
# Update Apache license header to modified files as necessary
|
||||
Always check the date range in the license header. For example, if you've modified a file in 2012 whose header still reads
|
||||
<pre>
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
</pre>
|
||||
then be sure to update it to 2012 appropriately
|
||||
<pre>
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
</pre>
|
||||
# Use @since tags for newly-added public API types and methods
|
||||
e.g.
|
||||
<pre>
|
||||
/**
|
||||
* ...
|
||||
*
|
||||
* @author First Last
|
||||
* @since 3.2
|
||||
* @see ...
|
||||
*/
|
||||
</pre>
|
||||
|
||||
#Submit JUnit test cases for all behavior changes
|
||||
Search the codebase to find related unit tests and add additional `@Test` methods within.
|
||||
|
||||
1. Any new tests should end in the name Tests (note this is plural). For example, a valid name would be `FilterChainProxyTests`. An invalid name would be `FilterChainProxyTest`.
|
||||
2. New test methods should not start with test. This is an old JUnit3 convention and is not necessary since the method is annotated with @Test.
|
||||
|
||||
# Squash commits
|
||||
Use git rebase --interactive, git add --patch and other tools to "squash" multiple commits into atomic changes. In addition to the man pages for git, there are many resources online to help you understand how these tools work. Here is one: http://book.git-scm.com/4_interactive_rebasing.html.
|
||||
|
||||
# Use real name in git commits
|
||||
Please configure git to use your real first and last name for any commits you intend to submit as pull requests. For example, this is not acceptable:
|
||||
|
||||
<pre>
|
||||
Author: Nickname <user@mail.com>
|
||||
</pre>
|
||||
Rather, please include your first and last name, properly capitalized, as submitted against the SpringSource contributor license agreement:
|
||||
<pre>
|
||||
Author: First Last <user@mail.com>
|
||||
</pre>
|
||||
This helps ensure traceability against the CLA, and also goes a long way to ensuring useful output from tools like git shortlog and others.
|
||||
|
||||
You can configure this globally via the account admin area GitHub (useful for fork-and-edit cases); globally with
|
||||
|
||||
<pre>
|
||||
git config --global user.name "First Last"
|
||||
git config --global user.email user@mail.com
|
||||
</pre>
|
||||
|
||||
or locally for the spring-security repository only by omitting the '--global' flag:
|
||||
<pre>
|
||||
cd spring-security
|
||||
git config user.name "First Last"
|
||||
git config user.email user@mail.com
|
||||
</pre>
|
||||
|
||||
# Format commit messages
|
||||
|
||||
<pre>
|
||||
SEC-1234: Short (50 chars or less) summary of changes
|
||||
|
||||
More detailed explanatory text, if necessary. Wrap it to about 72
|
||||
characters or so. In some contexts, the first line is treated as the
|
||||
subject of an email and the rest of the text as the body. The blank
|
||||
line separating the summary from the body is critical (unless you omit
|
||||
the body entirely); tools like rebase can get confused if you run the
|
||||
two together.
|
||||
|
||||
Further paragraphs come after blank lines.
|
||||
|
||||
- Bullet points are okay, too
|
||||
|
||||
- Typically a hyphen or asterisk is used for the bullet, preceded by a
|
||||
single space, with blank lines in between, but conventions vary here
|
||||
</pre>
|
||||
|
||||
|
||||
1. The commit subject should start with the associated jira issue followed by a : as shown in the example above
|
||||
2. Keep the subject line to 50 characters or less if possible
|
||||
3. Do not end the subject line with a period
|
||||
4. In the body of the commit message, explain how things worked before this commit, what has changed, and how things work now
|
||||
|
||||
# Run all tests prior to submission
|
||||
|
||||
<pre>
|
||||
cd spring-security
|
||||
./gradlew clean build integrationTest
|
||||
</pre>
|
||||
|
||||
# Submit your pull request
|
||||
Subject line:
|
||||
|
||||
Follow the same conventions for pull request subject lines as mentioned above for commit message subject lines.
|
||||
|
||||
In the body:
|
||||
|
||||
1. Explain your use case. What led you to submit this change? Why were existing mechanisms in the framework insufficient? Make a case that this is a general-purpose problem and that yours is a general-purpose solution, etc
|
||||
2. Add any additional information and ask questions; start a conversation, or continue one from JIRA
|
||||
3. Mention the JIRA issue ID
|
||||
4. Also mention that you have submitted the CLA as described above
|
||||
Note that for pull requests containing a single commit, GitHub will default the subject line and body of the pull request to match the subject line and body of the commit message. This is fine, but please also include the items above in the body of the request.
|
||||
|
||||
# Mention your pull request on the associated JIRA issue
|
||||
Add a comment to the associated JIRA issue(s) linking to your new pull request.
|
||||
|
||||
# Expect discussion and rework
|
||||
The Spring team takes a very conservative approach to accepting contributions to the framework. This is to keep code quality and stability as high as possible, and to keep complexity at a minimum. Your changes, if accepted, may be heavily modified prior to merging. You will retain "Author:" attribution for your Git commits granted that the bulk of your changes remain intact. You may be asked to rework the submission for style (as explained above) and/or substance. Again, we strongly recommend discussing any serious submissions with the Spring Framework team prior to engaging in serious development work.
|
||||
|
||||
Note that you can always force push (git push -f) reworked / rebased commits against the branch used to submit your pull request. i.e. you do not need to issue a new pull request when asked to make changes.
|
||||
@@ -2,10 +2,8 @@
|
||||
|
||||
dependencies {
|
||||
compile project(':spring-security-core'),
|
||||
'aopalliance:aopalliance:1.0',
|
||||
"net.sf.ehcache:ehcache:$ehcacheVersion",
|
||||
"org.springframework:spring-aop:$springVersion",
|
||||
"org.springframework:spring-beans:$springVersion",
|
||||
"org.springframework:spring-context:$springVersion",
|
||||
"org.springframework:spring-tx:$springVersion",
|
||||
"org.springframework:spring-jdbc:$springVersion"
|
||||
@@ -13,6 +11,4 @@ dependencies {
|
||||
testCompile "org.springframework:spring-beans:$springVersion",
|
||||
"org.springframework:spring-context-support:$springVersion",
|
||||
"org.springframework:spring-test:$springVersion"
|
||||
|
||||
testRuntime "hsqldb:hsqldb:$hsqlVersion"
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
<?xml version="1.0"?>
|
||||
<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">
|
||||
<parent>
|
||||
<artifactId>spring-security-parent</artifactId>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<version>@pomVersion@</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-acl</artifactId>
|
||||
<name>Spring Security - ACL module</name>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<classifier>tests</classifier>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context-support</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-jdbc</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sf.ehcache</groupId>
|
||||
<artifactId>ehcache</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>hsqldb</groupId>
|
||||
<artifactId>hsqldb</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<version>8.3-603.jdbc3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.springsource.bundlor</groupId>
|
||||
<artifactId>com.springsource.bundlor.maven</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -20,7 +20,6 @@ import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.security.access.AuthorizationServiceException;
|
||||
@@ -146,10 +145,14 @@ public class AclEntryVoter extends AbstractAclVoter {
|
||||
}
|
||||
|
||||
public boolean supports(ConfigAttribute attribute) {
|
||||
return (attribute.getAttribute() != null) && attribute.getAttribute().equals(getProcessConfigAttribute());
|
||||
if ((attribute.getAttribute() != null) && attribute.getAttribute().equals(getProcessConfigAttribute())) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int vote(Authentication authentication, MethodInvocation object, Collection<ConfigAttribute> attributes) {
|
||||
public int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {
|
||||
|
||||
for(ConfigAttribute attr : attributes) {
|
||||
|
||||
@@ -174,7 +177,7 @@ public class AclEntryVoter extends AbstractAclVoter {
|
||||
try {
|
||||
Class<?> clazz = domainObject.getClass();
|
||||
Method method = clazz.getMethod(internalMethod, new Class[0]);
|
||||
domainObject = method.invoke(domainObject);
|
||||
domainObject = method.invoke(domainObject, new Object[0]);
|
||||
} catch (NoSuchMethodException nsme) {
|
||||
throw new AuthorizationServiceException("Object of class '" + domainObject.getClass()
|
||||
+ "' does not provide the requested internalMethod: " + internalMethod);
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
package org.springframework.security.acls;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.security.access.PermissionCacheOptimizer;
|
||||
import org.springframework.security.acls.domain.ObjectIdentityRetrievalStrategyImpl;
|
||||
import org.springframework.security.acls.domain.SidRetrievalStrategyImpl;
|
||||
import org.springframework.security.acls.model.AclService;
|
||||
import org.springframework.security.acls.model.ObjectIdentity;
|
||||
import org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy;
|
||||
import org.springframework.security.acls.model.Sid;
|
||||
import org.springframework.security.acls.model.SidRetrievalStrategy;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
/**
|
||||
* Batch loads ACLs for collections of objects to allow optimised filtering.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @since 3.1
|
||||
*/
|
||||
public class AclPermissionCacheOptimizer implements PermissionCacheOptimizer {
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
private final AclService aclService;
|
||||
private SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();
|
||||
private ObjectIdentityRetrievalStrategy oidRetrievalStrategy = new ObjectIdentityRetrievalStrategyImpl();
|
||||
|
||||
public AclPermissionCacheOptimizer(AclService aclService) {
|
||||
this.aclService = aclService;
|
||||
}
|
||||
|
||||
public void cachePermissionsFor(Authentication authentication, Collection<?> objects) {
|
||||
if (objects.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<ObjectIdentity> oidsToCache = new ArrayList<ObjectIdentity>(objects.size());
|
||||
|
||||
for (Object domainObject : objects) {
|
||||
if (domainObject == null) {
|
||||
continue;
|
||||
}
|
||||
ObjectIdentity oid = oidRetrievalStrategy.getObjectIdentity(domainObject);
|
||||
oidsToCache.add(oid);
|
||||
}
|
||||
|
||||
List<Sid> sids = sidRetrievalStrategy.getSids(authentication);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Eagerly loading Acls for " + oidsToCache.size() + " objects");
|
||||
}
|
||||
|
||||
aclService.readAclsById(oidsToCache, sids);
|
||||
}
|
||||
|
||||
public void setObjectIdentityRetrievalStrategy(ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy) {
|
||||
this.oidRetrievalStrategy = objectIdentityRetrievalStrategy;
|
||||
}
|
||||
|
||||
public void setSidRetrievalStrategy(SidRetrievalStrategy sidRetrievalStrategy) {
|
||||
this.sidRetrievalStrategy = sidRetrievalStrategy;
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ public class AclPermissionEvaluator implements PermissionEvaluator {
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final AclService aclService;
|
||||
private AclService aclService;
|
||||
private ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy = new ObjectIdentityRetrievalStrategyImpl();
|
||||
private ObjectIdentityGenerator objectIdentityGenerator = new ObjectIdentityRetrievalStrategyImpl();
|
||||
private SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();
|
||||
@@ -70,30 +70,24 @@ public class AclPermissionEvaluator implements PermissionEvaluator {
|
||||
List<Sid> sids = sidRetrievalStrategy.getSids(authentication);
|
||||
List<Permission> requiredPermission = resolvePermission(permission);
|
||||
|
||||
final boolean debug = logger.isDebugEnabled();
|
||||
|
||||
if (debug) {
|
||||
logger.debug("Checking permission '" + permission + "' for object '" + oid + "'");
|
||||
}
|
||||
|
||||
try {
|
||||
// Lookup only ACLs for SIDs we're interested in
|
||||
Acl acl = aclService.readAclById(oid, sids);
|
||||
|
||||
if (acl.isGranted(requiredPermission, sids, false)) {
|
||||
if (debug) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Access is granted");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (debug) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Returning false - ACLs returned, but insufficient permissions for this principal");
|
||||
}
|
||||
|
||||
} catch (NotFoundException nfe) {
|
||||
if (debug) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Returning false - no ACLs apply for this principal");
|
||||
}
|
||||
}
|
||||
@@ -117,7 +111,7 @@ public class AclPermissionEvaluator implements PermissionEvaluator {
|
||||
|
||||
if (permission instanceof String) {
|
||||
String permString = (String)permission;
|
||||
Permission p;
|
||||
Permission p = null;
|
||||
|
||||
try {
|
||||
p = permissionFactory.buildFromName(permString);
|
||||
|
||||
+5
-3
@@ -43,12 +43,12 @@ import org.springframework.util.Assert;
|
||||
public abstract class AbstractAclProvider implements AfterInvocationProvider {
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
protected final AclService aclService;
|
||||
protected AclService aclService;
|
||||
protected Class<?> processDomainObjectClass = Object.class;
|
||||
protected ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy = new ObjectIdentityRetrievalStrategyImpl();
|
||||
protected SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();
|
||||
protected String processConfigAttribute;
|
||||
protected final List<Permission> requirePermission;
|
||||
protected List<Permission> requirePermission = Arrays.asList(BasePermission.READ);
|
||||
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
@@ -78,9 +78,11 @@ public abstract class AbstractAclProvider implements AfterInvocationProvider {
|
||||
// Obtain the SIDs applicable to the principal
|
||||
List<Sid> sids = sidRetrievalStrategy.getSids(authentication);
|
||||
|
||||
Acl acl = null;
|
||||
|
||||
try {
|
||||
// Lookup only ACLs for SIDs we're interested in
|
||||
Acl acl = aclService.readAclById(objectIdentity, sids);
|
||||
acl = aclService.readAclById(objectIdentity, sids);
|
||||
|
||||
return acl.isGranted(requirePermission, sids, false);
|
||||
} catch (NotFoundException ignore) {
|
||||
|
||||
+3
-1
@@ -75,7 +75,9 @@ public class AclEntryAfterInvocationCollectionFilteringProvider extends Abstract
|
||||
Object returnedObject) throws AccessDeniedException {
|
||||
|
||||
if (returnedObject == null) {
|
||||
logger.debug("Return object is null, skipping");
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Return object is null, skipping");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
+7
-3
@@ -65,7 +65,7 @@ public class AclEntryAfterInvocationProvider extends AbstractAclProvider impleme
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
public AclEntryAfterInvocationProvider(AclService aclService, List<Permission> requirePermission) {
|
||||
this(aclService, "AFTER_ACL_READ", requirePermission);
|
||||
super(aclService, "AFTER_ACL_READ", requirePermission);
|
||||
}
|
||||
|
||||
public AclEntryAfterInvocationProvider(AclService aclService, String processConfigAttribute,
|
||||
@@ -81,13 +81,17 @@ public class AclEntryAfterInvocationProvider extends AbstractAclProvider impleme
|
||||
if (returnedObject == null) {
|
||||
// AclManager interface contract prohibits nulls
|
||||
// As they have permission to null/nothing, grant access
|
||||
logger.debug("Return object is null, skipping");
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Return object is null, skipping");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!getProcessDomainObjectClass().isAssignableFrom(returnedObject.getClass())) {
|
||||
logger.debug("Return object is not applicable for this provider, skipping");
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Return object is not applicable for this provider, skipping");
|
||||
}
|
||||
|
||||
return returnedObject;
|
||||
}
|
||||
|
||||
+1
-1
@@ -95,7 +95,7 @@ class ArrayFilterer<T> implements Filterer<T> {
|
||||
}
|
||||
|
||||
public T next() {
|
||||
if (!hasNext()) {
|
||||
if (hasNext() == false) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
return list[index++];
|
||||
|
||||
+8
-3
@@ -37,9 +37,12 @@ class CollectionFilterer<T> implements Filterer<T> {
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private final Collection<T> collection;
|
||||
private Collection<T> collection;
|
||||
|
||||
private final Set<T> removeList;
|
||||
// collectionIter offers significant performance optimisations (as
|
||||
// per security-developer mailing list conversation 19/5/05)
|
||||
private Iterator<T> collectionIter;
|
||||
private Set<T> removeList;
|
||||
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
@@ -85,7 +88,9 @@ class CollectionFilterer<T> implements Filterer<T> {
|
||||
* @see org.springframework.security.acls.afterinvocation.Filterer#iterator()
|
||||
*/
|
||||
public Iterator<T> iterator() {
|
||||
return collection.iterator();
|
||||
collectionIter = collection.iterator();
|
||||
|
||||
return collectionIter;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,7 +12,7 @@ public abstract class AbstractPermission implements Permission {
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
protected final char code;
|
||||
protected char code;
|
||||
protected int mask;
|
||||
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
+4
-4
@@ -33,13 +33,13 @@ import java.io.Serializable;
|
||||
public class AccessControlEntryImpl implements AccessControlEntry, AuditableAccessControlEntry {
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private final Acl acl;
|
||||
private Acl acl;
|
||||
private Permission permission;
|
||||
private final Serializable id;
|
||||
private final Sid sid;
|
||||
private Serializable id;
|
||||
private Sid sid;
|
||||
private boolean auditFailure = false;
|
||||
private boolean auditSuccess = false;
|
||||
private final boolean granting;
|
||||
private boolean granting;
|
||||
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
|
||||
+14
-25
@@ -31,23 +31,19 @@ import org.springframework.util.Assert;
|
||||
/**
|
||||
* Default implementation of {@link AclAuthorizationStrategy}.
|
||||
* <p>
|
||||
* Permission will be granted if at least one of the following conditions is true for the current
|
||||
* principal.
|
||||
* <ul>
|
||||
* <li> is the owner (as defined by the ACL). </li>
|
||||
* <li> holds the relevant system-wide {@link GrantedAuthority} injected into the
|
||||
* constructor. </li>
|
||||
* <li> has {@link BasePermission#ADMINISTRATION} permission (as defined by the ACL). </li>
|
||||
* </ul>
|
||||
* Permission will be granted provided the current principal is either the owner (as defined by the ACL), has
|
||||
* {@link BasePermission#ADMINISTRATION} (as defined by the ACL and via a {@link Sid} retrieved for the current
|
||||
* principal via {@link #sidRetrievalStrategy}), or if the current principal holds the relevant system-wide
|
||||
* {@link GrantedAuthority} and injected into the constructor.
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
public class AclAuthorizationStrategyImpl implements AclAuthorizationStrategy {
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private final GrantedAuthority gaGeneralChanges;
|
||||
private final GrantedAuthority gaModifyAuditing;
|
||||
private final GrantedAuthority gaTakeOwnership;
|
||||
private GrantedAuthority gaGeneralChanges;
|
||||
private GrantedAuthority gaModifyAuditing;
|
||||
private GrantedAuthority gaTakeOwnership;
|
||||
private SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();
|
||||
|
||||
//~ Constructors ===================================================================================================
|
||||
@@ -56,23 +52,16 @@ public class AclAuthorizationStrategyImpl implements AclAuthorizationStrategy {
|
||||
* Constructor. The only mandatory parameter relates to the system-wide {@link GrantedAuthority} instances that
|
||||
* can be held to always permit ACL changes.
|
||||
*
|
||||
* @param auths the <code>GrantedAuthority</code>s that have
|
||||
* @param auths an array of <code>GrantedAuthority</code>s that have
|
||||
* special permissions (index 0 is the authority needed to change
|
||||
* ownership, index 1 is the authority needed to modify auditing details,
|
||||
* index 2 is the authority needed to change other ACL and ACE details) (required)
|
||||
* <p>
|
||||
* Alternatively, a single value can be supplied for all three permissions.
|
||||
*/
|
||||
public AclAuthorizationStrategyImpl(GrantedAuthority... auths) {
|
||||
Assert.isTrue(auths != null && (auths.length == 3 || auths.length == 1),
|
||||
"One or three GrantedAuthority instances required");
|
||||
if (auths.length == 3) {
|
||||
gaTakeOwnership = auths[0];
|
||||
gaModifyAuditing = auths[1];
|
||||
gaGeneralChanges = auths[2];
|
||||
} else {
|
||||
gaTakeOwnership = gaModifyAuditing = gaGeneralChanges = auths[0];
|
||||
}
|
||||
public AclAuthorizationStrategyImpl(GrantedAuthority[] auths) {
|
||||
Assert.isTrue(auths != null && auths.length == 3, "GrantedAuthority[] with three elements required");
|
||||
this.gaTakeOwnership = auths[0];
|
||||
this.gaModifyAuditing = auths[1];
|
||||
this.gaGeneralChanges = auths[2];
|
||||
}
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
@@ -95,7 +84,7 @@ public class AclAuthorizationStrategyImpl implements AclAuthorizationStrategy {
|
||||
}
|
||||
|
||||
// Not authorized by ACL ownership; try via adminstrative permissions
|
||||
GrantedAuthority requiredAuthority;
|
||||
GrantedAuthority requiredAuthority = null;
|
||||
|
||||
if (changeType == CHANGE_AUDITING) {
|
||||
requiredAuthority = this.gaModifyAuditing;
|
||||
|
||||
@@ -26,7 +26,6 @@ import org.springframework.security.acls.model.NotFoundException;
|
||||
import org.springframework.security.acls.model.ObjectIdentity;
|
||||
import org.springframework.security.acls.model.OwnershipAcl;
|
||||
import org.springframework.security.acls.model.Permission;
|
||||
import org.springframework.security.acls.model.PermissionGrantingStrategy;
|
||||
import org.springframework.security.acls.model.Sid;
|
||||
import org.springframework.security.acls.model.UnloadedSidException;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -42,8 +41,8 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
|
||||
|
||||
private Acl parentAcl;
|
||||
private transient AclAuthorizationStrategy aclAuthorizationStrategy;
|
||||
private transient PermissionGrantingStrategy permissionGrantingStrategy;
|
||||
private final List<AccessControlEntry> aces = new ArrayList<AccessControlEntry>();
|
||||
private transient AuditLogger auditLogger;
|
||||
private List<AccessControlEntry> aces = new ArrayList<AccessControlEntry>();
|
||||
private ObjectIdentity objectIdentity;
|
||||
private Serializable id;
|
||||
private Sid owner; // OwnershipAcl
|
||||
@@ -70,48 +69,39 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
|
||||
this.objectIdentity = objectIdentity;
|
||||
this.id = id;
|
||||
this.aclAuthorizationStrategy = aclAuthorizationStrategy;
|
||||
this.permissionGrantingStrategy = new DefaultPermissionGrantingStrategy(auditLogger);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use the version which takes a {@code PermissionGrantingStrategy} argument instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public AclImpl(ObjectIdentity objectIdentity, Serializable id, AclAuthorizationStrategy aclAuthorizationStrategy,
|
||||
AuditLogger auditLogger, Acl parentAcl, List<Sid> loadedSids, boolean entriesInheriting, Sid owner) {
|
||||
this(objectIdentity, id, aclAuthorizationStrategy, new DefaultPermissionGrantingStrategy(auditLogger),
|
||||
parentAcl, loadedSids, entriesInheriting, owner);
|
||||
this.auditLogger = auditLogger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Full constructor, which should be used by persistence tools that do not
|
||||
* provide field-level access features.
|
||||
*
|
||||
* @param objectIdentity the object identity this ACL relates to
|
||||
* @param id the primary key assigned to this ACL
|
||||
* @param aclAuthorizationStrategy authorization strategy
|
||||
* @param grantingStrategy the {@code PermissionGrantingStrategy} which will be used by the {@code isGranted()} method
|
||||
* @param parentAcl the parent (may be may be {@code null})
|
||||
* @param loadedSids the loaded SIDs if only a subset were loaded (may be {@code null})
|
||||
* @param objectIdentity the object identity this ACL relates to (required)
|
||||
* @param id the primary key assigned to this ACL (required)
|
||||
* @param aclAuthorizationStrategy authorization strategy (required)
|
||||
* @param auditLogger audit logger (required)
|
||||
* @param parentAcl the parent (may be <code>null</code>)
|
||||
* @param loadedSids the loaded SIDs if only a subset were loaded (may be
|
||||
* <code>null</code>)
|
||||
* @param entriesInheriting if ACEs from the parent should inherit into
|
||||
* this ACL
|
||||
* @param owner the owner (required)
|
||||
*/
|
||||
public AclImpl(ObjectIdentity objectIdentity, Serializable id, AclAuthorizationStrategy aclAuthorizationStrategy,
|
||||
PermissionGrantingStrategy grantingStrategy, Acl parentAcl, List<Sid> loadedSids, boolean entriesInheriting, Sid owner) {
|
||||
AuditLogger auditLogger, Acl parentAcl, List<Sid> loadedSids, boolean entriesInheriting, Sid owner) {
|
||||
Assert.notNull(objectIdentity, "Object Identity required");
|
||||
Assert.notNull(id, "Id required");
|
||||
Assert.notNull(aclAuthorizationStrategy, "AclAuthorizationStrategy required");
|
||||
Assert.notNull(owner, "Owner required");
|
||||
|
||||
Assert.notNull(auditLogger, "AuditLogger required");
|
||||
this.objectIdentity = objectIdentity;
|
||||
this.id = id;
|
||||
this.aclAuthorizationStrategy = aclAuthorizationStrategy;
|
||||
this.auditLogger = auditLogger;
|
||||
this.parentAcl = parentAcl; // may be null
|
||||
this.loadedSids = loadedSids; // may be null
|
||||
this.entriesInheriting = entriesInheriting;
|
||||
this.owner = owner;
|
||||
this.permissionGrantingStrategy = grantingStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,11 +168,35 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates to the {@link PermissionGrantingStrategy}.
|
||||
* Determines authorization. The order of the <code>permission</code> and <code>sid</code> arguments is
|
||||
* <em>extremely important</em>! The method will iterate through each of the <code>permission</code>s in the order
|
||||
* specified. For each iteration, all of the <code>sid</code>s will be considered, again in the order they are
|
||||
* presented. A search will then be performed for the first {@link AccessControlEntry} object that directly
|
||||
* matches that <code>permission:sid</code> combination. When the <em>first full match</em> is found (ie an ACE
|
||||
* that has the SID currently being searched for and the exact permission bit mask being search for), the grant or
|
||||
* deny flag for that ACE will prevail. If the ACE specifies to grant access, the method will return
|
||||
* <code>true</code>. If the ACE specifies to deny access, the loop will stop and the next <code>permission</code>
|
||||
* iteration will be performed. If each permission indicates to deny access, the first deny ACE found will be
|
||||
* considered the reason for the failure (as it was the first match found, and is therefore the one most logically
|
||||
* requiring changes - although not always). If absolutely no matching ACE was found at all for any permission,
|
||||
* the parent ACL will be tried (provided that there is a parent and {@link #isEntriesInheriting()} is
|
||||
* <code>true</code>. The parent ACL will also scan its parent and so on. If ultimately no matching ACE is found,
|
||||
* a <code>NotFoundException</code> will be thrown and the caller will need to decide how to handle the permission
|
||||
* check. Similarly, if any of the SID arguments presented to the method were not loaded by the ACL,
|
||||
* <code>UnloadedSidException</code> will be thrown.
|
||||
*
|
||||
* @param permission the exact permissions to scan for (order is important)
|
||||
* @param sids the exact SIDs to scan for (order is important)
|
||||
* @param administrativeMode if <code>true</code> denotes the query is for administrative purposes and no auditing
|
||||
* will be undertaken
|
||||
*
|
||||
* @return <code>true</code> if one of the permissions has been granted, <code>false</code> if one of the
|
||||
* permissions has been specifically revoked
|
||||
*
|
||||
* @throws NotFoundException if an exact ACE for one of the permission bit masks and SID combination could not be
|
||||
* found
|
||||
* @throws UnloadedSidException if the passed SIDs are unknown to this ACL because the ACL was only loaded for a
|
||||
* subset of SIDs
|
||||
* @see DefaultPermissionGrantingStrategy
|
||||
*/
|
||||
public boolean isGranted(List<Permission> permission, List<Sid> sids, boolean administrativeMode)
|
||||
throws NotFoundException, UnloadedSidException {
|
||||
@@ -193,7 +207,64 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
|
||||
throw new UnloadedSidException("ACL was not loaded for one or more SID");
|
||||
}
|
||||
|
||||
return permissionGrantingStrategy.isGranted(this, permission, sids, administrativeMode);
|
||||
AccessControlEntry firstRejection = null;
|
||||
|
||||
for (Permission p : permission) {
|
||||
for (Sid sid: sids) {
|
||||
// Attempt to find exact match for this permission mask and SID
|
||||
boolean scanNextSid = true;
|
||||
|
||||
for (AccessControlEntry ace : aces ) {
|
||||
|
||||
if ((ace.getPermission().getMask() == p.getMask()) && ace.getSid().equals(sid)) {
|
||||
// Found a matching ACE, so its authorization decision will prevail
|
||||
if (ace.isGranting()) {
|
||||
// Success
|
||||
if (!administrativeMode) {
|
||||
auditLogger.logIfNeeded(true, ace);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Failure for this permission, so stop search
|
||||
// We will see if they have a different permission
|
||||
// (this permission is 100% rejected for this SID)
|
||||
if (firstRejection == null) {
|
||||
// Store first rejection for auditing reasons
|
||||
firstRejection = ace;
|
||||
}
|
||||
|
||||
scanNextSid = false; // helps break the loop
|
||||
|
||||
break; // exit aces loop
|
||||
}
|
||||
}
|
||||
|
||||
if (!scanNextSid) {
|
||||
break; // exit SID for loop (now try next permission)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (firstRejection != null) {
|
||||
// We found an ACE to reject the request at this point, as no
|
||||
// other ACEs were found that granted a different permission
|
||||
if (!administrativeMode) {
|
||||
auditLogger.logIfNeeded(false, firstRejection);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// No matches have been found so far
|
||||
if (isEntriesInheriting() && (parentAcl != null)) {
|
||||
// We have a parent, so let them try to find a matching ACE
|
||||
return parentAcl.isGranted(permission, sids, false);
|
||||
} else {
|
||||
// We either have no parent, or we're the uppermost parent
|
||||
throw new NotFoundException("Unable to locate a matching ACE for passed permissions and SIDs");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isSidLoaded(List<Sid> sids) {
|
||||
@@ -249,6 +320,39 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
|
||||
return parentAcl;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("AclImpl[");
|
||||
sb.append("id: ").append(this.id).append("; ");
|
||||
sb.append("objectIdentity: ").append(this.objectIdentity).append("; ");
|
||||
sb.append("owner: ").append(this.owner).append("; ");
|
||||
|
||||
int count = 0;
|
||||
|
||||
for (AccessControlEntry ace : aces) {
|
||||
count++;
|
||||
|
||||
if (count == 1) {
|
||||
sb.append("\r\n");
|
||||
}
|
||||
|
||||
sb.append(ace).append("\r\n");
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
sb.append("no ACEs; ");
|
||||
}
|
||||
|
||||
sb.append("inheriting: ").append(this.entriesInheriting).append("; ");
|
||||
sb.append("parent: ").append((this.parentAcl == null) ? "Null" : this.parentAcl.getObjectIdentity().toString());
|
||||
sb.append("; ");
|
||||
sb.append("aclAuthorizationStrategy: ").append(this.aclAuthorizationStrategy).append("; ");
|
||||
sb.append("auditLogger: ").append(this.auditLogger);
|
||||
sb.append("]");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public void updateAce(int aceIndex, Permission permission)
|
||||
throws NotFoundException {
|
||||
aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_GENERAL);
|
||||
@@ -301,37 +405,4 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
|
||||
return false;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("AclImpl[");
|
||||
sb.append("id: ").append(this.id).append("; ");
|
||||
sb.append("objectIdentity: ").append(this.objectIdentity).append("; ");
|
||||
sb.append("owner: ").append(this.owner).append("; ");
|
||||
|
||||
int count = 0;
|
||||
|
||||
for (AccessControlEntry ace : aces) {
|
||||
count++;
|
||||
|
||||
if (count == 1) {
|
||||
sb.append("\n");
|
||||
}
|
||||
|
||||
sb.append(ace).append("\n");
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
sb.append("no ACEs; ");
|
||||
}
|
||||
|
||||
sb.append("inheriting: ").append(this.entriesInheriting).append("; ");
|
||||
sb.append("parent: ").append((this.parentAcl == null) ? "Null" : this.parentAcl.getObjectIdentity().toString());
|
||||
sb.append("; ");
|
||||
sb.append("aclAuthorizationStrategy: ").append(this.aclAuthorizationStrategy).append("; ");
|
||||
sb.append("permissionGrantingStrategy: ").append(this.permissionGrantingStrategy);
|
||||
sb.append("]");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+6
-7
@@ -64,19 +64,18 @@ public class DefaultPermissionFactory implements PermissionFactory {
|
||||
|
||||
Field[] fields = clazz.getFields();
|
||||
|
||||
for (Field field : fields) {
|
||||
for (int i = 0; i < fields.length; i++) {
|
||||
try {
|
||||
Object fieldValue = field.get(null);
|
||||
Object fieldValue = fields[i].get(null);
|
||||
|
||||
if (Permission.class.isAssignableFrom(fieldValue.getClass())) {
|
||||
// Found a Permission static field
|
||||
Permission perm = (Permission) fieldValue;
|
||||
String permissionName = field.getName();
|
||||
String permissionName = fields[i].getName();
|
||||
|
||||
registerPermission(perm, permissionName);
|
||||
}
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
} catch (Exception ignore) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +83,7 @@ public class DefaultPermissionFactory implements PermissionFactory {
|
||||
Assert.notNull(perm, "Permission required");
|
||||
Assert.hasText(permissionName, "Permission name required");
|
||||
|
||||
Integer mask = Integer.valueOf(perm.getMask());
|
||||
Integer mask = new Integer(perm.getMask());
|
||||
|
||||
// Ensure no existing Permission uses this integer or code
|
||||
Assert.isTrue(!registeredPermissionsByInteger.containsKey(mask), "An existing Permission already provides mask " + mask);
|
||||
@@ -98,7 +97,7 @@ public class DefaultPermissionFactory implements PermissionFactory {
|
||||
public Permission buildFromMask(int mask) {
|
||||
if (registeredPermissionsByInteger.containsKey(Integer.valueOf(mask))) {
|
||||
// The requested mask has an exact match against a statically-defined Permission, so return it
|
||||
return registeredPermissionsByInteger.get(Integer.valueOf(mask));
|
||||
return registeredPermissionsByInteger.get(new Integer(mask));
|
||||
}
|
||||
|
||||
// To get this far, we have to use a CumulativePermission
|
||||
|
||||
-119
@@ -1,119 +0,0 @@
|
||||
package org.springframework.security.acls.domain;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.security.acls.model.AccessControlEntry;
|
||||
import org.springframework.security.acls.model.Acl;
|
||||
import org.springframework.security.acls.model.NotFoundException;
|
||||
import org.springframework.security.acls.model.Permission;
|
||||
import org.springframework.security.acls.model.PermissionGrantingStrategy;
|
||||
import org.springframework.security.acls.model.Sid;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
public class DefaultPermissionGrantingStrategy implements PermissionGrantingStrategy {
|
||||
|
||||
private final transient AuditLogger auditLogger;
|
||||
|
||||
/**
|
||||
* Creates an instance with the logger which will be used to record granting and denial of requested permissions.
|
||||
*/
|
||||
public DefaultPermissionGrantingStrategy(AuditLogger auditLogger) {
|
||||
Assert.notNull(auditLogger, "auditLogger cannot be null");
|
||||
this.auditLogger = auditLogger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines authorization. The order of the <code>permission</code> and <code>sid</code> arguments is
|
||||
* <em>extremely important</em>! The method will iterate through each of the <code>permission</code>s in the order
|
||||
* specified. For each iteration, all of the <code>sid</code>s will be considered, again in the order they are
|
||||
* presented. A search will then be performed for the first {@link AccessControlEntry} object that directly
|
||||
* matches that <code>permission:sid</code> combination. When the <em>first full match</em> is found (ie an ACE
|
||||
* that has the SID currently being searched for and the exact permission bit mask being search for), the grant or
|
||||
* deny flag for that ACE will prevail. If the ACE specifies to grant access, the method will return
|
||||
* <code>true</code>. If the ACE specifies to deny access, the loop will stop and the next <code>permission</code>
|
||||
* iteration will be performed. If each permission indicates to deny access, the first deny ACE found will be
|
||||
* considered the reason for the failure (as it was the first match found, and is therefore the one most logically
|
||||
* requiring changes - although not always). If absolutely no matching ACE was found at all for any permission,
|
||||
* the parent ACL will be tried (provided that there is a parent and {@link Acl#isEntriesInheriting()} is
|
||||
* <code>true</code>. The parent ACL will also scan its parent and so on. If ultimately no matching ACE is found,
|
||||
* a <code>NotFoundException</code> will be thrown and the caller will need to decide how to handle the permission
|
||||
* check. Similarly, if any of the SID arguments presented to the method were not loaded by the ACL,
|
||||
* <code>UnloadedSidException</code> will be thrown.
|
||||
*
|
||||
* @param permission the exact permissions to scan for (order is important)
|
||||
* @param sids the exact SIDs to scan for (order is important)
|
||||
* @param administrativeMode if <code>true</code> denotes the query is for administrative purposes and no auditing
|
||||
* will be undertaken
|
||||
*
|
||||
* @return <code>true</code> if one of the permissions has been granted, <code>false</code> if one of the
|
||||
* permissions has been specifically revoked
|
||||
*
|
||||
* @throws NotFoundException if an exact ACE for one of the permission bit masks and SID combination could not be
|
||||
* found
|
||||
*/
|
||||
public boolean isGranted(Acl acl, List<Permission> permission, List<Sid> sids, boolean administrativeMode)
|
||||
throws NotFoundException {
|
||||
|
||||
final List<AccessControlEntry> aces = acl.getEntries();
|
||||
|
||||
AccessControlEntry firstRejection = null;
|
||||
|
||||
for (Permission p : permission) {
|
||||
for (Sid sid: sids) {
|
||||
// Attempt to find exact match for this permission mask and SID
|
||||
boolean scanNextSid = true;
|
||||
|
||||
for (AccessControlEntry ace : aces ) {
|
||||
|
||||
if ((ace.getPermission().getMask() == p.getMask()) && ace.getSid().equals(sid)) {
|
||||
// Found a matching ACE, so its authorization decision will prevail
|
||||
if (ace.isGranting()) {
|
||||
// Success
|
||||
if (!administrativeMode) {
|
||||
auditLogger.logIfNeeded(true, ace);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Failure for this permission, so stop search
|
||||
// We will see if they have a different permission
|
||||
// (this permission is 100% rejected for this SID)
|
||||
if (firstRejection == null) {
|
||||
// Store first rejection for auditing reasons
|
||||
firstRejection = ace;
|
||||
}
|
||||
|
||||
scanNextSid = false; // helps break the loop
|
||||
|
||||
break; // exit aces loop
|
||||
}
|
||||
}
|
||||
|
||||
if (!scanNextSid) {
|
||||
break; // exit SID for loop (now try next permission)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (firstRejection != null) {
|
||||
// We found an ACE to reject the request at this point, as no
|
||||
// other ACEs were found that granted a different permission
|
||||
if (!administrativeMode) {
|
||||
auditLogger.logIfNeeded(false, firstRejection);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// No matches have been found so far
|
||||
if (acl.isEntriesInheriting() && (acl.getParentAcl() != null)) {
|
||||
// We have a parent, so let them try to find a matching ACE
|
||||
return acl.getParentAcl().isGranted(permission, sids, false);
|
||||
} else {
|
||||
// We either have no parent, or we're the uppermost parent
|
||||
throw new NotFoundException("Unable to locate a matching ACE for passed permissions and SIDs");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+7
-21
@@ -23,48 +23,34 @@ import net.sf.ehcache.Element;
|
||||
import org.springframework.security.acls.model.AclCache;
|
||||
import org.springframework.security.acls.model.MutableAcl;
|
||||
import org.springframework.security.acls.model.ObjectIdentity;
|
||||
import org.springframework.security.acls.model.PermissionGrantingStrategy;
|
||||
import org.springframework.security.util.FieldUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
/**
|
||||
* Simple implementation of {@link AclCache} that delegates to EH-CACHE.
|
||||
*
|
||||
* <p>
|
||||
* Designed to handle the transient fields in {@link AclImpl}. Note that this implementation assumes all
|
||||
* {@link AclImpl} instances share the same {@link PermissionGrantingStrategy} and {@link AclAuthorizationStrategy}
|
||||
* instances.
|
||||
* {@link AclImpl} instances share the same {@link AuditLogger} and {@link AclAuthorizationStrategy} instance.
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
public class EhCacheBasedAclCache implements AclCache {
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private final Ehcache cache;
|
||||
private PermissionGrantingStrategy permissionGrantingStrategy;
|
||||
private Ehcache cache;
|
||||
private AuditLogger auditLogger;
|
||||
private AclAuthorizationStrategy aclAuthorizationStrategy;
|
||||
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
/**
|
||||
* @deprecated use the second constructor which injects the strategy objects. See SEC-1498.
|
||||
*/
|
||||
@Deprecated
|
||||
public EhCacheBasedAclCache(Ehcache cache) {
|
||||
Assert.notNull(cache, "Cache required");
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
public EhCacheBasedAclCache(Ehcache cache, PermissionGrantingStrategy permissionGrantingStrategy,
|
||||
AclAuthorizationStrategy aclAuthorizationStrategy) {
|
||||
Assert.notNull(cache, "Cache required");
|
||||
Assert.notNull(permissionGrantingStrategy, "PermissionGrantingStrategy required");
|
||||
Assert.notNull(aclAuthorizationStrategy, "AclAuthorizationStrategy required");
|
||||
this.cache = cache;
|
||||
this.permissionGrantingStrategy = permissionGrantingStrategy;
|
||||
this.aclAuthorizationStrategy = aclAuthorizationStrategy;
|
||||
}
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public void evictFromCache(Serializable pk) {
|
||||
@@ -129,7 +115,7 @@ public class EhCacheBasedAclCache implements AclCache {
|
||||
if (this.aclAuthorizationStrategy == null) {
|
||||
if (acl instanceof AclImpl) {
|
||||
this.aclAuthorizationStrategy = (AclAuthorizationStrategy) FieldUtils.getProtectedFieldValue("aclAuthorizationStrategy", acl);
|
||||
this.permissionGrantingStrategy = (PermissionGrantingStrategy) FieldUtils.getProtectedFieldValue("permissionGrantingStrategy", acl);
|
||||
this.auditLogger = (AuditLogger) FieldUtils.getProtectedFieldValue("auditLogger", acl);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,7 +130,7 @@ public class EhCacheBasedAclCache implements AclCache {
|
||||
private MutableAcl initializeTransientFields(MutableAcl value) {
|
||||
if (value instanceof AclImpl) {
|
||||
FieldUtils.setProtectedFieldValue("aclAuthorizationStrategy", value, this.aclAuthorizationStrategy);
|
||||
FieldUtils.setProtectedFieldValue("permissionGrantingStrategy", value, this.permissionGrantingStrategy);
|
||||
FieldUtils.setProtectedFieldValue("auditLogger", value, this.auditLogger);
|
||||
}
|
||||
|
||||
if (value.getParentAcl() != null) {
|
||||
|
||||
@@ -30,7 +30,7 @@ import org.springframework.util.Assert;
|
||||
public class GrantedAuthoritySid implements Sid {
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private final String grantedAuthority;
|
||||
private String grantedAuthority;
|
||||
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ public class ObjectIdentityImpl implements ObjectIdentity {
|
||||
|
||||
try {
|
||||
Method method = typeClass.getMethod("getId", new Class[] {});
|
||||
result = method.invoke(object);
|
||||
result = method.invoke(object, new Object[] {});
|
||||
} catch (Exception e) {
|
||||
throw new IdentityUnavailableException("Could not extract identity from object " + object, e);
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ import org.springframework.util.Assert;
|
||||
public class PrincipalSid implements Sid {
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private final String principal;
|
||||
private String principal;
|
||||
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
|
||||
+1
-1
@@ -51,7 +51,7 @@ public class SidRetrievalStrategyImpl implements SidRetrievalStrategy {
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public List<Sid> getSids(Authentication authentication) {
|
||||
Collection<? extends GrantedAuthority> authorities = roleHierarchy.getReachableGrantedAuthorities(authentication.getAuthorities());
|
||||
Collection<GrantedAuthority> authorities = roleHierarchy.getReachableGrantedAuthorities(authentication.getAuthorities());
|
||||
List<Sid> sids = new ArrayList<Sid>(authorities.size() + 1);
|
||||
|
||||
sids.add(new PrincipalSid(authentication));
|
||||
|
||||
-130
@@ -1,130 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.acls.domain;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.security.acls.model.AclCache;
|
||||
import org.springframework.security.acls.model.MutableAcl;
|
||||
import org.springframework.security.acls.model.ObjectIdentity;
|
||||
import org.springframework.security.acls.model.PermissionGrantingStrategy;
|
||||
import org.springframework.security.util.FieldUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
|
||||
/**
|
||||
* Simple implementation of {@link org.springframework.security.acls.model.AclCache} that delegates to {@link Cache} implementation.
|
||||
* <p>
|
||||
* Designed to handle the transient fields in {@link org.springframework.security.acls.domain.AclImpl}. Note that this implementation assumes all
|
||||
* {@link org.springframework.security.acls.domain.AclImpl} instances share the same {@link org.springframework.security.acls.model.PermissionGrantingStrategy} and {@link org.springframework.security.acls.domain.AclAuthorizationStrategy}
|
||||
* instances.
|
||||
*
|
||||
* @author Marten Deinum
|
||||
* @since 3.2
|
||||
*/
|
||||
public class SpringCacheBasedAclCache implements AclCache {
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private final Cache cache;
|
||||
private PermissionGrantingStrategy permissionGrantingStrategy;
|
||||
private AclAuthorizationStrategy aclAuthorizationStrategy;
|
||||
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
public SpringCacheBasedAclCache(Cache cache, PermissionGrantingStrategy permissionGrantingStrategy,
|
||||
AclAuthorizationStrategy aclAuthorizationStrategy) {
|
||||
Assert.notNull(cache, "Cache required");
|
||||
Assert.notNull(permissionGrantingStrategy, "PermissionGrantingStrategy required");
|
||||
Assert.notNull(aclAuthorizationStrategy, "AclAuthorizationStrategy required");
|
||||
this.cache = cache;
|
||||
this.permissionGrantingStrategy = permissionGrantingStrategy;
|
||||
this.aclAuthorizationStrategy = aclAuthorizationStrategy;
|
||||
}
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public void evictFromCache(Serializable pk) {
|
||||
Assert.notNull(pk, "Primary key (identifier) required");
|
||||
|
||||
MutableAcl acl = getFromCache(pk);
|
||||
|
||||
if (acl != null) {
|
||||
cache.evict(acl.getId());
|
||||
cache.evict(acl.getObjectIdentity());
|
||||
}
|
||||
}
|
||||
|
||||
public void evictFromCache(ObjectIdentity objectIdentity) {
|
||||
Assert.notNull(objectIdentity, "ObjectIdentity required");
|
||||
|
||||
MutableAcl acl = getFromCache(objectIdentity);
|
||||
|
||||
if (acl != null) {
|
||||
cache.evict(acl.getId());
|
||||
cache.evict(acl.getObjectIdentity());
|
||||
}
|
||||
}
|
||||
|
||||
public MutableAcl getFromCache(ObjectIdentity objectIdentity) {
|
||||
Assert.notNull(objectIdentity, "ObjectIdentity required");
|
||||
return getFromCache((Object)objectIdentity);
|
||||
}
|
||||
|
||||
public MutableAcl getFromCache(Serializable pk) {
|
||||
Assert.notNull(pk, "Primary key (identifier) required");
|
||||
return getFromCache((Object)pk);
|
||||
}
|
||||
|
||||
public void putInCache(MutableAcl acl) {
|
||||
Assert.notNull(acl, "Acl required");
|
||||
Assert.notNull(acl.getObjectIdentity(), "ObjectIdentity required");
|
||||
Assert.notNull(acl.getId(), "ID required");
|
||||
|
||||
if ((acl.getParentAcl() != null) && (acl.getParentAcl() instanceof MutableAcl)) {
|
||||
putInCache((MutableAcl) acl.getParentAcl());
|
||||
}
|
||||
|
||||
cache.put(acl.getObjectIdentity(), acl);
|
||||
cache.put(acl.getId(), acl);
|
||||
}
|
||||
|
||||
private MutableAcl getFromCache(Object key) {
|
||||
Cache.ValueWrapper element = cache.get(key);
|
||||
|
||||
if (element == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return initializeTransientFields((MutableAcl) element.get());
|
||||
}
|
||||
|
||||
private MutableAcl initializeTransientFields(MutableAcl value) {
|
||||
if (value instanceof AclImpl) {
|
||||
FieldUtils.setProtectedFieldValue("aclAuthorizationStrategy", value, this.aclAuthorizationStrategy);
|
||||
FieldUtils.setProtectedFieldValue("permissionGrantingStrategy", value, this.permissionGrantingStrategy);
|
||||
}
|
||||
|
||||
if (value.getParentAcl() != null) {
|
||||
initializeTransientFields((MutableAcl) value.getParentAcl());
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public void clearCache() {
|
||||
cache.clear();
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,7 @@ import java.util.Set;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.PreparedStatementSetter;
|
||||
import org.springframework.jdbc.core.ResultSetExtractor;
|
||||
@@ -37,7 +38,6 @@ import org.springframework.security.acls.domain.AclAuthorizationStrategy;
|
||||
import org.springframework.security.acls.domain.AclImpl;
|
||||
import org.springframework.security.acls.domain.AuditLogger;
|
||||
import org.springframework.security.acls.domain.DefaultPermissionFactory;
|
||||
import org.springframework.security.acls.domain.DefaultPermissionGrantingStrategy;
|
||||
import org.springframework.security.acls.domain.GrantedAuthoritySid;
|
||||
import org.springframework.security.acls.domain.ObjectIdentityImpl;
|
||||
import org.springframework.security.acls.domain.PermissionFactory;
|
||||
@@ -49,7 +49,6 @@ import org.springframework.security.acls.model.MutableAcl;
|
||||
import org.springframework.security.acls.model.NotFoundException;
|
||||
import org.springframework.security.acls.model.ObjectIdentity;
|
||||
import org.springframework.security.acls.model.Permission;
|
||||
import org.springframework.security.acls.model.PermissionGrantingStrategy;
|
||||
import org.springframework.security.acls.model.Sid;
|
||||
import org.springframework.security.acls.model.UnloadedSidException;
|
||||
import org.springframework.security.util.FieldUtils;
|
||||
@@ -107,11 +106,11 @@ public final class BasicLookupStrategy implements LookupStrategy {
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private final AclAuthorizationStrategy aclAuthorizationStrategy;
|
||||
private AclAuthorizationStrategy aclAuthorizationStrategy;
|
||||
private PermissionFactory permissionFactory = new DefaultPermissionFactory();
|
||||
private final AclCache aclCache;
|
||||
private final PermissionGrantingStrategy grantingStrategy;
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
private AclCache aclCache;
|
||||
private AuditLogger auditLogger;
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
private int batchSize = 50;
|
||||
|
||||
private final Field fieldAces = FieldUtils.getField(AclImpl.class, "aces");
|
||||
@@ -131,28 +130,19 @@ public final class BasicLookupStrategy implements LookupStrategy {
|
||||
* @param dataSource to access the database
|
||||
* @param aclCache the cache where fully-loaded elements can be stored
|
||||
* @param aclAuthorizationStrategy authorization strategy (required)
|
||||
*
|
||||
* @deprecated Use the version which takes a {@code PermissionGrantingStrategy} argument instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public BasicLookupStrategy(DataSource dataSource, AclCache aclCache,
|
||||
AclAuthorizationStrategy aclAuthorizationStrategy, AuditLogger auditLogger) {
|
||||
this(dataSource, aclCache, aclAuthorizationStrategy, new DefaultPermissionGrantingStrategy(auditLogger));
|
||||
}
|
||||
|
||||
public BasicLookupStrategy(DataSource dataSource, AclCache aclCache,
|
||||
AclAuthorizationStrategy aclAuthorizationStrategy, PermissionGrantingStrategy grantingStrategy) {
|
||||
AclAuthorizationStrategy aclAuthorizationStrategy, AuditLogger auditLogger) {
|
||||
Assert.notNull(dataSource, "DataSource required");
|
||||
Assert.notNull(aclCache, "AclCache required");
|
||||
Assert.notNull(aclAuthorizationStrategy, "AclAuthorizationStrategy required");
|
||||
Assert.notNull(grantingStrategy, "grantingStrategy required");
|
||||
Assert.notNull(auditLogger, "AuditLogger required");
|
||||
jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
this.aclCache = aclCache;
|
||||
this.aclAuthorizationStrategy = aclAuthorizationStrategy;
|
||||
this.grantingStrategy = grantingStrategy;
|
||||
this.auditLogger = auditLogger;
|
||||
fieldAces.setAccessible(true);
|
||||
fieldAcl.setAccessible(true);
|
||||
|
||||
}
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
@@ -405,7 +395,7 @@ public final class BasicLookupStrategy implements LookupStrategy {
|
||||
|
||||
// Now we have the parent (if there is one), create the true AclImpl
|
||||
AclImpl result = new AclImpl(inputAcl.getObjectIdentity(), (Long) inputAcl.getId(), aclAuthorizationStrategy,
|
||||
grantingStrategy, parent, null, inputAcl.isEntriesInheriting(), inputAcl.getOwner());
|
||||
auditLogger, parent, null, inputAcl.isEntriesInheriting(), inputAcl.getOwner());
|
||||
|
||||
// Copy the "aces" from the input to the destination
|
||||
|
||||
@@ -476,8 +466,8 @@ public final class BasicLookupStrategy implements LookupStrategy {
|
||||
//~ Inner Classes ==================================================================================================
|
||||
|
||||
private class ProcessResultSet implements ResultSetExtractor<Set<Long>> {
|
||||
private final Map<Serializable, Acl> acls;
|
||||
private final List<Sid> sids;
|
||||
private Map<Serializable, Acl> acls;
|
||||
private List<Sid> sids;
|
||||
|
||||
public ProcessResultSet(Map<Serializable, Acl> acls, List<Sid> sids) {
|
||||
Assert.notNull(acls, "ACLs cannot be null");
|
||||
@@ -494,8 +484,9 @@ public final class BasicLookupStrategy implements LookupStrategy {
|
||||
* @param rs The {@link ResultSet} to be processed
|
||||
* @return a list of parent IDs remaining to be looked up (may be empty, but never <tt>null</tt>)
|
||||
* @throws SQLException
|
||||
* @throws DataAccessException
|
||||
*/
|
||||
public Set<Long> extractData(ResultSet rs) throws SQLException {
|
||||
public Set<Long> extractData(ResultSet rs) throws SQLException, DataAccessException {
|
||||
Set<Long> parentIdsToLookup = new HashSet<Long>(); // Set of parent_id Longs
|
||||
|
||||
while (rs.next()) {
|
||||
@@ -564,7 +555,7 @@ public final class BasicLookupStrategy implements LookupStrategy {
|
||||
owner = new GrantedAuthoritySid(rs.getString("acl_sid"));
|
||||
}
|
||||
|
||||
acl = new AclImpl(objectIdentity, id, aclAuthorizationStrategy, grantingStrategy, parentAcl, null,
|
||||
acl = new AclImpl(objectIdentity, id, aclAuthorizationStrategy, auditLogger, parentAcl, null,
|
||||
entriesInheriting, owner);
|
||||
|
||||
acls.put(id, acl);
|
||||
@@ -603,7 +594,7 @@ public final class BasicLookupStrategy implements LookupStrategy {
|
||||
}
|
||||
|
||||
private class StubAclParent implements Acl {
|
||||
private final Long id;
|
||||
private Long id;
|
||||
|
||||
public StubAclParent(Long id) {
|
||||
this.id = id;
|
||||
|
||||
@@ -56,8 +56,8 @@ public class JdbcAclService implements AclService {
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
protected final JdbcTemplate jdbcTemplate;
|
||||
private final LookupStrategy lookupStrategy;
|
||||
protected JdbcTemplate jdbcTemplate;
|
||||
private LookupStrategy lookupStrategy;
|
||||
private String findChildrenSql = DEFAULT_SELECT_ACL_WITH_PARENT_SQL;
|
||||
|
||||
//~ Constructors ===================================================================================================
|
||||
@@ -109,9 +109,10 @@ public class JdbcAclService implements AclService {
|
||||
Map<ObjectIdentity, Acl> result = lookupStrategy.readAclsById(objects, sids);
|
||||
|
||||
// Check every requested object identity was found (throw NotFoundException if needed)
|
||||
for (ObjectIdentity oid : objects) {
|
||||
if (!result.containsKey(oid)) {
|
||||
throw new NotFoundException("Unable to find ACL information for object identity '" + oid + "'");
|
||||
for (int i = 0; i < objects.size(); i++) {
|
||||
if (!result.containsKey(objects.get(i))) {
|
||||
throw new NotFoundException("Unable to find ACL information for object identity '"
|
||||
+ objects.get(i) + "'");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+13
-11
@@ -61,7 +61,7 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private boolean foreignKeysInDatabase = true;
|
||||
private final AclCache aclCache;
|
||||
private AclCache aclCache;
|
||||
private String deleteEntryByObjectIdentityForeignKey = "delete from acl_entry where acl_object_identity=?";
|
||||
private String deleteObjectIdentityByPrimaryKey = "delete from acl_object_identity where id=?";
|
||||
private String classIdentityQuery = "call identity()";
|
||||
@@ -194,7 +194,7 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
|
||||
protected Long createOrRetrieveSidPrimaryKey(Sid sid, boolean allowCreate) {
|
||||
Assert.notNull(sid, "Sid required");
|
||||
|
||||
String sidName;
|
||||
String sidName = null;
|
||||
boolean sidIsPrincipal = true;
|
||||
|
||||
if (sid instanceof PrincipalSid) {
|
||||
@@ -214,7 +214,7 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
|
||||
}
|
||||
|
||||
if (allowCreate) {
|
||||
jdbcTemplate.update(insertSid, Boolean.valueOf(sidIsPrincipal), sidName);
|
||||
jdbcTemplate.update(insertSid, new Object[] {Boolean.valueOf(sidIsPrincipal), sidName});
|
||||
Assert.isTrue(TransactionSynchronizationManager.isSynchronizationActive(), "Transaction must be running");
|
||||
return new Long(jdbcTemplate.queryForLong(sidIdentityQuery));
|
||||
}
|
||||
@@ -229,8 +229,8 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
|
||||
if (deleteChildren) {
|
||||
List<ObjectIdentity> children = findChildren(objectIdentity);
|
||||
if (children != null) {
|
||||
for (ObjectIdentity child : children) {
|
||||
deleteAcl(child, true);
|
||||
for (int i = 0; i < children.size(); i++) {
|
||||
deleteAcl(children.get(i), true);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -263,7 +263,8 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
|
||||
* @param oidPrimaryKey the rows in acl_entry to delete
|
||||
*/
|
||||
protected void deleteEntries(Long oidPrimaryKey) {
|
||||
jdbcTemplate.update(deleteEntryByObjectIdentityForeignKey, oidPrimaryKey);
|
||||
jdbcTemplate.update(deleteEntryByObjectIdentityForeignKey,
|
||||
new Object[] {oidPrimaryKey});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -276,7 +277,7 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
|
||||
*/
|
||||
protected void deleteObjectIdentity(Long oidPrimaryKey) {
|
||||
// Delete the acl_object_identity row
|
||||
jdbcTemplate.update(deleteObjectIdentityByPrimaryKey, oidPrimaryKey);
|
||||
jdbcTemplate.update(deleteObjectIdentityByPrimaryKey, new Object[] {oidPrimaryKey});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -290,7 +291,8 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
|
||||
*/
|
||||
protected Long retrieveObjectIdentityPrimaryKey(ObjectIdentity oid) {
|
||||
try {
|
||||
return new Long(jdbcTemplate.queryForLong(selectObjectIdentityPrimaryKey, oid.getType(), oid.getIdentifier()));
|
||||
return new Long(jdbcTemplate.queryForLong(selectObjectIdentityPrimaryKey,
|
||||
new Object[] {oid.getType(), oid.getIdentifier()}));
|
||||
} catch (DataAccessException notFound) {
|
||||
return null;
|
||||
}
|
||||
@@ -324,8 +326,8 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
|
||||
Assert.notNull(objectIdentity, "ObjectIdentity required");
|
||||
List<ObjectIdentity> children = findChildren(objectIdentity);
|
||||
if (children != null) {
|
||||
for (ObjectIdentity child : children) {
|
||||
clearCacheIncludingChildren(child);
|
||||
for (int i = 0; i < children.size(); i++) {
|
||||
clearCacheIncludingChildren(children.get(i));
|
||||
}
|
||||
}
|
||||
aclCache.evictFromCache(objectIdentity);
|
||||
@@ -354,7 +356,7 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
|
||||
|
||||
Long ownerSid = createOrRetrieveSidPrimaryKey(acl.getOwner(), true);
|
||||
int count = jdbcTemplate.update(updateObjectIdentity,
|
||||
parentId, ownerSid, Boolean.valueOf(acl.isEntriesInheriting()), acl.getId());
|
||||
new Object[] {parentId, ownerSid, new Boolean(acl.isEntriesInheriting()), acl.getId()});
|
||||
|
||||
if (count != 1) {
|
||||
throw new NotFoundException("Unable to locate ACL to update");
|
||||
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
package org.springframework.security.acls.model;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Allow customization of the logic for determining whether a permission or permissions are granted to a particular
|
||||
* sid or sids by an {@link Acl}.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @since 3.0.2
|
||||
*/
|
||||
public interface PermissionGrantingStrategy {
|
||||
|
||||
/**
|
||||
* Returns true if the the supplied strategy decides that the supplied {@code Acl} grants access
|
||||
* based on the supplied list of permissions and sids.
|
||||
*/
|
||||
boolean isGranted(Acl acl, List<Permission> permission, List<Sid> sids, boolean administrativeMode);
|
||||
|
||||
}
|
||||
-56
@@ -1,56 +0,0 @@
|
||||
package org.springframework.security.acls;
|
||||
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.security.acls.domain.ObjectIdentityImpl;
|
||||
import org.springframework.security.acls.model.AclService;
|
||||
import org.springframework.security.acls.model.ObjectIdentity;
|
||||
import org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy;
|
||||
import org.springframework.security.acls.model.SidRetrievalStrategy;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public class AclPermissionCacheOptimizerTests {
|
||||
|
||||
@Test
|
||||
public void eagerlyLoadsRequiredAcls() throws Exception {
|
||||
AclService service = mock(AclService.class);
|
||||
AclPermissionCacheOptimizer pco = new AclPermissionCacheOptimizer(service);
|
||||
ObjectIdentityRetrievalStrategy oidStrat = mock(ObjectIdentityRetrievalStrategy.class);
|
||||
SidRetrievalStrategy sidStrat = mock(SidRetrievalStrategy.class);
|
||||
pco.setObjectIdentityRetrievalStrategy(oidStrat);
|
||||
pco.setSidRetrievalStrategy(sidStrat);
|
||||
Object[] dos = {new Object(), null, new Object()};
|
||||
ObjectIdentity[] oids = {new ObjectIdentityImpl("A", "1"), new ObjectIdentityImpl("A", "2")};
|
||||
when(oidStrat.getObjectIdentity(dos[0])).thenReturn(oids[0]);
|
||||
when(oidStrat.getObjectIdentity(dos[2])).thenReturn(oids[1]);
|
||||
|
||||
pco.cachePermissionsFor(mock(Authentication.class), Arrays.asList(dos));
|
||||
|
||||
// AclService should be invoked with the list of required Oids
|
||||
verify(service).readAclsById(eq(Arrays.asList(oids)), any(List.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ignoresEmptyCollection() {
|
||||
AclService service = mock(AclService.class);
|
||||
AclPermissionCacheOptimizer pco = new AclPermissionCacheOptimizer(service);
|
||||
ObjectIdentityRetrievalStrategy oids = mock(ObjectIdentityRetrievalStrategy.class);
|
||||
SidRetrievalStrategy sids = mock(SidRetrievalStrategy.class);
|
||||
pco.setObjectIdentityRetrievalStrategy(oids);
|
||||
pco.setSidRetrievalStrategy(sids);
|
||||
|
||||
pco.cachePermissionsFor(mock(Authentication.class), Collections.emptyList());
|
||||
|
||||
verifyZeroInteractions(service, sids, oids);
|
||||
}
|
||||
|
||||
}
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
package org.springframework.security.acls.afterinvocation;
|
||||
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyBoolean;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.access.SecurityConfig;
|
||||
import org.springframework.security.acls.model.*;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.SpringSecurityMessageSource;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public class AclEntryAfterInvocationCollectionFilteringProviderTests {
|
||||
@Test
|
||||
public void objectsAreRemovedIfPermissionDenied() throws Exception {
|
||||
AclService service = mock(AclService.class);
|
||||
Acl acl = mock(Acl.class);
|
||||
when(acl.isGranted(any(List.class), any(List.class), anyBoolean())).thenReturn(false);
|
||||
when(service.readAclById(any(ObjectIdentity.class), any(List.class))).thenReturn(acl);
|
||||
AclEntryAfterInvocationCollectionFilteringProvider provider = new AclEntryAfterInvocationCollectionFilteringProvider(service, Arrays.asList(mock(Permission.class)));
|
||||
provider.setObjectIdentityRetrievalStrategy(mock(ObjectIdentityRetrievalStrategy.class));
|
||||
provider.setProcessDomainObjectClass(Object.class);
|
||||
provider.setSidRetrievalStrategy(mock(SidRetrievalStrategy.class));
|
||||
|
||||
Object returned = provider.decide(mock(Authentication.class), new Object(), SecurityConfig.createList("AFTER_ACL_COLLECTION_READ"), new ArrayList(Arrays.asList(new Object(), new Object())));
|
||||
assertTrue(returned instanceof List);
|
||||
assertTrue(((List)returned).isEmpty());
|
||||
returned = provider.decide(mock(Authentication.class), new Object(), SecurityConfig.createList("UNSUPPORTED", "AFTER_ACL_COLLECTION_READ"), new Object[] {new Object(), new Object()});
|
||||
assertTrue(returned instanceof Object[]);
|
||||
assertTrue(((Object[])returned).length == 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void accessIsGrantedIfNoAttributesDefined() throws Exception {
|
||||
AclEntryAfterInvocationCollectionFilteringProvider provider = new AclEntryAfterInvocationCollectionFilteringProvider(mock(AclService.class), Arrays.asList(mock(Permission.class)));
|
||||
Object returned = new Object();
|
||||
|
||||
assertSame(returned, provider.decide(mock(Authentication.class), new Object(), Collections.<ConfigAttribute>emptyList(), returned));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nullReturnObjectIsIgnored() throws Exception {
|
||||
AclService service = mock(AclService.class);
|
||||
AclEntryAfterInvocationCollectionFilteringProvider provider = new AclEntryAfterInvocationCollectionFilteringProvider(service, Arrays.asList(mock(Permission.class)));
|
||||
|
||||
assertNull(provider.decide(mock(Authentication.class), new Object(), SecurityConfig.createList("AFTER_ACL_COLLECTION_READ"), null));
|
||||
verify(service, never()).readAclById(any(ObjectIdentity.class), any(List.class));
|
||||
}
|
||||
|
||||
}
|
||||
-101
@@ -1,101 +0,0 @@
|
||||
package org.springframework.security.acls.afterinvocation;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.access.SecurityConfig;
|
||||
import org.springframework.security.acls.model.*;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.SpringSecurityMessageSource;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public class AclEntryAfterInvocationProviderTests {
|
||||
|
||||
@Test(expected=IllegalArgumentException.class)
|
||||
public void rejectsMissingPermissions() throws Exception {
|
||||
try {
|
||||
new AclEntryAfterInvocationProvider(mock(AclService.class), null);
|
||||
fail("Exception expected");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
new AclEntryAfterInvocationProvider(mock(AclService.class), Collections.<Permission>emptyList());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void accessIsAllowedIfPermissionIsGranted() {
|
||||
AclService service = mock(AclService.class);
|
||||
Acl acl = mock(Acl.class);
|
||||
when(acl.isGranted(any(List.class), any(List.class), anyBoolean())).thenReturn(true);
|
||||
when(service.readAclById(any(ObjectIdentity.class), any(List.class))).thenReturn(acl);
|
||||
AclEntryAfterInvocationProvider provider = new AclEntryAfterInvocationProvider(service, Arrays.asList(mock(Permission.class)));
|
||||
provider.setMessageSource(new SpringSecurityMessageSource());
|
||||
provider.setObjectIdentityRetrievalStrategy(mock(ObjectIdentityRetrievalStrategy.class));
|
||||
provider.setProcessDomainObjectClass(Object.class);
|
||||
provider.setSidRetrievalStrategy(mock(SidRetrievalStrategy.class));
|
||||
Object returned = new Object();
|
||||
|
||||
assertSame(returned, provider.decide(mock(Authentication.class), new Object(), SecurityConfig.createList("AFTER_ACL_READ"), returned));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void accessIsGrantedIfNoAttributesDefined() throws Exception {
|
||||
AclEntryAfterInvocationProvider provider = new AclEntryAfterInvocationProvider(mock(AclService.class), Arrays.asList(mock(Permission.class)));
|
||||
Object returned = new Object();
|
||||
|
||||
assertSame(returned, provider.decide(mock(Authentication.class), new Object(), Collections.<ConfigAttribute>emptyList(), returned));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void accessIsGrantedIfObjectTypeNotSupported() throws Exception {
|
||||
AclEntryAfterInvocationProvider provider = new AclEntryAfterInvocationProvider(mock(AclService.class), Arrays.asList(mock(Permission.class)));
|
||||
provider.setProcessDomainObjectClass(String.class);
|
||||
// Not a String
|
||||
Object returned = new Object();
|
||||
|
||||
assertSame(returned, provider.decide(mock(Authentication.class), new Object(), SecurityConfig.createList("AFTER_ACL_READ"), returned));
|
||||
}
|
||||
|
||||
|
||||
@Test(expected= AccessDeniedException.class)
|
||||
public void accessIsDeniedIfPermissionIsNotGranted() {
|
||||
AclService service = mock(AclService.class);
|
||||
Acl acl = mock(Acl.class);
|
||||
when(acl.isGranted(any(List.class), any(List.class), anyBoolean())).thenReturn(false);
|
||||
// Try a second time with no permissions found
|
||||
when(acl.isGranted(any(List.class), any(List.class), anyBoolean())).thenThrow(new NotFoundException(""));
|
||||
when(service.readAclById(any(ObjectIdentity.class), any(List.class))).thenReturn(acl);
|
||||
AclEntryAfterInvocationProvider provider = new AclEntryAfterInvocationProvider(service, Arrays.asList(mock(Permission.class)));
|
||||
provider.setProcessConfigAttribute("MY_ATTRIBUTE");
|
||||
provider.setMessageSource(new SpringSecurityMessageSource());
|
||||
provider.setObjectIdentityRetrievalStrategy(mock(ObjectIdentityRetrievalStrategy.class));
|
||||
provider.setProcessDomainObjectClass(Object.class);
|
||||
provider.setSidRetrievalStrategy(mock(SidRetrievalStrategy.class));
|
||||
try {
|
||||
provider.decide(mock(Authentication.class), new Object(), SecurityConfig.createList("UNSUPPORTED", "MY_ATTRIBUTE"), new Object());
|
||||
fail();
|
||||
} catch (AccessDeniedException expected) {
|
||||
}
|
||||
// Second scenario with no acls found
|
||||
provider.decide(mock(Authentication.class), new Object(), SecurityConfig.createList("UNSUPPORTED", "MY_ATTRIBUTE"), new Object());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nullReturnObjectIsIgnored() throws Exception {
|
||||
AclService service = mock(AclService.class);
|
||||
AclEntryAfterInvocationProvider provider = new AclEntryAfterInvocationProvider(service, Arrays.asList(mock(Permission.class)));
|
||||
|
||||
assertNull(provider.decide(mock(Authentication.class), new Object(), SecurityConfig.createList("AFTER_ACL_COLLECTION_READ"), null));
|
||||
verify(service, never()).readAclById(any(ObjectIdentity.class), any(List.class));
|
||||
}
|
||||
}
|
||||
+22
-18
@@ -1,8 +1,10 @@
|
||||
package org.springframework.security.acls.domain;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.Mockery;
|
||||
import org.jmock.integration.junit4.JUnit4Mockery;
|
||||
import org.junit.Test;
|
||||
import org.springframework.security.acls.model.AccessControlEntry;
|
||||
import org.springframework.security.acls.model.Acl;
|
||||
@@ -16,6 +18,7 @@ import org.springframework.security.acls.model.Sid;
|
||||
* @author Andrei Stefan
|
||||
*/
|
||||
public class AccessControlImplEntryTests {
|
||||
Mockery jmock = new JUnit4Mockery();
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
@@ -32,7 +35,7 @@ public class AccessControlImplEntryTests {
|
||||
|
||||
// Check Sid field is present
|
||||
try {
|
||||
new AccessControlEntryImpl(null, mock(Acl.class), null,
|
||||
new AccessControlEntryImpl(null, jmock.mock(Acl.class), null,
|
||||
BasePermission.ADMINISTRATION, true, true, true);
|
||||
fail("It should have thrown IllegalArgumentException");
|
||||
}
|
||||
@@ -41,7 +44,7 @@ public class AccessControlImplEntryTests {
|
||||
|
||||
// Check Permission field is present
|
||||
try {
|
||||
new AccessControlEntryImpl(null, mock(Acl.class), new PrincipalSid("johndoe"), null,
|
||||
new AccessControlEntryImpl(null, jmock.mock(Acl.class), new PrincipalSid("johndoe"), null,
|
||||
true, true, true);
|
||||
fail("It should have thrown IllegalArgumentException");
|
||||
}
|
||||
@@ -51,11 +54,11 @@ public class AccessControlImplEntryTests {
|
||||
|
||||
@Test
|
||||
public void testAccessControlEntryImplGetters() {
|
||||
Acl mockAcl = mock(Acl.class);
|
||||
Acl mockAcl = jmock.mock(Acl.class);
|
||||
Sid sid = new PrincipalSid("johndoe");
|
||||
|
||||
// Create a sample entry
|
||||
AccessControlEntry ace = new AccessControlEntryImpl(Long.valueOf(1), mockAcl, sid, BasePermission.ADMINISTRATION,
|
||||
AccessControlEntry ace = new AccessControlEntryImpl(new Long(1), mockAcl, sid, BasePermission.ADMINISTRATION,
|
||||
true, true, true);
|
||||
|
||||
// and check every get() method
|
||||
@@ -70,31 +73,32 @@ public class AccessControlImplEntryTests {
|
||||
|
||||
@Test
|
||||
public void testEquals() {
|
||||
final Acl mockAcl = mock(Acl.class);
|
||||
final ObjectIdentity oid = mock(ObjectIdentity.class);
|
||||
|
||||
when(mockAcl.getObjectIdentity()).thenReturn(oid);
|
||||
final Acl mockAcl = jmock.mock(Acl.class);
|
||||
final ObjectIdentity oid = jmock.mock(ObjectIdentity.class);
|
||||
jmock.checking(new Expectations() {{
|
||||
allowing(mockAcl).getObjectIdentity(); will(returnValue(oid));
|
||||
}});
|
||||
Sid sid = new PrincipalSid("johndoe");
|
||||
|
||||
AccessControlEntry ace = new AccessControlEntryImpl(Long.valueOf(1), mockAcl, sid, BasePermission.ADMINISTRATION,
|
||||
AccessControlEntry ace = new AccessControlEntryImpl(new Long(1), mockAcl, sid, BasePermission.ADMINISTRATION,
|
||||
true, true, true);
|
||||
|
||||
assertFalse(ace.equals(null));
|
||||
assertFalse(ace.equals(Long.valueOf(100)));
|
||||
assertFalse(ace.equals(new Long(100)));
|
||||
assertTrue(ace.equals(ace));
|
||||
assertTrue(ace.equals(new AccessControlEntryImpl(Long.valueOf(1), mockAcl, sid,
|
||||
assertTrue(ace.equals(new AccessControlEntryImpl(new Long(1), mockAcl, sid,
|
||||
BasePermission.ADMINISTRATION, true, true, true)));
|
||||
assertFalse(ace.equals(new AccessControlEntryImpl(Long.valueOf(2), mockAcl, sid,
|
||||
assertFalse(ace.equals(new AccessControlEntryImpl(new Long(2), mockAcl, sid,
|
||||
BasePermission.ADMINISTRATION, true, true, true)));
|
||||
assertFalse(ace.equals(new AccessControlEntryImpl(Long.valueOf(1), mockAcl, new PrincipalSid("scott"),
|
||||
assertFalse(ace.equals(new AccessControlEntryImpl(new Long(1), mockAcl, new PrincipalSid("scott"),
|
||||
BasePermission.ADMINISTRATION, true, true, true)));
|
||||
assertFalse(ace.equals(new AccessControlEntryImpl(Long.valueOf(1), mockAcl, sid, BasePermission.WRITE, true,
|
||||
assertFalse(ace.equals(new AccessControlEntryImpl(new Long(1), mockAcl, sid, BasePermission.WRITE, true,
|
||||
true, true)));
|
||||
assertFalse(ace.equals(new AccessControlEntryImpl(Long.valueOf(1), mockAcl, sid,
|
||||
assertFalse(ace.equals(new AccessControlEntryImpl(new Long(1), mockAcl, sid,
|
||||
BasePermission.ADMINISTRATION, false, true, true)));
|
||||
assertFalse(ace.equals(new AccessControlEntryImpl(Long.valueOf(1), mockAcl, sid,
|
||||
assertFalse(ace.equals(new AccessControlEntryImpl(new Long(1), mockAcl, sid,
|
||||
BasePermission.ADMINISTRATION, true, false, true)));
|
||||
assertFalse(ace.equals(new AccessControlEntryImpl(Long.valueOf(1), mockAcl, sid,
|
||||
assertFalse(ace.equals(new AccessControlEntryImpl(new Long(1), mockAcl, sid,
|
||||
BasePermission.ADMINISTRATION, true, true, false)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,37 @@
|
||||
package org.springframework.security.acls.domain;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import org.junit.*;
|
||||
import org.springframework.security.acls.model.*;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.util.FieldUtils;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jmock.Mockery;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.security.acls.model.AccessControlEntry;
|
||||
import org.springframework.security.acls.model.Acl;
|
||||
import org.springframework.security.acls.model.AlreadyExistsException;
|
||||
import org.springframework.security.acls.model.AuditableAccessControlEntry;
|
||||
import org.springframework.security.acls.model.AuditableAcl;
|
||||
import org.springframework.security.acls.model.ChildrenExistException;
|
||||
import org.springframework.security.acls.model.MutableAcl;
|
||||
import org.springframework.security.acls.model.MutableAclService;
|
||||
import org.springframework.security.acls.model.NotFoundException;
|
||||
import org.springframework.security.acls.model.ObjectIdentity;
|
||||
import org.springframework.security.acls.model.OwnershipAcl;
|
||||
import org.springframework.security.acls.model.Permission;
|
||||
import org.springframework.security.acls.model.Sid;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.GrantedAuthorityImpl;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.util.FieldUtils;
|
||||
|
||||
|
||||
/**
|
||||
@@ -30,8 +49,8 @@ public class AclImplTests {
|
||||
private static final List<Sid> BEN = Arrays.asList((Sid)new PrincipalSid("ben"));
|
||||
|
||||
Authentication auth = new TestingAuthenticationToken("joe", "ignored", "ROLE_ADMINISTRATOR");
|
||||
AclAuthorizationStrategy authzStrategy;
|
||||
PermissionGrantingStrategy pgs;
|
||||
Mockery jmockCtx = new Mockery();
|
||||
AclAuthorizationStrategy mockAuthzStrategy;
|
||||
AuditLogger mockAuditLogger;
|
||||
ObjectIdentity objectIdentity = new ObjectIdentityImpl(TARGET_CLASS, 100);
|
||||
|
||||
@@ -40,9 +59,8 @@ public class AclImplTests {
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
SecurityContextHolder.getContext().setAuthentication(auth);
|
||||
authzStrategy = mock(AclAuthorizationStrategy.class);
|
||||
mockAuditLogger = mock(AuditLogger.class);
|
||||
pgs = new DefaultPermissionGrantingStrategy(mockAuditLogger);
|
||||
mockAuthzStrategy = mock(AclAuthorizationStrategy.class);
|
||||
mockAuditLogger = mock(AuditLogger.class);;
|
||||
auth.setAuthenticated(true);
|
||||
}
|
||||
|
||||
@@ -54,26 +72,25 @@ public class AclImplTests {
|
||||
@Test(expected=IllegalArgumentException.class)
|
||||
public void constructorsRejectNullObjectIdentity() throws Exception {
|
||||
try {
|
||||
new AclImpl(null, 1, authzStrategy, pgs, null, null, true, new PrincipalSid("joe"));
|
||||
new AclImpl(null, 1, mockAuthzStrategy, mockAuditLogger, null, null, true, new PrincipalSid("joe"));
|
||||
fail("Should have thrown IllegalArgumentException");
|
||||
}
|
||||
catch (IllegalArgumentException expected) {
|
||||
}
|
||||
new AclImpl(null, 1, authzStrategy, mockAuditLogger);
|
||||
new AclImpl(null, 1, mockAuthzStrategy, mockAuditLogger);
|
||||
}
|
||||
|
||||
@Test(expected=IllegalArgumentException.class)
|
||||
public void constructorsRejectNullId() throws Exception {
|
||||
try {
|
||||
new AclImpl(objectIdentity, null, authzStrategy, pgs, null, null, true, new PrincipalSid("joe"));
|
||||
new AclImpl(objectIdentity, null, mockAuthzStrategy, mockAuditLogger, null, null, true, new PrincipalSid("joe"));
|
||||
fail("Should have thrown IllegalArgumentException");
|
||||
}
|
||||
catch (IllegalArgumentException expected) {
|
||||
}
|
||||
new AclImpl(objectIdentity, null, authzStrategy, mockAuditLogger);
|
||||
new AclImpl(objectIdentity, null, mockAuthzStrategy, mockAuditLogger);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Test(expected=IllegalArgumentException.class)
|
||||
public void constructorsRejectNullAclAuthzStrategy() throws Exception {
|
||||
try {
|
||||
@@ -85,9 +102,20 @@ public class AclImplTests {
|
||||
new AclImpl(objectIdentity, 1, null, mockAuditLogger);
|
||||
}
|
||||
|
||||
@Test(expected=IllegalArgumentException.class)
|
||||
public void constructorsRejectNullAuditLogger() throws Exception {
|
||||
try {
|
||||
new AclImpl(objectIdentity, 1, mockAuthzStrategy, null, null, null, true, new PrincipalSid("joe"));
|
||||
fail("It should have thrown IllegalArgumentException");
|
||||
}
|
||||
catch (IllegalArgumentException expected) {
|
||||
}
|
||||
new AclImpl(objectIdentity, 1, mockAuthzStrategy, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void insertAceRejectsNullParameters() throws Exception {
|
||||
MutableAcl acl = new AclImpl(objectIdentity, 1, authzStrategy, pgs, null, null, true, new PrincipalSid(
|
||||
MutableAcl acl = new AclImpl(objectIdentity, 1, mockAuthzStrategy, mockAuditLogger, null, null, true, new PrincipalSid(
|
||||
"joe"));
|
||||
try {
|
||||
acl.insertAce(0, null, new GrantedAuthoritySid("ROLE_IGNORED"), true);
|
||||
@@ -105,7 +133,7 @@ public class AclImplTests {
|
||||
|
||||
@Test
|
||||
public void insertAceAddsElementAtCorrectIndex() throws Exception {
|
||||
MutableAcl acl = new AclImpl(objectIdentity, 1, authzStrategy, pgs, null, null, true, new PrincipalSid("joe"));
|
||||
MutableAcl acl = new AclImpl(objectIdentity, 1, mockAuthzStrategy, mockAuditLogger, null, null, true, new PrincipalSid("joe"));
|
||||
MockAclService service = new MockAclService();
|
||||
|
||||
// Insert one permission
|
||||
@@ -141,7 +169,7 @@ public class AclImplTests {
|
||||
|
||||
@Test(expected=NotFoundException.class)
|
||||
public void insertAceFailsForNonExistentElement() throws Exception {
|
||||
MutableAcl acl = new AclImpl(objectIdentity,1, authzStrategy, pgs, null, null, true, new PrincipalSid(
|
||||
MutableAcl acl = new AclImpl(objectIdentity,1, mockAuthzStrategy, mockAuditLogger, null, null, true, new PrincipalSid(
|
||||
"joe"));
|
||||
MockAclService service = new MockAclService();
|
||||
|
||||
@@ -154,7 +182,8 @@ public class AclImplTests {
|
||||
|
||||
@Test
|
||||
public void deleteAceKeepsInitialOrdering() throws Exception {
|
||||
MutableAcl acl = new AclImpl(objectIdentity,1, authzStrategy, pgs, null, null, true, new PrincipalSid("joe"));
|
||||
MutableAcl acl = new AclImpl(objectIdentity,1, mockAuthzStrategy, mockAuditLogger, null, null, true, new PrincipalSid(
|
||||
"joe"));
|
||||
MockAclService service = new MockAclService();
|
||||
|
||||
// Add several permissions
|
||||
@@ -185,10 +214,11 @@ public class AclImplTests {
|
||||
|
||||
@Test
|
||||
public void deleteAceFailsForNonExistentElement() throws Exception {
|
||||
AclAuthorizationStrategyImpl strategy = new AclAuthorizationStrategyImpl(
|
||||
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority("ROLE_AUDITING"),
|
||||
new SimpleGrantedAuthority("ROLE_GENERAL"));
|
||||
MutableAcl acl = new AclImpl(objectIdentity, (1), strategy, pgs, null, null, true, new PrincipalSid(
|
||||
AclAuthorizationStrategyImpl strategy = new AclAuthorizationStrategyImpl(new GrantedAuthority[] {
|
||||
new GrantedAuthorityImpl("ROLE_OWNERSHIP"), new GrantedAuthorityImpl("ROLE_AUDITING"),
|
||||
new GrantedAuthorityImpl("ROLE_GENERAL") });
|
||||
AuditLogger auditLogger = new ConsoleAuditLogger();
|
||||
MutableAcl acl = new AclImpl(objectIdentity, (1), strategy, auditLogger, null, null, true, new PrincipalSid(
|
||||
"joe"));
|
||||
try {
|
||||
acl.deleteAce(99);
|
||||
@@ -200,7 +230,8 @@ public class AclImplTests {
|
||||
|
||||
@Test
|
||||
public void isGrantingRejectsEmptyParameters() throws Exception {
|
||||
MutableAcl acl = new AclImpl(objectIdentity, 1, authzStrategy, pgs, null, null, true, new PrincipalSid("joe"));
|
||||
MutableAcl acl = new AclImpl(objectIdentity, 1, mockAuthzStrategy, mockAuditLogger, null, null, true, new PrincipalSid(
|
||||
"joe"));
|
||||
Sid ben = new PrincipalSid("ben");
|
||||
try {
|
||||
acl.isGranted(new ArrayList<Permission>(0), Arrays.asList(ben) , false);
|
||||
@@ -224,7 +255,8 @@ public class AclImplTests {
|
||||
ObjectIdentity rootOid = new ObjectIdentityImpl(TARGET_CLASS, 100);
|
||||
|
||||
// Create an ACL which owner is not the authenticated principal
|
||||
MutableAcl rootAcl = new AclImpl(rootOid, 1, authzStrategy, pgs, null, null, false, new PrincipalSid("joe"));
|
||||
MutableAcl rootAcl = new AclImpl(rootOid, 1, mockAuthzStrategy, mockAuditLogger, null, null, false, new PrincipalSid(
|
||||
"joe"));
|
||||
|
||||
// Grant some permissions
|
||||
rootAcl.insertAce(0, BasePermission.READ, new PrincipalSid("ben"), false);
|
||||
@@ -267,12 +299,16 @@ public class AclImplTests {
|
||||
ObjectIdentity childOid2 = new ObjectIdentityImpl(TARGET_CLASS, 104);
|
||||
|
||||
// Create ACLs
|
||||
PrincipalSid joe = new PrincipalSid("joe");
|
||||
MutableAcl grandParentAcl = new AclImpl(grandParentOid, 1, authzStrategy, pgs, null, null, false, joe);
|
||||
MutableAcl parentAcl1 = new AclImpl(parentOid1, 2, authzStrategy, pgs, null, null, true, joe);
|
||||
MutableAcl parentAcl2 = new AclImpl(parentOid2, 3, authzStrategy, pgs, null, null, true, joe);
|
||||
MutableAcl childAcl1 = new AclImpl(childOid1, 4, authzStrategy, pgs, null, null, true, joe);
|
||||
MutableAcl childAcl2 = new AclImpl(childOid2, 4, authzStrategy, pgs, null, null, false, joe);
|
||||
MutableAcl grandParentAcl = new AclImpl(grandParentOid, 1, mockAuthzStrategy, mockAuditLogger, null, null, false,
|
||||
new PrincipalSid("joe"));
|
||||
MutableAcl parentAcl1 = new AclImpl(parentOid1, 2, mockAuthzStrategy, mockAuditLogger, null, null, true,
|
||||
new PrincipalSid("joe"));
|
||||
MutableAcl parentAcl2 = new AclImpl(parentOid2, 3, mockAuthzStrategy, mockAuditLogger, null, null, true,
|
||||
new PrincipalSid("joe"));
|
||||
MutableAcl childAcl1 = new AclImpl(childOid1, 4, mockAuthzStrategy, mockAuditLogger, null, null, true,
|
||||
new PrincipalSid("joe"));
|
||||
MutableAcl childAcl2 = new AclImpl(childOid2, 4, mockAuthzStrategy, mockAuditLogger, null, null, false,
|
||||
new PrincipalSid("joe"));
|
||||
|
||||
// Create hierarchies
|
||||
childAcl2.setParent(childAcl1);
|
||||
@@ -330,7 +366,8 @@ public class AclImplTests {
|
||||
Authentication auth = new TestingAuthenticationToken("ben", "ignored","ROLE_GENERAL");
|
||||
auth.setAuthenticated(true);
|
||||
SecurityContextHolder.getContext().setAuthentication(auth);
|
||||
MutableAcl acl = new AclImpl(objectIdentity, 1, authzStrategy, pgs, null, null, false, new PrincipalSid("joe"));
|
||||
MutableAcl acl = new AclImpl(objectIdentity, 1, mockAuthzStrategy, mockAuditLogger, null, null, false, new PrincipalSid(
|
||||
"joe"));
|
||||
MockAclService service = new MockAclService();
|
||||
|
||||
acl.insertAce(0, BasePermission.READ, new GrantedAuthoritySid("ROLE_USER_READ"), true);
|
||||
@@ -347,7 +384,7 @@ public class AclImplTests {
|
||||
acl.updateAce(1, BasePermission.DELETE);
|
||||
acl.updateAce(2, BasePermission.READ);
|
||||
|
||||
// Check the change was successfully made
|
||||
// Check the change was successfuly made
|
||||
assertEquals(acl.getEntries().get(0).getPermission(), BasePermission.CREATE);
|
||||
assertEquals(acl.getEntries().get(1).getPermission(), BasePermission.DELETE);
|
||||
assertEquals(acl.getEntries().get(2).getPermission(), BasePermission.READ);
|
||||
@@ -358,7 +395,8 @@ public class AclImplTests {
|
||||
Authentication auth = new TestingAuthenticationToken("ben", "ignored", "ROLE_AUDITING", "ROLE_GENERAL");
|
||||
auth.setAuthenticated(true);
|
||||
SecurityContextHolder.getContext().setAuthentication(auth);
|
||||
MutableAcl acl = new AclImpl(objectIdentity, 1, authzStrategy, pgs, null, null, false, new PrincipalSid("joe"));
|
||||
MutableAcl acl = new AclImpl(objectIdentity, 1, mockAuthzStrategy, mockAuditLogger, null, null, false, new PrincipalSid(
|
||||
"joe"));
|
||||
MockAclService service = new MockAclService();
|
||||
|
||||
acl.insertAce(0, BasePermission.READ, new GrantedAuthoritySid("ROLE_USER_READ"), true);
|
||||
@@ -383,13 +421,16 @@ public class AclImplTests {
|
||||
|
||||
@Test
|
||||
public void gettersAndSettersAreConsistent() throws Exception {
|
||||
Authentication auth = new TestingAuthenticationToken("ben", "ignored", "ROLE_GENERAL");
|
||||
Authentication auth = new TestingAuthenticationToken("ben", "ignored", new GrantedAuthority[] {
|
||||
new GrantedAuthorityImpl("ROLE_GENERAL") });
|
||||
auth.setAuthenticated(true);
|
||||
SecurityContextHolder.getContext().setAuthentication(auth);
|
||||
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, (100));
|
||||
ObjectIdentity identity2 = new ObjectIdentityImpl(TARGET_CLASS, (101));
|
||||
MutableAcl acl = new AclImpl(identity, 1, authzStrategy, pgs, null, null, true, new PrincipalSid("joe"));
|
||||
MutableAcl parentAcl = new AclImpl(identity2, 2, authzStrategy, pgs, null, null, true, new PrincipalSid("joe"));
|
||||
MutableAcl acl = new AclImpl(identity, 1, mockAuthzStrategy, mockAuditLogger, null, null, true,
|
||||
new PrincipalSid("joe"));
|
||||
MutableAcl parentAcl = new AclImpl(identity2, 2, mockAuthzStrategy, mockAuditLogger, null, null, true,
|
||||
new PrincipalSid("joe"));
|
||||
MockAclService service = new MockAclService();
|
||||
acl.insertAce(0, BasePermission.READ, new GrantedAuthoritySid("ROLE_USER_READ"), true);
|
||||
acl.insertAce(1, BasePermission.WRITE, new GrantedAuthoritySid("ROLE_USER_READ"), true);
|
||||
@@ -408,14 +449,14 @@ public class AclImplTests {
|
||||
acl.setEntriesInheriting(false);
|
||||
assertFalse(acl.isEntriesInheriting());
|
||||
|
||||
acl.setOwner(new PrincipalSid("ben"));
|
||||
((OwnershipAcl) acl).setOwner(new PrincipalSid("ben"));
|
||||
assertEquals(acl.getOwner(), new PrincipalSid("ben"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSidLoadedBehavesAsExpected() throws Exception {
|
||||
List<Sid> loadedSids = Arrays.asList(new PrincipalSid("ben"), new GrantedAuthoritySid("ROLE_IGNORED"));
|
||||
MutableAcl acl = new AclImpl(objectIdentity, 1, authzStrategy, pgs, null, loadedSids, true,
|
||||
MutableAcl acl = new AclImpl(objectIdentity, 1, mockAuthzStrategy, mockAuditLogger, null, loadedSids, true,
|
||||
new PrincipalSid("joe"));
|
||||
|
||||
assertTrue(acl.isSidLoaded(loadedSids));
|
||||
@@ -431,19 +472,22 @@ public class AclImplTests {
|
||||
|
||||
@Test(expected=NotFoundException.class)
|
||||
public void insertAceRaisesNotFoundExceptionForIndexLessThanZero() throws Exception {
|
||||
AclImpl acl = new AclImpl(objectIdentity, 1, authzStrategy, pgs, null, null, true, new PrincipalSid("joe"));
|
||||
AclImpl acl = new AclImpl(objectIdentity, 1, mockAuthzStrategy, mockAuditLogger, null, null, true,
|
||||
new PrincipalSid("joe"));
|
||||
acl.insertAce(-1, mock(Permission.class), mock(Sid.class), true);
|
||||
}
|
||||
|
||||
@Test(expected=NotFoundException.class)
|
||||
public void deleteAceRaisesNotFoundExceptionForIndexLessThanZero() throws Exception {
|
||||
AclImpl acl = new AclImpl(objectIdentity, 1, authzStrategy, pgs, null, null, true, new PrincipalSid("joe"));
|
||||
AclImpl acl = new AclImpl(objectIdentity, 1, mockAuthzStrategy, mockAuditLogger, null, null, true,
|
||||
new PrincipalSid("joe"));
|
||||
acl.deleteAce(-1);
|
||||
}
|
||||
|
||||
@Test(expected=NotFoundException.class)
|
||||
public void insertAceRaisesNotFoundExceptionForIndexGreaterThanSize() throws Exception {
|
||||
AclImpl acl = new AclImpl(objectIdentity, 1, authzStrategy, pgs, null, null, true, new PrincipalSid("joe"));
|
||||
AclImpl acl = new AclImpl(objectIdentity, 1, mockAuthzStrategy, mockAuditLogger, null, null, true,
|
||||
new PrincipalSid("joe"));
|
||||
// Insert at zero, OK.
|
||||
acl.insertAce(0, mock(Permission.class), mock(Sid.class), true);
|
||||
// Size is now 1
|
||||
@@ -453,7 +497,8 @@ public class AclImplTests {
|
||||
// SEC-1151
|
||||
@Test(expected=NotFoundException.class)
|
||||
public void deleteAceRaisesNotFoundExceptionForIndexEqualToSize() throws Exception {
|
||||
AclImpl acl = new AclImpl(objectIdentity, 1, authzStrategy, pgs, null, null, true, new PrincipalSid("joe"));
|
||||
AclImpl acl = new AclImpl(objectIdentity, 1, mockAuthzStrategy, mockAuditLogger, null, null, true,
|
||||
new PrincipalSid("joe"));
|
||||
acl.insertAce(0, mock(Permission.class), mock(Sid.class), true);
|
||||
// Size is now 1
|
||||
acl.deleteAce(1);
|
||||
@@ -462,9 +507,9 @@ public class AclImplTests {
|
||||
// SEC-1795
|
||||
@Test
|
||||
public void changingParentIsSuccessful() throws Exception {
|
||||
AclImpl parentAcl = new AclImpl(objectIdentity, 1L, authzStrategy, mockAuditLogger);
|
||||
AclImpl childAcl = new AclImpl(objectIdentity, 2L, authzStrategy, mockAuditLogger);
|
||||
AclImpl changeParentAcl = new AclImpl(objectIdentity, 3L, authzStrategy, mockAuditLogger);
|
||||
AclImpl parentAcl = new AclImpl(objectIdentity, 1L, mockAuthzStrategy, mockAuditLogger);
|
||||
AclImpl childAcl = new AclImpl(objectIdentity, 2L, mockAuthzStrategy, mockAuditLogger);
|
||||
AclImpl changeParentAcl = new AclImpl(objectIdentity, 3L, mockAuthzStrategy, mockAuditLogger);
|
||||
|
||||
childAcl.setParent(parentAcl);
|
||||
childAcl.setParent(changeParentAcl);
|
||||
|
||||
+60
-59
@@ -1,8 +1,8 @@
|
||||
package org.springframework.security.acls.domain;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import junit.framework.Assert;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.junit.*;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.acls.model.Acl;
|
||||
import org.springframework.security.acls.model.MutableAcl;
|
||||
@@ -10,7 +10,8 @@ import org.springframework.security.acls.model.NotFoundException;
|
||||
import org.springframework.security.acls.model.ObjectIdentity;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.GrantedAuthorityImpl;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
|
||||
/**
|
||||
@@ -19,31 +20,28 @@ import org.springframework.security.core.context.SecurityContextHolder;
|
||||
*
|
||||
* @author Andrei Stefan
|
||||
*/
|
||||
public class AclImplementationSecurityCheckTests {
|
||||
public class AclImplementationSecurityCheckTests extends TestCase {
|
||||
private static final String TARGET_CLASS = "org.springframework.security.acls.TargetObject";
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
protected void setUp() throws Exception {
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
protected void tearDown() throws Exception {
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSecurityCheckNoACEs() throws Exception {
|
||||
Authentication auth = new TestingAuthenticationToken("user", "password","ROLE_GENERAL","ROLE_AUDITING","ROLE_OWNERSHIP");
|
||||
auth.setAuthenticated(true);
|
||||
SecurityContextHolder.getContext().setAuthentication(auth);
|
||||
|
||||
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, new Long(100));
|
||||
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
|
||||
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority("ROLE_AUDITING"),
|
||||
new SimpleGrantedAuthority("ROLE_GENERAL"));
|
||||
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(new GrantedAuthority[] {
|
||||
new GrantedAuthorityImpl("ROLE_OWNERSHIP"), new GrantedAuthorityImpl("ROLE_AUDITING"),
|
||||
new GrantedAuthorityImpl("ROLE_GENERAL") });
|
||||
|
||||
Acl acl = new AclImpl(identity, new Long(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
|
||||
|
||||
@@ -52,43 +50,43 @@ public class AclImplementationSecurityCheckTests {
|
||||
aclAuthorizationStrategy.securityCheck(acl, AclAuthorizationStrategy.CHANGE_OWNERSHIP);
|
||||
|
||||
// Create another authorization strategy
|
||||
AclAuthorizationStrategy aclAuthorizationStrategy2 = new AclAuthorizationStrategyImpl(
|
||||
new SimpleGrantedAuthority("ROLE_ONE"), new SimpleGrantedAuthority("ROLE_TWO"),
|
||||
new SimpleGrantedAuthority("ROLE_THREE"));
|
||||
AclAuthorizationStrategy aclAuthorizationStrategy2 = new AclAuthorizationStrategyImpl(new GrantedAuthority[] {
|
||||
new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl("ROLE_TWO"),
|
||||
new GrantedAuthorityImpl("ROLE_THREE") });
|
||||
Acl acl2 = new AclImpl(identity, new Long(1), aclAuthorizationStrategy2, new ConsoleAuditLogger());
|
||||
// Check access in case the principal has no authorization rights
|
||||
try {
|
||||
aclAuthorizationStrategy2.securityCheck(acl2, AclAuthorizationStrategy.CHANGE_GENERAL);
|
||||
fail("It should have thrown NotFoundException");
|
||||
Assert.fail("It should have thrown NotFoundException");
|
||||
}
|
||||
catch (NotFoundException expected) {
|
||||
}
|
||||
try {
|
||||
aclAuthorizationStrategy2.securityCheck(acl2, AclAuthorizationStrategy.CHANGE_AUDITING);
|
||||
fail("It should have thrown NotFoundException");
|
||||
Assert.fail("It should have thrown NotFoundException");
|
||||
}
|
||||
catch (NotFoundException expected) {
|
||||
}
|
||||
try {
|
||||
aclAuthorizationStrategy2.securityCheck(acl2, AclAuthorizationStrategy.CHANGE_OWNERSHIP);
|
||||
fail("It should have thrown NotFoundException");
|
||||
Assert.fail("It should have thrown NotFoundException");
|
||||
}
|
||||
catch (NotFoundException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSecurityCheckWithMultipleACEs() throws Exception {
|
||||
// Create a simple authentication with ROLE_GENERAL
|
||||
Authentication auth = new TestingAuthenticationToken("user", "password", "ROLE_GENERAL");
|
||||
Authentication auth = new TestingAuthenticationToken("user", "password",
|
||||
new GrantedAuthority[] { new GrantedAuthorityImpl("ROLE_GENERAL") });
|
||||
auth.setAuthenticated(true);
|
||||
SecurityContextHolder.getContext().setAuthentication(auth);
|
||||
|
||||
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, new Long(100));
|
||||
// Authorization strategy will require a different role for each access
|
||||
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
|
||||
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority("ROLE_AUDITING"),
|
||||
new SimpleGrantedAuthority("ROLE_GENERAL"));
|
||||
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(new GrantedAuthority[] {
|
||||
new GrantedAuthorityImpl("ROLE_OWNERSHIP"), new GrantedAuthorityImpl("ROLE_AUDITING"),
|
||||
new GrantedAuthorityImpl("ROLE_GENERAL") });
|
||||
|
||||
// Let's give the principal the ADMINISTRATION permission, without
|
||||
// granting access
|
||||
@@ -103,13 +101,13 @@ public class AclImplementationSecurityCheckTests {
|
||||
// nor granting access
|
||||
try {
|
||||
aclAuthorizationStrategy.securityCheck(aclFirstDeny, AclAuthorizationStrategy.CHANGE_AUDITING);
|
||||
fail("It should have thrown AccessDeniedException");
|
||||
Assert.fail("It should have thrown AccessDeniedException");
|
||||
}
|
||||
catch (AccessDeniedException expected) {
|
||||
}
|
||||
try {
|
||||
aclAuthorizationStrategy.securityCheck(aclFirstDeny, AclAuthorizationStrategy.CHANGE_OWNERSHIP);
|
||||
fail("It should have thrown AccessDeniedException");
|
||||
Assert.fail("It should have thrown AccessDeniedException");
|
||||
}
|
||||
catch (AccessDeniedException expected) {
|
||||
}
|
||||
@@ -120,7 +118,7 @@ public class AclImplementationSecurityCheckTests {
|
||||
// (false) will deny this access
|
||||
try {
|
||||
aclAuthorizationStrategy.securityCheck(aclFirstDeny, AclAuthorizationStrategy.CHANGE_AUDITING);
|
||||
fail("It should have thrown AccessDeniedException");
|
||||
Assert.fail("It should have thrown AccessDeniedException");
|
||||
}
|
||||
catch (AccessDeniedException expected) {
|
||||
}
|
||||
@@ -140,58 +138,58 @@ public class AclImplementationSecurityCheckTests {
|
||||
aclFirstAllow.insertAce(1, BasePermission.ADMINISTRATION, new PrincipalSid(auth), false);
|
||||
try {
|
||||
aclAuthorizationStrategy.securityCheck(aclFirstAllow, AclAuthorizationStrategy.CHANGE_AUDITING);
|
||||
assertTrue(true);
|
||||
Assert.assertTrue(true);
|
||||
}
|
||||
catch (AccessDeniedException notExpected) {
|
||||
fail("It shouldn't have thrown AccessDeniedException");
|
||||
Assert.fail("It shouldn't have thrown AccessDeniedException");
|
||||
}
|
||||
|
||||
// Create an ACL with no ACE
|
||||
MutableAcl aclNoACE = new AclImpl(identity, new Long(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
|
||||
try {
|
||||
aclAuthorizationStrategy.securityCheck(aclNoACE, AclAuthorizationStrategy.CHANGE_AUDITING);
|
||||
fail("It should have thrown NotFoundException");
|
||||
Assert.fail("It should have thrown NotFoundException");
|
||||
}
|
||||
catch (NotFoundException expected) {
|
||||
assertTrue(true);
|
||||
Assert.assertTrue(true);
|
||||
}
|
||||
// and still grant access for CHANGE_GENERAL
|
||||
try {
|
||||
aclAuthorizationStrategy.securityCheck(aclNoACE, AclAuthorizationStrategy.CHANGE_GENERAL);
|
||||
assertTrue(true);
|
||||
Assert.assertTrue(true);
|
||||
}
|
||||
catch (NotFoundException expected) {
|
||||
fail("It shouldn't have thrown NotFoundException");
|
||||
Assert.fail("It shouldn't have thrown NotFoundException");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSecurityCheckWithInheritableACEs() throws Exception {
|
||||
// Create a simple authentication with ROLE_GENERAL
|
||||
Authentication auth = new TestingAuthenticationToken("user", "password", "ROLE_GENERAL");
|
||||
Authentication auth = new TestingAuthenticationToken("user", "password",
|
||||
new GrantedAuthority[] { new GrantedAuthorityImpl("ROLE_GENERAL") });
|
||||
auth.setAuthenticated(true);
|
||||
SecurityContextHolder.getContext().setAuthentication(auth);
|
||||
|
||||
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, 100);
|
||||
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, new Long(100));
|
||||
// Authorization strategy will require a different role for each access
|
||||
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
|
||||
new SimpleGrantedAuthority("ROLE_ONE"), new SimpleGrantedAuthority("ROLE_TWO"),
|
||||
new SimpleGrantedAuthority("ROLE_GENERAL"));
|
||||
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(new GrantedAuthority[] {
|
||||
new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl("ROLE_TWO"),
|
||||
new GrantedAuthorityImpl("ROLE_GENERAL") });
|
||||
|
||||
// Let's give the principal an ADMINISTRATION permission, with granting
|
||||
// access
|
||||
MutableAcl parentAcl = new AclImpl(identity, 1, aclAuthorizationStrategy, new ConsoleAuditLogger());
|
||||
MutableAcl parentAcl = new AclImpl(identity, new Long(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
|
||||
parentAcl.insertAce(0, BasePermission.ADMINISTRATION, new PrincipalSid(auth), true);
|
||||
MutableAcl childAcl = new AclImpl(identity, 2, aclAuthorizationStrategy, new ConsoleAuditLogger());
|
||||
MutableAcl childAcl = new AclImpl(identity, new Long(2), aclAuthorizationStrategy, new ConsoleAuditLogger());
|
||||
|
||||
// Check against the 'child' acl, which doesn't offer any authorization
|
||||
// rights on CHANGE_OWNERSHIP
|
||||
try {
|
||||
aclAuthorizationStrategy.securityCheck(childAcl, AclAuthorizationStrategy.CHANGE_OWNERSHIP);
|
||||
fail("It should have thrown NotFoundException");
|
||||
Assert.fail("It should have thrown NotFoundException");
|
||||
}
|
||||
catch (NotFoundException expected) {
|
||||
assertTrue(true);
|
||||
Assert.assertTrue(true);
|
||||
}
|
||||
|
||||
// Link the child with its parent and test again against the
|
||||
@@ -200,60 +198,63 @@ public class AclImplementationSecurityCheckTests {
|
||||
childAcl.setEntriesInheriting(true);
|
||||
try {
|
||||
aclAuthorizationStrategy.securityCheck(childAcl, AclAuthorizationStrategy.CHANGE_OWNERSHIP);
|
||||
assertTrue(true);
|
||||
Assert.assertTrue(true);
|
||||
}
|
||||
catch (NotFoundException expected) {
|
||||
fail("It shouldn't have thrown NotFoundException");
|
||||
Assert.fail("It shouldn't have thrown NotFoundException");
|
||||
}
|
||||
|
||||
// Create a root parent and link it to the middle parent
|
||||
MutableAcl rootParentAcl = new AclImpl(identity, 1, aclAuthorizationStrategy,
|
||||
MutableAcl rootParentAcl = new AclImpl(identity, new Long(1), aclAuthorizationStrategy,
|
||||
new ConsoleAuditLogger());
|
||||
parentAcl = new AclImpl(identity, 1, aclAuthorizationStrategy, new ConsoleAuditLogger());
|
||||
parentAcl = new AclImpl(identity, new Long(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
|
||||
rootParentAcl.insertAce(0, BasePermission.ADMINISTRATION, new PrincipalSid(auth), true);
|
||||
parentAcl.setEntriesInheriting(true);
|
||||
parentAcl.setParent(rootParentAcl);
|
||||
childAcl.setParent(parentAcl);
|
||||
try {
|
||||
aclAuthorizationStrategy.securityCheck(childAcl, AclAuthorizationStrategy.CHANGE_OWNERSHIP);
|
||||
assertTrue(true);
|
||||
Assert.assertTrue(true);
|
||||
}
|
||||
catch (NotFoundException expected) {
|
||||
fail("It shouldn't have thrown NotFoundException");
|
||||
Assert.fail("It shouldn't have thrown NotFoundException");
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Test
|
||||
public void testSecurityCheckPrincipalOwner() throws Exception {
|
||||
Authentication auth = new TestingAuthenticationToken("user", "password", "ROLE_ONE");
|
||||
Authentication auth = new TestingAuthenticationToken("user", "password", new GrantedAuthority[] {
|
||||
new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl("ROLE_ONE"),
|
||||
new GrantedAuthorityImpl("ROLE_ONE") });
|
||||
auth.setAuthenticated(true);
|
||||
SecurityContextHolder.getContext().setAuthentication(auth);
|
||||
|
||||
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, 100);
|
||||
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
|
||||
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority("ROLE_AUDITING"),
|
||||
new SimpleGrantedAuthority("ROLE_GENERAL"));
|
||||
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, new Long(100));
|
||||
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(new GrantedAuthority[] {
|
||||
new GrantedAuthorityImpl("ROLE_OWNERSHIP"), new GrantedAuthorityImpl("ROLE_AUDITING"),
|
||||
new GrantedAuthorityImpl("ROLE_GENERAL") });
|
||||
|
||||
Acl acl = new AclImpl(identity, 1, aclAuthorizationStrategy, new ConsoleAuditLogger(), null, null,
|
||||
Acl acl = new AclImpl(identity, new Long(1), aclAuthorizationStrategy, new ConsoleAuditLogger(), null, null,
|
||||
false, new PrincipalSid(auth));
|
||||
try {
|
||||
aclAuthorizationStrategy.securityCheck(acl, AclAuthorizationStrategy.CHANGE_GENERAL);
|
||||
Assert.assertTrue(true);
|
||||
}
|
||||
catch (AccessDeniedException notExpected) {
|
||||
fail("It shouldn't have thrown AccessDeniedException");
|
||||
Assert.fail("It shouldn't have thrown AccessDeniedException");
|
||||
}
|
||||
try {
|
||||
aclAuthorizationStrategy.securityCheck(acl, AclAuthorizationStrategy.CHANGE_AUDITING);
|
||||
fail("It shouldn't have thrown AccessDeniedException");
|
||||
Assert.fail("It shouldn't have thrown AccessDeniedException");
|
||||
}
|
||||
catch (NotFoundException expected) {
|
||||
Assert.assertTrue(true);
|
||||
}
|
||||
try {
|
||||
aclAuthorizationStrategy.securityCheck(acl, AclAuthorizationStrategy.CHANGE_OWNERSHIP);
|
||||
Assert.assertTrue(true);
|
||||
}
|
||||
catch (AccessDeniedException notExpected) {
|
||||
fail("It shouldn't have thrown AccessDeniedException");
|
||||
Assert.fail("It shouldn't have thrown AccessDeniedException");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package org.springframework.security.acls.domain;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintStream;
|
||||
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.Mockery;
|
||||
import org.jmock.integration.junit4.JUnit4Mockery;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -19,17 +21,29 @@ import org.springframework.security.acls.model.AuditableAccessControlEntry;
|
||||
*/
|
||||
public class AuditLoggerTests {
|
||||
//~ Instance fields ================================================================================================
|
||||
private Mockery jmock = new JUnit4Mockery();
|
||||
private PrintStream console;
|
||||
private ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||
private ConsoleAuditLogger logger;
|
||||
private AuditableAccessControlEntry ace;
|
||||
private Expectations aceRequiresAudit;
|
||||
private Expectations aceDoesntRequireAudit;
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
logger = new ConsoleAuditLogger();
|
||||
ace = mock(AuditableAccessControlEntry.class);
|
||||
ace = jmock.mock(AuditableAccessControlEntry.class);
|
||||
aceRequiresAudit = new Expectations() {{
|
||||
allowing(ace).isAuditSuccess(); will(returnValue(true));
|
||||
allowing(ace).isAuditFailure(); will(returnValue(true));
|
||||
}};
|
||||
aceDoesntRequireAudit = new Expectations() {{
|
||||
allowing(ace).isAuditSuccess(); will(returnValue(false));
|
||||
allowing(ace).isAuditFailure(); will(returnValue(false));
|
||||
}};
|
||||
|
||||
console = System.out;
|
||||
System.setOut(new PrintStream(bytes));
|
||||
}
|
||||
@@ -42,36 +56,35 @@ public class AuditLoggerTests {
|
||||
|
||||
@Test
|
||||
public void nonAuditableAceIsIgnored() {
|
||||
AccessControlEntry ace = mock(AccessControlEntry.class);
|
||||
AccessControlEntry ace = jmock.mock(AccessControlEntry.class);
|
||||
logger.logIfNeeded(true, ace);
|
||||
assertEquals(0, bytes.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void successIsNotLoggedIfAceDoesntRequireSuccessAudit() throws Exception {
|
||||
when(ace.isAuditSuccess()).thenReturn(false);
|
||||
jmock.checking(aceDoesntRequireAudit);
|
||||
logger.logIfNeeded(true, ace);
|
||||
assertEquals(0, bytes.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void successIsLoggedIfAceRequiresSuccessAudit() throws Exception {
|
||||
when(ace.isAuditSuccess()).thenReturn(true);
|
||||
|
||||
jmock.checking(aceRequiresAudit);
|
||||
logger.logIfNeeded(true, ace);
|
||||
assertTrue(bytes.toString().startsWith("GRANTED due to ACE"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failureIsntLoggedIfAceDoesntRequireFailureAudit() throws Exception {
|
||||
when(ace.isAuditFailure()).thenReturn(false);
|
||||
jmock.checking(aceDoesntRequireAudit);
|
||||
logger.logIfNeeded(false, ace);
|
||||
assertEquals(0, bytes.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failureIsLoggedIfAceRequiresFailureAudit() throws Exception {
|
||||
when(ace.isAuditFailure()).thenReturn(true);
|
||||
jmock.checking(aceRequiresAudit);
|
||||
logger.logIfNeeded(false, ace);
|
||||
assertTrue(bytes.toString().startsWith("DENIED due to ACE"));
|
||||
}
|
||||
|
||||
+1
-1
@@ -140,7 +140,7 @@ public class ObjectIdentityImplTests {
|
||||
@Test
|
||||
public void longAndIntegerIdsWithSameValueAreEqualAndHaveSameHashcode() {
|
||||
ObjectIdentity obj = new ObjectIdentityImpl(Object.class, new Long(5));
|
||||
ObjectIdentity obj2 = new ObjectIdentityImpl(Object.class, Integer.valueOf(5));
|
||||
ObjectIdentity obj2 = new ObjectIdentityImpl(Object.class, new Integer(5));
|
||||
|
||||
assertEquals(obj, obj2);
|
||||
assertEquals(obj.hashCode(), obj2.hashCode());
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ public class ObjectIdentityRetrievalStrategyImplTests extends TestCase {
|
||||
|
||||
public void testObjectIdentityCreation() throws Exception {
|
||||
MockIdDomainObject domain = new MockIdDomainObject();
|
||||
domain.setId(Integer.valueOf(1));
|
||||
domain.setId(new Integer(1));
|
||||
|
||||
ObjectIdentityRetrievalStrategy retStrategy = new ObjectIdentityRetrievalStrategyImpl();
|
||||
ObjectIdentity identity = retStrategy.getObjectIdentity(domain);
|
||||
|
||||
+41
-33
@@ -1,10 +1,19 @@
|
||||
package org.springframework.security.acls.jdbc;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import junit.framework.Assert;
|
||||
import net.sf.ehcache.Cache;
|
||||
import net.sf.ehcache.CacheManager;
|
||||
import net.sf.ehcache.Ehcache;
|
||||
import org.junit.*;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
@@ -14,7 +23,6 @@ import org.springframework.security.acls.domain.AclAuthorizationStrategyImpl;
|
||||
import org.springframework.security.acls.domain.BasePermission;
|
||||
import org.springframework.security.acls.domain.ConsoleAuditLogger;
|
||||
import org.springframework.security.acls.domain.DefaultPermissionFactory;
|
||||
import org.springframework.security.acls.domain.DefaultPermissionGrantingStrategy;
|
||||
import org.springframework.security.acls.domain.EhCacheBasedAclCache;
|
||||
import org.springframework.security.acls.domain.ObjectIdentityImpl;
|
||||
import org.springframework.security.acls.domain.PrincipalSid;
|
||||
@@ -25,11 +33,10 @@ import org.springframework.security.acls.model.NotFoundException;
|
||||
import org.springframework.security.acls.model.ObjectIdentity;
|
||||
import org.springframework.security.acls.model.Permission;
|
||||
import org.springframework.security.acls.model.Sid;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.GrantedAuthorityImpl;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Tests {@link BasicLookupStrategy}
|
||||
*
|
||||
@@ -93,9 +100,10 @@ public class BasicLookupStrategyTests {
|
||||
@Before
|
||||
public void initializeBeans() {
|
||||
EhCacheBasedAclCache cache = new EhCacheBasedAclCache(getCache());
|
||||
AclAuthorizationStrategy authorizationStrategy = new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority("ROLE_ADMINISTRATOR"));
|
||||
strategy = new BasicLookupStrategy(dataSource, cache, authorizationStrategy,
|
||||
new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger()));
|
||||
AclAuthorizationStrategy authorizationStrategy = new AclAuthorizationStrategyImpl(new GrantedAuthority[] {
|
||||
new GrantedAuthorityImpl("ROLE_ADMINISTRATOR"), new GrantedAuthorityImpl("ROLE_ADMINISTRATOR"),
|
||||
new GrantedAuthorityImpl("ROLE_ADMINISTRATOR") });
|
||||
strategy = new BasicLookupStrategy(dataSource, cache, authorizationStrategy, new ConsoleAuditLogger());
|
||||
strategy.setPermissionFactory(new DefaultPermissionFactory());
|
||||
}
|
||||
|
||||
@@ -120,7 +128,7 @@ public class BasicLookupStrategyTests {
|
||||
ObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(100));
|
||||
ObjectIdentity middleParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(101));
|
||||
// Deliberately use an integer for the child, to reproduce bug report in SEC-819
|
||||
ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, Integer.valueOf(102));
|
||||
ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, new Integer(102));
|
||||
|
||||
Map<ObjectIdentity, Acl> map = this.strategy.readAclsById(Arrays.asList(topParentOid, middleParentOid, childOid), null);
|
||||
checkEntries(topParentOid, middleParentOid, childOid, map);
|
||||
@@ -128,7 +136,7 @@ public class BasicLookupStrategyTests {
|
||||
|
||||
@Test
|
||||
public void testAclsRetrievalFromCacheOnly() throws Exception {
|
||||
ObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS, Integer.valueOf(100));
|
||||
ObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Integer(100));
|
||||
ObjectIdentity middleParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(101));
|
||||
ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(102));
|
||||
|
||||
@@ -145,11 +153,11 @@ public class BasicLookupStrategyTests {
|
||||
@Test
|
||||
public void testAclsRetrievalWithCustomBatchSize() throws Exception {
|
||||
ObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(100));
|
||||
ObjectIdentity middleParentOid = new ObjectIdentityImpl(TARGET_CLASS, Integer.valueOf(101));
|
||||
ObjectIdentity middleParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Integer(101));
|
||||
ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(102));
|
||||
|
||||
// Set a batch size to allow multiple database queries in order to retrieve all acls
|
||||
this.strategy.setBatchSize(1);
|
||||
((BasicLookupStrategy) this.strategy).setBatchSize(1);
|
||||
Map<ObjectIdentity, Acl> map = this.strategy.readAclsById(Arrays.asList(topParentOid, middleParentOid, childOid), null);
|
||||
checkEntries(topParentOid, middleParentOid, childOid, map);
|
||||
}
|
||||
@@ -184,36 +192,36 @@ public class BasicLookupStrategyTests {
|
||||
|
||||
// Check each entry
|
||||
Assert.assertTrue(topParent.isEntriesInheriting());
|
||||
Assert.assertEquals(topParent.getId(), Long.valueOf(1));
|
||||
Assert.assertEquals(topParent.getId(), new Long(1));
|
||||
Assert.assertEquals(topParent.getOwner(), new PrincipalSid("ben"));
|
||||
Assert.assertEquals(topParent.getEntries().get(0).getId(), Long.valueOf(1));
|
||||
Assert.assertEquals(topParent.getEntries().get(0).getId(), new Long(1));
|
||||
Assert.assertEquals(topParent.getEntries().get(0).getPermission(), BasePermission.READ);
|
||||
Assert.assertEquals(topParent.getEntries().get(0).getSid(), new PrincipalSid("ben"));
|
||||
Assert.assertFalse(((AuditableAccessControlEntry) topParent.getEntries().get(0)).isAuditFailure());
|
||||
Assert.assertFalse(((AuditableAccessControlEntry) topParent.getEntries().get(0)).isAuditSuccess());
|
||||
Assert.assertTrue((topParent.getEntries().get(0)).isGranting());
|
||||
Assert.assertTrue(((AuditableAccessControlEntry) topParent.getEntries().get(0)).isGranting());
|
||||
|
||||
Assert.assertEquals(topParent.getEntries().get(1).getId(), Long.valueOf(2));
|
||||
Assert.assertEquals(topParent.getEntries().get(1).getId(), new Long(2));
|
||||
Assert.assertEquals(topParent.getEntries().get(1).getPermission(), BasePermission.WRITE);
|
||||
Assert.assertEquals(topParent.getEntries().get(1).getSid(), new PrincipalSid("ben"));
|
||||
Assert.assertFalse(((AuditableAccessControlEntry) topParent.getEntries().get(1)).isAuditFailure());
|
||||
Assert.assertFalse(((AuditableAccessControlEntry) topParent.getEntries().get(1)).isAuditSuccess());
|
||||
Assert.assertFalse(topParent.getEntries().get(1).isGranting());
|
||||
Assert.assertFalse(((AuditableAccessControlEntry) topParent.getEntries().get(1)).isGranting());
|
||||
|
||||
Assert.assertTrue(middleParent.isEntriesInheriting());
|
||||
Assert.assertEquals(middleParent.getId(), Long.valueOf(2));
|
||||
Assert.assertEquals(middleParent.getId(), new Long(2));
|
||||
Assert.assertEquals(middleParent.getOwner(), new PrincipalSid("ben"));
|
||||
Assert.assertEquals(middleParent.getEntries().get(0).getId(), Long.valueOf(3));
|
||||
Assert.assertEquals(middleParent.getEntries().get(0).getId(), new Long(3));
|
||||
Assert.assertEquals(middleParent.getEntries().get(0).getPermission(), BasePermission.DELETE);
|
||||
Assert.assertEquals(middleParent.getEntries().get(0).getSid(), new PrincipalSid("ben"));
|
||||
Assert.assertFalse(((AuditableAccessControlEntry) middleParent.getEntries().get(0)).isAuditFailure());
|
||||
Assert.assertFalse(((AuditableAccessControlEntry) middleParent.getEntries().get(0)).isAuditSuccess());
|
||||
Assert.assertTrue(middleParent.getEntries().get(0).isGranting());
|
||||
Assert.assertTrue(((AuditableAccessControlEntry) middleParent.getEntries().get(0)).isGranting());
|
||||
|
||||
Assert.assertTrue(child.isEntriesInheriting());
|
||||
Assert.assertEquals(child.getId(), Long.valueOf(3));
|
||||
Assert.assertEquals(child.getId(), new Long(3));
|
||||
Assert.assertEquals(child.getOwner(), new PrincipalSid("ben"));
|
||||
Assert.assertEquals(child.getEntries().get(0).getId(), Long.valueOf(4));
|
||||
Assert.assertEquals(child.getEntries().get(0).getId(), new Long(4));
|
||||
Assert.assertEquals(child.getEntries().get(0).getPermission(), BasePermission.DELETE);
|
||||
Assert.assertEquals(child.getEntries().get(0).getSid(), new PrincipalSid("ben"));
|
||||
Assert.assertFalse(((AuditableAccessControlEntry) child.getEntries().get(0)).isAuditFailure());
|
||||
@@ -226,21 +234,21 @@ public class BasicLookupStrategyTests {
|
||||
String query = "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (4,2,103,1,1,1);";
|
||||
jdbcTemplate.execute(query);
|
||||
|
||||
ObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(100));
|
||||
ObjectIdentity middleParentOid = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(101));
|
||||
ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(102));
|
||||
ObjectIdentity middleParent2Oid = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(103));
|
||||
ObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(100));
|
||||
ObjectIdentity middleParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Integer(101));
|
||||
ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(102));
|
||||
ObjectIdentity middleParent2Oid = new ObjectIdentityImpl(TARGET_CLASS, new Long(103));
|
||||
|
||||
// Retrieve the child
|
||||
Map<ObjectIdentity, Acl> map = this.strategy.readAclsById(Arrays.asList(childOid), null);
|
||||
|
||||
// Check that the child and all its parents were retrieved
|
||||
Assert.assertNotNull(map.get(childOid));
|
||||
Assert.assertEquals(childOid, map.get(childOid).getObjectIdentity());
|
||||
Assert.assertEquals(childOid, ((Acl) map.get(childOid)).getObjectIdentity());
|
||||
Assert.assertNotNull(map.get(middleParentOid));
|
||||
Assert.assertEquals(middleParentOid, map.get(middleParentOid).getObjectIdentity());
|
||||
Assert.assertEquals(middleParentOid, ((Acl) map.get(middleParentOid)).getObjectIdentity());
|
||||
Assert.assertNotNull(map.get(topParentOid));
|
||||
Assert.assertEquals(topParentOid, map.get(topParentOid).getObjectIdentity());
|
||||
Assert.assertEquals(topParentOid, ((Acl) map.get(topParentOid)).getObjectIdentity());
|
||||
|
||||
// The second parent shouldn't have been retrieved
|
||||
Assert.assertNull(map.get(middleParent2Oid));
|
||||
@@ -260,8 +268,8 @@ public class BasicLookupStrategyTests {
|
||||
|
||||
ObjectIdentity grandParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(104));
|
||||
ObjectIdentity parent1Oid = new ObjectIdentityImpl(TARGET_CLASS, new Long(105));
|
||||
ObjectIdentity parent2Oid = new ObjectIdentityImpl(TARGET_CLASS, Integer.valueOf(106));
|
||||
ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, Integer.valueOf(107));
|
||||
ObjectIdentity parent2Oid = new ObjectIdentityImpl(TARGET_CLASS, new Integer(106));
|
||||
ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, new Integer(107));
|
||||
|
||||
// First lookup only child, thus populating the cache with grandParent, parent1 and child
|
||||
List<Permission> checkPermission = Arrays.asList(BasePermission.READ);
|
||||
@@ -271,7 +279,7 @@ public class BasicLookupStrategyTests {
|
||||
strategy.setBatchSize(6);
|
||||
Map<ObjectIdentity, Acl> foundAcls = strategy.readAclsById(childOids, sids);
|
||||
|
||||
Acl foundChildAcl = foundAcls.get(childOid);
|
||||
Acl foundChildAcl = (Acl) foundAcls.get(childOid);
|
||||
Assert.assertNotNull(foundChildAcl);
|
||||
Assert.assertTrue(foundChildAcl.isGranted(checkPermission, sids, false));
|
||||
|
||||
@@ -285,7 +293,7 @@ public class BasicLookupStrategyTests {
|
||||
Assert.fail("It shouldn't have thrown NotFoundException");
|
||||
}
|
||||
|
||||
Acl foundParent2Acl = foundAcls.get(parent2Oid);
|
||||
Acl foundParent2Acl = (Acl) foundAcls.get(parent2Oid);
|
||||
Assert.assertNotNull(foundParent2Acl);
|
||||
Assert.assertTrue(foundParent2Acl.isGranted(checkPermission, sids, false));
|
||||
}
|
||||
|
||||
+45
-39
@@ -2,10 +2,22 @@ package org.springframework.security.acls.jdbc;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
import net.sf.ehcache.Cache;
|
||||
import net.sf.ehcache.CacheManager;
|
||||
import net.sf.ehcache.Ehcache;
|
||||
import org.junit.*;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.springframework.security.acls.domain.AclAuthorizationStrategy;
|
||||
import org.springframework.security.acls.domain.AclAuthorizationStrategyImpl;
|
||||
import org.springframework.security.acls.domain.AclImpl;
|
||||
@@ -16,18 +28,11 @@ import org.springframework.security.acls.model.MutableAcl;
|
||||
import org.springframework.security.acls.model.ObjectIdentity;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.GrantedAuthorityImpl;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.util.FieldUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Tests {@link EhCacheBasedAclCache}
|
||||
*
|
||||
@@ -122,11 +127,11 @@ public class EhCacheBasedAclCacheTests {
|
||||
// SEC-527
|
||||
@Test
|
||||
public void testDiskSerializationOfMutableAclObjectInstance() throws Exception {
|
||||
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(100));
|
||||
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
|
||||
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority("ROLE_AUDITING"),
|
||||
new SimpleGrantedAuthority("ROLE_GENERAL"));
|
||||
MutableAcl acl = new AclImpl(identity, Long.valueOf(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
|
||||
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, new Long(100));
|
||||
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(new GrantedAuthority[] {
|
||||
new GrantedAuthorityImpl("ROLE_OWNERSHIP"), new GrantedAuthorityImpl("ROLE_AUDITING"),
|
||||
new GrantedAuthorityImpl("ROLE_GENERAL") });
|
||||
MutableAcl acl = new AclImpl(identity, new Long(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
|
||||
|
||||
// Serialization test
|
||||
File file = File.createTempFile("SEC_TEST", ".object");
|
||||
@@ -145,7 +150,7 @@ public class EhCacheBasedAclCacheTests {
|
||||
Object retrieved1 = FieldUtils.getProtectedFieldValue("aclAuthorizationStrategy", retrieved);
|
||||
assertEquals(null, retrieved1);
|
||||
|
||||
Object retrieved2 = FieldUtils.getProtectedFieldValue("permissionGrantingStrategy", retrieved);
|
||||
Object retrieved2 = FieldUtils.getProtectedFieldValue("auditLogger", retrieved);
|
||||
assertEquals(null, retrieved2);
|
||||
}
|
||||
|
||||
@@ -154,11 +159,11 @@ public class EhCacheBasedAclCacheTests {
|
||||
Ehcache cache = getCache();
|
||||
EhCacheBasedAclCache myCache = new EhCacheBasedAclCache(cache);
|
||||
|
||||
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(100));
|
||||
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
|
||||
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority("ROLE_AUDITING"),
|
||||
new SimpleGrantedAuthority("ROLE_GENERAL"));
|
||||
MutableAcl acl = new AclImpl(identity, Long.valueOf(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
|
||||
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, new Long(100));
|
||||
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(new GrantedAuthority[] {
|
||||
new GrantedAuthorityImpl("ROLE_OWNERSHIP"), new GrantedAuthorityImpl("ROLE_AUDITING"),
|
||||
new GrantedAuthorityImpl("ROLE_GENERAL") });
|
||||
MutableAcl acl = new AclImpl(identity, new Long(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
|
||||
|
||||
assertEquals(0, cache.getDiskStoreSize());
|
||||
myCache.putInCache(acl);
|
||||
@@ -168,29 +173,29 @@ public class EhCacheBasedAclCacheTests {
|
||||
assertFalse(cache.isElementInMemory(acl.getObjectIdentity()));
|
||||
|
||||
// Check we can get from cache the same objects we put in
|
||||
assertEquals(myCache.getFromCache(Long.valueOf(1)), acl);
|
||||
assertEquals(myCache.getFromCache(new Long(1)), acl);
|
||||
assertEquals(myCache.getFromCache(identity), acl);
|
||||
|
||||
// Put another object in cache
|
||||
ObjectIdentity identity2 = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(101));
|
||||
MutableAcl acl2 = new AclImpl(identity2, Long.valueOf(2), aclAuthorizationStrategy, new ConsoleAuditLogger());
|
||||
ObjectIdentity identity2 = new ObjectIdentityImpl(TARGET_CLASS, new Long(101));
|
||||
MutableAcl acl2 = new AclImpl(identity2, new Long(2), aclAuthorizationStrategy, new ConsoleAuditLogger());
|
||||
|
||||
myCache.putInCache(acl2);
|
||||
assertEquals(cache.getSize(), 4);
|
||||
assertEquals(4, cache.getDiskStoreSize());
|
||||
|
||||
// Try to evict an entry that doesn't exist
|
||||
myCache.evictFromCache(Long.valueOf(3));
|
||||
myCache.evictFromCache(new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(102)));
|
||||
myCache.evictFromCache(new Long(3));
|
||||
myCache.evictFromCache(new ObjectIdentityImpl(TARGET_CLASS, new Long(102)));
|
||||
assertEquals(cache.getSize(), 4);
|
||||
assertEquals(4, cache.getDiskStoreSize());
|
||||
|
||||
myCache.evictFromCache(Long.valueOf(1));
|
||||
myCache.evictFromCache(new Long(1));
|
||||
assertEquals(cache.getSize(), 2);
|
||||
assertEquals(2, cache.getDiskStoreSize());
|
||||
|
||||
// Check the second object inserted
|
||||
assertEquals(myCache.getFromCache(Long.valueOf(2)), acl2);
|
||||
assertEquals(myCache.getFromCache(new Long(2)), acl2);
|
||||
assertEquals(myCache.getFromCache(identity2), acl2);
|
||||
|
||||
myCache.evictFromCache(identity2);
|
||||
@@ -203,17 +208,18 @@ public class EhCacheBasedAclCacheTests {
|
||||
Ehcache cache = getCache();
|
||||
EhCacheBasedAclCache myCache = new EhCacheBasedAclCache(cache);
|
||||
|
||||
Authentication auth = new TestingAuthenticationToken("user", "password", "ROLE_GENERAL");
|
||||
Authentication auth = new TestingAuthenticationToken("user", "password", new GrantedAuthority[] {
|
||||
new GrantedAuthorityImpl("ROLE_GENERAL") });
|
||||
auth.setAuthenticated(true);
|
||||
SecurityContextHolder.getContext().setAuthentication(auth);
|
||||
|
||||
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(1));
|
||||
ObjectIdentity identityParent = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(2));
|
||||
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
|
||||
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority("ROLE_AUDITING"),
|
||||
new SimpleGrantedAuthority("ROLE_GENERAL"));
|
||||
MutableAcl acl = new AclImpl(identity, Long.valueOf(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
|
||||
MutableAcl parentAcl = new AclImpl(identityParent, Long.valueOf(2), aclAuthorizationStrategy, new ConsoleAuditLogger());
|
||||
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, new Long(1));
|
||||
ObjectIdentity identityParent = new ObjectIdentityImpl(TARGET_CLASS, new Long(2));
|
||||
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(new GrantedAuthority[] {
|
||||
new GrantedAuthorityImpl("ROLE_OWNERSHIP"), new GrantedAuthorityImpl("ROLE_AUDITING"),
|
||||
new GrantedAuthorityImpl("ROLE_GENERAL") });
|
||||
MutableAcl acl = new AclImpl(identity, new Long(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
|
||||
MutableAcl parentAcl = new AclImpl(identityParent, new Long(2), aclAuthorizationStrategy, new ConsoleAuditLogger());
|
||||
|
||||
acl.setParent(parentAcl);
|
||||
|
||||
@@ -234,17 +240,17 @@ public class EhCacheBasedAclCacheTests {
|
||||
}
|
||||
|
||||
// Check we can get from cache the same objects we put in
|
||||
AclImpl aclFromCache = (AclImpl) myCache.getFromCache(Long.valueOf(1));
|
||||
AclImpl aclFromCache = (AclImpl) myCache.getFromCache(new Long(1));
|
||||
// For the checks on transient fields, we need to be sure that the object is being loaded from the cache,
|
||||
// not from the ehcache spool or elsewhere...
|
||||
assertFalse(acl == aclFromCache);
|
||||
assertEquals(acl, aclFromCache);
|
||||
// SEC-951 check transient fields are set on parent
|
||||
assertNotNull(FieldUtils.getFieldValue(aclFromCache.getParentAcl(), "aclAuthorizationStrategy"));
|
||||
assertNotNull(FieldUtils.getFieldValue(aclFromCache.getParentAcl(), "permissionGrantingStrategy"));
|
||||
assertNotNull(FieldUtils.getFieldValue(aclFromCache.getParentAcl(), "auditLogger"));
|
||||
assertEquals(acl, myCache.getFromCache(identity));
|
||||
assertNotNull(FieldUtils.getFieldValue(aclFromCache, "aclAuthorizationStrategy"));
|
||||
AclImpl parentAclFromCache = (AclImpl) myCache.getFromCache(Long.valueOf(2));
|
||||
AclImpl parentAclFromCache = (AclImpl) myCache.getFromCache(new Long(2));
|
||||
assertEquals(parentAcl, parentAclFromCache);
|
||||
assertNotNull(FieldUtils.getFieldValue(parentAclFromCache, "aclAuthorizationStrategy"));
|
||||
assertEquals(parentAcl, myCache.getFromCache(identityParent));
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
package org.springframework.security.acls.jdbc;
|
||||
|
||||
import static org.mockito.Matchers.anyListOf;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.security.acls.domain.ObjectIdentityImpl;
|
||||
import org.springframework.security.acls.domain.PrincipalSid;
|
||||
import org.springframework.security.acls.model.Acl;
|
||||
import org.springframework.security.acls.model.NotFoundException;
|
||||
import org.springframework.security.acls.model.ObjectIdentity;
|
||||
import org.springframework.security.acls.model.Sid;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class JdbcAclServiceTests {
|
||||
@Mock
|
||||
private DataSource dataSource;
|
||||
|
||||
@Mock
|
||||
private LookupStrategy lookupStrategy;
|
||||
|
||||
private JdbcAclService aclService;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
aclService = new JdbcAclService(dataSource, lookupStrategy);
|
||||
}
|
||||
|
||||
// SEC-1898
|
||||
@Test(expected = NotFoundException.class)
|
||||
public void readAclByIdMissingAcl() {
|
||||
Map<ObjectIdentity, Acl> result = new HashMap<ObjectIdentity, Acl>();
|
||||
when(lookupStrategy.readAclsById(anyListOf(ObjectIdentity.class), anyListOf(Sid.class))).thenReturn(result);
|
||||
ObjectIdentity objectIdentity = new ObjectIdentityImpl(Object.class, 1);
|
||||
List<Sid> sids = Arrays.<Sid> asList(new PrincipalSid("user"));
|
||||
|
||||
aclService.readAclById(objectIdentity, sids);
|
||||
}
|
||||
}
|
||||
-142
@@ -1,142 +0,0 @@
|
||||
package org.springframework.security.acls.jdbc;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
|
||||
import org.springframework.security.acls.domain.*;
|
||||
import org.springframework.security.acls.model.MutableAcl;
|
||||
import org.springframework.security.acls.model.ObjectIdentity;
|
||||
import org.springframework.security.acls.model.PermissionGrantingStrategy;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.util.FieldUtils;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Tests {@link org.springframework.security.acls.domain.SpringCacheBasedAclCache}
|
||||
*
|
||||
* @author Marten Deinum
|
||||
*/
|
||||
public class SpringCacheBasedAclCacheTests {
|
||||
private static final String TARGET_CLASS = "org.springframework.security.acls.TargetObject";
|
||||
|
||||
private static CacheManager cacheManager;
|
||||
|
||||
@BeforeClass
|
||||
public static void initCacheManaer() {
|
||||
cacheManager = new ConcurrentMapCacheManager();
|
||||
// Use disk caching immediately (to test for serialization issue reported in SEC-527)
|
||||
cacheManager.getCache("springcasebasedacltests");
|
||||
}
|
||||
|
||||
@After
|
||||
public void clearContext() {
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
private Cache getCache() {
|
||||
Cache cache = cacheManager.getCache("springcasebasedacltests");
|
||||
cache.clear();
|
||||
return cache;
|
||||
}
|
||||
|
||||
@Test(expected=IllegalArgumentException.class)
|
||||
public void constructorRejectsNullParameters() throws Exception {
|
||||
new SpringCacheBasedAclCache(null, null, null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Test
|
||||
public void cacheOperationsAclWithoutParent() throws Exception {
|
||||
Cache cache = getCache();
|
||||
Map realCache = (Map) cache.getNativeCache();
|
||||
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(100));
|
||||
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
|
||||
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority("ROLE_AUDITING"),
|
||||
new SimpleGrantedAuthority("ROLE_GENERAL"));
|
||||
AuditLogger auditLogger = new ConsoleAuditLogger();
|
||||
|
||||
PermissionGrantingStrategy permissionGrantingStrategy = new DefaultPermissionGrantingStrategy(auditLogger);
|
||||
SpringCacheBasedAclCache myCache = new SpringCacheBasedAclCache(cache, permissionGrantingStrategy, aclAuthorizationStrategy);
|
||||
MutableAcl acl = new AclImpl(identity, Long.valueOf(1), aclAuthorizationStrategy, auditLogger);
|
||||
|
||||
assertEquals(0, realCache.size());
|
||||
myCache.putInCache(acl);
|
||||
|
||||
// Check we can get from cache the same objects we put in
|
||||
assertEquals(myCache.getFromCache(Long.valueOf(1)), acl);
|
||||
assertEquals(myCache.getFromCache(identity), acl);
|
||||
|
||||
// Put another object in cache
|
||||
ObjectIdentity identity2 = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(101));
|
||||
MutableAcl acl2 = new AclImpl(identity2, Long.valueOf(2), aclAuthorizationStrategy, new ConsoleAuditLogger());
|
||||
|
||||
myCache.putInCache(acl2);
|
||||
|
||||
// Try to evict an entry that doesn't exist
|
||||
myCache.evictFromCache(Long.valueOf(3));
|
||||
myCache.evictFromCache(new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(102)));
|
||||
assertEquals(realCache.size(), 4);
|
||||
|
||||
myCache.evictFromCache(Long.valueOf(1));
|
||||
assertEquals(realCache.size(), 2);
|
||||
|
||||
// Check the second object inserted
|
||||
assertEquals(myCache.getFromCache(Long.valueOf(2)), acl2);
|
||||
assertEquals(myCache.getFromCache(identity2), acl2);
|
||||
|
||||
myCache.evictFromCache(identity2);
|
||||
assertEquals(realCache.size(), 0);
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Test
|
||||
public void cacheOperationsAclWithParent() throws Exception {
|
||||
Cache cache = getCache();
|
||||
Map realCache = (Map) cache.getNativeCache();
|
||||
|
||||
Authentication auth = new TestingAuthenticationToken("user", "password", "ROLE_GENERAL");
|
||||
auth.setAuthenticated(true);
|
||||
SecurityContextHolder.getContext().setAuthentication(auth);
|
||||
|
||||
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(1));
|
||||
ObjectIdentity identityParent = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(2));
|
||||
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
|
||||
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority("ROLE_AUDITING"),
|
||||
new SimpleGrantedAuthority("ROLE_GENERAL"));
|
||||
AuditLogger auditLogger = new ConsoleAuditLogger();
|
||||
|
||||
PermissionGrantingStrategy permissionGrantingStrategy = new DefaultPermissionGrantingStrategy(auditLogger);
|
||||
SpringCacheBasedAclCache myCache = new SpringCacheBasedAclCache(cache, permissionGrantingStrategy, aclAuthorizationStrategy);
|
||||
|
||||
MutableAcl acl = new AclImpl(identity, Long.valueOf(1), aclAuthorizationStrategy, auditLogger);
|
||||
MutableAcl parentAcl = new AclImpl(identityParent, Long.valueOf(2), aclAuthorizationStrategy, auditLogger);
|
||||
|
||||
acl.setParent(parentAcl);
|
||||
|
||||
assertEquals(0, realCache.size());
|
||||
myCache.putInCache(acl);
|
||||
assertEquals(realCache.size(), 4);
|
||||
|
||||
// Check we can get from cache the same objects we put in
|
||||
AclImpl aclFromCache = (AclImpl) myCache.getFromCache(Long.valueOf(1));
|
||||
assertEquals(acl, aclFromCache);
|
||||
// SEC-951 check transient fields are set on parent
|
||||
assertNotNull(FieldUtils.getFieldValue(aclFromCache.getParentAcl(), "aclAuthorizationStrategy"));
|
||||
assertNotNull(FieldUtils.getFieldValue(aclFromCache.getParentAcl(), "permissionGrantingStrategy"));
|
||||
assertEquals(acl, myCache.getFromCache(identity));
|
||||
assertNotNull(FieldUtils.getFieldValue(aclFromCache, "aclAuthorizationStrategy"));
|
||||
AclImpl parentAclFromCache = (AclImpl) myCache.getFromCache(Long.valueOf(2));
|
||||
assertEquals(parentAcl, parentAclFromCache);
|
||||
assertNotNull(FieldUtils.getFieldValue(parentAclFromCache, "aclAuthorizationStrategy"));
|
||||
assertEquals(parentAcl, myCache.getFromCache(identityParent));
|
||||
}
|
||||
}
|
||||
+2
-3
@@ -4,7 +4,6 @@ import static org.junit.Assert.*;
|
||||
import static org.mockito.Matchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
@@ -54,8 +53,8 @@ public class SidRetrievalStrategyTests {
|
||||
@Test
|
||||
public void roleHierarchyIsUsedWhenSet() throws Exception {
|
||||
RoleHierarchy rh = mock(RoleHierarchy.class);
|
||||
List rhAuthorities = AuthorityUtils.createAuthorityList("D");
|
||||
when(rh.getReachableGrantedAuthorities(anyCollection())).thenReturn(rhAuthorities);
|
||||
List<GrantedAuthority> rhAuthorities = AuthorityUtils.createAuthorityList("D");
|
||||
when(rh.getReachableGrantedAuthorities(anyList())).thenReturn(rhAuthorities);
|
||||
SidRetrievalStrategy strat = new SidRetrievalStrategyImpl(rh);
|
||||
|
||||
List<Sid> sids = strat.getSids(authentication);
|
||||
|
||||
@@ -2,13 +2,14 @@ package org.springframework.security.acls.sid;
|
||||
|
||||
import junit.framework.Assert;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.springframework.security.acls.domain.GrantedAuthoritySid;
|
||||
import org.springframework.security.acls.domain.PrincipalSid;
|
||||
import org.springframework.security.acls.model.Sid;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.authority.GrantedAuthorityImpl;
|
||||
|
||||
public class SidTests extends TestCase {
|
||||
|
||||
@@ -108,7 +109,7 @@ public class SidTests extends TestCase {
|
||||
}
|
||||
|
||||
try {
|
||||
GrantedAuthority ga = new SimpleGrantedAuthority(null);
|
||||
GrantedAuthority ga = new GrantedAuthorityImpl(null);
|
||||
new GrantedAuthoritySid(ga);
|
||||
Assert.fail("It should have thrown IllegalArgumentException");
|
||||
}
|
||||
@@ -117,7 +118,7 @@ public class SidTests extends TestCase {
|
||||
}
|
||||
|
||||
try {
|
||||
GrantedAuthority ga = new SimpleGrantedAuthority("ROLE_TEST");
|
||||
GrantedAuthority ga = new GrantedAuthorityImpl("ROLE_TEST");
|
||||
new GrantedAuthoritySid(ga);
|
||||
Assert.assertTrue(true);
|
||||
}
|
||||
@@ -141,15 +142,15 @@ public class SidTests extends TestCase {
|
||||
}
|
||||
|
||||
public void testGrantedAuthoritySidEquals() throws Exception {
|
||||
GrantedAuthority ga = new SimpleGrantedAuthority("ROLE_TEST");
|
||||
GrantedAuthority ga = new GrantedAuthorityImpl("ROLE_TEST");
|
||||
Sid gaSid = new GrantedAuthoritySid(ga);
|
||||
|
||||
Assert.assertFalse(gaSid.equals(null));
|
||||
Assert.assertFalse(gaSid.equals("DIFFERENT_TYPE_OBJECT"));
|
||||
Assert.assertTrue(gaSid.equals(gaSid));
|
||||
Assert.assertTrue(gaSid.equals(new GrantedAuthoritySid(ga)));
|
||||
Assert.assertTrue(gaSid.equals(new GrantedAuthoritySid(new SimpleGrantedAuthority("ROLE_TEST"))));
|
||||
Assert.assertFalse(gaSid.equals(new GrantedAuthoritySid(new SimpleGrantedAuthority("ROLE_NOT_EQUAL"))));
|
||||
Assert.assertTrue(gaSid.equals(new GrantedAuthoritySid(new GrantedAuthorityImpl("ROLE_TEST"))));
|
||||
Assert.assertFalse(gaSid.equals(new GrantedAuthoritySid(new GrantedAuthorityImpl("ROLE_NOT_EQUAL"))));
|
||||
Assert.assertTrue(gaSid.equals(new GrantedAuthoritySid("ROLE_TEST")));
|
||||
Assert.assertFalse(gaSid.equals(new GrantedAuthoritySid("ROLE_NOT_EQUAL")));
|
||||
}
|
||||
@@ -158,26 +159,26 @@ public class SidTests extends TestCase {
|
||||
Authentication authentication = new TestingAuthenticationToken("johndoe", "password");
|
||||
Sid principalSid = new PrincipalSid(authentication);
|
||||
|
||||
Assert.assertTrue(principalSid.hashCode() == "johndoe".hashCode());
|
||||
Assert.assertTrue(principalSid.hashCode() == new String("johndoe").hashCode());
|
||||
Assert.assertTrue(principalSid.hashCode() == new PrincipalSid("johndoe").hashCode());
|
||||
Assert.assertTrue(principalSid.hashCode() != new PrincipalSid("scott").hashCode());
|
||||
Assert.assertTrue(principalSid.hashCode() != new PrincipalSid(new TestingAuthenticationToken("scott", "password")).hashCode());
|
||||
}
|
||||
|
||||
public void testGrantedAuthoritySidHashCode() throws Exception {
|
||||
GrantedAuthority ga = new SimpleGrantedAuthority("ROLE_TEST");
|
||||
GrantedAuthority ga = new GrantedAuthorityImpl("ROLE_TEST");
|
||||
Sid gaSid = new GrantedAuthoritySid(ga);
|
||||
|
||||
Assert.assertTrue(gaSid.hashCode() == "ROLE_TEST".hashCode());
|
||||
Assert.assertTrue(gaSid.hashCode() == new String("ROLE_TEST").hashCode());
|
||||
Assert.assertTrue(gaSid.hashCode() == new GrantedAuthoritySid("ROLE_TEST").hashCode());
|
||||
Assert.assertTrue(gaSid.hashCode() != new GrantedAuthoritySid("ROLE_TEST_2").hashCode());
|
||||
Assert.assertTrue(gaSid.hashCode() != new GrantedAuthoritySid(new SimpleGrantedAuthority("ROLE_TEST_2")).hashCode());
|
||||
Assert.assertTrue(gaSid.hashCode() != new GrantedAuthoritySid(new GrantedAuthorityImpl("ROLE_TEST_2")).hashCode());
|
||||
}
|
||||
|
||||
public void testGetters() throws Exception {
|
||||
Authentication authentication = new TestingAuthenticationToken("johndoe", "password");
|
||||
PrincipalSid principalSid = new PrincipalSid(authentication);
|
||||
GrantedAuthority ga = new SimpleGrantedAuthority("ROLE_TEST");
|
||||
GrantedAuthority ga = new GrantedAuthorityImpl("ROLE_TEST");
|
||||
GrantedAuthoritySid gaSid = new GrantedAuthoritySid(ga);
|
||||
|
||||
Assert.assertTrue("johndoe".equals(principalSid.getPrincipal()));
|
||||
|
||||
@@ -36,13 +36,13 @@
|
||||
<bean id="aclAuthorizationStrategy" class="org.springframework.security.acls.domain.AclAuthorizationStrategyImpl">
|
||||
<constructor-arg>
|
||||
<list>
|
||||
<bean class="org.springframework.security.core.authority.SimpleGrantedAuthority">
|
||||
<bean class="org.springframework.security.core.authority.GrantedAuthorityImpl">
|
||||
<constructor-arg value="ROLE_ADMINISTRATOR"/>
|
||||
</bean>
|
||||
<bean class="org.springframework.security.core.authority.SimpleGrantedAuthority">
|
||||
<bean class="org.springframework.security.core.authority.GrantedAuthorityImpl">
|
||||
<constructor-arg value="ROLE_ADMINISTRATOR"/>
|
||||
</bean>
|
||||
<bean class="org.springframework.security.core.authority.SimpleGrantedAuthority">
|
||||
<bean class="org.springframework.security.core.authority.GrantedAuthorityImpl">
|
||||
<constructor-arg value="ROLE_ADMINISTRATOR"/>
|
||||
</bean>
|
||||
</list>
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<logger name="org.springframework.security" level="${sec.log.level}:-WARN"/>
|
||||
|
||||
<root level="${root.level}:-WARN">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
|
||||
</configuration>
|
||||
+2
-3
@@ -5,13 +5,12 @@ Bundle-Name: Spring Security Acls
|
||||
Bundle-Vendor: SpringSource
|
||||
Bundle-Version: ${version}
|
||||
Bundle-ManifestVersion: 2
|
||||
Ignored-Existing-Headers:
|
||||
Ignored-Existing-Headers:
|
||||
Import-Package,
|
||||
Export-Package
|
||||
Import-Template:
|
||||
Import-Template:
|
||||
org.aopalliance.*;version="${aopAllianceRange}",
|
||||
org.apache.commons.logging.*;version="${cloggingRange}",
|
||||
org.springframework.cache.*;version="${springRange}";resolution:=optional,
|
||||
org.springframework.security.core.*;version="${secRange}",
|
||||
org.springframework.security.access.*;version="${secRange}",
|
||||
org.springframework.security.util.*;version="${secRange}",
|
||||
|
||||
@@ -3,6 +3,4 @@ dependencies {
|
||||
compile project(':spring-security-core'),
|
||||
"org.springframework:spring-beans:$springVersion",
|
||||
"org.springframework:spring-context:$springVersion"
|
||||
|
||||
testCompile 'aopalliance:aopalliance:1.0'
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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.springframework.security</groupId>
|
||||
<artifactId>spring-security-parent</artifactId>
|
||||
<version>@pomVersion@</version>
|
||||
</parent>
|
||||
<packaging>jar</packaging>
|
||||
<artifactId>spring-security-aspects</artifactId>
|
||||
<name>Spring Security - Aspects</name>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>aspectjweaver</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-core</artifactId>
|
||||
<version>@pomVersion@</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>aspectj-maven-plugin</artifactId>
|
||||
<version>1.2</version>
|
||||
<dependencies>
|
||||
<!--
|
||||
NB: You must use Maven 2.0.9 or above or these
|
||||
are ignored (see MNG-2972)
|
||||
-->
|
||||
<dependency>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>aspectjrt</artifactId>
|
||||
<version>1.6.8</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>aspectjtools</artifactId>
|
||||
<version>1.6.8</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
<goal>test-compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<source>1.5</source>
|
||||
<target>1.5</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
+9
-89
@@ -1,10 +1,6 @@
|
||||
package org.springframework.security.access.intercept.aspectj.aspect;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
@@ -18,17 +14,12 @@ import org.springframework.security.access.annotation.Secured;
|
||||
import org.springframework.security.access.annotation.SecuredAnnotationSecurityMetadataSource;
|
||||
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
|
||||
import org.springframework.security.access.expression.method.ExpressionBasedAnnotationAttributeFactory;
|
||||
import org.springframework.security.access.expression.method.ExpressionBasedPostInvocationAdvice;
|
||||
import org.springframework.security.access.expression.method.ExpressionBasedPreInvocationAdvice;
|
||||
import org.springframework.security.access.intercept.AfterInvocationProviderManager;
|
||||
import org.springframework.security.access.intercept.aspectj.AspectJMethodSecurityInterceptor;
|
||||
import org.springframework.security.access.prepost.PostFilter;
|
||||
import org.springframework.security.access.prepost.PostInvocationAdviceProvider;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter;
|
||||
import org.springframework.security.access.prepost.PrePostAnnotationSecurityMetadataSource;
|
||||
import org.springframework.security.access.vote.AffirmativeBased;
|
||||
import org.springframework.security.access.vote.RoleVoter;
|
||||
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
@@ -40,23 +31,18 @@ import org.springframework.security.core.context.SecurityContextHolder;
|
||||
* @since 3.0.3
|
||||
*/
|
||||
public class AnnotationSecurityAspectTests {
|
||||
private AffirmativeBased adm;
|
||||
private @Mock AccessDecisionManager adm;
|
||||
private @Mock AuthenticationManager authman;
|
||||
private TestingAuthenticationToken anne = new TestingAuthenticationToken("anne", "", "ROLE_A");
|
||||
// private TestingAuthenticationToken bob = new TestingAuthenticationToken("bob", "", "ROLE_B");
|
||||
private AspectJMethodSecurityInterceptor interceptor;
|
||||
private SecuredImpl secured = new SecuredImpl();
|
||||
private SecuredImplSubclass securedSub = new SecuredImplSubclass();
|
||||
private PrePostSecured prePostSecured = new PrePostSecured();
|
||||
|
||||
@Before
|
||||
public final void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
interceptor = new AspectJMethodSecurityInterceptor();
|
||||
adm = new AffirmativeBased();
|
||||
AccessDecisionVoter[] voters = new AccessDecisionVoter[]
|
||||
{new RoleVoter(), new PreInvocationAuthorizationAdviceVoter(new ExpressionBasedPreInvocationAdvice())};
|
||||
adm.setDecisionVoters(Arrays.asList(voters));
|
||||
interceptor.setAccessDecisionManager(adm);
|
||||
interceptor.setAuthenticationManager(authman);
|
||||
interceptor.setSecurityMetadataSource(new SecuredAnnotationSecurityMetadataSource());
|
||||
@@ -85,57 +71,18 @@ public class AnnotationSecurityAspectTests {
|
||||
secured.securedClassMethod();
|
||||
}
|
||||
|
||||
@Test(expected=AccessDeniedException.class)
|
||||
public void internalPrivateCallIsIntercepted() {
|
||||
SecurityContextHolder.getContext().setAuthentication(anne);
|
||||
|
||||
try {
|
||||
secured.publicCallsPrivate();
|
||||
fail("Expected AccessDeniedException");
|
||||
} catch (AccessDeniedException expected) {
|
||||
}
|
||||
securedSub.publicCallsPrivate();
|
||||
}
|
||||
|
||||
@Test(expected=AccessDeniedException.class)
|
||||
public void protectedMethodIsIntercepted() throws Exception {
|
||||
SecurityContextHolder.getContext().setAuthentication(anne);
|
||||
|
||||
secured.protectedMethod();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void overriddenProtectedMethodIsNotIntercepted() throws Exception {
|
||||
// AspectJ doesn't inherit annotations
|
||||
securedSub.protectedMethod();
|
||||
}
|
||||
|
||||
// SEC-1262
|
||||
@Test(expected=AccessDeniedException.class)
|
||||
public void denyAllPreAuthorizeDeniesAccess() throws Exception {
|
||||
configureForElAnnotations();
|
||||
SecurityContextHolder.getContext().setAuthentication(anne);
|
||||
prePostSecured.denyAllMethod();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postFilterIsApplied() throws Exception {
|
||||
configureForElAnnotations();
|
||||
SecurityContextHolder.getContext().setAuthentication(anne);
|
||||
List<String> objects = prePostSecured.postFilterMethod();
|
||||
assertEquals(2, objects.size());
|
||||
assertTrue(objects.contains("apple"));
|
||||
assertTrue(objects.contains("aubergine"));
|
||||
}
|
||||
|
||||
private void configureForElAnnotations() {
|
||||
DefaultMethodSecurityExpressionHandler eh = new DefaultMethodSecurityExpressionHandler();
|
||||
interceptor.setSecurityMetadataSource(new PrePostAnnotationSecurityMetadataSource(
|
||||
new ExpressionBasedAnnotationAttributeFactory(eh)));
|
||||
new ExpressionBasedAnnotationAttributeFactory(new DefaultMethodSecurityExpressionHandler())));
|
||||
AffirmativeBased adm = new AffirmativeBased();
|
||||
AccessDecisionVoter[] voters = new AccessDecisionVoter[]
|
||||
{new PreInvocationAuthorizationAdviceVoter(new ExpressionBasedPreInvocationAdvice())};
|
||||
adm.setDecisionVoters(Arrays.asList(voters));
|
||||
interceptor.setAccessDecisionManager(adm);
|
||||
AfterInvocationProviderManager aim = new AfterInvocationProviderManager();
|
||||
aim.setProviders(Arrays.asList(new PostInvocationAdviceProvider(new ExpressionBasedPostInvocationAdvice(eh))));
|
||||
interceptor.setAfterInvocationManager(aim);
|
||||
prePostSecured.denyAllMethod();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,6 +92,7 @@ interface SecuredInterface {
|
||||
}
|
||||
|
||||
class SecuredImpl implements SecuredInterface {
|
||||
|
||||
// Not really secured because AspectJ doesn't inherit annotations from interfaces
|
||||
public void securedMethod() {
|
||||
}
|
||||
@@ -152,39 +100,11 @@ class SecuredImpl implements SecuredInterface {
|
||||
@Secured("ROLE_A")
|
||||
public void securedClassMethod() {
|
||||
}
|
||||
|
||||
@Secured("ROLE_X")
|
||||
private void privateMethod() {
|
||||
}
|
||||
|
||||
@Secured("ROLE_X")
|
||||
protected void protectedMethod() {
|
||||
}
|
||||
|
||||
@Secured("ROLE_X")
|
||||
public void publicCallsPrivate() {
|
||||
privateMethod();
|
||||
}
|
||||
}
|
||||
|
||||
class SecuredImplSubclass extends SecuredImpl {
|
||||
protected void protectedMethod() {
|
||||
}
|
||||
|
||||
public void publicCallsPrivate() {
|
||||
super.publicCallsPrivate();
|
||||
}
|
||||
}
|
||||
|
||||
class PrePostSecured {
|
||||
|
||||
@PreAuthorize("denyAll")
|
||||
public void denyAllMethod() {
|
||||
}
|
||||
|
||||
@PostFilter("filterObject.startsWith('a')")
|
||||
public List<String> postFilterMethod() {
|
||||
ArrayList<String> objects = new ArrayList<String>();
|
||||
objects.addAll(Arrays.asList(new String[] {"apple", "banana", "aubergine", "orange"}));
|
||||
return objects;
|
||||
}
|
||||
}
|
||||
|
||||
+11
-13
@@ -1,7 +1,15 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
maven { url "http://repo.springsource.org/plugins-release" }
|
||||
}
|
||||
dependencies {
|
||||
classpath('me.champeau.gradle:gradle-javadoc-hotfix-plugin:0.1')
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'base'
|
||||
|
||||
description = 'Spring Security'
|
||||
|
||||
allprojects {
|
||||
ext.releaseBuild = version.endsWith('RELEASE')
|
||||
ext.snapshotBuild = version.endsWith('SNAPSHOT')
|
||||
@@ -9,10 +17,8 @@ allprojects {
|
||||
group = 'org.springframework.security'
|
||||
|
||||
repositories {
|
||||
maven { url "http://repo.springsource.org/libs-release" }
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// Set up different subproject lists for individual configuration
|
||||
@@ -21,14 +27,8 @@ ext.sampleProjects = subprojects.findAll { project -> project.name.startsWith('s
|
||||
ext.itestProjects = subprojects.findAll { project -> project.name.startsWith('itest') }
|
||||
ext.coreModuleProjects = javaProjects - sampleProjects - itestProjects
|
||||
ext.aspectjProjects = [project(':spring-security-aspects'), project(':spring-security-samples-aspectj')]
|
||||
|
||||
configure(subprojects - coreModuleProjects) {
|
||||
tasks.findByPath("artifactoryPublish")?.enabled = false
|
||||
}
|
||||
|
||||
configure(javaProjects) {
|
||||
apply from: "$rootDir/gradle/javaprojects.gradle"
|
||||
apply from: "$rootDir/gradle/release-checks.gradle"
|
||||
}
|
||||
|
||||
configure(coreModuleProjects) {
|
||||
@@ -53,9 +53,7 @@ configure (aspectjProjects) {
|
||||
task dist(type: Zip) {
|
||||
dependsOn subprojects*.tasks*.matching { task -> task.name == 'assemble' || task.name.endsWith('Zip') }
|
||||
classifier = 'dist'
|
||||
|
||||
evaluationDependsOn(':docs')
|
||||
|
||||
def zipRootDir = "${project.name}-$version"
|
||||
into(zipRootDir) {
|
||||
from(rootDir) {
|
||||
@@ -82,5 +80,5 @@ artifacts {
|
||||
apply from: "$rootDir/gradle/ide-integration.gradle"
|
||||
|
||||
task wrapper(type: Wrapper) {
|
||||
gradleVersion = '1.3'
|
||||
gradleVersion = '1.9'
|
||||
}
|
||||
|
||||
@@ -49,12 +49,6 @@ dependencies {
|
||||
'com.springsource.bundlor:com.springsource.bundlor.blint:1.0.0.RELEASE'
|
||||
}
|
||||
|
||||
// Trang
|
||||
dependencies {
|
||||
compile 'com.thaiopensource:trang:20091111',
|
||||
'net.sourceforge.saxon:saxon:9.1.0.8'
|
||||
}
|
||||
|
||||
task ide(type: Copy) {
|
||||
from configurations.runtime
|
||||
into 'ide'
|
||||
|
||||
@@ -38,21 +38,26 @@ class AspectJPlugin implements Plugin<Project> {
|
||||
project.configurations.add('aspectpath')
|
||||
}
|
||||
|
||||
project.tasks.add(name: 'compileJava', overwrite: true, description: 'Compiles AspectJ Source', type: Ajc) {
|
||||
project.tasks.add(name: 'compileAspect', overwrite: true, description: 'Compiles AspectJ Source', type: Ajc) {
|
||||
dependsOn project.configurations*.getTaskDependencyFromProjectDependency(true, "compileJava")
|
||||
dependsOn project.processResources
|
||||
sourceSet = project.sourceSets.main
|
||||
inputs.files(sourceSet.java.srcDirs)
|
||||
outputs.dir(sourceSet.output.classesDir)
|
||||
aspectPath = project.configurations.aspectpath
|
||||
}
|
||||
project.tasks.compileJava.deleteAllActions()
|
||||
project.tasks.compileJava.dependsOn project.tasks.compileAspect
|
||||
|
||||
project.tasks.add(name: 'compileTestJava', overwrite: true, description: 'Compiles AspectJ Test Source', type: Ajc) {
|
||||
project.tasks.add(name: 'compileTestAspect', overwrite: true, description: 'Compiles AspectJ Test Source', type: Ajc) {
|
||||
dependsOn project.processTestResources, project.compileJava, project.jar
|
||||
sourceSet = project.sourceSets.test
|
||||
inputs.files(sourceSet.java.srcDirs)
|
||||
outputs.dir(sourceSet.output.classesDir)
|
||||
aspectPath = project.files(project.configurations.aspectpath, project.jar.archivePath)
|
||||
}
|
||||
project.tasks.compileTestJava.deleteAllActions()
|
||||
project.tasks.compileTestJava.dependsOn project.tasks.compileTestAspect
|
||||
|
||||
project.tasks.withType(GenerateEclipseProject) {
|
||||
project.eclipse.project.file.whenMerged { p ->
|
||||
@@ -65,9 +70,9 @@ class AspectJPlugin implements Plugin<Project> {
|
||||
project.eclipse.classpath.file.whenMerged { classpath ->
|
||||
def entries = classpath.entries.findAll { it instanceof ProjectDependency}.findAll { entry ->
|
||||
def projectPath = entry.path.replaceAll('/','')
|
||||
project.rootProject.allprojects.find{ p->
|
||||
project.rootProject.allprojects.find{ p->
|
||||
if(p.plugins.findPlugin(EclipsePlugin)) {
|
||||
return p.eclipse.project.name == projectPath && p.plugins.findPlugin(AspectJPlugin)
|
||||
return p.eclipse.project.name == projectPath && p.plugins.findPlugin(AspectJPlugin)
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
package trang;
|
||||
|
||||
import com.thaiopensource.relaxng.translate.Driver
|
||||
|
||||
import javax.xml.transform.Transformer
|
||||
import javax.xml.transform.TransformerFactory
|
||||
import javax.xml.transform.stream.StreamSource
|
||||
import javax.xml.transform.stream.StreamResult
|
||||
|
||||
import org.gradle.api.*;
|
||||
import org.gradle.api.tasks.*
|
||||
import org.gradle.api.file.FileCollection
|
||||
|
||||
/**
|
||||
* Used for converting .rnc files to .xsd files.
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class TrangPlugin implements Plugin<Project> {
|
||||
public void apply(Project project) {
|
||||
Task rncToXsd = project.tasks.add('rncToXsd', RncToXsd.class)
|
||||
rncToXsd.description = 'Converts .rnc to .xsd'
|
||||
rncToXsd.group = 'Build'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts .rnc files to .xsd files using trang and then applies an xsl file to cleanup the results.
|
||||
*/
|
||||
public class RncToXsd extends DefaultTask {
|
||||
@InputDirectory
|
||||
File rncDir
|
||||
|
||||
@InputFile
|
||||
File xslFile
|
||||
|
||||
@OutputDirectory
|
||||
File xsdDir
|
||||
|
||||
@TaskAction
|
||||
public final void transform() {
|
||||
String xslPath = xslFile.absolutePath
|
||||
rncDir.listFiles( { dir, file -> file.endsWith('.rnc')} as FilenameFilter).each { rncFile ->
|
||||
File xsdFile = new File(xsdDir, rncFile.name.replace('.rnc', '.xsd'))
|
||||
String xsdOutputPath = xsdFile.absolutePath
|
||||
new Driver().run([rncFile.absolutePath, xsdOutputPath] as String[]);
|
||||
|
||||
TransformerFactory tFactory = new net.sf.saxon.TransformerFactoryImpl()
|
||||
Transformer transformer =
|
||||
tFactory.newTransformer(new StreamSource(xslPath))
|
||||
File temp = File.createTempFile("gradle-trang-" + xsdFile.name, ".xsd")
|
||||
xsdFile.withInputStream { is ->
|
||||
temp << is
|
||||
}
|
||||
StreamSource xmlSource = new StreamSource(temp)
|
||||
transformer.transform(xmlSource, new StreamResult(xsdFile))
|
||||
temp.delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
implementation-class=trang.TrangPlugin
|
||||
+3
-2
@@ -4,9 +4,10 @@ dependencies {
|
||||
project(':spring-security-web'),
|
||||
"org.springframework:spring-context:$springVersion",
|
||||
"org.springframework:spring-beans:$springVersion",
|
||||
"org.springframework:spring-tx:$springVersion",
|
||||
"org.springframework:spring-web:$springVersion",
|
||||
"org.jasig.cas.client:cas-client-core:3.1.12",
|
||||
"org.jasig.cas:cas-client-core:3.1.9",
|
||||
"net.sf.ehcache:ehcache:$ehcacheVersion"
|
||||
|
||||
provided "org.apache.tomcat:tomcat-servlet-api:$servletApiVersion"
|
||||
provided 'javax.servlet:servlet-api:2.5'
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
<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.springframework.security</groupId>
|
||||
<artifactId>spring-security-parent</artifactId>
|
||||
<version>@pomVersion@</version>
|
||||
</parent>
|
||||
<artifactId>spring-security-cas-client</artifactId>
|
||||
<name>Spring Security - CAS support</name>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-web</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>servlet-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-tx</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jasig.cas</groupId>
|
||||
<artifactId>cas-client-core</artifactId>
|
||||
<version>3.1.10</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sf.ehcache</groupId>
|
||||
<artifactId>ehcache</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.springsource.bundlor</groupId>
|
||||
<artifactId>com.springsource.bundlor.maven</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -38,8 +38,6 @@ public class ServiceProperties implements InitializingBean {
|
||||
|
||||
private String service;
|
||||
|
||||
private boolean authenticateAllArtifacts;
|
||||
|
||||
private boolean sendRenew = false;
|
||||
|
||||
private String artifactParameter = DEFAULT_CAS_ARTIFACT_PARAMETER;
|
||||
@@ -49,9 +47,7 @@ public class ServiceProperties implements InitializingBean {
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if(!authenticateAllArtifacts) {
|
||||
Assert.hasLength(this.service, "service must be specified unless authenticateAllArtifacts is true.");
|
||||
}
|
||||
Assert.hasLength(this.service, "service must be specified.");
|
||||
Assert.hasLength(this.artifactParameter, "artifactParameter cannot be empty.");
|
||||
Assert.hasLength(this.serviceParameter, "serviceParameter cannot be empty.");
|
||||
}
|
||||
@@ -119,19 +115,4 @@ public class ServiceProperties implements InitializingBean {
|
||||
public final void setServiceParameter(final String serviceParameter) {
|
||||
this.serviceParameter = serviceParameter;
|
||||
}
|
||||
|
||||
public final boolean isAuthenticateAllArtifacts() {
|
||||
return authenticateAllArtifacts;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, then any non-null artifact (ticket) should be authenticated.
|
||||
* Additionally, the service will be determined dynamically in order to
|
||||
* ensure the service matches the expected value for this artifact.
|
||||
*
|
||||
* @param authenticateAllArtifacts
|
||||
*/
|
||||
public final void setAuthenticateAllArtifacts(final boolean authenticateAllArtifacts) {
|
||||
this.authenticateAllArtifacts = authenticateAllArtifacts;
|
||||
}
|
||||
}
|
||||
|
||||
-3
@@ -19,7 +19,6 @@ import java.util.ArrayList;
|
||||
import org.jasig.cas.client.validation.Assertion;
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.SpringSecurityCoreVersion;
|
||||
|
||||
/**
|
||||
* Temporary authentication object needed to load the user details service.
|
||||
@@ -29,8 +28,6 @@ import org.springframework.security.core.SpringSecurityCoreVersion;
|
||||
*/
|
||||
public final class CasAssertionAuthenticationToken extends AbstractAuthenticationToken {
|
||||
|
||||
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
|
||||
|
||||
private final Assertion assertion;
|
||||
|
||||
private final String ticket;
|
||||
|
||||
+7
-48
@@ -15,8 +15,6 @@
|
||||
|
||||
package org.springframework.security.cas.authentication;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jasig.cas.client.validation.Assertion;
|
||||
import org.jasig.cas.client.validation.TicketValidationException;
|
||||
import org.jasig.cas.client.validation.TicketValidator;
|
||||
@@ -30,12 +28,9 @@ import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.cas.ServiceProperties;
|
||||
import org.springframework.security.cas.web.CasAuthenticationFilter;
|
||||
import org.springframework.security.cas.web.authentication.ServiceAuthenticationDetails;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.SpringSecurityMessageSource;
|
||||
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
|
||||
import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper;
|
||||
import org.springframework.security.core.userdetails.*;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -53,22 +48,17 @@ import org.springframework.util.Assert;
|
||||
* @author Scott Battaglia
|
||||
*/
|
||||
public class CasAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware {
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
|
||||
private static final Log logger = LogFactory.getLog(CasAuthenticationProvider.class);
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private AuthenticationUserDetailsService<CasAssertionAuthenticationToken> authenticationUserDetailsService;
|
||||
private AuthenticationUserDetailsService authenticationUserDetailsService;
|
||||
|
||||
private final UserDetailsChecker userDetailsChecker = new AccountStatusUserDetailsChecker();
|
||||
private UserDetailsChecker userDetailsChecker = new AccountStatusUserDetailsChecker();
|
||||
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
|
||||
private StatelessTicketCache statelessTicketCache = new NullStatelessTicketCache();
|
||||
private String key;
|
||||
private TicketValidator ticketValidator;
|
||||
private ServiceProperties serviceProperties;
|
||||
private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();
|
||||
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
@@ -78,6 +68,7 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
|
||||
Assert.notNull(this.statelessTicketCache, "A statelessTicketCache must be set");
|
||||
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");
|
||||
Assert.notNull(this.serviceProperties, "serviceProperties is a required field.");
|
||||
}
|
||||
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
@@ -137,42 +128,15 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
|
||||
|
||||
private CasAuthenticationToken authenticateNow(final Authentication authentication) throws AuthenticationException {
|
||||
try {
|
||||
final Assertion assertion = this.ticketValidator.validate(authentication.getCredentials().toString(), getServiceUrl(authentication));
|
||||
final Assertion assertion = this.ticketValidator.validate(authentication.getCredentials().toString(), serviceProperties.getService());
|
||||
final UserDetails userDetails = loadUserByAssertion(assertion);
|
||||
userDetailsChecker.check(userDetails);
|
||||
return new CasAuthenticationToken(this.key, userDetails, authentication.getCredentials(),
|
||||
authoritiesMapper.mapAuthorities(userDetails.getAuthorities()), userDetails, assertion);
|
||||
return new CasAuthenticationToken(this.key, userDetails, authentication.getCredentials(), userDetails.getAuthorities(), userDetails, assertion);
|
||||
} catch (final TicketValidationException e) {
|
||||
throw new BadCredentialsException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the serviceUrl. If the {@link Authentication#getDetails()} is an
|
||||
* instance of {@link ServiceAuthenticationDetails}, then
|
||||
* {@link ServiceAuthenticationDetails#getServiceUrl()} is used. Otherwise,
|
||||
* the {@link ServiceProperties#getService()} is used.
|
||||
*
|
||||
* @param authentication
|
||||
* @return
|
||||
*/
|
||||
private String getServiceUrl(Authentication authentication) {
|
||||
String serviceUrl;
|
||||
if(authentication.getDetails() instanceof ServiceAuthenticationDetails) {
|
||||
serviceUrl = ((ServiceAuthenticationDetails)authentication.getDetails()).getServiceUrl();
|
||||
}else if(serviceProperties == null){
|
||||
throw new IllegalStateException("serviceProperties cannot be null unless Authentication.getDetails() implements ServiceAuthenticationDetails.");
|
||||
}else if(serviceProperties.getService() == null){
|
||||
throw new IllegalStateException("serviceProperties.getService() cannot be null unless Authentication.getDetails() implements ServiceAuthenticationDetails.");
|
||||
}else {
|
||||
serviceUrl = serviceProperties.getService();
|
||||
}
|
||||
if(logger.isDebugEnabled()) {
|
||||
logger.debug("serviceUrl = "+serviceUrl);
|
||||
}
|
||||
return serviceUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method for retrieving the UserDetails based on the assertion. Default is to call configured userDetailsService and pass the username. Deployers
|
||||
* can override this method and retrieve the user based on any criteria they desire.
|
||||
@@ -186,7 +150,6 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@SuppressWarnings("unchecked")
|
||||
/**
|
||||
* @deprecated as of 3.0. Use the {@link org.springframework.security.cas.authentication.CasAuthenticationProvider#setAuthenticationUserDetailsService(org.springframework.security.core.userdetails.AuthenticationUserDetailsService)} instead.
|
||||
*/
|
||||
@@ -194,7 +157,7 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
|
||||
this.authenticationUserDetailsService = new UserDetailsByNameServiceWrapper(userDetailsService);
|
||||
}
|
||||
|
||||
public void setAuthenticationUserDetailsService(final AuthenticationUserDetailsService<CasAssertionAuthenticationToken> authenticationUserDetailsService) {
|
||||
public void setAuthenticationUserDetailsService(final AuthenticationUserDetailsService authenticationUserDetailsService) {
|
||||
this.authenticationUserDetailsService = authenticationUserDetailsService;
|
||||
}
|
||||
|
||||
@@ -230,11 +193,7 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
|
||||
this.ticketValidator = ticketValidator;
|
||||
}
|
||||
|
||||
public void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) {
|
||||
this.authoritiesMapper = authoritiesMapper;
|
||||
}
|
||||
|
||||
public boolean supports(final Class<?> authentication) {
|
||||
public boolean supports(final Class<? extends Object> authentication) {
|
||||
return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication)) ||
|
||||
(CasAuthenticationToken.class.isAssignableFrom(authentication)) ||
|
||||
(CasAssertionAuthenticationToken.class.isAssignableFrom(authentication));
|
||||
|
||||
+2
-4
@@ -21,7 +21,6 @@ import java.util.Collection;
|
||||
import org.jasig.cas.client.validation.Assertion;
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.SpringSecurityCoreVersion;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
/**
|
||||
@@ -31,10 +30,9 @@ import org.springframework.security.core.userdetails.UserDetails;
|
||||
* @author Scott Battaglia
|
||||
*/
|
||||
public class CasAuthenticationToken extends AbstractAuthenticationToken implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final Object credentials;
|
||||
private final Object principal;
|
||||
private final UserDetails userDetails;
|
||||
|
||||
+18
-7
@@ -15,17 +15,23 @@
|
||||
|
||||
package org.springframework.security.cas.authentication;
|
||||
|
||||
import net.sf.ehcache.Ehcache;
|
||||
import net.sf.ehcache.CacheException;
|
||||
import net.sf.ehcache.Element;
|
||||
import net.sf.ehcache.Ehcache;
|
||||
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
import org.springframework.dao.DataRetrievalFailureException;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
/**
|
||||
* Caches tickets using a Spring IoC defined <a href="http://ehcache.sourceforge.net">EHCACHE</a>.
|
||||
* Caches tickets using a Spring IoC defined <A HREF="http://ehcache.sourceforge.net">EHCACHE</a>.
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
@@ -45,13 +51,18 @@ public class EhCacheBasedTicketCache implements StatelessTicketCache, Initializi
|
||||
}
|
||||
|
||||
public CasAuthenticationToken getByTicketId(final String serviceTicket) {
|
||||
final Element element = cache.get(serviceTicket);
|
||||
try {
|
||||
final Element element = cache.get(serviceTicket);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Cache hit: " + (element != null) + "; service ticket: " + serviceTicket);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Cache hit: " + (element != null) + "; service ticket: " + serviceTicket);
|
||||
}
|
||||
|
||||
return element == null ? null : (CasAuthenticationToken) element.getValue();
|
||||
|
||||
} catch (CacheException cacheException) {
|
||||
throw new DataRetrievalFailureException("Cache failure: " + cacheException.getMessage());
|
||||
}
|
||||
|
||||
return element == null ? null : (CasAuthenticationToken) element.getValue();
|
||||
}
|
||||
|
||||
public Ehcache getCache() {
|
||||
|
||||
-80
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.cas.authentication;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
/**
|
||||
* Caches tickets using a Spring IoC defined {@link Cache}.
|
||||
*
|
||||
* @author Marten Deinum
|
||||
* @since 3.2
|
||||
*
|
||||
*/
|
||||
public class SpringCacheBasedTicketCache implements StatelessTicketCache {
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
|
||||
private static final Log logger = LogFactory.getLog(SpringCacheBasedTicketCache.class);
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private final Cache cache;
|
||||
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
public SpringCacheBasedTicketCache(Cache cache) throws Exception {
|
||||
Assert.notNull(cache, "cache mandatory");
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public CasAuthenticationToken getByTicketId(final String serviceTicket) {
|
||||
final Cache.ValueWrapper element = serviceTicket != null ? cache.get(serviceTicket) : null;
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Cache hit: " + (element != null) + "; service ticket: " + serviceTicket);
|
||||
}
|
||||
|
||||
return element == null ? null : (CasAuthenticationToken) element.get();
|
||||
}
|
||||
|
||||
public void putTicketInCache(final CasAuthenticationToken token) {
|
||||
String key = token.getCredentials().toString();
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Cache put: " + key);
|
||||
}
|
||||
|
||||
cache.put(key, token);
|
||||
}
|
||||
|
||||
public void removeTicketFromCache(final CasAuthenticationToken token) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Cache remove: " + token.getCredentials().toString());
|
||||
}
|
||||
|
||||
this.removeTicketFromCache(token.getCredentials().toString());
|
||||
}
|
||||
|
||||
public void removeTicketFromCache(final String serviceTicket) {
|
||||
cache.evict(serviceTicket);
|
||||
}
|
||||
}
|
||||
+9
-6
@@ -14,10 +14,13 @@
|
||||
*/
|
||||
package org.springframework.security.cas.userdetails;
|
||||
|
||||
import org.jasig.cas.client.validation.Assertion;
|
||||
import org.springframework.security.cas.authentication.CasAssertionAuthenticationToken;
|
||||
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.cas.authentication.CasAssertionAuthenticationToken;
|
||||
import org.springframework.util.Assert;
|
||||
import org.jasig.cas.client.validation.Assertion;
|
||||
|
||||
/**
|
||||
* Abstract class for using the provided CAS assertion to construct a new User object. This generally is most
|
||||
@@ -26,11 +29,11 @@ import org.springframework.security.core.userdetails.UserDetails;
|
||||
* @author Scott Battaglia
|
||||
* @since 3.0
|
||||
*/
|
||||
public abstract class AbstractCasAssertionUserDetailsService
|
||||
implements AuthenticationUserDetailsService<CasAssertionAuthenticationToken> {
|
||||
public abstract class AbstractCasAssertionUserDetailsService implements AuthenticationUserDetailsService {
|
||||
|
||||
public final UserDetails loadUserDetails(final CasAssertionAuthenticationToken token) {
|
||||
return loadUserDetails(token.getAssertion());
|
||||
public final UserDetails loadUserDetails(final Authentication token) throws UsernameNotFoundException {
|
||||
Assert.isInstanceOf(CasAssertionAuthenticationToken.class, token, "The provided token MUST be an instance of CasAssertionAuthenticationToken.class");
|
||||
return loadUserDetails(((CasAssertionAuthenticationToken) token).getAssertion());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+4
-4
@@ -17,7 +17,7 @@ package org.springframework.security.cas.userdetails;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.authority.GrantedAuthorityImpl;
|
||||
import org.springframework.util.Assert;
|
||||
import org.jasig.cas.client.validation.Assertion;
|
||||
|
||||
@@ -36,7 +36,7 @@ public final class GrantedAuthorityFromAssertionAttributesUserDetailsService ext
|
||||
|
||||
private static final String NON_EXISTENT_PASSWORD_VALUE = "NO_PASSWORD";
|
||||
|
||||
private final String[] attributes;
|
||||
private String[] attributes;
|
||||
|
||||
private boolean convertToUpperCase = true;
|
||||
|
||||
@@ -62,11 +62,11 @@ public final class GrantedAuthorityFromAssertionAttributesUserDetailsService ext
|
||||
final List list = (List) value;
|
||||
|
||||
for (final Object o : list) {
|
||||
grantedAuthorities.add(new SimpleGrantedAuthority(this.convertToUpperCase ? o.toString().toUpperCase() : o.toString()));
|
||||
grantedAuthorities.add(new GrantedAuthorityImpl(this.convertToUpperCase ? o.toString().toUpperCase() : o.toString()));
|
||||
}
|
||||
|
||||
} else {
|
||||
grantedAuthorities.add(new SimpleGrantedAuthority(this.convertToUpperCase ? value.toString().toUpperCase() : value.toString()));
|
||||
grantedAuthorities.add(new GrantedAuthorityImpl(this.convertToUpperCase ? value.toString().toUpperCase() : value.toString()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-1
@@ -65,7 +65,6 @@ public class CasAuthenticationEntryPoint implements AuthenticationEntryPoint, In
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.hasLength(this.loginUrl, "loginUrl must be specified");
|
||||
Assert.notNull(this.serviceProperties, "serviceProperties must be specified");
|
||||
Assert.notNull(this.serviceProperties.getService(),"serviceProperties.getService() cannot be null.");
|
||||
}
|
||||
|
||||
public final void commence(final HttpServletRequest servletRequest, final HttpServletResponse response,
|
||||
|
||||
+19
-268
@@ -17,139 +17,41 @@ package org.springframework.security.cas.web;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage;
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
import org.jasig.cas.client.validation.TicketValidator;
|
||||
import org.springframework.security.authentication.AnonymousAuthenticationToken;
|
||||
import org.springframework.security.authentication.AuthenticationDetailsSource;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.authentication.event.InteractiveAuthenticationSuccessEvent;
|
||||
import org.springframework.security.cas.ServiceProperties;
|
||||
import org.springframework.security.cas.web.authentication.ServiceAuthenticationDetails;
|
||||
import org.springframework.security.cas.web.authentication.ServiceAuthenticationDetailsSource;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
/**
|
||||
* Processes a CAS service ticket, obtains proxy granting tickets, and processes proxy tickets.
|
||||
* <h2>Service Tickets</h2>
|
||||
* Processes a CAS service ticket.
|
||||
* <p>
|
||||
* A service ticket consists of an opaque ticket string. It arrives at this filter by the user's browser successfully
|
||||
* authenticating using CAS, and then receiving a HTTP redirect to a <code>service</code>. The opaque ticket string is
|
||||
* presented in the <code>ticket</code> request parameter.
|
||||
* <p>
|
||||
* This filter monitors the <code>service</code> URL so it can
|
||||
* receive the service ticket and process it. By default this filter processes the URL <tt>/j_spring_cas_security_check</tt>.
|
||||
* When processing this URL, the value of {@link ServiceProperties#getService()} is used as the <tt>service</tt> when validating
|
||||
* the <code>ticket</code>. This means that it is important that {@link ServiceProperties#getService()} specifies the same value
|
||||
* as the <tt>filterProcessesUrl</tt>.
|
||||
* presented in the <code>ticket</code> request parameter. This filter monitors the <code>service</code> URL so it can
|
||||
* receive the service ticket and process it. The CAS server knows which <code>service</code> URL to use via the
|
||||
* {@link ServiceProperties#getService()} method.
|
||||
* <p>
|
||||
* Processing the service ticket involves creating a <code>UsernamePasswordAuthenticationToken</code> which
|
||||
* uses {@link #CAS_STATEFUL_IDENTIFIER} for the <code>principal</code> and the opaque ticket string as the
|
||||
* <code>credentials</code>.
|
||||
* <h2>Obtaining Proxy Granting Tickets</h2>
|
||||
* <p>
|
||||
* If specified, the filter can also monitor the <code>proxyReceptorUrl</code>. The filter will respond to requests matching
|
||||
* this url so that the CAS Server can provide a PGT to the filter. Note that in addition to the <code>proxyReceptorUrl</code> a non-null
|
||||
* <code>proxyGrantingTicketStorage</code> must be provided in order for the filter to respond to proxy receptor requests. By configuring
|
||||
* a shared {@link ProxyGrantingTicketStorage} between the {@link TicketValidator} and the CasAuthenticationFilter one can have the
|
||||
* CasAuthenticationFilter handle the proxying requirements for CAS.
|
||||
* <h2>Proxy Tickets</h2>
|
||||
* <p>
|
||||
* The filter can process tickets present on any url. This is useful when wanting to process proxy tickets. In order for proxy
|
||||
* tickets to get processed {@link ServiceProperties#isAuthenticateAllArtifacts()} must return <code>true</code>. Additionally,
|
||||
* if the request is already authenticated, authentication will <b>not</b> occur. Last, {@link AuthenticationDetailsSource#buildDetails(Object)}
|
||||
* must return a {@link ServiceAuthenticationDetails}. This can be accomplished using the {@link ServiceAuthenticationDetailsSource}.
|
||||
* In this case {@link ServiceAuthenticationDetails#getServiceUrl()} will be used for the service url.
|
||||
* <p>
|
||||
* Processing the proxy ticket involves creating a <code>UsernamePasswordAuthenticationToken</code> which
|
||||
* uses {@link #CAS_STATELESS_IDENTIFIER} for the <code>principal</code> and the opaque ticket string as the
|
||||
* <code>credentials</code>. When a proxy ticket is successfully authenticated, the FilterChain continues and the
|
||||
* <code>authenticationSuccessHandler</code> is not used.
|
||||
* <h2>Notes about the <code>AuthenticationManager</code></h2>
|
||||
* <p>
|
||||
* The configured <code>AuthenticationManager</code> is expected to provide a provider that can recognise
|
||||
* <code>UsernamePasswordAuthenticationToken</code>s containing this special <code>principal</code> name, and process
|
||||
* them accordingly by validation with the CAS server. Additionally, it should be capable of using the result of
|
||||
* {@link ServiceAuthenticationDetails#getServiceUrl()} as the service when validating the ticket.
|
||||
* <h2>Example Configuration</h2>
|
||||
* them accordingly by validation with the CAS server.
|
||||
* <p>
|
||||
* An example configuration that supports service tickets, obtaining proxy granting tickets, and proxy tickets is
|
||||
* illustrated below:
|
||||
*
|
||||
* <pre>
|
||||
* <b:bean id="serviceProperties"
|
||||
* class="org.springframework.security.cas.ServiceProperties"
|
||||
* p:service="https://service.example.com/cas-sample/j_spring_cas_security_check"
|
||||
* p:authenticateAllArtifacts="true"/>
|
||||
* <b:bean id="casEntryPoint"
|
||||
* class="org.springframework.security.cas.web.CasAuthenticationEntryPoint"
|
||||
* p:serviceProperties-ref="serviceProperties" p:loginUrl="https://login.example.org/cas/login" />
|
||||
* <b:bean id="casFilter"
|
||||
* class="org.springframework.security.cas.web.CasAuthenticationFilter"
|
||||
* p:authenticationManager-ref="authManager"
|
||||
* p:serviceProperties-ref="serviceProperties"
|
||||
* p:proxyGrantingTicketStorage-ref="pgtStorage"
|
||||
* p:proxyReceptorUrl="/j_spring_cas_security_proxyreceptor">
|
||||
* <b:property name="authenticationDetailsSource">
|
||||
* <b:bean class="org.springframework.security.cas.web.authentication.ServiceAuthenticationDetailsSource"/>
|
||||
* </b:property>
|
||||
* <b:property name="authenticationFailureHandler">
|
||||
* <b:bean class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler"
|
||||
* p:defaultFailureUrl="/casfailed.jsp"/>
|
||||
* </b:property>
|
||||
* </b:bean>
|
||||
* <!--
|
||||
* NOTE: In a real application you should not use an in memory implementation. You will also want
|
||||
* to ensure to clean up expired tickets by calling ProxyGrantingTicketStorage.cleanup()
|
||||
* -->
|
||||
* <b:bean id="pgtStorage" class="org.jasig.cas.client.proxy.ProxyGrantingTicketStorageImpl"/>
|
||||
* <b:bean id="casAuthProvider" class="org.springframework.security.cas.authentication.CasAuthenticationProvider"
|
||||
* p:serviceProperties-ref="serviceProperties"
|
||||
* p:key="casAuthProviderKey">
|
||||
* <b:property name="authenticationUserDetailsService">
|
||||
* <b:bean
|
||||
* class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
|
||||
* <b:constructor-arg ref="userService" />
|
||||
* </b:bean>
|
||||
* </b:property>
|
||||
* <b:property name="ticketValidator">
|
||||
* <b:bean
|
||||
* class="org.jasig.cas.client.validation.Cas20ProxyTicketValidator"
|
||||
* p:acceptAnyProxy="true"
|
||||
* p:proxyCallbackUrl="https://service.example.com/cas-sample/j_spring_cas_security_proxyreceptor"
|
||||
* p:proxyGrantingTicketStorage-ref="pgtStorage">
|
||||
* <b:constructor-arg value="https://login.example.org/cas" />
|
||||
* </b:bean>
|
||||
* </b:property>
|
||||
* <b:property name="statelessTicketCache">
|
||||
* <b:bean class="org.springframework.security.cas.authentication.EhCacheBasedTicketCache">
|
||||
* <b:property name="cache">
|
||||
* <b:bean class="net.sf.ehcache.Cache"
|
||||
* init-method="initialise"
|
||||
* destroy-method="dispose">
|
||||
* <b:constructor-arg value="casTickets"/>
|
||||
* <b:constructor-arg value="50"/>
|
||||
* <b:constructor-arg value="true"/>
|
||||
* <b:constructor-arg value="false"/>
|
||||
* <b:constructor-arg value="3600"/>
|
||||
* <b:constructor-arg value="900"/>
|
||||
* </b:bean>
|
||||
* </b:property>
|
||||
* </b:bean>
|
||||
* </b:property>
|
||||
* </b:bean>
|
||||
* </pre>
|
||||
* By configuring a shared {@link ProxyGrantingTicketStorage} between the {@link TicketValidator} and the
|
||||
* CasAuthenticationFilter one can have the CasAuthenticationFilter handle the proxying requirements for CAS. In addition, the
|
||||
* URI endpoint for the proxying would also need to be configured (i.e. the part after protocol, hostname, and port).
|
||||
* <p>
|
||||
* By default this filter processes the URL <tt>/j_spring_cas_security_check</tt>.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Rob Winch
|
||||
@@ -179,59 +81,26 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
|
||||
|
||||
private String artifactParameter = ServiceProperties.DEFAULT_CAS_ARTIFACT_PARAMETER;
|
||||
|
||||
private boolean authenticateAllArtifacts;
|
||||
|
||||
private AuthenticationFailureHandler proxyFailureHandler = new SimpleUrlAuthenticationFailureHandler();
|
||||
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
public CasAuthenticationFilter() {
|
||||
super("/j_spring_cas_security_check");
|
||||
setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler());
|
||||
}
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
@Override
|
||||
protected final void successfulAuthentication(HttpServletRequest request,
|
||||
HttpServletResponse response, FilterChain chain, Authentication authResult)
|
||||
throws IOException, ServletException {
|
||||
boolean continueFilterChain = proxyTicketRequest(serviceTicketRequest(request, response),request);
|
||||
if(!continueFilterChain) {
|
||||
super.successfulAuthentication(request, response, chain, authResult);
|
||||
return;
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Authentication success. Updating SecurityContextHolder to contain: " + authResult);
|
||||
}
|
||||
|
||||
SecurityContextHolder.getContext().setAuthentication(authResult);
|
||||
|
||||
// Fire event
|
||||
if (this.eventPublisher != null) {
|
||||
eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
|
||||
}
|
||||
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authentication attemptAuthentication(final HttpServletRequest request, final HttpServletResponse response)
|
||||
throws AuthenticationException, IOException {
|
||||
// if the request is a proxy request process it and return null to indicate the request has been processed
|
||||
if(proxyReceptorRequest(request)) {
|
||||
logger.debug("Responding to proxy receptor request");
|
||||
if(isProxyRequest(request)) {
|
||||
CommonUtils.readAndRespondToProxyReceptorRequest(request, response, this.proxyGrantingTicketStorage);
|
||||
return null;
|
||||
}
|
||||
|
||||
final boolean serviceTicketRequest = serviceTicketRequest(request, response);
|
||||
final String username = serviceTicketRequest ? CAS_STATEFUL_IDENTIFIER : CAS_STATELESS_IDENTIFIER;
|
||||
String password = obtainArtifact(request);
|
||||
final String username = CAS_STATEFUL_IDENTIFIER;
|
||||
String password = request.getParameter(this.artifactParameter);
|
||||
|
||||
if (password == null) {
|
||||
logger.debug("Failed to obtain an artifact (cas ticket)");
|
||||
password = "";
|
||||
}
|
||||
|
||||
@@ -242,47 +111,11 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
|
||||
return this.getAuthenticationManager().authenticate(authRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* If present, gets the artifact (CAS ticket) from the {@link HttpServletRequest}.
|
||||
* @param request
|
||||
* @return if present the artifact from the {@link HttpServletRequest}, else null
|
||||
*/
|
||||
protected String obtainArtifact(HttpServletRequest request) {
|
||||
return request.getParameter(artifactParameter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridden to provide proxying capabilities.
|
||||
*/
|
||||
@Override
|
||||
protected boolean requiresAuthentication(final HttpServletRequest request, final HttpServletResponse response) {
|
||||
final boolean serviceTicketRequest = serviceTicketRequest(request, response);
|
||||
final boolean result = serviceTicketRequest || proxyReceptorRequest(request) || (proxyTicketRequest(serviceTicketRequest, request));
|
||||
if(logger.isDebugEnabled()) {
|
||||
logger.debug("requiresAuthentication = "+result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link AuthenticationFailureHandler} for proxy requests.
|
||||
* @param proxyFailureHandler
|
||||
*/
|
||||
public final void setProxyAuthenticationFailureHandler(
|
||||
AuthenticationFailureHandler proxyFailureHandler) {
|
||||
Assert.notNull(proxyFailureHandler,"proxyFailureHandler cannot be null");
|
||||
this.proxyFailureHandler = proxyFailureHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the {@link AuthenticationFailureHandler} to distinguish between
|
||||
* handling proxy ticket authentication failures and service ticket
|
||||
* failures.
|
||||
*/
|
||||
@Override
|
||||
public final void setAuthenticationFailureHandler(
|
||||
AuthenticationFailureHandler failureHandler) {
|
||||
super.setAuthenticationFailureHandler(new CasAuthenticationFailureHandler(failureHandler));
|
||||
return isProxyRequest(request) || super.requiresAuthentication(request, response);
|
||||
}
|
||||
|
||||
public final void setProxyReceptorUrl(final String proxyReceptorUrl) {
|
||||
@@ -296,97 +129,15 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
|
||||
|
||||
public final void setServiceProperties(final ServiceProperties serviceProperties) {
|
||||
this.artifactParameter = serviceProperties.getArtifactParameter();
|
||||
this.authenticateAllArtifacts = serviceProperties.isAuthenticateAllArtifacts();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the request is elgible to process a service ticket. This method exists for readability.
|
||||
* @param request
|
||||
* @param response
|
||||
* @return
|
||||
*/
|
||||
private boolean serviceTicketRequest(final HttpServletRequest request, final HttpServletResponse response) {
|
||||
boolean result = super.requiresAuthentication(request, response);
|
||||
if(logger.isDebugEnabled()) {
|
||||
logger.debug("serviceTicketRequest = "+result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the request is elgible to process a proxy ticket.
|
||||
* Indicates if the request is eligible to be processed as a proxy request.
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
private boolean proxyTicketRequest(final boolean serviceTicketRequest, final HttpServletRequest request) {
|
||||
if(serviceTicketRequest) {
|
||||
return false;
|
||||
}
|
||||
final boolean result = authenticateAllArtifacts && obtainArtifact(request) != null && !authenticated();
|
||||
if(logger.isDebugEnabled()) {
|
||||
logger.debug("proxyTicketRequest = "+result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a user is already authenticated.
|
||||
* @return
|
||||
*/
|
||||
private boolean authenticated() {
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
return authentication != null && authentication.isAuthenticated() && !(authentication instanceof AnonymousAuthenticationToken);
|
||||
}
|
||||
/**
|
||||
* Indicates if the request is elgible to be processed as the proxy receptor.
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
private boolean proxyReceptorRequest(final HttpServletRequest request) {
|
||||
private boolean isProxyRequest(final HttpServletRequest request) {
|
||||
final String requestUri = request.getRequestURI();
|
||||
final boolean result = proxyReceptorConfigured() && requestUri.endsWith(this.proxyReceptorUrl);
|
||||
if(logger.isDebugEnabled()) {
|
||||
logger.debug("proxyReceptorRequest = "+result);
|
||||
}
|
||||
return result;
|
||||
return this.proxyGrantingTicketStorage != null && !CommonUtils.isEmpty(this.proxyReceptorUrl) && requestUri.endsWith(this.proxyReceptorUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the {@link CasAuthenticationFilter} is configured to handle the proxy receptor requests.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private boolean proxyReceptorConfigured() {
|
||||
final boolean result = this.proxyGrantingTicketStorage != null && !CommonUtils.isEmpty(this.proxyReceptorUrl);
|
||||
if(logger.isDebugEnabled()) {
|
||||
logger.debug("proxyReceptorConfigured = "+result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper for the AuthenticationFailureHandler that will flex the {@link AuthenticationFailureHandler} that is used. The value
|
||||
* {@link CasAuthenticationFilter#setProxyAuthenticationFailureHandler(AuthenticationFailureHandler) will be used for proxy requests
|
||||
* that fail. The value {@link CasAuthenticationFilter#setAuthenticationFailureHandler(AuthenticationFailureHandler)} will be used for
|
||||
* service tickets that fail.
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
private class CasAuthenticationFailureHandler implements AuthenticationFailureHandler {
|
||||
private final AuthenticationFailureHandler serviceTicketFailureHandler;
|
||||
public CasAuthenticationFailureHandler(AuthenticationFailureHandler failureHandler) {
|
||||
Assert.notNull(failureHandler,"failureHandler");
|
||||
this.serviceTicketFailureHandler = failureHandler;
|
||||
}
|
||||
public void onAuthenticationFailure(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
AuthenticationException exception) throws IOException,
|
||||
ServletException {
|
||||
if(serviceTicketRequest(request, response)) {
|
||||
serviceTicketFailureHandler.onAuthenticationFailure(request, response, exception);
|
||||
}else {
|
||||
proxyFailureHandler.onAuthenticationFailure(request, response, exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-131
@@ -1,131 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.cas.web.authentication;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.security.web.authentication.WebAuthenticationDetails;
|
||||
import org.springframework.security.web.util.UrlUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A default implementation of {@link ServiceAuthenticationDetails} that figures
|
||||
* out the value for {@link #getServiceUrl()} by inspecting the current
|
||||
* {@link HttpServletRequest} and using the current URL minus the artifact and
|
||||
* the corresponding value.
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
final class DefaultServiceAuthenticationDetails extends WebAuthenticationDetails implements ServiceAuthenticationDetails {
|
||||
private static final long serialVersionUID = 6192409090610517700L;
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private final String serviceUrl;
|
||||
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
* @param request
|
||||
* the current {@link HttpServletRequest} to obtain the
|
||||
* {@link #getServiceUrl()} from.
|
||||
* @param artifactPattern
|
||||
* the {@link Pattern} that will be used to clean up the query
|
||||
* string from containing the artifact name and value. This can
|
||||
* be created using {@link #createArtifactPattern(String)}.
|
||||
*/
|
||||
DefaultServiceAuthenticationDetails(HttpServletRequest request, Pattern artifactPattern) {
|
||||
super(request);
|
||||
final String query = getQueryString(request,artifactPattern);
|
||||
this.serviceUrl = UrlUtils.buildFullRequestUrl(request.getScheme(),
|
||||
request.getServerName(), request.getServerPort(),
|
||||
request.getRequestURI(), query);
|
||||
}
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
/**
|
||||
* Returns the current URL minus the artifact parameter and its value, if present.
|
||||
* @see org.springframework.security.cas.web.authentication.ServiceAuthenticationDetails#getServiceUrl()
|
||||
*/
|
||||
public String getServiceUrl() {
|
||||
return serviceUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = super.hashCode();
|
||||
result = prime * result
|
||||
+ serviceUrl.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!super.equals(obj) || !(obj instanceof DefaultServiceAuthenticationDetails)) {
|
||||
return false;
|
||||
}
|
||||
ServiceAuthenticationDetails that = (ServiceAuthenticationDetails) obj;
|
||||
return serviceUrl.equals(that.getServiceUrl());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder result = new StringBuilder();
|
||||
result.append(super.toString());
|
||||
result.append("ServiceUrl: ");
|
||||
result.append(serviceUrl);
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* If present, removes the artifactParameterName and the corresponding value from the query String.
|
||||
* @param request
|
||||
* @return the query String minus the artifactParameterName and the corresponding value.
|
||||
*/
|
||||
private String getQueryString(final HttpServletRequest request, final Pattern artifactPattern) {
|
||||
final String query = request.getQueryString();
|
||||
if(query == null) {
|
||||
return null;
|
||||
}
|
||||
final String result = artifactPattern.matcher(query).replaceFirst("");
|
||||
if(result.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
// strip off the trailing & only if the artifact was the first query param
|
||||
return result.startsWith("&") ? result.substring(1) : result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Pattern} that can be passed into the constructor. This
|
||||
* allows the {@link Pattern} to be reused for every instance of
|
||||
* {@link DefaultServiceAuthenticationDetails}.
|
||||
*
|
||||
* @param artifactParameterName
|
||||
* @return
|
||||
*/
|
||||
static Pattern createArtifactPattern(String artifactParameterName) {
|
||||
Assert.hasLength(artifactParameterName);
|
||||
return Pattern.compile("&?"+Pattern.quote(artifactParameterName)+"=[^&]*");
|
||||
}
|
||||
}
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.cas.web.authentication;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.springframework.security.cas.ServiceProperties;
|
||||
import org.springframework.security.cas.authentication.CasAuthenticationProvider;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
/**
|
||||
* In order for the {@link CasAuthenticationProvider} to provide the correct
|
||||
* service url to authenticate the ticket, the returned value of
|
||||
* {@link Authentication#getDetails()} should implement this interface when
|
||||
* tickets can be sent to any URL rather than only
|
||||
* {@link ServiceProperties#getService()}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
* @see ServiceAuthenticationDetailsSource
|
||||
*/
|
||||
public interface ServiceAuthenticationDetails extends Serializable {
|
||||
|
||||
/**
|
||||
* Gets the absolute service url (i.e. https://example.com/service/).
|
||||
*
|
||||
* @return the service url. Cannot be <code>null</code>.
|
||||
*/
|
||||
String getServiceUrl();
|
||||
}
|
||||
-71
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.cas.web.authentication;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.security.authentication.AuthenticationDetailsSource;
|
||||
import org.springframework.security.cas.ServiceProperties;
|
||||
|
||||
/**
|
||||
* The {@code AuthenticationDetailsSource} that is set on the
|
||||
* {@code CasAuthenticationFilter} should return a value that implements
|
||||
* {@code ServiceAuthenticationDetails} if the application needs to authenticate
|
||||
* dynamic service urls. The
|
||||
* {@code ServiceAuthenticationDetailsSource#buildDetails(HttpServletRequest)}
|
||||
* creates a default {@code ServiceAuthenticationDetails}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public class ServiceAuthenticationDetailsSource implements AuthenticationDetailsSource<HttpServletRequest,
|
||||
ServiceAuthenticationDetails> {
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private final Pattern artifactPattern;
|
||||
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
/**
|
||||
* Creates an implementation that uses the default CAS artifactParameterName.
|
||||
*/
|
||||
public ServiceAuthenticationDetailsSource() {
|
||||
this(ServiceProperties.DEFAULT_CAS_ARTIFACT_PARAMETER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an implementation that uses the specified artifactParameterName
|
||||
*
|
||||
* @param artifactParameterName
|
||||
* the artifactParameterName that is removed from the current
|
||||
* URL. The result becomes the service url. Cannot be null and
|
||||
* cannot be an empty String.
|
||||
*/
|
||||
public ServiceAuthenticationDetailsSource(final String artifactParameterName) {
|
||||
this.artifactPattern = DefaultServiceAuthenticationDetails.createArtifactPattern(artifactParameterName);
|
||||
}
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
/**
|
||||
* @param context the {@code HttpServletRequest} object.
|
||||
* @return the {@code ServiceAuthenticationDetails} containing information about the current request
|
||||
*/
|
||||
public ServiceAuthenticationDetails buildDetails(HttpServletRequest context) {
|
||||
return new DefaultServiceAuthenticationDetails(context,artifactPattern);
|
||||
}
|
||||
}
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
/**
|
||||
* Authentication processing mechanisms which respond to the submission of authentication
|
||||
* credentials using CAS.
|
||||
*/
|
||||
package org.springframework.security.cas.web.authentication;
|
||||
+11
-93
@@ -15,31 +15,30 @@
|
||||
|
||||
package org.springframework.security.cas.authentication;
|
||||
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jasig.cas.client.validation.Assertion;
|
||||
import org.jasig.cas.client.validation.AssertionImpl;
|
||||
import org.jasig.cas.client.validation.TicketValidationException;
|
||||
import org.jasig.cas.client.validation.TicketValidator;
|
||||
import org.junit.*;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.junit.Test;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.cas.ServiceProperties;
|
||||
import org.springframework.security.cas.web.CasAuthenticationFilter;
|
||||
import org.springframework.security.cas.web.authentication.ServiceAuthenticationDetails;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.authority.GrantedAuthorityImpl;
|
||||
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.web.authentication.WebAuthenticationDetails;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
||||
/**
|
||||
@@ -48,7 +47,6 @@ import java.util.*;
|
||||
* @author Ben Alex
|
||||
* @author Scott Battaglia
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class CasAuthenticationProviderTests {
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
@@ -99,8 +97,8 @@ public class CasAuthenticationProviderTests {
|
||||
CasAuthenticationToken casResult = (CasAuthenticationToken) result;
|
||||
assertEquals(makeUserDetailsFromAuthoritiesPopulator(), casResult.getPrincipal());
|
||||
assertEquals("ST-123", casResult.getCredentials());
|
||||
assertTrue(casResult.getAuthorities().contains(new SimpleGrantedAuthority("ROLE_A")));
|
||||
assertTrue(casResult.getAuthorities().contains(new SimpleGrantedAuthority("ROLE_B")));
|
||||
assertTrue(casResult.getAuthorities().contains(new GrantedAuthorityImpl("ROLE_A")));
|
||||
assertTrue(casResult.getAuthorities().contains(new GrantedAuthorityImpl("ROLE_B")));
|
||||
assertEquals(cap.getKey().hashCode(), casResult.getKeyHash());
|
||||
assertEquals("details", casResult.getDetails());
|
||||
|
||||
@@ -151,87 +149,6 @@ public class CasAuthenticationProviderTests {
|
||||
assertEquals("ST-456", newResult.getCredentials());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticateAllNullService() throws Exception {
|
||||
String serviceUrl = "https://service/context";
|
||||
ServiceAuthenticationDetails details = mock(ServiceAuthenticationDetails.class);
|
||||
when(details.getServiceUrl()).thenReturn(serviceUrl);
|
||||
TicketValidator validator = mock(TicketValidator.class);
|
||||
when(validator.validate(any(String.class),any(String.class))).thenReturn(new AssertionImpl("rod"));
|
||||
|
||||
ServiceProperties serviceProperties = makeServiceProperties();
|
||||
serviceProperties.setAuthenticateAllArtifacts(true);
|
||||
|
||||
CasAuthenticationProvider cap = new CasAuthenticationProvider();
|
||||
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
|
||||
cap.setKey("qwerty");
|
||||
|
||||
cap.setTicketValidator(validator);
|
||||
cap.setServiceProperties(serviceProperties);
|
||||
cap.afterPropertiesSet();
|
||||
|
||||
String ticket = "ST-456";
|
||||
UsernamePasswordAuthenticationToken token =
|
||||
new UsernamePasswordAuthenticationToken(CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER, ticket);
|
||||
|
||||
Authentication result = cap.authenticate(token);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticateAllAuthenticationIsSuccessful() throws Exception {
|
||||
String serviceUrl = "https://service/context";
|
||||
ServiceAuthenticationDetails details = mock(ServiceAuthenticationDetails.class);
|
||||
when(details.getServiceUrl()).thenReturn(serviceUrl);
|
||||
TicketValidator validator = mock(TicketValidator.class);
|
||||
when(validator.validate(any(String.class),any(String.class))).thenReturn(new AssertionImpl("rod"));
|
||||
|
||||
ServiceProperties serviceProperties = makeServiceProperties();
|
||||
serviceProperties.setAuthenticateAllArtifacts(true);
|
||||
|
||||
CasAuthenticationProvider cap = new CasAuthenticationProvider();
|
||||
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
|
||||
cap.setKey("qwerty");
|
||||
|
||||
cap.setTicketValidator(validator);
|
||||
cap.setServiceProperties(serviceProperties);
|
||||
cap.afterPropertiesSet();
|
||||
|
||||
String ticket = "ST-456";
|
||||
UsernamePasswordAuthenticationToken token =
|
||||
new UsernamePasswordAuthenticationToken(CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER, ticket);
|
||||
|
||||
Authentication result = cap.authenticate(token);
|
||||
verify(validator).validate(ticket, serviceProperties.getService());
|
||||
|
||||
serviceProperties.setAuthenticateAllArtifacts(true);
|
||||
result = cap.authenticate(token);
|
||||
verify(validator,times(2)).validate(ticket, serviceProperties.getService());
|
||||
|
||||
token.setDetails(details);
|
||||
result = cap.authenticate(token);
|
||||
verify(validator).validate(ticket, serviceUrl);
|
||||
|
||||
serviceProperties.setAuthenticateAllArtifacts(false);
|
||||
serviceProperties.setService(null);
|
||||
cap.setServiceProperties(serviceProperties);
|
||||
cap.afterPropertiesSet();
|
||||
result = cap.authenticate(token);
|
||||
verify(validator,times(2)).validate(ticket, serviceUrl);
|
||||
|
||||
token.setDetails(new WebAuthenticationDetails(new MockHttpServletRequest()));
|
||||
try {
|
||||
cap.authenticate(token);
|
||||
fail("Expected Exception");
|
||||
}catch(IllegalStateException success) {}
|
||||
|
||||
cap.setServiceProperties(null);
|
||||
cap.afterPropertiesSet();
|
||||
try {
|
||||
cap.authenticate(token);
|
||||
fail("Expected Exception");
|
||||
}catch(IllegalStateException success) {}
|
||||
}
|
||||
|
||||
@Test(expected = BadCredentialsException.class)
|
||||
public void missingTicketIdIsDetected() throws Exception {
|
||||
CasAuthenticationProvider cap = new CasAuthenticationProvider();
|
||||
@@ -338,7 +255,8 @@ public class CasAuthenticationProviderTests {
|
||||
cap.setServiceProperties(makeServiceProperties());
|
||||
cap.afterPropertiesSet();
|
||||
|
||||
TestingAuthenticationToken token = new TestingAuthenticationToken("user", "password", "ROLE_A");
|
||||
TestingAuthenticationToken token = new TestingAuthenticationToken("user", "password",
|
||||
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_A")});
|
||||
assertFalse(cap.supports(TestingAuthenticationToken.class));
|
||||
|
||||
// Try it anyway
|
||||
|
||||
+6
-5
@@ -15,18 +15,19 @@
|
||||
|
||||
package org.springframework.security.cas.authentication;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.jasig.cas.client.validation.Assertion;
|
||||
import org.jasig.cas.client.validation.AssertionImpl;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.authority.GrantedAuthorityImpl;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Tests {@link CasAuthenticationToken}.
|
||||
*
|
||||
@@ -107,8 +108,8 @@ public class CasAuthenticationTokenTests extends TestCase {
|
||||
assertEquals("key".hashCode(), token.getKeyHash());
|
||||
assertEquals(makeUserDetails(), token.getPrincipal());
|
||||
assertEquals("Password", token.getCredentials());
|
||||
assertTrue(token.getAuthorities().contains(new SimpleGrantedAuthority("ROLE_ONE")));
|
||||
assertTrue(token.getAuthorities().contains(new SimpleGrantedAuthority("ROLE_TWO")));
|
||||
assertTrue(token.getAuthorities().contains(new GrantedAuthorityImpl("ROLE_ONE")));
|
||||
assertTrue(token.getAuthorities().contains(new GrantedAuthorityImpl("ROLE_TWO")));
|
||||
assertEquals(assertion, token.getAssertion());
|
||||
assertEquals(makeUserDetails().getUsername(), token.getUserDetails().getUsername());
|
||||
}
|
||||
|
||||
-66
@@ -1,66 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.cas.authentication;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
|
||||
/**
|
||||
* Tests {@link org.springframework.security.cas.authentication.SpringCacheBasedTicketCache}.
|
||||
*
|
||||
* @author Marten Deinum
|
||||
* @since 3.2
|
||||
*/
|
||||
public class SpringCacheBasedTicketCacheTests extends AbstractStatelessTicketCacheTests {
|
||||
private static CacheManager cacheManager;
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
@BeforeClass
|
||||
public static void initCacheManaer() {
|
||||
cacheManager = new ConcurrentMapCacheManager();
|
||||
cacheManager.getCache("castickets");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCacheOperation() throws Exception {
|
||||
SpringCacheBasedTicketCache cache = new SpringCacheBasedTicketCache(cacheManager.getCache("castickets"));
|
||||
|
||||
final CasAuthenticationToken token = getToken();
|
||||
|
||||
// Check it gets stored in the cache
|
||||
cache.putTicketInCache(token);
|
||||
assertEquals(token, cache.getByTicketId("ST-0-ER94xMJmn6pha35CQRoZ"));
|
||||
|
||||
// Check it gets removed from the cache
|
||||
cache.removeTicketFromCache(getToken());
|
||||
assertNull(cache.getByTicketId("ST-0-ER94xMJmn6pha35CQRoZ"));
|
||||
|
||||
// Check it doesn't return values for null or unknown service tickets
|
||||
assertNull(cache.getByTicketId(null));
|
||||
assertNull(cache.getByTicketId("UNKNOWN_SERVICE_TICKET"));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testStartupDetectsMissingCache() throws Exception {
|
||||
new SpringCacheBasedTicketCache(null);
|
||||
}
|
||||
}
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
package org.springframework.security.cas.userdetails;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.jasig.cas.client.authentication.AttributePrincipal;
|
||||
import org.jasig.cas.client.validation.Assertion;
|
||||
import org.junit.Test;
|
||||
import org.springframework.security.cas.authentication.CasAssertionAuthenticationToken;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
public class GrantedAuthorityFromAssertionAttributesUserDetailsServiceTests {
|
||||
|
||||
@Test
|
||||
public void correctlyExtractsNamedAttributesFromAssertionAndConvertsThemToAuthorities() {
|
||||
GrantedAuthorityFromAssertionAttributesUserDetailsService uds =
|
||||
new GrantedAuthorityFromAssertionAttributesUserDetailsService(new String[] {"a", "b", "c", "d"});
|
||||
uds.setConvertToUpperCase(false);
|
||||
Assertion assertion = mock(Assertion.class);
|
||||
AttributePrincipal principal = mock(AttributePrincipal.class);
|
||||
Map<String, Object> attributes = new HashMap<String, Object>();
|
||||
attributes.put("a", Arrays.asList("role_a1", "role_a2"));
|
||||
attributes.put("b", "role_b");
|
||||
attributes.put("c", "role_c");
|
||||
attributes.put("d", null);
|
||||
attributes.put("someother", "unused");
|
||||
when(assertion.getPrincipal()).thenReturn(principal);
|
||||
when(principal.getAttributes()).thenReturn(attributes);
|
||||
when(principal.getName()).thenReturn("somebody");
|
||||
CasAssertionAuthenticationToken token = new CasAssertionAuthenticationToken(assertion, "ticket");
|
||||
UserDetails user = uds.loadUserDetails(token);
|
||||
Set<String> roles = AuthorityUtils.authorityListToSet(user.getAuthorities());
|
||||
assertTrue(roles.size() == 4);
|
||||
assertTrue(roles.contains("role_a1"));
|
||||
assertTrue(roles.contains("role_a2"));
|
||||
assertTrue(roles.contains("role_b"));
|
||||
assertTrue(roles.contains("role_c"));
|
||||
}
|
||||
}
|
||||
+4
-88
@@ -16,34 +16,18 @@
|
||||
package org.springframework.security.cas.web;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage;
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.security.authentication.AnonymousAuthenticationToken;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.cas.ServiceProperties;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||
import org.springframework.security.web.authentication.NullRememberMeServices;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
|
||||
/**
|
||||
@@ -55,23 +39,15 @@ import org.springframework.util.ReflectionUtils;
|
||||
public class CasAuthenticationFilterTests {
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGettersSetters() {
|
||||
public void testGetters() {
|
||||
CasAuthenticationFilter filter = new CasAuthenticationFilter();
|
||||
assertEquals("/j_spring_cas_security_check", filter.getFilterProcessesUrl());
|
||||
filter.setProxyGrantingTicketStorage(mock(ProxyGrantingTicketStorage.class));
|
||||
filter.setProxyReceptorUrl("/someurl");
|
||||
filter.setServiceProperties(new ServiceProperties());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNormalOperation() throws Exception {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/j_spring_cas_security_check");
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.addParameter("ticket", "ST-0-ER94xMJmn6pha35CQRoZ");
|
||||
|
||||
CasAuthenticationFilter filter = new CasAuthenticationFilter();
|
||||
@@ -81,8 +57,6 @@ public class CasAuthenticationFilterTests {
|
||||
}
|
||||
});
|
||||
|
||||
assertTrue(filter.requiresAuthentication(request, new MockHttpServletResponse()));
|
||||
|
||||
Authentication result = filter.attemptAuthentication(request, new MockHttpServletResponse());
|
||||
assertTrue(result != null);
|
||||
}
|
||||
@@ -125,31 +99,6 @@ public class CasAuthenticationFilterTests {
|
||||
assertFalse(filter.requiresAuthentication(request, response));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequiresAuthenticationAuthAll() {
|
||||
ServiceProperties properties = new ServiceProperties();
|
||||
properties.setAuthenticateAllArtifacts(true);
|
||||
|
||||
CasAuthenticationFilter filter = new CasAuthenticationFilter();
|
||||
filter.setServiceProperties(properties);
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
|
||||
request.setRequestURI(filter.getFilterProcessesUrl());
|
||||
assertTrue(filter.requiresAuthentication(request, response));
|
||||
|
||||
request.setRequestURI("/other");
|
||||
assertFalse(filter.requiresAuthentication(request, response));
|
||||
request.setParameter(properties.getArtifactParameter(), "value");
|
||||
assertTrue(filter.requiresAuthentication(request, response));
|
||||
SecurityContextHolder.getContext().setAuthentication(new AnonymousAuthenticationToken("key", "principal", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")));
|
||||
assertTrue(filter.requiresAuthentication(request, response));
|
||||
SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken("un", "principal", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")));
|
||||
assertTrue(filter.requiresAuthentication(request, response));
|
||||
SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken("un", "principal", "ROLE_ANONYMOUS"));
|
||||
assertFalse(filter.requiresAuthentication(request, response));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAuthenticateProxyUrl() throws Exception {
|
||||
CasAuthenticationFilter filter = new CasAuthenticationFilter();
|
||||
@@ -162,42 +111,9 @@ public class CasAuthenticationFilterTests {
|
||||
assertNull(filter.attemptAuthentication(request, response));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoFilterAuthenticateAll() throws Exception {
|
||||
AuthenticationSuccessHandler successHandler = mock(AuthenticationSuccessHandler.class);
|
||||
AuthenticationManager manager = mock(AuthenticationManager.class);
|
||||
Authentication authentication = new TestingAuthenticationToken("un", "pwd","ROLE_USER");
|
||||
when(manager.authenticate(any(Authentication.class))).thenReturn(authentication);
|
||||
ServiceProperties serviceProperties = new ServiceProperties();
|
||||
serviceProperties.setAuthenticateAllArtifacts(true);
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.setParameter("ticket", "ST-1-123");
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
FilterChain chain = mock(FilterChain.class);
|
||||
|
||||
CasAuthenticationFilter filter = new CasAuthenticationFilter();
|
||||
filter.setServiceProperties(serviceProperties);
|
||||
filter.setAuthenticationSuccessHandler(successHandler);
|
||||
filter.setProxyGrantingTicketStorage(mock(ProxyGrantingTicketStorage.class));
|
||||
filter.setAuthenticationManager(manager);
|
||||
filter.afterPropertiesSet();
|
||||
|
||||
filter.doFilter(request,response,chain);
|
||||
assertFalse("Authentication should not be null",SecurityContextHolder.getContext().getAuthentication() == null);
|
||||
verify(chain).doFilter(request, response);
|
||||
verifyZeroInteractions(successHandler);
|
||||
|
||||
// validate for when the filterProcessUrl matches
|
||||
filter.setFilterProcessesUrl(request.getRequestURI());
|
||||
SecurityContextHolder.clearContext();
|
||||
filter.doFilter(request,response,chain);
|
||||
verifyNoMoreInteractions(chain);
|
||||
verify(successHandler).onAuthenticationSuccess(request, response, authentication);
|
||||
}
|
||||
|
||||
// SEC-1592
|
||||
@Test
|
||||
public void testChainNotInvokedForProxyReceptor() throws Exception {
|
||||
public void testChainNotInvokedForProxy() throws Exception {
|
||||
CasAuthenticationFilter filter = new CasAuthenticationFilter();
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
@@ -210,4 +126,4 @@ public class CasAuthenticationFilterTests {
|
||||
filter.doFilter(request,response,chain);
|
||||
verifyZeroInteractions(chain);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+21
-36
@@ -15,55 +15,40 @@
|
||||
|
||||
package org.springframework.security.cas.web;
|
||||
|
||||
import static junit.framework.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.security.cas.SamlServiceProperties;
|
||||
import org.springframework.security.cas.ServiceProperties;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
|
||||
/**
|
||||
* Tests {@link ServiceProperties}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
public class ServicePropertiesTests {
|
||||
public class ServicePropertiesTests extends TestCase {
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
@Test(expected=IllegalArgumentException.class)
|
||||
public void detectsMissingService() throws Exception {
|
||||
public void testDetectsMissingLoginFormUrl() throws Exception {
|
||||
ServiceProperties sp = new ServiceProperties();
|
||||
sp.afterPropertiesSet();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void allowNullServiceWhenAuthenticateAllTokens() throws Exception {
|
||||
ServiceProperties sp = new ServiceProperties();
|
||||
sp.setAuthenticateAllArtifacts(true);
|
||||
sp.afterPropertiesSet();
|
||||
sp.setAuthenticateAllArtifacts(false);
|
||||
try {
|
||||
sp.afterPropertiesSet();
|
||||
fail("Expected Exception");
|
||||
}catch(IllegalArgumentException success) {}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGettersSetters() throws Exception {
|
||||
ServiceProperties[] sps = {new ServiceProperties(), new SamlServiceProperties()};
|
||||
for(ServiceProperties sp : sps) {
|
||||
sp.setSendRenew(false);
|
||||
assertFalse(sp.isSendRenew());
|
||||
sp.setSendRenew(true);
|
||||
assertTrue(sp.isSendRenew());
|
||||
sp.setArtifactParameter("notticket");
|
||||
assertEquals("notticket", sp.getArtifactParameter());
|
||||
sp.setServiceParameter("notservice");
|
||||
assertEquals("notservice", sp.getServiceParameter());
|
||||
|
||||
sp.setService("https://mycompany.com/service");
|
||||
assertEquals("https://mycompany.com/service", sp.getService());
|
||||
|
||||
sp.afterPropertiesSet();
|
||||
fail("Should have thrown IllegalArgumentException");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertEquals("service must be specified.", expected.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void testGettersSetters() throws Exception {
|
||||
ServiceProperties sp = new ServiceProperties();
|
||||
sp.setSendRenew(false);
|
||||
assertFalse(sp.isSendRenew());
|
||||
sp.setSendRenew(true);
|
||||
assertTrue(sp.isSendRenew());
|
||||
|
||||
sp.setService("https://mycompany.com/service");
|
||||
assertEquals("https://mycompany.com/service", sp.getService());
|
||||
|
||||
sp.afterPropertiesSet();
|
||||
}
|
||||
}
|
||||
|
||||
-88
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.cas.web.authentication;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.security.cas.ServiceProperties;
|
||||
import org.springframework.security.web.util.UrlUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public class DefaultServiceAuthenticationDetailsTests {
|
||||
private DefaultServiceAuthenticationDetails details;
|
||||
private MockHttpServletRequest request;
|
||||
private Pattern artifactPattern;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
request = new MockHttpServletRequest();
|
||||
request.setScheme("https");
|
||||
request.setServerName("localhost");
|
||||
request.setServerPort(8443);
|
||||
request.setRequestURI("/cas-sample/secure/");
|
||||
artifactPattern = DefaultServiceAuthenticationDetails.createArtifactPattern(ServiceProperties.DEFAULT_CAS_ARTIFACT_PARAMETER);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getServiceUrlNullQuery() throws Exception {
|
||||
details = new DefaultServiceAuthenticationDetails(request,artifactPattern);
|
||||
assertEquals(UrlUtils.buildFullRequestUrl(request),details.getServiceUrl());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getServiceUrlTicketOnlyParam() {
|
||||
request.setQueryString("ticket=123");
|
||||
details = new DefaultServiceAuthenticationDetails(request,artifactPattern);
|
||||
String serviceUrl = details.getServiceUrl();
|
||||
request.setQueryString(null);
|
||||
assertEquals(UrlUtils.buildFullRequestUrl(request),serviceUrl);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getServiceUrlTicketFirstMultiParam() {
|
||||
request.setQueryString("ticket=123&other=value");
|
||||
details = new DefaultServiceAuthenticationDetails(request,artifactPattern);
|
||||
String serviceUrl = details.getServiceUrl();
|
||||
request.setQueryString("other=value");
|
||||
assertEquals(UrlUtils.buildFullRequestUrl(request),serviceUrl);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getServiceUrlTicketLastMultiParam() {
|
||||
request.setQueryString("other=value&ticket=123");
|
||||
details = new DefaultServiceAuthenticationDetails(request,artifactPattern);
|
||||
String serviceUrl = details.getServiceUrl();
|
||||
request.setQueryString("other=value");
|
||||
assertEquals(UrlUtils.buildFullRequestUrl(request),serviceUrl);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getServiceUrlTicketMiddleMultiParam() {
|
||||
request.setQueryString("other=value&ticket=123&last=this");
|
||||
details = new DefaultServiceAuthenticationDetails(request,artifactPattern);
|
||||
String serviceUrl = details.getServiceUrl();
|
||||
request.setQueryString("other=value&last=this");
|
||||
assertEquals(UrlUtils.buildFullRequestUrl(request),serviceUrl);
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,6 @@ Import-Template:
|
||||
org.springframework.security.authentication.*;version="${secRange}",
|
||||
org.springframework.security.web.*;version="${secRange}",
|
||||
org.springframework.beans.factory;version="${springRange}",
|
||||
org.springframework.cache.*;version="${springRange}";resolution:=optional,
|
||||
org.springframework.context.*;version="${springRange}",
|
||||
org.springframework.dao;version="${springRange}",
|
||||
org.springframework.util;version="${springRange}",
|
||||
|
||||
+4
-42
@@ -1,56 +1,30 @@
|
||||
// Config Module build file
|
||||
|
||||
apply plugin: 'groovy'
|
||||
apply plugin: 'trang'
|
||||
|
||||
compileTestJava.dependsOn(':spring-security-core:compileTestJava')
|
||||
|
||||
configurations {
|
||||
// GRADLE-1124
|
||||
compile.extendsFrom = []
|
||||
testCompile.extendsFrom groovy
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// NB: Don't add other compile time dependencies to the config module as this breaks tooling
|
||||
compile project(':spring-security-core'),
|
||||
project(':spring-security-web'),
|
||||
project(':spring-security-openid'),
|
||||
project(':spring-security-ldap'),
|
||||
"org.aspectj:aspectjweaver:$aspectjVersion",
|
||||
'aopalliance:aopalliance:1.0',
|
||||
"org.aspectj:aspectjweaver:$aspectjVersion",
|
||||
"org.springframework:spring-aop:$springVersion",
|
||||
"org.springframework:spring-context:$springVersion",
|
||||
"org.springframework:spring-web:$springVersion",
|
||||
"org.springframework:spring-beans:$springVersion",
|
||||
"org.springframework:spring-jdbc:$springVersion",
|
||||
"org.springframework:spring-tx:$springVersion",
|
||||
"org.springframework.ldap:spring-ldap-core:$springLdapVersion"
|
||||
compile('org.openid4java:openid4java-nodeps:0.9.6') {
|
||||
exclude group: 'com.google.code.guice', module: 'guice'
|
||||
}
|
||||
compile 'com.google.inject:guice:2.0'
|
||||
compile apacheds_libs
|
||||
"org.springframework:spring-tx:$springVersion"
|
||||
|
||||
provided "org.apache.tomcat:tomcat-servlet-api:$servletApiVersion"
|
||||
|
||||
groovy 'org.codehaus.groovy:groovy:1.8.7'
|
||||
provided "javax.servlet:servlet-api:2.5"
|
||||
|
||||
testCompile project(':spring-security-ldap'),
|
||||
project(':spring-security-openid'),
|
||||
project(':spring-security-cas'),
|
||||
project(':spring-security-core').sourceSets.test.output,
|
||||
'javax.annotation:jsr250-api:1.0',
|
||||
"org.springframework.ldap:spring-ldap-core:$springLdapVersion",
|
||||
"org.springframework:spring-expression:$springVersion",
|
||||
"org.springframework:spring-jdbc:$springVersion",
|
||||
"org.springframework:spring-orm:$springVersion",
|
||||
"org.springframework:spring-tx:$springVersion",
|
||||
'org.spockframework:spock-core:0.6-groovy-1.8',
|
||||
"org.slf4j:jcl-over-slf4j:$slf4jVersion",
|
||||
"org.hibernate.javax.persistence:hibernate-jpa-2.0-api:1.0.1.Final",
|
||||
"org.hibernate:hibernate-entitymanager:4.1.0.Final",
|
||||
powerMockDependencies
|
||||
"org.slf4j:jcl-over-slf4j:$slf4jVersion"
|
||||
testCompile('org.openid4java:openid4java-nodeps:0.9.6') {
|
||||
exclude group: 'com.google.code.guice', module: 'guice'
|
||||
}
|
||||
@@ -60,18 +34,6 @@ dependencies {
|
||||
"cglib:cglib-nodep:2.2"
|
||||
}
|
||||
|
||||
test {
|
||||
inputs.file file("$rootDir/docs/manual/src/docbook/appendix-namespace.xml")
|
||||
}
|
||||
|
||||
integrationTest {
|
||||
systemProperties['apacheDSWorkDir'] = "${buildDir}/apacheDSWork"
|
||||
}
|
||||
|
||||
rncToXsd {
|
||||
rncDir = file('src/main/resources/org/springframework/security/config/')
|
||||
xsdDir = rncDir
|
||||
xslFile = new File(rncDir, 'spring-security.xsl')
|
||||
}
|
||||
|
||||
build.dependsOn rncToXsd
|
||||
Executable
+11
@@ -0,0 +1,11 @@
|
||||
#! /bin/sh
|
||||
|
||||
pushd src/main/resources/org/springframework/security/config/
|
||||
|
||||
echo "Converting rnc file to xsd ..."
|
||||
java -jar ~/bin/trang.jar spring-security-3.0.4.rnc spring-security-3.0.4.xsd
|
||||
|
||||
echo "Applying XSL transformation to xsd ..."
|
||||
xsltproc --output spring-security-3.0.4.xsd spring-security.xsl spring-security-3.0.4.xsd
|
||||
|
||||
popd
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
<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.springframework.security</groupId>
|
||||
<artifactId>spring-security-parent</artifactId>
|
||||
<version>@pomVersion@</version>
|
||||
</parent>
|
||||
<packaging>jar</packaging>
|
||||
<artifactId>spring-security-config</artifactId>
|
||||
<name>Spring Security - Namespace Configuration Module</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-web</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<classifier>tests</classifier>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-web</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<classifier>tests</classifier>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-openid</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-ldap</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-jdbc</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.directory.server</groupId>
|
||||
<artifactId>apacheds-core</artifactId>
|
||||
<version>1.5.5</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.directory.server</groupId>
|
||||
<artifactId>apacheds-server-jndi</artifactId>
|
||||
<version>1.5.5</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
<version>1.6.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>hsqldb</groupId>
|
||||
<artifactId>hsqldb</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cglib</groupId>
|
||||
<artifactId>cglib-nodep</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.annotation</groupId>
|
||||
<artifactId>jsr250-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>servlet-api</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.springsource.bundlor</groupId>
|
||||
<artifactId>com.springsource.bundlor.maven</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
-153
@@ -1,153 +0,0 @@
|
||||
package org.springframework.security.config.ldap
|
||||
|
||||
import java.text.MessageFormat
|
||||
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer
|
||||
import org.springframework.security.config.AbstractXmlConfigTests
|
||||
import org.springframework.security.config.BeanIds
|
||||
import org.springframework.security.util.FieldUtils
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
import org.springframework.context.ApplicationContextException
|
||||
import org.springframework.security.core.AuthenticationException
|
||||
import org.springframework.security.ldap.userdetails.InetOrgPersonContextMapper
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
class LdapProviderBeanDefinitionParserTests extends AbstractXmlConfigTests {
|
||||
|
||||
// SEC-1182
|
||||
def multipleProvidersAreSupported() {
|
||||
xml.'ldap-server'(url: 'ldap://blah:389/dc=blah')
|
||||
xml.'authentication-manager'() {
|
||||
'ldap-authentication-provider'('group-search-filter': 'member={0}')
|
||||
'ldap-authentication-provider'('group-search-filter': 'uniqueMember={0}')
|
||||
}
|
||||
|
||||
createAppContext('')
|
||||
|
||||
def providers = appContext.getBean(BeanIds.AUTHENTICATION_MANAGER).providers
|
||||
|
||||
expect:
|
||||
|
||||
providers.size() == 2
|
||||
providers[0].authoritiesPopulator.groupSearchFilter == "member={0}"
|
||||
providers[1].authoritiesPopulator.groupSearchFilter == "uniqueMember={0}"
|
||||
}
|
||||
|
||||
|
||||
def simpleProviderAuthenticatesCorrectly() {
|
||||
xml.'ldap-server'()
|
||||
xml.'authentication-manager'{
|
||||
'ldap-authentication-provider'('group-search-filter':'member={0}')
|
||||
}
|
||||
|
||||
createAppContext('')
|
||||
|
||||
def am = appContext.getBean(BeanIds.AUTHENTICATION_MANAGER)
|
||||
|
||||
when:
|
||||
def auth = am.authenticate(new UsernamePasswordAuthenticationToken("ben", "benspassword"))
|
||||
def ben = auth.principal;
|
||||
|
||||
then:
|
||||
ben.authorities.size() == 3
|
||||
}
|
||||
|
||||
def missingServerEltCausesConfigException() {
|
||||
xml.'authentication-manager'{
|
||||
'ldap-authentication-provider'()
|
||||
}
|
||||
|
||||
when:
|
||||
createAppContext('')
|
||||
|
||||
then:
|
||||
thrown(ApplicationContextException)
|
||||
}
|
||||
|
||||
def supportsPasswordComparisonAuthentication() {
|
||||
xml.'ldap-server'()
|
||||
xml.'authentication-manager'{
|
||||
'ldap-authentication-provider'('user-dn-pattern': 'uid={0},ou=people')
|
||||
'password-compare'
|
||||
}
|
||||
createAppContext('')
|
||||
def am = appContext.getBean(BeanIds.AUTHENTICATION_MANAGER)
|
||||
|
||||
when:
|
||||
def auth = am.authenticate(new UsernamePasswordAuthenticationToken("ben", "benspassword"))
|
||||
|
||||
then:
|
||||
auth != null
|
||||
notThrown(AuthenticationException)
|
||||
}
|
||||
|
||||
def supportsPasswordComparisonAuthenticationWithHashAttribute() {
|
||||
xml.'ldap-server'()
|
||||
xml.'authentication-manager'{
|
||||
'ldap-authentication-provider'('user-dn-pattern': 'uid={0},ou=people') {
|
||||
'password-compare'('password-attribute': 'uid', hash: 'plaintext')
|
||||
}
|
||||
}
|
||||
createAppContext('')
|
||||
def am = appContext.getBean(BeanIds.AUTHENTICATION_MANAGER)
|
||||
|
||||
when:
|
||||
def auth = am.authenticate(new UsernamePasswordAuthenticationToken("ben", "ben"))
|
||||
|
||||
then:
|
||||
auth != null
|
||||
notThrown(AuthenticationException)
|
||||
|
||||
}
|
||||
|
||||
def supportsPasswordComparisonAuthenticationWithPasswordEncoder() {
|
||||
xml.'ldap-server'()
|
||||
xml.'authentication-manager'{
|
||||
'ldap-authentication-provider'('user-dn-pattern': 'uid={0},ou=people') {
|
||||
'password-compare'('password-attribute': 'uid') {
|
||||
'password-encoder'(hash: 'plaintext')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
createAppContext('')
|
||||
def am = appContext.getBean(BeanIds.AUTHENTICATION_MANAGER)
|
||||
|
||||
when:
|
||||
def auth = am.authenticate(new UsernamePasswordAuthenticationToken("ben", "ben"))
|
||||
|
||||
then:
|
||||
auth != null
|
||||
notThrown(AuthenticationException)
|
||||
}
|
||||
|
||||
def inetOrgContextMapperIsSupported() {
|
||||
xml.'ldap-server'(url: 'ldap://127.0.0.1:343/dc=springframework,dc=org')
|
||||
xml.'authentication-manager'{
|
||||
'ldap-authentication-provider'('user-details-class' :'inetOrgPerson')
|
||||
}
|
||||
createAppContext('')
|
||||
|
||||
expect:
|
||||
appContext.getBean(BeanIds.AUTHENTICATION_MANAGER).providers[0].userDetailsContextMapper instanceof InetOrgPersonContextMapper
|
||||
}
|
||||
|
||||
def ldapAuthenticationProviderWorksWithPlaceholders() {
|
||||
System.setProperty('udp','people')
|
||||
System.setProperty('gsf','member')
|
||||
|
||||
xml.'ldap-server'()
|
||||
xml.'authentication-manager'{
|
||||
'ldap-authentication-provider'('user-dn-pattern':'uid={0},ou=${udp}','group-search-filter':'${gsf}={0}')
|
||||
}
|
||||
bean(PropertyPlaceholderConfigurer.class.name, PropertyPlaceholderConfigurer.class)
|
||||
|
||||
createAppContext('')
|
||||
def provider = this.appContext.getBean(BeanIds.AUTHENTICATION_MANAGER).providers[0]
|
||||
|
||||
expect:
|
||||
[new MessageFormat("uid={0},ou=people")] == FieldUtils.getFieldValue(provider,"authenticator.userDnFormat")
|
||||
"member={0}" == FieldUtils.getFieldValue(provider, "authoritiesPopulator.groupSearchFilter")
|
||||
}
|
||||
}
|
||||
-177
@@ -1,177 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.authentication.ldap
|
||||
|
||||
import org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.ldap.core.ContextSource;
|
||||
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.AuthenticationProvider
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.SecurityBuilder;
|
||||
import org.springframework.security.config.annotation.authentication.AuthenticationManagerBuilder
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
|
||||
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
|
||||
import org.springframework.security.ldap.server.ApacheDSContainer;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
class LdapAuthenticationProviderBuilderSecurityBuilderTests extends BaseSpringSpec {
|
||||
def "default configuration"() {
|
||||
when:
|
||||
loadConfig(DefaultLdapConfig)
|
||||
LdapAuthenticationProvider provider = ldapProvider()
|
||||
then:
|
||||
provider.authoritiesPopulator.groupRoleAttribute == "cn"
|
||||
provider.authoritiesPopulator.groupSearchBase == ""
|
||||
provider.authoritiesPopulator.groupSearchFilter == "(uniqueMember={0})"
|
||||
ReflectionTestUtils.getField(provider,"authoritiesMapper").prefix == "ROLE_"
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class DefaultLdapConfig extends BaseLdapProviderConfig {
|
||||
protected void registerAuthentication(
|
||||
AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.ldapAuthentication()
|
||||
.contextSource(contextSource())
|
||||
}
|
||||
}
|
||||
|
||||
def "group roles custom"() {
|
||||
when:
|
||||
loadConfig(GroupRolesConfig)
|
||||
LdapAuthenticationProvider provider = ldapProvider()
|
||||
then:
|
||||
provider.authoritiesPopulator.groupRoleAttribute == "group"
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class GroupRolesConfig extends BaseLdapProviderConfig {
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.ldapAuthentication()
|
||||
.contextSource(contextSource())
|
||||
.groupRoleAttribute("group")
|
||||
}
|
||||
}
|
||||
|
||||
def "group search custom"() {
|
||||
when:
|
||||
loadConfig(GroupSearchConfig)
|
||||
LdapAuthenticationProvider provider = ldapProvider()
|
||||
then:
|
||||
provider.authoritiesPopulator.groupSearchFilter == "ou=groupName"
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class GroupSearchConfig extends BaseLdapProviderConfig {
|
||||
protected void registerAuthentication(
|
||||
AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.ldapAuthentication()
|
||||
.contextSource(contextSource())
|
||||
.groupSearchFilter("ou=groupName");
|
||||
}
|
||||
}
|
||||
|
||||
def "role prefix custom"() {
|
||||
when:
|
||||
loadConfig(RolePrefixConfig)
|
||||
LdapAuthenticationProvider provider = ldapProvider()
|
||||
then:
|
||||
ReflectionTestUtils.getField(provider,"authoritiesMapper").prefix == "role_"
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class RolePrefixConfig extends BaseLdapProviderConfig {
|
||||
protected void registerAuthentication(
|
||||
AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.ldapAuthentication()
|
||||
.contextSource(contextSource())
|
||||
.rolePrefix("role_")
|
||||
}
|
||||
}
|
||||
|
||||
def "bind authentication"() {
|
||||
when:
|
||||
loadConfig(BindAuthenticationConfig)
|
||||
AuthenticationManager auth = context.getBean(AuthenticationManager)
|
||||
then:
|
||||
auth
|
||||
auth.authenticate(new UsernamePasswordAuthenticationToken("admin","password")).authorities.collect { it.authority }.sort() == ["ROLE_ADMIN","ROLE_USER"]
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class BindAuthenticationConfig extends BaseLdapServerConfig {
|
||||
protected void registerAuthentication(
|
||||
AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.ldapAuthentication()
|
||||
.contextSource(contextSource())
|
||||
.groupSearchBase("ou=groups")
|
||||
.userDnPatterns("uid={0},ou=people");
|
||||
}
|
||||
}
|
||||
|
||||
def ldapProvider() {
|
||||
context.getBean(AuthenticationManager).providers[0]
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static abstract class BaseLdapServerConfig extends BaseLdapProviderConfig {
|
||||
@Bean
|
||||
public ApacheDSContainer ldapServer() throws Exception {
|
||||
ApacheDSContainer apacheDSContainer = new ApacheDSContainer("dc=springframework,dc=org", "classpath:/users.ldif");
|
||||
apacheDSContainer.setPort(33389);
|
||||
return apacheDSContainer;
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static abstract class BaseLdapProviderConfig {
|
||||
@Bean
|
||||
public AuthenticationManager authenticationManager() {
|
||||
AuthenticationManagerBuilder registry = new AuthenticationManagerBuilder();
|
||||
registerAuthentication(registry);
|
||||
return registry.build();
|
||||
}
|
||||
|
||||
protected abstract void registerAuthentication(
|
||||
AuthenticationManagerBuilder auth) throws Exception;
|
||||
|
||||
@Bean
|
||||
public BaseLdapPathContextSource contextSource() throws Exception {
|
||||
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(
|
||||
"ldap://127.0.0.1:33389/dc=springframework,dc=org")
|
||||
contextSource.userDn = "uid=admin,ou=system"
|
||||
contextSource.password = "secret"
|
||||
contextSource.afterPropertiesSet();
|
||||
return contextSource;
|
||||
}
|
||||
}
|
||||
}
|
||||
-68
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.authentication.ldap
|
||||
|
||||
import static org.springframework.security.config.annotation.authentication.ldap.NamespaceLdapAuthenticationProviderTestsConfigs.*
|
||||
|
||||
import org.springframework.ldap.core.support.BaseLdapPathContextSource
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.AuthenticationProvider
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.authentication.ldap.NamespaceLdapAuthenticationProviderTestsConfigs.LdapAuthenticationProviderConfig;
|
||||
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
|
||||
import org.springframework.security.ldap.authentication.PasswordComparisonAuthenticator;
|
||||
import org.springframework.security.ldap.userdetails.PersonContextMapper;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
class NamespaceLdapAuthenticationProviderTests extends BaseSpringSpec {
|
||||
def "ldap-authentication-provider"() {
|
||||
when:
|
||||
loadConfig(LdapAuthenticationProviderConfig)
|
||||
then:
|
||||
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("user","password")).authorities*.authority.sort() == ['ROLE_USER']
|
||||
}
|
||||
|
||||
def "ldap-authentication-provider custom"() {
|
||||
when:
|
||||
loadConfig(CustomLdapAuthenticationProviderConfig)
|
||||
LdapAuthenticationProvider provider = findAuthenticationProvider(LdapAuthenticationProvider)
|
||||
then:
|
||||
provider.authoritiesPopulator.groupRoleAttribute == "cn"
|
||||
provider.authoritiesPopulator.groupSearchBase == "ou=groups"
|
||||
provider.authoritiesPopulator.groupSearchFilter == "(member={0})"
|
||||
ReflectionTestUtils.getField(provider,"authoritiesMapper").prefix == "PREFIX_"
|
||||
provider.userDetailsContextMapper instanceof PersonContextMapper
|
||||
provider.authenticator.getUserDns("user") == ["uid=user,ou=people"]
|
||||
provider.authenticator.userSearch.searchBase == "ou=users"
|
||||
provider.authenticator.userSearch.searchFilter == "(uid={0})"
|
||||
}
|
||||
|
||||
def "ldap-authentication-provider password compare"() {
|
||||
when:
|
||||
loadConfig(PasswordCompareLdapConfig)
|
||||
LdapAuthenticationProvider provider = findAuthenticationProvider(LdapAuthenticationProvider)
|
||||
then:
|
||||
provider.authenticator instanceof PasswordComparisonAuthenticator
|
||||
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("user","password")).authorities*.authority.sort() == ['ROLE_USER']
|
||||
}
|
||||
}
|
||||
-84
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.authentication.ldap;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.authentication.encoding.PlaintextPasswordEncoder;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.ldap.userdetails.PersonContextMapper;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public class NamespaceLdapAuthenticationProviderTestsConfigs {
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class LdapAuthenticationProviderConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void registerAuthentication(
|
||||
AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.ldapAuthentication()
|
||||
.groupSearchBase("ou=groups")
|
||||
.userDnPatterns("uid={0},ou=people"); // ldap-server@user-dn-pattern
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class CustomLdapAuthenticationProviderConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void registerAuthentication(
|
||||
AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.ldapAuthentication()
|
||||
.groupRoleAttribute("cn") // ldap-authentication-provider@group-role-attribute
|
||||
.groupSearchBase("ou=groups") // ldap-authentication-provider@group-search-base
|
||||
.groupSearchFilter("(member={0})") // ldap-authentication-provider@group-search-filter
|
||||
.rolePrefix("PREFIX_") // ldap-authentication-provider@group-search-filter
|
||||
.userDetailsContextMapper(new PersonContextMapper()) // ldap-authentication-provider@user-context-mapper-ref / ldap-authentication-provider@user-details-class
|
||||
.userDnPatterns("uid={0},ou=people") // ldap-authentication-provider@user-dn-pattern
|
||||
.userSearchBase("ou=users") // ldap-authentication-provider@user-dn-pattern
|
||||
.userSearchFilter("(uid={0})") // ldap-authentication-provider@user-search-filter
|
||||
// .contextSource(contextSource) // ldap-authentication-provider@server-ref
|
||||
.contextSource()
|
||||
.ldif("classpath:user.ldif") // ldap-server@ldif
|
||||
.managerDn("uid=admin,ou=system") // ldap-server@manager-dn
|
||||
.managerPassword("secret") // ldap-server@manager-password
|
||||
.port(33399) // ldap-server@port
|
||||
.root("dc=springframework,dc=org") // ldap-server@root
|
||||
// .url("ldap://localhost:33389/dc-springframework,dc=org") this overrides root and port and is used for external
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class PasswordCompareLdapConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void registerAuthentication(
|
||||
AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.ldapAuthentication()
|
||||
.groupSearchBase("ou=groups")
|
||||
.userSearchFilter("(uid={0})")
|
||||
.passwordCompare()
|
||||
.passwordEncoder(new PlaintextPasswordEncoder()) // ldap-authentication-provider/password-compare/password-encoder@ref
|
||||
.passwordAttribute("userPassword"); // ldap-authentication-provider/password-compare@password-attribute
|
||||
}
|
||||
}
|
||||
}
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
package org.springframework.security.config.ldap;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.security.config.ldap.LdapProviderBeanDefinitionParser.*;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.ApplicationContextException;
|
||||
import org.springframework.security.authentication.ProviderManager;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.config.BeanIds;
|
||||
import org.springframework.security.config.util.InMemoryXmlApplicationContext;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.ldap.authentication.BindAuthenticator;
|
||||
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
|
||||
import org.springframework.security.ldap.authentication.PasswordComparisonAuthenticator;
|
||||
import org.springframework.security.ldap.userdetails.InetOrgPersonContextMapper;
|
||||
import org.springframework.security.ldap.userdetails.LdapUserDetailsImpl;
|
||||
import org.springframework.security.util.FieldUtils;
|
||||
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
public class LdapProviderBeanDefinitionParserTests {
|
||||
InMemoryXmlApplicationContext appCtx;
|
||||
|
||||
@After
|
||||
public void closeAppContext() {
|
||||
if (appCtx != null) {
|
||||
appCtx.close();
|
||||
appCtx = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void beanClassNamesAreCorrect() throws Exception {
|
||||
assertEquals(PROVIDER_CLASS, LdapAuthenticationProvider.class.getName());
|
||||
assertEquals(BIND_AUTH_CLASS, BindAuthenticator.class.getName());
|
||||
assertEquals(PASSWD_AUTH_CLASS, PasswordComparisonAuthenticator.class.getName());
|
||||
}
|
||||
|
||||
// SEC-1182
|
||||
@Test
|
||||
public void multipleProvidersAreSupported() throws Exception {
|
||||
setContext("<ldap-server url='ldap://blah:389/dc=blah'/>" +
|
||||
"<authentication-manager>" +
|
||||
" <ldap-authentication-provider group-search-filter='member={0}' />" +
|
||||
" <ldap-authentication-provider group-search-filter='uniqueMember={0}' />" +
|
||||
"</authentication-manager>");
|
||||
|
||||
ProviderManager authManager = (ProviderManager) appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER);
|
||||
|
||||
assertEquals(2, authManager.getProviders().size());
|
||||
assertEquals("member={0}", FieldUtils.getFieldValue(authManager.getProviders().get(0), "authoritiesPopulator.groupSearchFilter"));
|
||||
assertEquals("uniqueMember={0}", FieldUtils.getFieldValue(authManager.getProviders().get(1), "authoritiesPopulator.groupSearchFilter"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simpleProviderAuthenticatesCorrectly() {
|
||||
setContext("<ldap-server />" +
|
||||
"<authentication-manager>" +
|
||||
" <ldap-authentication-provider group-search-filter='member={0}' />" +
|
||||
"</authentication-manager>");
|
||||
|
||||
LdapAuthenticationProvider provider = getProvider();
|
||||
Authentication auth = provider.authenticate(new UsernamePasswordAuthenticationToken("ben", "benspassword"));
|
||||
LdapUserDetailsImpl ben = (LdapUserDetailsImpl) auth.getPrincipal();
|
||||
|
||||
assertEquals(3, ben.getAuthorities().size());
|
||||
}
|
||||
|
||||
@Test(expected = ApplicationContextException.class)
|
||||
public void missingServerEltCausesConfigException() {
|
||||
setContext(
|
||||
"<authentication-manager>" +
|
||||
" <ldap-authentication-provider />" +
|
||||
"</authentication-manager>");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsPasswordComparisonAuthentication() {
|
||||
setContext("<ldap-server /> " +
|
||||
"<authentication-manager>" +
|
||||
"<ldap-authentication-provider user-dn-pattern='uid={0},ou=people'>" +
|
||||
" <password-compare />" +
|
||||
"</ldap-authentication-provider>"+
|
||||
"</authentication-manager>");
|
||||
LdapAuthenticationProvider provider = getProvider();
|
||||
provider.authenticate(new UsernamePasswordAuthenticationToken("ben", "benspassword"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void supportsPasswordComparisonAuthenticationWithHashAttribute() {
|
||||
setContext("<ldap-server /> " +
|
||||
"<authentication-manager>" +
|
||||
"<ldap-authentication-provider user-dn-pattern='uid={0},ou=people'>" +
|
||||
" <password-compare password-attribute='uid' hash='plaintext'/>" +
|
||||
"</ldap-authentication-provider>" +
|
||||
"</authentication-manager>");
|
||||
LdapAuthenticationProvider provider = getProvider();
|
||||
provider.authenticate(new UsernamePasswordAuthenticationToken("ben", "ben"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsPasswordComparisonAuthenticationWithPasswordEncoder() {
|
||||
setContext("<ldap-server /> " +
|
||||
"<authentication-manager>" +
|
||||
"<ldap-authentication-provider user-dn-pattern='uid={0},ou=people'>" +
|
||||
" <password-compare password-attribute='uid'>" +
|
||||
" <password-encoder hash='plaintext'/>" +
|
||||
" </password-compare>" +
|
||||
"</ldap-authentication-provider>" +
|
||||
"</authentication-manager>");
|
||||
LdapAuthenticationProvider provider = getProvider();
|
||||
provider.authenticate(new UsernamePasswordAuthenticationToken("ben", "ben"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void detectsNonStandardServerId() {
|
||||
setContext("<ldap-server id='myServer'/> " +
|
||||
"<authentication-manager>" +
|
||||
" <ldap-authentication-provider />" +
|
||||
"</authentication-manager>");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void inetOrgContextMapperIsSupported() throws Exception {
|
||||
setContext(
|
||||
"<ldap-server id='someServer' url='ldap://127.0.0.1:343/dc=springframework,dc=org'/>" +
|
||||
"<authentication-manager>" +
|
||||
" <ldap-authentication-provider user-details-class='inetOrgPerson'/>" +
|
||||
"</authentication-manager>");
|
||||
LdapAuthenticationProvider provider = getProvider();
|
||||
assertTrue(FieldUtils.getFieldValue(provider, "userDetailsContextMapper") instanceof InetOrgPersonContextMapper);
|
||||
}
|
||||
|
||||
private void setContext(String context) {
|
||||
appCtx = new InMemoryXmlApplicationContext(context);
|
||||
}
|
||||
|
||||
private LdapAuthenticationProvider getProvider() {
|
||||
ProviderManager authManager = (ProviderManager) appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER);
|
||||
|
||||
assertEquals(1, authManager.getProviders().size());
|
||||
|
||||
LdapAuthenticationProvider provider = (LdapAuthenticationProvider) authManager.getProviders().get(0);
|
||||
return provider;
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<logger name="org.springframework.security" level="${sec.log.level}:-WARN"/>
|
||||
|
||||
<logger name="org.apache.directory" level="ERROR"/>
|
||||
<logger name="JdbmTable" level="INFO"/>
|
||||
<logger name="JdbmIndex" level="INFO"/>
|
||||
<logger name="org.apache.mina" level="WARN"/>
|
||||
|
||||
<root level="${root.level}:-WARN">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
|
||||
</configuration>
|
||||
@@ -1,42 +0,0 @@
|
||||
dn: ou=groups,dc=springframework,dc=org
|
||||
objectclass: top
|
||||
objectclass: organizationalUnit
|
||||
ou: groups
|
||||
|
||||
dn: ou=people,dc=springframework,dc=org
|
||||
objectclass: top
|
||||
objectclass: organizationalUnit
|
||||
ou: people
|
||||
|
||||
dn: uid=admin,ou=people,dc=springframework,dc=org
|
||||
objectclass: top
|
||||
objectclass: person
|
||||
objectclass: organizationalPerson
|
||||
objectclass: inetOrgPerson
|
||||
cn: Rod Johnson
|
||||
sn: Johnson
|
||||
uid: admin
|
||||
userPassword: password
|
||||
|
||||
dn: uid=user,ou=people,dc=springframework,dc=org
|
||||
objectclass: top
|
||||
objectclass: person
|
||||
objectclass: organizationalPerson
|
||||
objectclass: inetOrgPerson
|
||||
cn: Dianne Emu
|
||||
sn: Emu
|
||||
uid: user
|
||||
userPassword: password
|
||||
|
||||
dn: cn=user,ou=groups,dc=springframework,dc=org
|
||||
objectclass: top
|
||||
objectclass: groupOfNames
|
||||
cn: user
|
||||
uniqueMember: uid=admin,ou=people,dc=springframework,dc=org
|
||||
uniqueMember: uid=user,ou=people,dc=springframework,dc=org
|
||||
|
||||
dn: cn=admin,ou=groups,dc=springframework,dc=org
|
||||
objectclass: top
|
||||
objectclass: groupOfNames
|
||||
cn: admin
|
||||
uniqueMember: uid=admin,ou=people,dc=springframework,dc=org
|
||||
@@ -6,12 +6,11 @@ package org.springframework.security.config;
|
||||
* These are intended for internal use.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
public abstract class BeanIds {
|
||||
private static final String PREFIX = "org.springframework.security.";
|
||||
|
||||
/** The "global" AuthenticationManager instance, registered by the <authentication-manager> element */
|
||||
/** The "global" AuthenticationManager instance, registered by the <authentication-manager> element */
|
||||
public static final String AUTHENTICATION_MANAGER = PREFIX + "authenticationManager";
|
||||
|
||||
/** External alias for FilterChainProxy bean, for use in web.xml files */
|
||||
@@ -20,16 +19,12 @@ public abstract class BeanIds {
|
||||
public static final String CONTEXT_SOURCE_SETTING_POST_PROCESSOR = PREFIX + "contextSettingPostProcessor";
|
||||
|
||||
public static final String USER_DETAILS_SERVICE = PREFIX + "userDetailsService";
|
||||
public static final String USER_DETAILS_SERVICE_FACTORY = PREFIX + "userDetailsServiceFactory";
|
||||
|
||||
public static final String METHOD_ACCESS_MANAGER = PREFIX + "defaultMethodAccessManager";
|
||||
|
||||
public static final String FILTER_CHAIN_PROXY = PREFIX + "filterChainProxy";
|
||||
public static final String FILTER_CHAINS = PREFIX + "filterChains";
|
||||
|
||||
public static final String METHOD_SECURITY_METADATA_SOURCE_ADVISOR = PREFIX + "methodSecurityMetadataSourceAdvisor";
|
||||
public static final String EMBEDDED_APACHE_DS = PREFIX + "apacheDirectoryServerContainer";
|
||||
public static final String CONTEXT_SOURCE = PREFIX + "securityContextSource";
|
||||
|
||||
public static final String DEBUG_FILTER = PREFIX + "debugFilter";
|
||||
}
|
||||
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
package org.springframework.security.config;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.security.config.debug.SecurityDebugBeanFactoryPostProcessor;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
public class DebugBeanDefinitionParser implements BeanDefinitionParser {
|
||||
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
RootBeanDefinition debugPP = new RootBeanDefinition(SecurityDebugBeanFactoryPostProcessor.class);
|
||||
parserContext.getReaderContext().registerWithGeneratedName(debugPP);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package org.springframework.security.config;
|
||||
|
||||
/**
|
||||
* Contains all the element names used by Spring Security 3 namespace support.
|
||||
* Contains all the element names used by Spring Security 2 namespace support.
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
@@ -46,12 +46,9 @@ public abstract class Elements {
|
||||
public static final String CUSTOM_FILTER = "custom-filter";
|
||||
public static final String REQUEST_CACHE = "request-cache";
|
||||
public static final String X509 = "x509";
|
||||
public static final String JEE = "jee";
|
||||
public static final String FILTER_SECURITY_METADATA_SOURCE = "filter-security-metadata-source";
|
||||
public static final String METHOD_SECURITY_METADATA_SOURCE = "method-security-metadata-source";
|
||||
@Deprecated
|
||||
public static final String FILTER_INVOCATION_DEFINITION_SOURCE = "filter-invocation-definition-source";
|
||||
public static final String LDAP_PASSWORD_COMPARE = "password-compare";
|
||||
public static final String DEBUG = "debug";
|
||||
public static final String HTTP_FIREWALL = "http-firewall";
|
||||
}
|
||||
|
||||
+16
-47
@@ -1,18 +1,3 @@
|
||||
/*
|
||||
* Copyright 2009-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config;
|
||||
|
||||
import java.util.HashMap;
|
||||
@@ -30,7 +15,6 @@ import org.springframework.security.config.authentication.AuthenticationManagerB
|
||||
import org.springframework.security.config.authentication.AuthenticationProviderBeanDefinitionParser;
|
||||
import org.springframework.security.config.authentication.JdbcUserServiceBeanDefinitionParser;
|
||||
import org.springframework.security.config.authentication.UserServiceBeanDefinitionParser;
|
||||
import org.springframework.security.config.http.FilterChainBeanDefinitionParser;
|
||||
import org.springframework.security.config.http.FilterChainMapBeanDefinitionDecorator;
|
||||
import org.springframework.security.config.http.FilterInvocationSecurityMetadataSourceParser;
|
||||
import org.springframework.security.config.http.HttpFirewallBeanDefinitionParser;
|
||||
@@ -40,7 +24,6 @@ import org.springframework.security.config.ldap.LdapServerBeanDefinitionParser;
|
||||
import org.springframework.security.config.ldap.LdapUserServiceBeanDefinitionParser;
|
||||
import org.springframework.security.config.method.GlobalMethodSecurityBeanDefinitionParser;
|
||||
import org.springframework.security.config.method.InterceptMethodsBeanDefinitionDecorator;
|
||||
import org.springframework.security.config.method.MethodSecurityMetadataSourceBeanDefinitionParser;
|
||||
import org.springframework.security.core.SpringSecurityCoreVersion;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.w3c.dom.Element;
|
||||
@@ -51,12 +34,11 @@ import org.w3c.dom.Node;
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @author Ben Alex
|
||||
* @author Rob Winch
|
||||
* @since 2.0
|
||||
*/
|
||||
public final class SecurityNamespaceHandler implements NamespaceHandler {
|
||||
private static final String FILTER_CHAIN_PROXY_CLASSNAME = "org.springframework.security.web.FilterChainProxy";
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final Map<String, BeanDefinitionParser> parsers = new HashMap<String, BeanDefinitionParser>();
|
||||
private final BeanDefinitionDecorator interceptMethodsBDD = new InterceptMethodsBeanDefinitionDecorator();
|
||||
private BeanDefinitionDecorator filterChainMapBDD;
|
||||
@@ -81,8 +63,9 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
|
||||
|
||||
public BeanDefinition parse(Element element, ParserContext pc) {
|
||||
if (!namespaceMatchesVersion(element)) {
|
||||
pc.getReaderContext().fatal("You cannot use a spring-security-2.0.xsd or spring-security-3.0.xsd or spring-security-3.1.xsd schema " +
|
||||
"with Spring Security 3.2. Please update your schema declarations to the 3.2 schema.", element);
|
||||
pc.getReaderContext().fatal("You must use a 3.0 schema with Spring Security 3.0." +
|
||||
"(2.0 or 3.1 versions are not valid)" +
|
||||
" Please update your schema declarations to the 3.0.3 schema (spring-security-3.0.3.xsd).", element);
|
||||
}
|
||||
String name = pc.getDelegate().getLocalName(element);
|
||||
BeanDefinitionParser parser = parsers.get(name);
|
||||
@@ -93,20 +76,18 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
|
||||
}
|
||||
|
||||
if (parser == null) {
|
||||
if (Elements.HTTP.equals(name) || Elements.FILTER_SECURITY_METADATA_SOURCE.equals(name) ||
|
||||
Elements.FILTER_CHAIN_MAP.equals(name) || Elements.FILTER_CHAIN.equals(name)) {
|
||||
if (Elements.HTTP.equals(name) || Elements.FILTER_SECURITY_METADATA_SOURCE.equals(name)) {
|
||||
reportMissingWebClasses(name, pc, element);
|
||||
} else {
|
||||
reportUnsupportedNodeType(name, pc, element);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return parser.parse(element, pc);
|
||||
}
|
||||
|
||||
public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext pc) {
|
||||
BeanDefinitionDecorator decorator = null;
|
||||
String name = pc.getDelegate().getLocalName(node);
|
||||
|
||||
// We only handle elements
|
||||
@@ -126,7 +107,9 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
|
||||
}
|
||||
}
|
||||
|
||||
reportUnsupportedNodeType(name, pc, node);
|
||||
if (decorator == null) {
|
||||
reportUnsupportedNodeType(name, pc, node);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -137,16 +120,8 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
|
||||
}
|
||||
|
||||
private void reportMissingWebClasses(String nodeName, ParserContext pc, Node node) {
|
||||
String errorMessage = "The classes from the spring-security-web jar "
|
||||
+ "(or one of its dependencies) are not available. You need these to use <" + nodeName + ">";
|
||||
try {
|
||||
ClassUtils.forName(FILTER_CHAIN_PROXY_CLASSNAME, getClass().getClassLoader());
|
||||
// no details available
|
||||
pc.getReaderContext().fatal(errorMessage, node);
|
||||
} catch (Throwable cause) {
|
||||
// provide details on why it could not be loaded
|
||||
pc.getReaderContext().fatal(errorMessage, node, cause);
|
||||
}
|
||||
pc.getReaderContext().fatal("spring-security-web classes are not available. " +
|
||||
"You need these to use <" + Elements.FILTER_CHAIN_MAP + ">", node);
|
||||
}
|
||||
|
||||
public void init() {
|
||||
@@ -164,27 +139,21 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
|
||||
parsers.put(Elements.AUTHENTICATION_PROVIDER, new AuthenticationProviderBeanDefinitionParser());
|
||||
parsers.put(Elements.GLOBAL_METHOD_SECURITY, new GlobalMethodSecurityBeanDefinitionParser());
|
||||
parsers.put(Elements.AUTHENTICATION_MANAGER, new AuthenticationManagerBeanDefinitionParser());
|
||||
parsers.put(Elements.METHOD_SECURITY_METADATA_SOURCE, new MethodSecurityMetadataSourceBeanDefinitionParser());
|
||||
|
||||
// Only load the web-namespace parsers if the web classes are available
|
||||
if(ClassUtils.isPresent(FILTER_CHAIN_PROXY_CLASSNAME, getClass().getClassLoader())) {
|
||||
parsers.put(Elements.DEBUG, new DebugBeanDefinitionParser());
|
||||
if (ClassUtils.isPresent("org.springframework.security.web.FilterChainProxy", getClass().getClassLoader())) {
|
||||
parsers.put(Elements.HTTP, new HttpSecurityBeanDefinitionParser());
|
||||
parsers.put(Elements.HTTP_FIREWALL, new HttpFirewallBeanDefinitionParser());
|
||||
parsers.put(Elements.FILTER_INVOCATION_DEFINITION_SOURCE, new FilterInvocationSecurityMetadataSourceParser());
|
||||
parsers.put(Elements.FILTER_SECURITY_METADATA_SOURCE, new FilterInvocationSecurityMetadataSourceParser());
|
||||
parsers.put(Elements.FILTER_CHAIN, new FilterChainBeanDefinitionParser());
|
||||
filterChainMapBDD = new FilterChainMapBeanDefinitionDecorator();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the schema location declared in the source file being parsed matches the Spring Security version.
|
||||
* The old 2.0 schema is not compatible with the 3.1 parser, so it is an error to explicitly use
|
||||
* 2.0.
|
||||
* <p>
|
||||
* There are also differences between 3.0 and 3.1 which are sufficient that we report using 3.0 as an error too.
|
||||
* It might be an error to declare spring-security.xsd as an alias, but you are only going to find that out
|
||||
* The old 2.0 schema is not compatible with the new 3.0 parser, so it is an error to explicitly use
|
||||
* 3.0. It might be an error to declare spring-security.xsd as an alias, but you are only going to find that out
|
||||
* when one of the sub parsers breaks.
|
||||
*
|
||||
* @param element the element that is to be parsed next
|
||||
@@ -196,8 +165,8 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
|
||||
|
||||
private boolean matchesVersionInternal(Element element) {
|
||||
String schemaLocation = element.getAttributeNS("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation");
|
||||
return schemaLocation.matches("(?m).*spring-security-3\\.2.*.xsd.*")
|
||||
|| schemaLocation.matches("(?m).*spring-security.xsd.*")
|
||||
return schemaLocation.matches("(?m).*spring-security-3\\.0.*xsd.*")
|
||||
|| schemaLocation.matches("(?m).*spring-security\\.xsd.*")
|
||||
|| !schemaLocation.matches("(?m).*spring-security.*");
|
||||
}
|
||||
|
||||
|
||||
-432
@@ -1,432 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.filter.DelegatingFilterProxy;
|
||||
|
||||
import com.google.inject.internal.ImmutableList.Builder;
|
||||
|
||||
/**
|
||||
* <p>A base {@link SecurityBuilder} that allows {@link SecurityConfigurer} to be
|
||||
* applied to it. This makes modifying the {@link SecurityBuilder} a strategy
|
||||
* that can be customized and broken up into a number of
|
||||
* {@link SecurityConfigurer} objects that have more specific goals than that
|
||||
* of the {@link SecurityBuilder}.</p>
|
||||
*
|
||||
* <p>For example, a {@link SecurityBuilder} may build an
|
||||
* {@link DelegatingFilterProxy}, but a {@link SecurityConfigurer} might
|
||||
* populate the {@link SecurityBuilder} with the filters necessary for session
|
||||
* management, form based login, authorization, etc.</p>
|
||||
*
|
||||
* @see WebSecurity
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
* @param <O>
|
||||
* The object that this builder returns
|
||||
* @param <B>
|
||||
* The type of this builder (that is returned by the base class)
|
||||
*/
|
||||
public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBuilder<O>> extends AbstractSecurityBuilder<O> {
|
||||
|
||||
private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers =
|
||||
new LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>>();
|
||||
|
||||
private final Map<Class<Object>,Object> sharedObjects = new HashMap<Class<Object>,Object>();
|
||||
|
||||
private final boolean allowConfigurersOfSameType;
|
||||
|
||||
private BuildState buildState = BuildState.UNBUILT;
|
||||
|
||||
private ObjectPostProcessor<Object> objectPostProcessor;
|
||||
|
||||
/**
|
||||
* Creates a new instance without post processing
|
||||
*/
|
||||
protected AbstractConfiguredSecurityBuilder() {
|
||||
this(ObjectPostProcessor.QUIESCENT_POSTPROCESSOR);
|
||||
}
|
||||
|
||||
/***
|
||||
* Creates a new instance with the provided {@link ObjectPostProcessor}.
|
||||
* This post processor must support Object since there are many types of
|
||||
* objects that may be post processed.
|
||||
*
|
||||
* @param objectPostProcessor the {@link ObjectPostProcessor} to use
|
||||
*/
|
||||
protected AbstractConfiguredSecurityBuilder(ObjectPostProcessor<Object> objectPostProcessor) {
|
||||
this(objectPostProcessor,false);
|
||||
}
|
||||
|
||||
/***
|
||||
* Creates a new instance with the provided {@link ObjectPostProcessor}.
|
||||
* This post processor must support Object since there are many types of
|
||||
* objects that may be post processed.
|
||||
*
|
||||
* @param objectPostProcessor the {@link ObjectPostProcessor} to use
|
||||
* @param allowConfigurersOfSameType if true, will not override other {@link SecurityConfigurer}'s when performing apply
|
||||
*/
|
||||
protected AbstractConfiguredSecurityBuilder(ObjectPostProcessor<Object> objectPostProcessor, boolean allowConfigurersOfSameType) {
|
||||
Assert.notNull(objectPostProcessor, "objectPostProcessor cannot be null");
|
||||
this.objectPostProcessor = objectPostProcessor;
|
||||
this.allowConfigurersOfSameType = allowConfigurersOfSameType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a {@link SecurityConfigurerAdapter} to this
|
||||
* {@link SecurityBuilder} and invokes
|
||||
* {@link SecurityConfigurerAdapter#setBuilder(SecurityBuilder)}.
|
||||
*
|
||||
* @param configurer
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <C extends SecurityConfigurerAdapter<O, B>> C apply(C configurer)
|
||||
throws Exception {
|
||||
add(configurer);
|
||||
configurer.addObjectPostProcessor(objectPostProcessor);
|
||||
configurer.setBuilder((B) this);
|
||||
return configurer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a {@link SecurityConfigurer} to this {@link SecurityBuilder}
|
||||
* overriding any {@link SecurityConfigurer} of the exact same class. Note
|
||||
* that object hierarchies are not considered.
|
||||
*
|
||||
* @param configurer
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public <C extends SecurityConfigurer<O, B>> C apply(C configurer)
|
||||
throws Exception {
|
||||
add(configurer);
|
||||
return configurer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an object that is shared by multiple {@link SecurityConfigurer}.
|
||||
*
|
||||
* @param sharedType the Class to key the shared object by.
|
||||
* @param object the Object to store
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <C> void setSharedObject(Class<C> sharedType, C object) {
|
||||
this.sharedObjects.put((Class<Object>) sharedType, object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a shared Object. Note that object heirarchies are not considered.
|
||||
*
|
||||
* @param sharedType the type of the shared Object
|
||||
* @return the shared Object or null if it is not found
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <C> C getSharedObject(Class<C> sharedType) {
|
||||
return (C) this.sharedObjects.get(sharedType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the shared objects
|
||||
* @return
|
||||
*/
|
||||
public Map<Class<Object>,Object> getSharedObjects() {
|
||||
return Collections.unmodifiableMap(this.sharedObjects);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds {@link SecurityConfigurer} ensuring that it is allowed and
|
||||
* invoking {@link SecurityConfigurer#init(SecurityBuilder)} immediately
|
||||
* if necessary.
|
||||
*
|
||||
* @param configurer the {@link SecurityConfigurer} to add
|
||||
* @throws Exception if an error occurs
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private <C extends SecurityConfigurer<O, B>> void add(C configurer) throws Exception {
|
||||
Assert.notNull(configurer, "configurer cannot be null");
|
||||
|
||||
Class<? extends SecurityConfigurer<O, B>> clazz = (Class<? extends SecurityConfigurer<O, B>>) configurer
|
||||
.getClass();
|
||||
synchronized(configurers) {
|
||||
if(buildState.isConfigured()) {
|
||||
throw new IllegalStateException("Cannot apply "+configurer+" to already built object");
|
||||
}
|
||||
List<SecurityConfigurer<O, B>> configs = allowConfigurersOfSameType ? this.configurers.get(clazz) : null;
|
||||
if(configs == null) {
|
||||
configs = new ArrayList<SecurityConfigurer<O,B>>(1);
|
||||
}
|
||||
configs.add(configurer);
|
||||
this.configurers.put(clazz, configs);
|
||||
if(buildState.isInitializing()) {
|
||||
configurer.init((B)this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the {@link SecurityConfigurer} instances by its class name or an
|
||||
* empty List if not found. Note that object hierarchies are not considered.
|
||||
*
|
||||
* @param clazz the {@link SecurityConfigurer} class to look for
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <C extends SecurityConfigurer<O, B>> List<C> getConfigurers(
|
||||
Class<C> clazz) {
|
||||
List<C> configs = (List<C>) this.configurers.get(clazz);
|
||||
if(configs == null) {
|
||||
return new ArrayList<C>();
|
||||
}
|
||||
return new ArrayList<C>(configs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all the {@link SecurityConfigurer} instances by its class name or an
|
||||
* empty List if not found. Note that object hierarchies are not considered.
|
||||
*
|
||||
* @param clazz the {@link SecurityConfigurer} class to look for
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <C extends SecurityConfigurer<O, B>> List<C> removeConfigurers(
|
||||
Class<C> clazz) {
|
||||
List<C> configs = (List<C>) this.configurers.remove(clazz);
|
||||
if(configs == null) {
|
||||
return new ArrayList<C>();
|
||||
}
|
||||
return new ArrayList<C>(configs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link SecurityConfigurer} by its class name or
|
||||
* <code>null</code> if not found. Note that object hierarchies are not
|
||||
* considered.
|
||||
*
|
||||
* @param clazz
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <C extends SecurityConfigurer<O, B>> C getConfigurer(
|
||||
Class<C> clazz) {
|
||||
List<SecurityConfigurer<O,B>> configs = this.configurers.get(clazz);
|
||||
if(configs == null) {
|
||||
return null;
|
||||
}
|
||||
if(configs.size() != 1) {
|
||||
throw new IllegalStateException("Only one configurer expected for type " + clazz + ", but got " + configs);
|
||||
}
|
||||
return (C) configs.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes and returns the {@link SecurityConfigurer} by its class name or
|
||||
* <code>null</code> if not found. Note that object hierarchies are not
|
||||
* considered.
|
||||
*
|
||||
* @param clazz
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <C extends SecurityConfigurer<O,B>> C removeConfigurer(Class<C> clazz) {
|
||||
List<SecurityConfigurer<O,B>> configs = this.configurers.remove(clazz);
|
||||
if(configs == null) {
|
||||
return null;
|
||||
}
|
||||
if(configs.size() != 1) {
|
||||
throw new IllegalStateException("Only one configurer expected for type " + clazz + ", but got " + configs);
|
||||
}
|
||||
return (C) configs.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the {@link ObjectPostProcessor} to use.
|
||||
* @param objectPostProcessor the {@link ObjectPostProcessor} to use. Cannot be null
|
||||
* @return the {@link SecurityBuilder} for further customizations
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public O objectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {
|
||||
Assert.notNull(objectPostProcessor,"objectPostProcessor cannot be null");
|
||||
this.objectPostProcessor = objectPostProcessor;
|
||||
return (O) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs post processing of an object. The default is to delegate to the
|
||||
* {@link ObjectPostProcessor}.
|
||||
*
|
||||
* @param object the Object to post process
|
||||
* @return the possibly modified Object to use
|
||||
*/
|
||||
protected <P> P postProcess(P object) {
|
||||
return (P) this.objectPostProcessor.postProcess(object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the build using the {@link SecurityConfigurer}'s that have been applied using the following steps:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Invokes {@link #beforeInit()} for any subclass to hook into</li>
|
||||
* <li>Invokes {@link SecurityConfigurer#init(SecurityBuilder)} for any {@link SecurityConfigurer} that was applied to this builder.</li>
|
||||
* <li>Invokes {@link #beforeConfigure()} for any subclass to hook into</li>
|
||||
* <li>Invokes {@link #performBuild()} which actually builds the Object</li>
|
||||
* </ul>
|
||||
*/
|
||||
@Override
|
||||
protected final O doBuild() throws Exception {
|
||||
synchronized(configurers) {
|
||||
buildState = BuildState.INITIALIZING;
|
||||
|
||||
beforeInit();
|
||||
init();
|
||||
|
||||
buildState = BuildState.CONFIGURING;
|
||||
|
||||
beforeConfigure();
|
||||
configure();
|
||||
|
||||
buildState = BuildState.BUILDING;
|
||||
|
||||
O result = performBuild();
|
||||
|
||||
buildState = BuildState.BUILT;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked prior to invoking each
|
||||
* {@link SecurityConfigurer#init(SecurityBuilder)} method. Subclasses may
|
||||
* override this method to hook into the lifecycle without using a
|
||||
* {@link SecurityConfigurer}.
|
||||
*/
|
||||
protected void beforeInit() throws Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked prior to invoking each
|
||||
* {@link SecurityConfigurer#configure(SecurityBuilder)} method.
|
||||
* Subclasses may override this method to hook into the lifecycle without
|
||||
* using a {@link SecurityConfigurer}.
|
||||
*/
|
||||
protected void beforeConfigure() throws Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses must implement this method to build the object that is being returned.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected abstract O performBuild() throws Exception;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void init() throws Exception {
|
||||
Collection<SecurityConfigurer<O,B>> configurers = getConfigurers();
|
||||
|
||||
for(SecurityConfigurer<O,B> configurer : configurers ) {
|
||||
configurer.init((B) this);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void configure() throws Exception {
|
||||
Collection<SecurityConfigurer<O,B>> configurers = getConfigurers();
|
||||
|
||||
for(SecurityConfigurer<O,B> configurer : configurers ) {
|
||||
configurer.configure((B) this);
|
||||
}
|
||||
}
|
||||
|
||||
private Collection<SecurityConfigurer<O, B>> getConfigurers() {
|
||||
List<SecurityConfigurer<O,B>> result = new ArrayList<SecurityConfigurer<O,B>>();
|
||||
for(List<SecurityConfigurer<O,B>> configs : this.configurers.values()) {
|
||||
result.addAll(configs);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* The build state for the application
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
private static enum BuildState {
|
||||
/**
|
||||
* This is the state before the {@link Builder#build()} is invoked
|
||||
*/
|
||||
UNBUILT(0),
|
||||
|
||||
/**
|
||||
* The state from when {@link Builder#build()} is first invoked until
|
||||
* all the {@link SecurityConfigurer#init(SecurityBuilder)} methods
|
||||
* have been invoked.
|
||||
*/
|
||||
INITIALIZING(1),
|
||||
|
||||
/**
|
||||
* The state from after all
|
||||
* {@link SecurityConfigurer#init(SecurityBuilder)} have been invoked
|
||||
* until after all the
|
||||
* {@link SecurityConfigurer#configure(SecurityBuilder)} methods have
|
||||
* been invoked.
|
||||
*/
|
||||
CONFIGURING(2),
|
||||
|
||||
/**
|
||||
* From the point after all the
|
||||
* {@link SecurityConfigurer#configure(SecurityBuilder)} have
|
||||
* completed to just after
|
||||
* {@link AbstractConfiguredSecurityBuilder#performBuild()}.
|
||||
*/
|
||||
BUILDING(3),
|
||||
|
||||
/**
|
||||
* After the object has been completely built.
|
||||
*/
|
||||
BUILT(4);
|
||||
|
||||
private final int order;
|
||||
|
||||
BuildState(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
public boolean isInitializing() {
|
||||
return INITIALIZING.order == order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the state is CONFIGURING or later
|
||||
* @return
|
||||
*/
|
||||
public boolean isConfigured() {
|
||||
return order >= CONFIGURING.order;
|
||||
}
|
||||
}
|
||||
}
|
||||
-67
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* A base {@link SecurityBuilder} that ensures the object being built is only
|
||||
* built one time.
|
||||
*
|
||||
* @param <O> the type of Object that is being built
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> {
|
||||
private AtomicBoolean building = new AtomicBoolean();
|
||||
|
||||
private O object;
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.security.config.annotation.SecurityBuilder#build()
|
||||
*/
|
||||
@Override
|
||||
public final O build() throws Exception {
|
||||
if(building.compareAndSet(false, true)) {
|
||||
object = doBuild();
|
||||
return object;
|
||||
}
|
||||
throw new IllegalStateException("This object has already been built");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the object that was built. If it has not been built yet an Exception
|
||||
* is thrown.
|
||||
*
|
||||
* @return the Object that was built
|
||||
*/
|
||||
public final O getObject() {
|
||||
if(!building.get()) {
|
||||
throw new IllegalStateException("This object has not been built");
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses should implement this to perform the build.
|
||||
*
|
||||
* @return the object that should be returned by {@link #build()}.
|
||||
*
|
||||
* @throws Exception if an error occurs
|
||||
*/
|
||||
protected abstract O doBuild() throws Exception;
|
||||
}
|
||||
-52
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation;
|
||||
|
||||
import org.springframework.beans.factory.Aware;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
/**
|
||||
* Allows initialization of Objects. Typically this is used to call the
|
||||
* {@link Aware} methods, {@link InitializingBean#afterPropertiesSet()}, and
|
||||
* ensure that {@link DisposableBean#destroy()} has been invoked.
|
||||
*
|
||||
* @param <T> the bound of the types of Objects this {@link ObjectPostProcessor} supports.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
public interface ObjectPostProcessor<T> {
|
||||
|
||||
/**
|
||||
* Initialize the object possibly returning a modified instance that should
|
||||
* be used instead.
|
||||
*
|
||||
* @param object the object to initialize
|
||||
* @return the initialized version of the object
|
||||
*/
|
||||
<O extends T> O postProcess(O object);
|
||||
|
||||
/**
|
||||
* A do nothing implementation of the {@link ObjectPostProcessor}
|
||||
*/
|
||||
ObjectPostProcessor<Object> QUIESCENT_POSTPROCESSOR = new ObjectPostProcessor<Object>() {
|
||||
@Override
|
||||
public <T> T postProcess(T object) {
|
||||
return object;
|
||||
}
|
||||
};
|
||||
}
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation;
|
||||
|
||||
/**
|
||||
* Interface for building an Object
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*
|
||||
* @param <O> The type of the Object being built
|
||||
*/
|
||||
public interface SecurityBuilder<O> {
|
||||
|
||||
/**
|
||||
* Builds the object and returns it or null.
|
||||
*
|
||||
* @return the Object to be built or null if the implementation allows it.
|
||||
* @throws Exception if an error occured when building the Object
|
||||
*/
|
||||
O build() throws Exception;
|
||||
}
|
||||
-55
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation;
|
||||
|
||||
/**
|
||||
* Allows for configuring a {@link SecurityBuilder}. All
|
||||
* {@link SecurityConfigurer} first have their {@link #init(SecurityBuilder)}
|
||||
* method invoked. After all {@link #init(SecurityBuilder)} methods have been
|
||||
* invoked, each {@link #configure(SecurityBuilder)} method is invoked.
|
||||
*
|
||||
* @see AbstractConfiguredSecurityBuilder
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
* @param <O>
|
||||
* The object being built by the {@link SecurityBuilder} B
|
||||
* @param <B>
|
||||
* The {@link SecurityBuilder} that builds objects of type O. This is
|
||||
* also the {@link SecurityBuilder} that is being configured.
|
||||
*/
|
||||
public interface SecurityConfigurer<O, B extends SecurityBuilder<O>> {
|
||||
/**
|
||||
* Initialize the {@link SecurityBuilder}. Here only shared state should be
|
||||
* created and modified, but not properties on the {@link SecurityBuilder}
|
||||
* used for building the object. This ensures that the
|
||||
* {@link #configure(SecurityBuilder)} method uses the correct shared
|
||||
* objects when building.
|
||||
*
|
||||
* @param builder
|
||||
* @throws Exception
|
||||
*/
|
||||
void init(B builder) throws Exception;
|
||||
|
||||
/**
|
||||
* Configure the {@link SecurityBuilder} by setting the necessary properties
|
||||
* on the {@link SecurityBuilder}.
|
||||
*
|
||||
* @param builder
|
||||
* @throws Exception
|
||||
*/
|
||||
void configure(B builder) throws Exception;
|
||||
}
|
||||
-135
@@ -1,135 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
|
||||
/**
|
||||
* A base class for {@link SecurityConfigurer} that allows subclasses to only
|
||||
* implement the methods they are interested in. It also provides a mechanism
|
||||
* for using the {@link SecurityConfigurer} and when done gaining access to the
|
||||
* {@link SecurityBuilder} that is being configured.
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
* @param <O>
|
||||
* The Object being built by B
|
||||
* @param <B>
|
||||
* The Builder that is building O and is configured by {@link SecurityConfigurerAdapter}
|
||||
*/
|
||||
public abstract class SecurityConfigurerAdapter<O,B extends SecurityBuilder<O>> implements SecurityConfigurer<O,B> {
|
||||
private B securityBuilder;
|
||||
|
||||
private CompositeObjectPostProcessor objectPostProcessor = new CompositeObjectPostProcessor();
|
||||
|
||||
@Override
|
||||
public void init(B builder) throws Exception {}
|
||||
|
||||
@Override
|
||||
public void configure(B builder) throws Exception {}
|
||||
|
||||
/**
|
||||
* Return the {@link SecurityBuilder} when done using the
|
||||
* {@link SecurityConfigurer}. This is useful for method chaining.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public B and() {
|
||||
return getBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link SecurityBuilder}. Cannot be null.
|
||||
*
|
||||
* @return the {@link SecurityBuilder}
|
||||
* @throw {@link IllegalStateException} if {@link SecurityBuilder} is null
|
||||
*/
|
||||
protected final B getBuilder() {
|
||||
if(securityBuilder == null) {
|
||||
throw new IllegalStateException("securityBuilder cannot be null");
|
||||
}
|
||||
return securityBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs post processing of an object. The default is to delegate to the
|
||||
* {@link ObjectPostProcessor}.
|
||||
*
|
||||
* @param object the Object to post process
|
||||
* @return the possibly modified Object to use
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <T> T postProcess(T object) {
|
||||
return (T) this.objectPostProcessor.postProcess(object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an {@link ObjectPostProcessor} to be used for this
|
||||
* {@link SecurityConfigurerAdapter}. The default implementation does
|
||||
* nothing to the object.
|
||||
*
|
||||
* @param objectPostProcessor the {@link ObjectPostProcessor} to use
|
||||
*/
|
||||
public void addObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
|
||||
this.objectPostProcessor.addObjectPostProcessor(objectPostProcessor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link SecurityBuilder} to be used. This is automatically set
|
||||
* when using
|
||||
* {@link AbstractConfiguredSecurityBuilder#apply(SecurityConfigurerAdapter)}
|
||||
*
|
||||
* @param builder the {@link SecurityBuilder} to set
|
||||
*/
|
||||
public void setBuilder(B builder) {
|
||||
this.securityBuilder = builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* An {@link ObjectPostProcessor} that delegates work to numerous
|
||||
* {@link ObjectPostProcessor} implementations.
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
private static final class CompositeObjectPostProcessor implements ObjectPostProcessor<Object> {
|
||||
private List<ObjectPostProcessor<? extends Object>> postProcessors = new ArrayList<ObjectPostProcessor<?>>();
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public Object postProcess(Object object) {
|
||||
for(ObjectPostProcessor opp : postProcessors) {
|
||||
Class<?> oppClass = opp.getClass();
|
||||
Class<?> oppType = GenericTypeResolver.resolveTypeArgument(oppClass,ObjectPostProcessor.class);
|
||||
if(oppType == null || oppType.isAssignableFrom(object.getClass())) {
|
||||
object = opp.postProcess(object);
|
||||
}
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an {@link ObjectPostProcessor} to use
|
||||
* @param objectPostProcessor the {@link ObjectPostProcessor} to add
|
||||
* @return true if the {@link ObjectPostProcessor} was added, else false
|
||||
*/
|
||||
private boolean addObjectPostProcessor(ObjectPostProcessor<?extends Object> objectPostProcessor) {
|
||||
return this.postProcessors.add(objectPostProcessor);
|
||||
}
|
||||
}
|
||||
}
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.authentication;
|
||||
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.ProviderManager;
|
||||
import org.springframework.security.config.annotation.SecurityBuilder;
|
||||
|
||||
/**
|
||||
* Interface for operating on a SecurityBuilder that creates a {@link ProviderManager}
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
* @param <B> the type of the {@link SecurityBuilder}
|
||||
*/
|
||||
public interface ProviderManagerBuilder<B extends ProviderManagerBuilder<B>> extends SecurityBuilder<AuthenticationManager> {
|
||||
|
||||
/**
|
||||
* Add authentication based upon the custom {@link AuthenticationProvider}
|
||||
* that is passed in. Since the {@link AuthenticationProvider}
|
||||
* implementation is unknown, all customizations must be done externally and
|
||||
* the {@link ProviderManagerBuilder} is returned immediately.
|
||||
*
|
||||
* @return a {@link ProviderManagerBuilder} to allow further authentication
|
||||
* to be provided to the {@link ProviderManagerBuilder}
|
||||
* @throws Exception
|
||||
* if an error occurs when adding the {@link AuthenticationProvider}
|
||||
*/
|
||||
B authenticationProvider(AuthenticationProvider authenticationProvider);
|
||||
}
|
||||
-254
@@ -1,254 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.authentication.builders;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.security.authentication.AuthenticationEventPublisher;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.ProviderManager;
|
||||
import org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.SecurityBuilder;
|
||||
import org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;
|
||||
import org.springframework.security.config.annotation.authentication.configurers.ldap.LdapAuthenticationProviderConfigurer;
|
||||
import org.springframework.security.config.annotation.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer;
|
||||
import org.springframework.security.config.annotation.authentication.configurers.provisioning.JdbcUserDetailsManagerConfigurer;
|
||||
import org.springframework.security.config.annotation.authentication.configurers.userdetails.DaoAuthenticationConfigurer;
|
||||
import org.springframework.security.config.annotation.authentication.configurers.userdetails.UserDetailsAwareConfigurer;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link SecurityBuilder} used to create an {@link AuthenticationManager}.
|
||||
* Allows for easily building in memory authentication, LDAP authentication,
|
||||
* JDBC based authentication, adding {@link UserDetailsService}, and adding
|
||||
* {@link AuthenticationProvider}'s.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
public class AuthenticationManagerBuilder extends AbstractConfiguredSecurityBuilder<AuthenticationManager, AuthenticationManagerBuilder> implements ProviderManagerBuilder<AuthenticationManagerBuilder> {
|
||||
|
||||
private AuthenticationManager parentAuthenticationManager;
|
||||
private List<AuthenticationProvider> authenticationProviders = new ArrayList<AuthenticationProvider>();
|
||||
private UserDetailsService defaultUserDetailsService;
|
||||
private Boolean eraseCredentials;
|
||||
private AuthenticationEventPublisher eventPublisher;
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
*/
|
||||
public AuthenticationManagerBuilder() {
|
||||
super(ObjectPostProcessor.QUIESCENT_POSTPROCESSOR,true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows providing a parent {@link AuthenticationManager} that will be
|
||||
* tried if this {@link AuthenticationManager} was unable to attempt to
|
||||
* authenticate the provided {@link Authentication}.
|
||||
*
|
||||
* @param authenticationManager
|
||||
* the {@link AuthenticationManager} that should be used if the
|
||||
* current {@link AuthenticationManager} was unable to attempt to
|
||||
* authenticate the provided {@link Authentication}.
|
||||
* @return the {@link AuthenticationManagerBuilder} for further adding types
|
||||
* of authentication
|
||||
*/
|
||||
public AuthenticationManagerBuilder parentAuthenticationManager(
|
||||
AuthenticationManager authenticationManager) {
|
||||
this.parentAuthenticationManager = authenticationManager;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link AuthenticationEventPublisher}
|
||||
*
|
||||
* @param eventPublisher
|
||||
* the {@link AuthenticationEventPublisher} to use
|
||||
* @return the {@link AuthenticationManagerBuilder} for further
|
||||
* customizations
|
||||
*/
|
||||
public AuthenticationManagerBuilder authenticationEventPublisher(AuthenticationEventPublisher eventPublisher) {
|
||||
Assert.notNull(eventPublisher, "AuthenticationEventPublisher cannot be null");
|
||||
this.eventPublisher = eventPublisher;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param eraseCredentials
|
||||
* true if {@link AuthenticationManager} should clear the
|
||||
* credentials from the {@link Authentication} object after
|
||||
* authenticating
|
||||
* @return the {@link AuthenticationManagerBuilder} for further customizations
|
||||
*/
|
||||
public AuthenticationManagerBuilder eraseCredentials(boolean eraseCredentials) {
|
||||
this.eraseCredentials = eraseCredentials;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add in memory authentication to the {@link AuthenticationManagerBuilder}
|
||||
* and return a {@link InMemoryUserDetailsManagerConfigurer} to
|
||||
* allow customization of the in memory authentication.
|
||||
*
|
||||
* <p>
|
||||
* This method also ensure that a {@link UserDetailsService} is available
|
||||
* for the {@link #getDefaultUserDetailsService()} method. Note that
|
||||
* additional {@link UserDetailsService}'s may override this
|
||||
* {@link UserDetailsService} as the default.
|
||||
* </p>
|
||||
*
|
||||
* @return a {@link InMemoryUserDetailsManagerConfigurer} to allow
|
||||
* customization of the in memory authentication
|
||||
* @throws Exception
|
||||
* if an error occurs when adding the in memory authentication
|
||||
*/
|
||||
public InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> inMemoryAuthentication()
|
||||
throws Exception {
|
||||
return apply(new InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add JDBC authentication to the {@link AuthenticationManagerBuilder} and
|
||||
* return a {@link JdbcUserDetailsManagerConfigurer} to allow customization of the
|
||||
* JDBC authentication.
|
||||
*
|
||||
* <p>
|
||||
* This method also ensure that a {@link UserDetailsService} is available
|
||||
* for the {@link #getDefaultUserDetailsService()} method. Note that
|
||||
* additional {@link UserDetailsService}'s may override this
|
||||
* {@link UserDetailsService} as the default.
|
||||
* </p>
|
||||
*
|
||||
* @return a {@link JdbcUserDetailsManagerConfigurer} to allow customization of the
|
||||
* JDBC authentication
|
||||
* @throws Exception if an error occurs when adding the JDBC authentication
|
||||
*/
|
||||
public JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder> jdbcAuthentication()
|
||||
throws Exception {
|
||||
return apply(new JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add authentication based upon the custom {@link UserDetailsService} that
|
||||
* is passed in. It then returns a {@link DaoAuthenticationConfigurer} to
|
||||
* allow customization of the authentication.
|
||||
*
|
||||
* <p>
|
||||
* This method also ensure that the {@link UserDetailsService} is available
|
||||
* for the {@link #getDefaultUserDetailsService()} method. Note that
|
||||
* additional {@link UserDetailsService}'s may override this
|
||||
* {@link UserDetailsService} as the default.
|
||||
* </p>
|
||||
*
|
||||
* @return a {@link DaoAuthenticationConfigurer} to allow customization
|
||||
* of the DAO authentication
|
||||
* @throws Exception
|
||||
* if an error occurs when adding the {@link UserDetailsService}
|
||||
* based authentication
|
||||
*/
|
||||
public <T extends UserDetailsService> DaoAuthenticationConfigurer<AuthenticationManagerBuilder,T> userDetailsService(
|
||||
T userDetailsService) throws Exception {
|
||||
this.defaultUserDetailsService = userDetailsService;
|
||||
return apply(new DaoAuthenticationConfigurer<AuthenticationManagerBuilder,T>(userDetailsService));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add LDAP authentication to the {@link AuthenticationManagerBuilder} and
|
||||
* return a {@link LdapAuthenticationProviderConfigurer} to allow
|
||||
* customization of the LDAP authentication.
|
||||
*
|
||||
* <p>
|
||||
* This method <b>does NOT</b> ensure that a {@link UserDetailsService} is
|
||||
* available for the {@link #getDefaultUserDetailsService()} method.
|
||||
* </p>
|
||||
*
|
||||
* @return a {@link LdapAuthenticationProviderConfigurer} to allow
|
||||
* customization of the LDAP authentication
|
||||
* @throws Exception
|
||||
* if an error occurs when adding the LDAP authentication
|
||||
*/
|
||||
public LdapAuthenticationProviderConfigurer<AuthenticationManagerBuilder> ldapAuthentication()
|
||||
throws Exception {
|
||||
return apply(new LdapAuthenticationProviderConfigurer<AuthenticationManagerBuilder>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add authentication based upon the custom {@link AuthenticationProvider}
|
||||
* that is passed in. Since the {@link AuthenticationProvider}
|
||||
* implementation is unknown, all customizations must be done externally and
|
||||
* the {@link AuthenticationManagerBuilder} is returned immediately.
|
||||
*
|
||||
* <p>
|
||||
* This method <b>does NOT</b> ensure that the {@link UserDetailsService} is
|
||||
* available for the {@link #getDefaultUserDetailsService()} method.
|
||||
* </p>
|
||||
*
|
||||
* @return a {@link AuthenticationManagerBuilder} to allow further authentication
|
||||
* to be provided to the {@link AuthenticationManagerBuilder}
|
||||
* @throws Exception
|
||||
* if an error occurs when adding the {@link AuthenticationProvider}
|
||||
*/
|
||||
public AuthenticationManagerBuilder authenticationProvider(
|
||||
AuthenticationProvider authenticationProvider) {
|
||||
this.authenticationProviders.add(authenticationProvider);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ProviderManager performBuild() throws Exception {
|
||||
ProviderManager providerManager = new ProviderManager(authenticationProviders, parentAuthenticationManager);
|
||||
if(eraseCredentials != null) {
|
||||
providerManager.setEraseCredentialsAfterAuthentication(eraseCredentials);
|
||||
}
|
||||
if(eventPublisher != null) {
|
||||
providerManager.setAuthenticationEventPublisher(eventPublisher);
|
||||
}
|
||||
providerManager = postProcess(providerManager);
|
||||
return providerManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default {@link UserDetailsService} for the
|
||||
* {@link AuthenticationManagerBuilder}. The result may be null in some
|
||||
* circumstances.
|
||||
*
|
||||
* @return the default {@link UserDetailsService} for the
|
||||
* {@link AuthenticationManagerBuilder}
|
||||
*/
|
||||
public UserDetailsService getDefaultUserDetailsService() {
|
||||
return this.defaultUserDetailsService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Captures the {@link UserDetailsService} from any {@link UserDetailsAwareConfigurer}.
|
||||
*
|
||||
* @param configurer the {@link UserDetailsAwareConfigurer} to capture the {@link UserDetailsService} from.
|
||||
* @return the {@link UserDetailsAwareConfigurer} for further customizations
|
||||
* @throws Exception if an error occurs
|
||||
*/
|
||||
private <C extends UserDetailsAwareConfigurer<AuthenticationManagerBuilder,? extends UserDetailsService>> C apply(C configurer) throws Exception {
|
||||
this.defaultUserDetailsService = configurer.getUserDetailsService();
|
||||
return (C) super.apply(configurer);
|
||||
}
|
||||
}
|
||||
-469
@@ -1,469 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.authentication.configurers.ldap;
|
||||
|
||||
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.encoding.PasswordEncoder;
|
||||
import org.springframework.security.authentication.encoding.PlaintextPasswordEncoder;
|
||||
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;
|
||||
import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;
|
||||
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
|
||||
import org.springframework.security.ldap.authentication.AbstractLdapAuthenticator;
|
||||
import org.springframework.security.ldap.authentication.BindAuthenticator;
|
||||
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
|
||||
import org.springframework.security.ldap.authentication.LdapAuthenticator;
|
||||
import org.springframework.security.ldap.authentication.PasswordComparisonAuthenticator;
|
||||
import org.springframework.security.ldap.search.FilterBasedLdapUserSearch;
|
||||
import org.springframework.security.ldap.search.LdapUserSearch;
|
||||
import org.springframework.security.ldap.server.ApacheDSContainer;
|
||||
import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator;
|
||||
import org.springframework.security.ldap.userdetails.InetOrgPersonContextMapper;
|
||||
import org.springframework.security.ldap.userdetails.LdapUserDetailsMapper;
|
||||
import org.springframework.security.ldap.userdetails.PersonContextMapper;
|
||||
import org.springframework.security.ldap.userdetails.UserDetailsContextMapper;
|
||||
|
||||
/**
|
||||
* Configures LDAP {@link AuthenticationProvider} in the {@link ProviderManagerBuilder}.
|
||||
*
|
||||
* @param <B> the {@link ProviderManagerBuilder} type that this is configuring.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuilder<B>> extends SecurityConfigurerAdapter<AuthenticationManager,B> {
|
||||
private String groupRoleAttribute = "cn";
|
||||
private String groupSearchBase = "";
|
||||
private String groupSearchFilter = "(uniqueMember={0})";
|
||||
private String rolePrefix = "ROLE_";
|
||||
private String userSearchBase = ""; // only for search
|
||||
private String userSearchFilter = null;//"uid={0}"; // only for search
|
||||
private String[] userDnPatterns;
|
||||
private BaseLdapPathContextSource contextSource;
|
||||
private ContextSourceBuilder contextSourceBuilder = new ContextSourceBuilder();
|
||||
private UserDetailsContextMapper userDetailsContextMapper;
|
||||
private PasswordEncoder passwordEncoder;
|
||||
private String passwordAttribute;
|
||||
|
||||
private LdapAuthenticationProvider build() throws Exception {
|
||||
BaseLdapPathContextSource contextSource = getContextSource();
|
||||
LdapAuthenticator ldapAuthenticator = createLdapAuthenticator(contextSource);
|
||||
|
||||
DefaultLdapAuthoritiesPopulator authoritiesPopulator = new DefaultLdapAuthoritiesPopulator(
|
||||
contextSource, groupSearchBase);
|
||||
authoritiesPopulator.setGroupRoleAttribute(groupRoleAttribute);
|
||||
authoritiesPopulator.setGroupSearchFilter(groupSearchFilter);
|
||||
|
||||
LdapAuthenticationProvider ldapAuthenticationProvider = new LdapAuthenticationProvider(
|
||||
ldapAuthenticator, authoritiesPopulator);
|
||||
SimpleAuthorityMapper simpleAuthorityMapper = new SimpleAuthorityMapper();
|
||||
simpleAuthorityMapper.setPrefix(rolePrefix);
|
||||
simpleAuthorityMapper.afterPropertiesSet();
|
||||
ldapAuthenticationProvider.setAuthoritiesMapper(simpleAuthorityMapper);
|
||||
if(userDetailsContextMapper != null) {
|
||||
ldapAuthenticationProvider.setUserDetailsContextMapper(userDetailsContextMapper);
|
||||
}
|
||||
return ldapAuthenticationProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the {@link LdapAuthenticator} to use
|
||||
*
|
||||
* @param contextSource the {@link BaseLdapPathContextSource} to use
|
||||
* @return the {@link LdapAuthenticator} to use
|
||||
*/
|
||||
private LdapAuthenticator createLdapAuthenticator(BaseLdapPathContextSource contextSource) {
|
||||
AbstractLdapAuthenticator ldapAuthenticator = passwordEncoder == null ? createBindAuthenticator(contextSource) : createPasswordCompareAuthenticator(contextSource);
|
||||
LdapUserSearch userSearch = createUserSearch();
|
||||
if(userSearch != null) {
|
||||
ldapAuthenticator.setUserSearch(userSearch);
|
||||
}
|
||||
if(userDnPatterns != null && userDnPatterns.length > 0) {
|
||||
ldapAuthenticator.setUserDnPatterns(userDnPatterns);
|
||||
}
|
||||
return postProcess(ldapAuthenticator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates {@link PasswordComparisonAuthenticator}
|
||||
*
|
||||
* @param contextSource the {@link BaseLdapPathContextSource} to use
|
||||
* @return
|
||||
*/
|
||||
private PasswordComparisonAuthenticator createPasswordCompareAuthenticator(BaseLdapPathContextSource contextSource) {
|
||||
PasswordComparisonAuthenticator ldapAuthenticator = new PasswordComparisonAuthenticator(contextSource);
|
||||
ldapAuthenticator.setPasswordAttributeName(passwordAttribute);
|
||||
ldapAuthenticator.setPasswordEncoder(passwordEncoder);
|
||||
return ldapAuthenticator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link BindAuthenticator}
|
||||
*
|
||||
* @param contextSource the {@link BaseLdapPathContextSource} to use
|
||||
* @return the {@link BindAuthenticator} to use
|
||||
*/
|
||||
private BindAuthenticator createBindAuthenticator(
|
||||
BaseLdapPathContextSource contextSource) {
|
||||
return new BindAuthenticator(contextSource);
|
||||
}
|
||||
|
||||
private LdapUserSearch createUserSearch() {
|
||||
if(userSearchFilter == null) {
|
||||
return null;
|
||||
}
|
||||
return new FilterBasedLdapUserSearch(userSearchBase, userSearchFilter, contextSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the {@link BaseLdapPathContextSource} to be used. If not
|
||||
* specified, an embedded LDAP server will be created using
|
||||
* {@link #contextSource()}.
|
||||
*
|
||||
* @param contextSource
|
||||
* the {@link BaseLdapPathContextSource} to use
|
||||
* @return the {@link LdapAuthenticationProviderConfigurer} for further
|
||||
* customizations
|
||||
* @see #contextSource()
|
||||
*/
|
||||
public LdapAuthenticationProviderConfigurer<B> contextSource(BaseLdapPathContextSource contextSource) {
|
||||
this.contextSource = contextSource;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows easily configuring of a {@link BaseLdapPathContextSource} with
|
||||
* defaults pointing to an embedded LDAP server that is created.
|
||||
*
|
||||
* @return the {@link ContextSourceBuilder} for further customizations
|
||||
*/
|
||||
public ContextSourceBuilder contextSource() {
|
||||
return contextSourceBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the {@link PasswordEncoder} to be used when authenticating with
|
||||
* password comparison.
|
||||
*
|
||||
* @param passwordEncoder the {@link PasswordEncoder} to use
|
||||
* @return the {@link LdapAuthenticationProviderConfigurer} for further customization
|
||||
*/
|
||||
public LdapAuthenticationProviderConfigurer<B> passwordEncoder(PasswordEncoder passwordEncoder) {
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* If your users are at a fixed location in the directory (i.e. you can work
|
||||
* out the DN directly from the username without doing a directory search),
|
||||
* you can use this attribute to map directly to the DN. It maps directly to
|
||||
* the userDnPatterns property of AbstractLdapAuthenticator. The value is a
|
||||
* specific pattern used to build the user's DN, for example
|
||||
* "uid={0},ou=people". The key "{0}" must be present and will be
|
||||
* substituted with the username.
|
||||
*
|
||||
* @param userDnPatterns the LDAP patterns for finding the usernames
|
||||
* @return the {@link LdapAuthenticationProviderConfigurer} for further customizations
|
||||
*/
|
||||
public LdapAuthenticationProviderConfigurer<B> userDnPatterns(String...userDnPatterns) {
|
||||
this.userDnPatterns = userDnPatterns;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows explicit customization of the loaded user object by specifying a
|
||||
* UserDetailsContextMapper bean which will be called with the context
|
||||
* information from the user's directory entry.
|
||||
*
|
||||
* @param userDetailsContextMapper the {@link UserDetailsContextMapper} to use
|
||||
* @return the {@link LdapAuthenticationProviderConfigurer} for further
|
||||
* customizations
|
||||
*
|
||||
* @see PersonContextMapper
|
||||
* @see InetOrgPersonContextMapper
|
||||
* @see LdapUserDetailsMapper
|
||||
*/
|
||||
public LdapAuthenticationProviderConfigurer<B> userDetailsContextMapper(UserDetailsContextMapper userDetailsContextMapper) {
|
||||
this.userDetailsContextMapper = userDetailsContextMapper;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the attribute name which contains the role name. Default is "cn".
|
||||
* @param groupRoleAttribute the attribute name that maps a group to a role.
|
||||
* @return
|
||||
*/
|
||||
public LdapAuthenticationProviderConfigurer<B> groupRoleAttribute(String groupRoleAttribute) {
|
||||
this.groupRoleAttribute = groupRoleAttribute;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The search base for group membership searches. Defaults to "".
|
||||
* @param groupSearchBase
|
||||
* @return the {@link LdapAuthenticationProviderConfigurer} for further customizations
|
||||
*/
|
||||
public LdapAuthenticationProviderConfigurer<B> groupSearchBase(String groupSearchBase) {
|
||||
this.groupSearchBase = groupSearchBase;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The LDAP filter to search for groups. Defaults to "(uniqueMember={0})".
|
||||
* The substituted parameter is the DN of the user.
|
||||
*
|
||||
* @param groupSearchFilter the LDAP filter to search for groups
|
||||
* @return the {@link LdapAuthenticationProviderConfigurer} for further customizations
|
||||
*/
|
||||
public LdapAuthenticationProviderConfigurer<B> groupSearchFilter(String groupSearchFilter) {
|
||||
this.groupSearchFilter = groupSearchFilter;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* A non-empty string prefix that will be added as a prefix to the existing
|
||||
* roles. The default is "ROLE_".
|
||||
*
|
||||
* @param rolePrefix the prefix to be added to the roles that are loaded.
|
||||
* @return the {@link LdapAuthenticationProviderConfigurer} for further customizations
|
||||
* @see SimpleAuthorityMapper#setPrefix(String)
|
||||
*/
|
||||
public LdapAuthenticationProviderConfigurer<B> rolePrefix(String rolePrefix) {
|
||||
this.rolePrefix = rolePrefix;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Search base for user searches. Defaults to "". Only used with {@link #userSearchFilter(String)}.
|
||||
*
|
||||
* @param userSearchBase search base for user searches
|
||||
* @return the {@link LdapAuthenticationProviderConfigurer} for further customizations
|
||||
*/
|
||||
public LdapAuthenticationProviderConfigurer<B> userSearchBase(String userSearchBase) {
|
||||
this.userSearchBase = userSearchBase;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The LDAP filter used to search for users (optional). For example
|
||||
* "(uid={0})". The substituted parameter is the user's login name.
|
||||
*
|
||||
* @param userSearchFilter
|
||||
* the LDAP filter used to search for users
|
||||
* @return the {@link LdapAuthenticationProviderConfigurer} for further
|
||||
* customizations
|
||||
*/
|
||||
public LdapAuthenticationProviderConfigurer<B> userSearchFilter(String userSearchFilter) {
|
||||
this.userSearchFilter = userSearchFilter;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(B builder) throws Exception {
|
||||
LdapAuthenticationProvider provider = postProcess(build());
|
||||
builder.authenticationProvider(provider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up Password based comparison
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public final class PasswordCompareConfigurer {
|
||||
|
||||
/**
|
||||
* Allows specifying the {@link PasswordEncoder} to use. The default is {@link PlaintextPasswordEncoder}.
|
||||
* @param passwordEncoder the {@link PasswordEncoder} to use
|
||||
* @return the {@link PasswordEncoder} to use
|
||||
*/
|
||||
public PasswordCompareConfigurer passwordEncoder(PasswordEncoder passwordEncoder) {
|
||||
LdapAuthenticationProviderConfigurer.this.passwordEncoder = passwordEncoder;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The attribute in the directory which contains the user password. Defaults to "userPassword".
|
||||
*
|
||||
* @param passwordAttribute the attribute in the directory which contains the user password
|
||||
* @return the {@link PasswordCompareConfigurer} for further customizations
|
||||
*/
|
||||
public PasswordCompareConfigurer passwordAttribute(String passwordAttribute) {
|
||||
LdapAuthenticationProviderConfigurer.this.passwordAttribute = passwordAttribute;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows obtaining a reference to the
|
||||
* {@link LdapAuthenticationProviderConfigurer} for further
|
||||
* customizations
|
||||
*
|
||||
* @return attribute in the directory which contains the user password
|
||||
*/
|
||||
public LdapAuthenticationProviderConfigurer<B> and() {
|
||||
return LdapAuthenticationProviderConfigurer.this;
|
||||
}
|
||||
|
||||
private PasswordCompareConfigurer() {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows building a {@link BaseLdapPathContextSource} and optionally
|
||||
* creating an embedded LDAP instance.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
public final class ContextSourceBuilder {
|
||||
private String ldif = "classpath*:*.ldif";
|
||||
private String managerPassword;
|
||||
private String managerDn;
|
||||
private int port = 33389;
|
||||
private String root = "dc=springframework,dc=org";
|
||||
private String url;
|
||||
|
||||
/**
|
||||
* Specifies an ldif to load at startup for an embedded LDAP server.
|
||||
* This only loads if using an embedded instance. The default is
|
||||
* "classpath*:*.ldif".
|
||||
*
|
||||
* @param ldif
|
||||
* the ldif to load at startup for an embedded LDAP server.
|
||||
* @return the {@link ContextSourceBuilder} for further customization
|
||||
*/
|
||||
public ContextSourceBuilder ldif(String ldif) {
|
||||
this.ldif = ldif;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Username (DN) of the "manager" user identity (i.e.
|
||||
* "uid=admin,ou=system") which will be used to authenticate to a
|
||||
* (non-embedded) LDAP server. If omitted, anonymous access will be
|
||||
* used.
|
||||
*
|
||||
* @param managerDn
|
||||
* the username (DN) of the "manager" user identity used to
|
||||
* authenticate to a LDAP server.
|
||||
* @return the {@link ContextSourceBuilder} for further customization
|
||||
*/
|
||||
public ContextSourceBuilder managerDn(String managerDn) {
|
||||
this.managerDn = managerDn;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The password for the manager DN. This is required if the manager-dn is specified.
|
||||
* @param managerPassword password for the manager DN
|
||||
* @return the {@link ContextSourceBuilder} for further customization
|
||||
*/
|
||||
public ContextSourceBuilder managerPassword(String managerPassword) {
|
||||
this.managerPassword = managerPassword;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The port to connect to LDAP to (the default is 33389).
|
||||
* @param port the port to connect to
|
||||
* @return the {@link ContextSourceBuilder} for further customization
|
||||
*/
|
||||
public ContextSourceBuilder port(int port) {
|
||||
this.port = port;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional root suffix for the embedded LDAP server. Default is
|
||||
* "dc=springframework,dc=org"
|
||||
*
|
||||
* @param root
|
||||
* root suffix for the embedded LDAP server
|
||||
* @return the {@link ContextSourceBuilder} for further customization
|
||||
*/
|
||||
public ContextSourceBuilder root(String root) {
|
||||
this.root = root;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the ldap server URL when not using the embedded LDAP
|
||||
* server. For example, "ldaps://ldap.example.com:33389/dc=myco,dc=org".
|
||||
*
|
||||
* @param url
|
||||
* the ldap server URL
|
||||
* @return the {@link ContextSourceBuilder} for further customization
|
||||
*/
|
||||
public ContextSourceBuilder url(String url) {
|
||||
this.url = url;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link LdapAuthenticationProviderConfigurer} for further
|
||||
* customizations
|
||||
*
|
||||
* @return the {@link LdapAuthenticationProviderConfigurer} for further
|
||||
* customizations
|
||||
*/
|
||||
public LdapAuthenticationProviderConfigurer<B> and() {
|
||||
return LdapAuthenticationProviderConfigurer.this;
|
||||
}
|
||||
|
||||
private DefaultSpringSecurityContextSource build() throws Exception {
|
||||
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(getProviderUrl());
|
||||
if(managerDn != null) {
|
||||
contextSource.setUserDn(managerDn);
|
||||
if(managerPassword == null) {
|
||||
throw new IllegalStateException("managerPassword is required if managerDn is supplied");
|
||||
}
|
||||
contextSource.setPassword(managerPassword);
|
||||
}
|
||||
contextSource = postProcess(contextSource);
|
||||
if(url != null) {
|
||||
return contextSource;
|
||||
}
|
||||
ApacheDSContainer apacheDsContainer = new ApacheDSContainer(root, ldif);
|
||||
apacheDsContainer.setPort(port);
|
||||
postProcess(apacheDsContainer);
|
||||
return contextSource;
|
||||
}
|
||||
|
||||
private String getProviderUrl() {
|
||||
if(url == null) {
|
||||
return "ldap://127.0.0.1:" + port + "/" + root;
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
private ContextSourceBuilder() {}
|
||||
}
|
||||
|
||||
private BaseLdapPathContextSource getContextSource() throws Exception {
|
||||
if(contextSource == null) {
|
||||
contextSource = contextSourceBuilder.build();
|
||||
}
|
||||
return contextSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public PasswordCompareConfigurer passwordCompare() {
|
||||
return new PasswordCompareConfigurer()
|
||||
.passwordAttribute("password")
|
||||
.passwordEncoder(new PlaintextPasswordEncoder());
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user