1
0
mirror of synced 2026-07-13 22:55:10 +00:00

Compare commits

..

211 Commits

Author SHA1 Message Date
Rob Winch 23bdc7d766 Release 2.0.8.RELEASE 2012-10-08 21:18:15 -05:00
Rob Winch d07d97838a Update for javadoc execute with package 2012-10-08 21:09:58 -05:00
Rob Winch f5fc94e1be SEC-2056: DaoAuthenticationProvider performs isPasswordValid when user not found
Previously authenticating a user could take significantly longer than
determining that a user does not exist. This was due to the fact that only
users that were found would use the password encoder and comparing a
password can take a significant amount of time. The difference in the
time required could allow a side channel attack that reveals if a user
exists.

The code has been updated to do comparison against a dummy password
even when the the user was not found.
2012-10-08 15:52:40 -05:00
Rob Winch a4f13a9ae0 Added SCM information to pom for OSS requirements 2012-10-08 07:41:26 -05:00
Rob Winch 5c308c0215 Further maven 3 cleanup 2012-10-07 18:56:51 -05:00
Rob Winch a0f91b2dd2 Update maven-resources-plugin to 2.6 to work with m2e 2012-10-07 18:21:16 -05:00
Rob Winch 6cf44b9de0 Update maven-dependency-plugin to 2.5.1 to support m2e 2012-10-07 18:09:46 -05:00
Luke Taylor 55e501711d Set version to 2.0.8.CI-SNAPSHOT 2011-08-19 13:23:04 -07:00
Luke Taylor d5e6f0b575 Set release version to 2.0.7.RELEASE 2011-08-19 13:18:45 -07:00
Luke Taylor 76dc21469e SEC-1750: Make sure RunAs replacement is constrained to the SecurityContext of the current thread. 2011-08-19 13:18:45 -07:00
Luke Taylor 22b7c9b905 SEC-1742: Make extraInformation in AuthenticationException transient. 2011-08-19 13:18:45 -07:00
Luke Taylor 0cdf202b10 SEC-1744: Do not trust authorities contained in the authentication request in JaasAuthenticationProvider. 2011-08-19 13:18:45 -07:00
Luke Taylor a507e3612a SEC-1741: Modify ContextPropagatingRemoteInvocation to pass a simple combination of principal/credentials as Strings, rather than serializing the whole SecurityContext object from the client. 2011-08-19 13:18:45 -07:00
Luke Taylor f5fbda42e5 SEC-1790: Reject redirect locations containing CR or LF. 2011-08-19 13:18:35 -07:00
Rob Winch d5b72275e5 SEC-1639: FirewalledRequest is now called on the specific FirewalledRequest instance rather that looping through ServletRequestWrappers.
VirtualFilterChain now accepts the FirewalledRequest in the constructor. The reset method is called directly on the instance passed in instead of looping through the ServletRequestWrappers.
2010-12-17 09:42:25 -06:00
Luke Taylor 08a933f930 SEC-1608: Ensure request wrapper is reset for empty filter chains. 2010-12-08 13:56:08 +00:00
Rob Winch 54ffc98bb4 SEC-1606: Added a FirewalledRequestAwareRequestDispatcher that will call FirewalledRequest.reset() before a forward 2010-11-03 15:01:39 -05:00
Luke Taylor 1c3d530b60 Switch versions to 2.0.7.CI-SNAPSHOT 2010-10-25 17:20:25 +01:00
Luke Taylor beb0ec4ba9 Version 2.0.6.RELEASE 2010-10-25 17:18:16 +01:00
Luke Taylor dec2e59fba SEC-1584: Backport of namespace support for injecting custom HttpFirewall instance into FilterChainProxy. 2010-10-14 20:32:01 +01:00
Luke Taylor ed7f589998 SEC-1584: Additional integration tests. 2010-10-13 00:05:38 +01:00
Luke Taylor 8f6ddb0f17 SEC-1584: Backport to 2.0.x branch of request firewalling (normalization checks and path-parameter stripping from servletPath and pathInfo). 2010-10-13 00:04:44 +01:00
Luke Taylor 62a8aca853 .gitignore updates 2010-10-03 23:39:33 +01:00
Luke Taylor 9c6a5135a3 SEC-1532: Patch applied to 2.0.x branch 2010-08-26 14:13:01 +01:00
Luke Taylor 0acf262546 SEC-1462: Added suggested patch (effectively the same as changes in 3.0.x and master branches). 2010-04-20 18:16:45 +01:00
Luke Taylor 6ad652ae97 Update 2.0 branch pom versions. 2010-04-20 18:15:51 +01:00
Luke Taylor 068b3d48ec Add .gitignore to 2.0.x branch 2010-04-16 15:15:54 +01:00
Luke Taylor d6f6a54455 SEC-1444: Backport of changes to 2.0.x 2010-04-16 15:14:01 +01:00
Luke Taylor 4361211c21 Change release from milestone to release 2009-07-14 12:29:51 +00:00
Luke Taylor 71adc26b0f [maven-release-plugin] prepare release spring-security-2.0.5.RELEASE 2009-07-14 00:29:53 +00:00
Luke Taylor eb3288ca34 Removing unnecessary repository declarations 2009-07-13 23:53:12 +00:00
Luke Taylor f3f4cfe804 Minor changes to readme 2009-07-13 23:48:55 +00:00
Luke Taylor 40fa884860 Updated release plugin version 2009-07-13 23:47:53 +00:00
Luke Taylor 3e393c9df6 Tidying test class 2009-07-13 23:47:33 +00:00
Luke Taylor a61aca1abf Update to bundlor M5 2009-07-13 13:07:44 +00:00
Luke Taylor 52d2c904f9 Disable adapters build 2009-07-09 12:38:59 +00:00
Luke Taylor 149fd5d8de Add bundlor templates 2009-07-09 12:26:11 +00:00
Luke Taylor f3f02d8aed Update sec-2.0.x branch to use bundlor 2009-07-09 11:51:26 +00:00
Luke Taylor 781c99f257 SEC-1145: Updated LDAP code to make sure pooling flag is removed when binding as a specific user (for real this time) 2009-06-03 16:57:33 +00:00
Luke Taylor b77f780993 SEC-1145: Updated LDAP code to make sure pooling flag is removed when binding as a specific user 2009-06-03 16:12:54 +00:00
Scott Battaglia 22964837e9 SEC-1066
upgraded to CAS Client for Java 3.1.5
2008-12-22 19:37:50 +00:00
Scott Battaglia 7566802a08 SEC-1046
upgrade to CAS Client for Java 3.1.4
2008-12-16 14:50:04 +00:00
Luke Taylor 4c3867718e SEC-1031: Ported change from trunk. 2008-11-11 23:36:47 +00:00
Luke Taylor ad4b5c487f Temporarily store webflow test sample in sandbox 2008-10-02 23:24:58 +00:00
Luke Taylor 48013b2c93 typo 2008-10-02 15:26:20 +00:00
Luke Taylor 03b21494bc Corrected typo 2008-10-02 14:53:24 +00:00
Luke Taylor ac54976f9e Added appendices to end of doc 2008-10-02 14:50:58 +00:00
Scott Battaglia 7594e1ae2f SEC-984
added template method to allow to override the default of retrieving user by username.
2008-10-01 18:49:52 +00:00
Luke Taylor 97381fb448 SEC-974: Made getExceptionMappings() protected. 2008-10-01 16:25:20 +00:00
Carlos Sanchez af3c77f56f [SEC-997] Remove unnecessary repository 2008-10-01 00:52:52 +00:00
Luke Taylor 4542f00b14 SEC-975: Namespace security syntax does not interpret properties
http://jira.springframework.org/browse/SEC-975. Changed creation of AccessDeniedHandler to use a BeanDefinition to make sure placeholders work OK.
2008-09-12 19:06:53 +00:00
Luke Taylor 5e4634d216 Minor Javadoc improvement. 2008-09-12 14:57:21 +00:00
Luke Taylor d291def963 Removed invalid comment. 2008-09-12 10:18:40 +00:00
Luke Taylor df59cb9dcd Import cleaning. 2008-09-11 14:41:00 +00:00
Luke Taylor ef0389ae79 SEC-976: Removed checks for presence of core-tiger classes. 2008-09-11 14:37:55 +00:00
Luke Taylor 5b9bb8ba54 [maven-release-plugin] prepare for next development iteration 2008-09-05 19:04:22 +00:00
Luke Taylor 73eed2656d [maven-release-plugin] prepare release spring-security-parent-2.0.4 2008-09-05 18:57:43 +00:00
Luke Taylor f935830cdf Class index generation files 2008-09-05 14:26:26 +00:00
Luke Taylor ee04b189b7 Updated schema verisions to 2.0.4 2008-09-05 14:23:42 +00:00
Luke Taylor 8661e17df9 OPEN - issue SEC-960: DN Encoding in LDAPUserDetailsManager.changePassword() causes bind errors
http://jira.springframework.org/browse/SEC-960. Replaced call to toUrl() with toString() to prevent URL encoding when setting up principal name for reconnect() in changePassword() method.
2008-09-05 13:49:38 +00:00
Ben Alex c45b4e0989 SEC-951: Overcome serialization error caused by BasicLookupStrategy failing to modify AccessControlEntryImpl.acl field to the replacement AclImpl (previously old references to StubAclParent were retained). 2008-09-05 05:33:41 +00:00
Ben Alex 0f8ea229c2 SEC-908: Correct issue with BasePermission static initialization failure. 2008-09-05 04:33:52 +00:00
Luke Taylor 5102be3a59 SEC-971: getter for cookieName in AbstractRememberMeServices
http://jira.springframework.org/browse/SEC-971. Added getCookieName() method.
2008-09-04 16:05:34 +00:00
Luke Taylor de379dc2ac Converted literals to classname/interfacename docbook tags for easier indexing 2008-09-02 01:05:57 +00:00
Luke Taylor 09c70bb28e SEC-970: Corrected link in docbook html banner 2008-09-01 16:56:50 +00:00
Luke Taylor 4e2d6f8b2e SEC-967: TextUtils.java does not escape ampersand character
http://jira.springframework.org/browse/SEC-967. Added escaping of '&' character
2008-08-29 12:01:45 +00:00
Luke Taylor d781deffe7 OPEN - issue SEC-966: Consider adding escapeXml attribute to security:authentication
http://jira.springframework.org/browse/SEC-966.  Added escaping of rendered text as default.
2008-08-26 16:21:29 +00:00
Luke Taylor a4e4120443 SEC-963: LDAP Group Search Root
http://jira.springframework.org/browse/SEC-963. Changed namespace instances of DefaultAuthoritiesPopulator to use the root as the default search location.
2008-08-26 13:51:01 +00:00
Luke Taylor 83868a7334 SEC-955: ability to externalize port mapping for secured channel to a property file
http://jira.springframework.org/browse/SEC-955. Changed schema to make port-mapping type xsd:string to allow placeholders.
2008-08-26 13:20:01 +00:00
Luke Taylor 150f3d97d0 SEC-832: NamingEnumeration.hasMore fails on MS AD with PartialResultException
http://jira.springframework.org/browse/SEC-832. Changed searchForSingleEntry method to ignore PartialResultException, similar to Spring LDAP's approach.
2008-08-26 12:49:37 +00:00
Luke Taylor 7f28a8bc5d Refactored DefaultLdapAuthoritiesPopulator to remove contextSource field and setter method. 2008-08-26 12:38:02 +00:00
Luke Taylor 1cfd886517 SEC-922: Spring Security should respect Spring XML boolean operators for AJ pointcut
http://jira.springframework.org/browse/SEC-922. Added method to substitute boolean operators "and, not, or" with aspectj versions "&&, !, ||".
2008-08-18 23:31:14 +00:00
Luke Taylor bb457e1d07 SEC-957: logger.debug without guard causing massive performance hit
http://jira.springframework.org/browse/SEC-957. Added debug logging guard as requested.
2008-08-18 18:20:48 +00:00
Luke Taylor 09cf90258f SEC-758: Both AspectJSecurityInterceptor and AspectJAnnotationSecurityInterceptor not usable with @AspectJ notation
http://jira.springframework.org/browse/SEC-758. Added "throws Throwable" to AspectJAnnotationCallback signature.
2008-08-18 14:47:28 +00:00
Luke Taylor e15d7a78cd SEC-956: Remove MapBasedMethodDefinitionSource.lookupAttributes
http://jira.springframework.org/browse/SEC-956. Done.
2008-08-18 13:13:18 +00:00
Luke Taylor 3bf5e406b7 SEC-936: NPE in AbstractFallbackMethodDefinitionSource
http://jira.springframework.org/browse/SEC-936. Changed to check if the value of MethodInvocation.getThis() is null to prevent NPE. MapBasedMethodDefinitionSource now ignores calls to findAttributes() with a null target class (all its entries require a class) and the fallback option in AbstractFallbackMethodDefinitionSource is used if the targetClass is null (i.e. Method.getDeclaringClass() will be used as the Class)
2008-08-16 02:31:36 +00:00
Luke Taylor 6a68a2531c SEC-904: Moved test module out of sandbox 2008-08-16 02:24:32 +00:00
Luke Taylor 959cdd8335 SEC-936: Tests 2008-08-16 01:58:45 +00:00
Luke Taylor 8e0a6b9d1a SEC-904: Test module updates 2008-08-15 23:54:16 +00:00
Luke Taylor 827d0e1ebf OPEN - issue SEC-865: Re-Challenge NTLM Clients after Authentication Failure
http://jira.springframework.org/browse/SEC-865. Changed NTLM filter to re-challenge if retryOnAuthFailure is set and the Smb logon call fails. Updated JCIFS version in pom.
2008-08-15 22:44:22 +00:00
Luke Taylor 55d357f42d OPEN - issue SEC-905: <protect-pointcut /> pointcuts do not respect method arguments
http://jira.springframework.org/browse/SEC-905. Added extra registration method to MapBasedMethodDefinitionSource which takes a Method instance rather than the method name.
2008-08-12 17:11:38 +00:00
Luke Taylor d9ab0758ee SEC-954: Removed test dependency on AbstractMethodDefinitionSource. 2008-08-12 17:08:55 +00:00
Luke Taylor 36b35e3b1f CLOSED - issue SEC-953: Query string isn't ignored while url - filterchain pattern matching
http://jira.springframework.org/browse/SEC-953. Fixed autoboxing issue.
2008-08-11 21:15:09 +00:00
Luke Taylor 39a656eb78 OPEN - issue SEC-953: Query string isn't ignored while url - filterchain pattern matching
http://jira.springframework.org/browse/SEC-953. Added stripQueryStringFromUrls parameter to FilterChainProxy which works the same as the one on DefaultFilterInvocationDefinitionSource. This defaults to true when used with ant path matching.
2008-08-11 19:15:33 +00:00
Luke Taylor b6dec19e90 SEC-932: Added supplied class and test class. 2008-08-11 16:36:01 +00:00
Luke Taylor 69e776f581 Fixed invalid tags in faq.fml 2008-08-11 16:18:26 +00:00
Luke Taylor 3ab9fcdcaf Tidying. 2008-08-11 15:05:16 +00:00
Luke Taylor 5e3204a5cb Typo 2008-08-09 11:22:35 +00:00
Luke Taylor 6409f140e0 SEC-902: Changed Ntlm entry point to send 403 if no failure URL set 2008-08-08 16:44:13 +00:00
Luke Taylor 130e70373f SEC-936: Initial test which fails to reproduce the problem 2008-08-08 16:41:23 +00:00
Luke Taylor 3a9eb018ba SEC-950: Added test to attempt to reproduce problem. 2008-08-08 15:41:14 +00:00
Luke Taylor 8b376ccdeb SEC-910: Finished LDAP ns reference 2008-08-08 14:59:44 +00:00
Luke Taylor b3a23b4377 Some minor improvements to schema comments 2008-08-07 19:15:13 +00:00
Luke Taylor 7461d0e5f1 Added authentication, method security and start of LDAP ns info 2008-08-07 19:12:56 +00:00
Luke Taylor 566f656eba Added ldap-server xml:id 2008-08-07 19:11:43 +00:00
Luke Taylor e5d2578aec Added example of @Secured use and some extra explanation 2008-08-07 19:10:53 +00:00
Luke Taylor fb3d0b7f25 Fixed link 2008-08-07 19:09:49 +00:00
Luke Taylor 2d0b594a97 Fixed missing section closing tag 2008-08-07 15:21:25 +00:00
Luke Taylor 42af39a59e Some corrections to explicit FilterChainProxy information 2008-08-07 13:33:36 +00:00
Luke Taylor 930be9338b Added info on default target options when using form-login 2008-08-07 12:41:12 +00:00
Luke Taylor c1a6ae0832 Added faq on required dependencies 2008-08-07 12:40:25 +00:00
Luke Taylor c49bc7ffbb SEC-910: Finishing off http part of namespace appendix 2008-08-07 10:47:59 +00:00
Luke Taylor 25814d341d Tidying. 2008-08-06 16:18:05 +00:00
Luke Taylor 21eb70c576 Corrected context file name in petclinic tutorial 2008-08-06 15:40:01 +00:00
Luke Taylor e951c42c2b Improved javadoc. Some tidying up. 2008-08-06 15:28:04 +00:00
Luke Taylor 7258d30e13 Reinstated missing author tag and some minor tidying (de-jalopying). Removed unused logger. 2008-08-06 13:41:01 +00:00
Luke Taylor 22a64c1555 Added faq on missing session listener 2008-08-06 11:03:53 +00:00
Luke Taylor ff13df03ac SEC-910: More updates to namespace appendix 2008-08-06 00:21:46 +00:00
Luke Taylor ecd63cabda Added use of ANY_CHANNEL attribute to channel-security docbook 2008-08-06 00:20:58 +00:00
Luke Taylor f31bcbee07 Minor formatting 2008-08-06 00:19:46 +00:00
Luke Taylor 3ee3591feb SEC-947: Added check on "before" and "after" values to make sure they don't overflow when decremented/incremented respectfully. 2008-08-05 23:26:01 +00:00
Luke Taylor fbeb47d559 SEC-947: Added clarification to docs that FIRST and LAST should be used with position attribute 2008-08-05 23:24:49 +00:00
Luke Taylor 1c9c8f0883 SEC-910: Updates to ns appendix 2008-08-05 12:03:50 +00:00
Luke Taylor f821b0f0f8 Fix issues with move of TestingAuthenticationToken 2008-08-04 20:42:48 +00:00
Luke Taylor 4165e15861 Fix issues with move of TestingAuthenticationToken 2008-08-04 20:14:20 +00:00
Luke Taylor aa75b2fa6d Fixes to match TestingAuthenticationToken changes 2008-08-04 13:50:27 +00:00
Luke Taylor d6918c88a7 Fixes to match TestingAuthenticationToken changes 2008-08-04 13:48:44 +00:00
Luke Taylor b6d088e40d SEC-944: Minor updates to schema appendix 2008-08-04 13:29:42 +00:00
Luke Taylor 069a75b8fc minor change to wording 2008-08-04 13:29:06 +00:00
Luke Taylor 1af7eed433 SEC-883: RoleHierarchyVoter
http://jira.springframework.org/browse/SEC-883. Added RoleHierarchyVoter and deprecated existing approach. Also moved TestingAuthenticationToken to test package structure.
2008-08-04 13:08:03 +00:00
Luke Taylor e982e91846 SEC-944: Added db schema reference (and start of namespace appendix) 2008-08-01 13:57:42 +00:00
Luke Taylor 54ac7b3e46 SEC-935: Updated schema to include OpenID filter name. Also updated some doc comments and added default schema name (spring-security.xsd) to schemas. 2008-08-01 12:51:31 +00:00
Luke Taylor 3049b933d9 Moved XML test snippet to ConfigTestUtils class and removed context files from core-tiger tests in favour of in-memory XML 2008-07-31 21:35:29 +00:00
Luke Taylor c8b22d8e36 SEC-923: Fixed broken build due to missing test class. 2008-07-31 21:22:19 +00:00
Luke Taylor 1d96283876 Removed commented out line. 2008-07-31 20:45:25 +00:00
Luke Taylor ef44bd91f2 SEC-933: Added test for security pointcut applied to a UserDetailsService. 2008-07-31 20:32:43 +00:00
Luke Taylor d7926f3557 SEC-943: Forgot to commit tests. 2008-07-31 20:30:56 +00:00
Luke Taylor e5d86b13b7 SEC-941: Embedded ldap-server uses hard-coded ldap url for importing ldif files
http://jira.springframework.org/browse/SEC-941. Changed LdapUtils.parseRootDnFromUrl to use URI.getRawPath() so the returned root value still contains the escaping. I think this should be Ok.
2008-07-31 19:50:08 +00:00
Ray Krueger 3393ea7aaa SEC-923: Realm support for discovering relying parties.
A new "realmMapping" property can be configured on the OpenIDAuthenticationProcessingFilter to map the "return_to" url to a realm. If there is no mapping present the "return_to" url will be parsed and the protocol, hostname and port will be used with a trailing "/"
2008-07-31 19:23:12 +00:00
Luke Taylor 67e5afbb79 OPEN - issue SEC-881: PreAuthenticatedFilter continues filter chain after unsuccessfulAuthentication(...)
http://jira.springframework.org/browse/SEC-881. Updated Javadoc.
2008-07-31 15:56:37 +00:00
Luke Taylor 000bb1cbed OPEN - issue SEC-881: PreAuthenticatedFilter continues filter chain after unsuccessfulAuthentication(...)
http://jira.springframework.org/browse/SEC-881. Added test class.
2008-07-31 15:42:04 +00:00
Luke Taylor 243c4f22d4 OPEN - issue SEC-899: GrantedAuthorityImpl.compareTo should handle null roles
http://jira.springframework.org/browse/SEC-899. Changed to return -1 when compared to custom auhority which returns null from getAuthority()
2008-07-31 13:01:22 +00:00
Luke Taylor d4c105d8ba OPEN - issue SEC-934: security:intercept-url throws NPE if defined twice with the same url
http://jira.springframework.org/browse/SEC-934. Added log warning when the same url is used multiple times.
2008-07-30 15:03:47 +00:00
Luke Taylor f6ff958411 Renamed rnc file. 2008-07-30 11:05:44 +00:00
Luke Taylor 4bb3eb12c3 SEC-933: global-method-security and aop:aspectj-autoproxy throws NullPointerException in some situations
http://jira.springframework.org/browse/SEC-933. Removed the setting of the attributeSource field from the interceptor in MethodDefinitionSourceAdvisor as this was overwriting the version supplied with the constructor with null (causing the NPE).
Also implemented lazy initialization of the authentication provider list from the bean factory in a custom NamespaceAuthenticationManager (extends ProviderManager and introspects the BeanFactory when getProviders() is first called). This should prevent the perennial problem of the eager initialization of UserDetailsService and other beans when the interceptor is eagerly initialized by something like aspectj-autoproxy.
2008-07-30 11:01:23 +00:00
Luke Taylor f538a36cd3 SEC-939: Changed XML header to include schema locations for clarification. 2008-07-29 10:40:50 +00:00
Luke Taylor 6e06789a28 SEC-937: Added CAS logout filter to sample application 2008-07-28 10:53:55 +00:00
Luke Taylor 6b45eda37c SEC-877, SEC-553: Added code to sandbox/other 2008-07-17 17:46:11 +00:00
Luke Taylor f453264bde SEC-909: custom remember me services doesn't get registered as logout handler
http://jira.springframework.org/browse/SEC-909. HttpSecurityBeanDefinitionParser now passes the resolved RememberMeServices bean name to the LogoutBeanDefinitionparser so that it an use it explicitly.
2008-07-15 18:22:53 +00:00
Luke Taylor 1ddc033fe5 SEC-903: Wrong attribute mapping when using jdbc-user-service bean
http://jira.springframework.org/browse/SEC-903. Corrected property name set by JdbcUserServiceBeanDefinitionParser (was setting authorities query rather than groups one).
2008-07-15 16:43:57 +00:00
Luke Taylor e303e8b71a SEC-924: Implement automatic injection of namespace created RememberMeServices into custom AbstractProcessingFilter based beans.
http://jira.springframework.org/browse/SEC-924. Delayed setting of NullRememberMeServices in AbstractProcessingFilter until afterPropertiesSet method is called, allowing the null value to be read by the namespace and the confgiured RememberMeServices bean injected.
2008-07-15 14:52:13 +00:00
Luke Taylor bf5896600e OPEN - issue SEC-913: SwitchUserProcessingFilter modifies the switchFailureUrl member variable on failure
http://jira.springframework.org/browse/SEC-913. Applied patch as suggested (use sendRedirect method for failure URL).
2008-07-15 13:42:30 +00:00
Luke Taylor b4c63db680 SEC-921: Improved messages_zh_CN.properties for Chinese
http://jira.springframework.org/browse/SEC-921. Added contributed file.
2008-07-15 11:11:21 +00:00
Luke Taylor a56c13fb22 SEC-912: Added callback methods to BasicProcessingFilter for successful and unsuccessful authentication. 2008-07-12 17:40:39 +00:00
Luke Taylor 697c7c5f48 SEC-918: Added more info on DB schema to javadoc 2008-07-12 15:21:24 +00:00
Luke Taylor b32a418175 Added mmore info on 'springSecurityFilter' chain and warning not to use this bean name explicitly 2008-07-12 15:14:43 +00:00
Luke Taylor 4cebc67088 Added example config for JDBCDaoImpl and user-service-ref in namespace 2008-07-11 19:33:15 +00:00
Luke Taylor fbc7c31b5e SEC-918: Added DDL or user and authorities tables to section on JDBC UserDetailsService 2008-07-11 19:21:00 +00:00
Luke Taylor 7dc998196a Added faq on JDK and Spring version requirements 2008-07-11 14:43:36 +00:00
Luke Taylor 768219af81 Added exta sub-headings to facilitate searching for particular topics from content page 2008-07-11 13:27:19 +00:00
Luke Taylor 7039bfdfbe Minor text spacing correction 2008-07-11 13:11:35 +00:00
Luke Taylor d13b32c77f Clarified that paths are relative to the checked out source tree 2008-07-11 12:19:19 +00:00
Luke Taylor dce709a669 Minor code formatting in docbookk 2008-07-11 12:14:00 +00:00
Luke Taylor d9634bcb39 SEC-920: Update preauth sample to make use of internal authentication manager
http://jira.springframework.org/browse/SEC-920. Updated context file to use <custom-authentication-provider>.
2008-07-11 10:56:57 +00:00
Luke Taylor 8fe1b4b402 SEC-914: Slight modification of tld description text for readability. 2008-07-11 08:14:28 +00:00
Luke Taylor 30f1e5729a SEC-914: Corrected tagllib descriptor documentation for var attribute in authentication tag. 2008-07-11 07:52:52 +00:00
Luke Taylor 6d179122d3 SEC-916: Added Spanish messages contribution. 2008-07-10 15:32:01 +00:00
Luke Taylor bd4ed794ea SEC-904: Renamed SessionRegistryImplMultithreadedTests 2008-07-02 19:25:28 +00:00
Luke Taylor 2cda6242c8 SEC-904: Moved multi-threaded tests into sandbox 2008-07-02 19:19:21 +00:00
Luke Taylor 479693ced7 SEC-900: Added extra checks on expiry time 2008-07-02 18:40:55 +00:00
Luke Taylor d5df35f739 Update sandbox poms post-release 2008-07-02 16:27:02 +00:00
Luke Taylor b99a5dec29 Various mods to heavyduty app 2008-07-02 16:25:18 +00:00
Luke Taylor e1fcacbca5 Added general question on other security concerns 2008-07-01 21:00:30 +00:00
Luke Taylor bf45ff94e7 SEC-901: Improve docs on custom-filter and avoiding conflicts with namespace filters 2008-07-01 14:20:18 +00:00
Luke Taylor c372c2df87 SEC-896: Changed result.toString() to String.valueOf(result) in tag class to prevent NPE when value of property is null 2008-06-30 21:02:23 +00:00
Luke Taylor dd5edbcce9 Added labels to faqs 2008-06-30 20:59:27 +00:00
Luke Taylor 3a25766da1 Adding sub-headings etc to 'secure objects' section 2008-06-27 13:12:27 +00:00
Luke Taylor 6ff0b969d5 Corrected ldap sample config (traditional bean version was wrong) 2008-06-23 23:43:48 +00:00
Luke Taylor 775a6c3939 [maven-release-plugin] prepare for next development iteration 2008-06-23 14:10:35 +00:00
Luke Taylor 87d50aecce [maven-release-plugin] prepare release spring-security-parent-2.0.3 2008-06-23 14:05:36 +00:00
Luke Taylor 125f5911c0 Heavyduty sample additions to check multiple-parameter values 2008-06-23 13:27:08 +00:00
Luke Taylor 57558de3ec Added error page URL to openid login sample 2008-06-23 13:18:35 +00:00
Luke Taylor 456e737d31 Corrections to readme 2008-06-23 13:16:50 +00:00
Luke Taylor 66008817c4 Changed OSGi version prior to 2.0.3 release 2008-06-23 13:14:42 +00:00
Luke Taylor 5ec06778f5 removed optional scope from jaxen dependecy in preauth sample as it breaks war file 2008-06-23 13:00:03 +00:00
Luke Taylor 2fa991c44f Some reorganization of itest module 2008-06-22 21:42:25 +00:00
Luke Taylor 3ee8733261 SEC-879: Added required BeanPostProcessor to set SessionRegistry is set on namespace registered AbstractProcessingFilter and SessionFixationProtectionFilter when using custom ConcurrentSessionController
http://jira.springframework.org/browse/SEC-879.
2008-06-20 22:08:05 +00:00
Luke Taylor d5ee89bb7c Correct typo in error message. 2008-06-19 15:21:03 +00:00
Luke Taylor ff5bfccdba SEC-892: Linked use of create-session='never' in namespace to corresponding properties in ExceptionTranslationFilter and AbstractProcessingFilter 2008-06-19 13:46:45 +00:00
Scott Battaglia 5b089aea16 SEC-852
provided mechanism to do get a proxy ticket
2008-06-18 17:34:14 +00:00
Scott Battaglia d7f194df78 SEC-886
upgraded to the most recent CAS Client for Java (3.1.3)
2008-06-18 17:22:20 +00:00
Luke Taylor c56d524bd9 SEC-887: Added setter method for account status checker. 2008-06-18 12:00:45 +00:00
Luke Taylor af5f193ec1 SEC-890: Corrected use of dataSource property name in RememberMeBDP. 2008-06-18 10:35:30 +00:00
Luke Taylor 7d79ae5424 SEC-880: Fix incorrect index value. 2008-06-13 10:58:01 +00:00
Luke Taylor 3e5b65bd85 Updated version names etc in petclinic tutorial 2008-06-12 12:23:25 +00:00
Luke Taylor 64b5fa0131 Added OWASP and Spring Framework links to site template 2008-06-11 17:46:43 +00:00
Luke Taylor fe929bf9b9 Added reference to OWASP site to preface of ref manual 2008-06-11 17:35:27 +00:00
Luke Taylor 8a2581c939 Experimental integration test module 2008-06-10 22:17:44 +00:00
Luke Taylor 55caab3bbc Added spring-jdbc dep back in core-tiger (since it's optional in core) hence not transient) 2008-06-10 22:00:27 +00:00
Luke Taylor 269865ca65 Removed spring deps from core-tiger pom as they are transiently available anyway 2008-06-10 21:41:20 +00:00
Luke Taylor 32b8009bee SEC-875: Removed duplicated parameters from SavedRequestWrapper.getParameterValues() 2008-06-09 23:33:36 +00:00
Luke Taylor 3b775d29d3 SEC-870: Polish messages file contribution 2008-06-08 22:09:47 +00:00
Luke Taylor 0401dddda8 SEC-868: Added example siteminder config 2008-06-08 18:53:22 +00:00
Ben Alex 358f284f42 SEC-760: Correct bug where more than one concurrent JaasAuthenticationProvider used. 2008-06-06 06:13:14 +00:00
Ben Alex b403216494 SEC-838: Make fields in AbstractAclProvider protected to facilitate subclass reuse. 2008-06-06 03:01:51 +00:00
Ben Alex 371769740a SEC-831: Improve support for Postges, which requires "AS" for table aliasing, together with stored procedures for sequence allocation. 2008-06-06 02:55:53 +00:00
Ben Alex e38d5dfd87 SEC-813: Allow custom Permission classes to be used. 2008-06-06 02:37:19 +00:00
Ben Alex ff5666ae83 SEC-819: Properly support integer (and other numeric) identifiers. 2008-06-06 01:05:46 +00:00
Ben Alex de897ad1ac SEC-867: Remove superfluous <property /> entry. 2008-06-05 22:51:47 +00:00
Luke Taylor ff785a829f [maven-release-plugin] prepare for next development iteration 2008-06-03 16:07:20 +00:00
Luke Taylor db1d8604a6 [maven-release-plugin] prepare release spring-security-parent-2.0.2 2008-06-03 16:05:40 +00:00
Luke Taylor f762920239 Typo 2008-06-03 15:49:21 +00:00
Luke Taylor 70826f1202 Shorten faq question 2008-06-03 15:48:30 +00:00
Luke Taylor ea25299bd0 Removed sandbox from build because of site generation problems 2008-06-03 15:37:41 +00:00
Luke Taylor d784d854cd Corrected log file name. 2008-06-03 14:57:40 +00:00
Luke Taylor 9308284bd4 SEC-864: Removed duplicate OpenID provider. 2008-06-03 14:53:43 +00:00
Luke Taylor c34eb497c8 Correct captcha module dependency 2008-06-03 14:11:30 +00:00
Luke Taylor de250d2073 Add sandbox code to build for 2.0.2 release 2008-06-03 13:41:38 +00:00
Luke Taylor 8df56c8ac5 Test log4j properties file for core-tiger module 2008-06-03 13:21:23 +00:00
Luke Taylor 192aa25b60 Addtional sample app files 2008-06-03 13:04:50 +00:00
Luke Taylor d95a5597c8 Bug-testing changes to heavyduty sample 2008-06-03 12:58:13 +00:00
341 changed files with 15727 additions and 4125 deletions
+15
View File
@@ -0,0 +1,15 @@
target
.gradle
build/
.idea
out/
*.ipr
*.iml
*.iws
*.log
intellij/
.settings
.classpath
.project
.DS_Store
atlassian-ide-plugin.xml
+19 -28
View File
@@ -3,14 +3,13 @@
<parent>
<artifactId>spring-security-parent</artifactId>
<groupId>org.springframework.security</groupId>
<version>2.0.2</version>
<version>2.0.8.RELEASE</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-acl</artifactId>
<name>Spring Security - ACL module</name>
<version>2.0.2</version>
<packaging>bundle</packaging>
<packaging>jar</packaging>
<dependencies>
<dependency>
@@ -25,6 +24,13 @@
<classifier>tests</classifier>
<scope>test</scope>
</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-jdbc</artifactId>
@@ -45,29 +51,14 @@
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<spring.osgi.export>
org.springframework.security.*;version=${pom.version.osgi}
</spring.osgi.export>
<spring.osgi.import>
net.sf.ehcache.*;version="[1.4.1, 2.0.0)";resolution:=optional,
org.springframework.security.*;version="[${pom.version.osgi},${pom.version.osgi}]",
org.springframework.context.*;version="${spring.version.osgi}",
org.springframework.dao.*;version="${spring.version.osgi}";resolution:=optional,
org.springframework.jdbc.*;version="${spring.version.osgi}";resolution:=optional,
org.springframework.transaction.support.*;version="${spring.version.osgi}";resolution:=optional,
org.springframework.util.*;version="${spring.version.osgi}",
org.apache.commons.logging.*;version="[1.1.1, 2.0.0)",
javax.sql.*
</spring.osgi.import>
<spring.osgi.private.pkg>
!org.springframework.security.*
</spring.osgi.private.pkg>
<spring.osgi.symbolic.name>org.springframework.security.acls</spring.osgi.symbolic.name>
</properties>
<!--
<build>
<plugins>
<plugin>
<groupId>com.springsource.bundlor</groupId>
<artifactId>com.springsource.bundlor.maven</artifactId>
</plugin>
</plugins>
</build>
-->
</project>
@@ -0,0 +1,59 @@
package org.springframework.security.acls.domain;
import org.springframework.security.acls.AclFormattingUtils;
import org.springframework.security.acls.Permission;
/**
* Provides an abstract superclass for {@link Permission} implementations.
*
* @author Ben Alex
* @since 2.0.3
* @see AbstractRegisteredPermission
*
*/
public abstract class AbstractPermission implements Permission {
//~ Instance fields ================================================================================================
protected char code;
protected int mask;
//~ Constructors ===================================================================================================
protected AbstractPermission(int mask, char code) {
this.mask = mask;
this.code = code;
}
//~ Methods ========================================================================================================
public final boolean equals(Object arg0) {
if (arg0 == null) {
return false;
}
if (!(arg0 instanceof Permission)) {
return false;
}
Permission rhs = (Permission) arg0;
return (this.mask == rhs.getMask());
}
public final int getMask() {
return mask;
}
public String getPattern() {
return AclFormattingUtils.printBinary(mask, code);
}
public final String toString() {
return this.getClass().getSimpleName() + "[" + getPattern() + "=" + mask + "]";
}
public final int hashCode() {
return this.mask;
}
}
@@ -14,157 +14,59 @@
*/
package org.springframework.security.acls.domain;
import org.springframework.security.acls.AclFormattingUtils;
import org.springframework.security.acls.Permission;
import org.springframework.util.Assert;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
/**
* A set of standard permissions.
*
*
* <p>
* You may subclass this class to add additional permissions, or use this class as a guide
* for creating your own permission classes.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public final class BasePermission implements Permission {
//~ Static fields/initializers =====================================================================================
public class BasePermission extends AbstractPermission {
public static final Permission READ = new BasePermission(1 << 0, 'R'); // 1
public static final Permission WRITE = new BasePermission(1 << 1, 'W'); // 2
public static final Permission CREATE = new BasePermission(1 << 2, 'C'); // 4
public static final Permission DELETE = new BasePermission(1 << 3, 'D'); // 8
public static final Permission ADMINISTRATION = new BasePermission(1 << 4, 'A'); // 16
private static Map locallyDeclaredPermissionsByInteger = new HashMap();
private static Map locallyDeclaredPermissionsByName = new HashMap();
protected static DefaultPermissionFactory defaultPermissionFactory = new DefaultPermissionFactory();
static {
Field[] fields = BasePermission.class.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
try {
Object fieldValue = fields[i].get(null);
if (BasePermission.class.isAssignableFrom(fieldValue.getClass())) {
// Found a BasePermission static field
BasePermission perm = (BasePermission) fieldValue;
locallyDeclaredPermissionsByInteger.put(new Integer(perm.getMask()), perm);
locallyDeclaredPermissionsByName.put(fields[i].getName(), perm);
}
} catch (Exception ignore) {}
}
}
//~ Instance fields ================================================================================================
private char code;
private int mask;
//~ Constructors ===================================================================================================
private BasePermission(int mask, char code) {
this.mask = mask;
this.code = code;
}
//~ Methods ========================================================================================================
/**
* Dynamically creates a <code>CumulativePermission</code> or <code>BasePermission</code> representing the
* active bits in the passed mask.
*
* @param mask to build
*
* @return a Permission representing the requested object
/**
* Registers the public static permissions defined on this class. This is mandatory so
* that the static methods will operate correctly.
*/
public static Permission buildFromMask(int mask) {
if (locallyDeclaredPermissionsByInteger.containsKey(new Integer(mask))) {
// The requested mask has an exactly match against a statically-defined BasePermission, so return it
return (Permission) locallyDeclaredPermissionsByInteger.get(new Integer(mask));
}
// To get this far, we have to use a CumulativePermission
CumulativePermission permission = new CumulativePermission();
for (int i = 0; i < 32; i++) {
int permissionToCheck = 1 << i;
if ((mask & permissionToCheck) == permissionToCheck) {
Permission p = (Permission) locallyDeclaredPermissionsByInteger.get(new Integer(permissionToCheck));
Assert.state(p != null,
"Mask " + permissionToCheck + " does not have a corresponding static BasePermission");
permission.set(p);
}
}
return permission;
static {
registerPermissionsFor(BasePermission.class);
}
public static Permission[] buildFromMask(int[] masks) {
if ((masks == null) || (masks.length == 0)) {
return new Permission[0];
}
Permission[] permissions = new Permission[masks.length];
for (int i = 0; i < masks.length; i++) {
permissions[i] = buildFromMask(masks[i]);
}
return permissions;
protected BasePermission(int mask, char code) {
super(mask, code);
}
public static Permission buildFromName(String name) {
Assert.isTrue(locallyDeclaredPermissionsByName.containsKey(name), "Unknown permission '" + name + "'");
return (Permission) locallyDeclaredPermissionsByName.get(name);
protected final static void registerPermissionsFor(Class subClass) {
defaultPermissionFactory.registerPublicPermissions(subClass);
}
public final static Permission buildFromMask(int mask) {
return defaultPermissionFactory.buildFromMask(mask);
}
public static Permission[] buildFromName(String[] names) {
if ((names == null) || (names.length == 0)) {
return new Permission[0];
}
Permission[] permissions = new Permission[names.length];
for (int i = 0; i < names.length; i++) {
permissions[i] = buildFromName(names[i]);
}
return permissions;
public final static Permission[] buildFromMask(int[] masks) {
return defaultPermissionFactory.buildFromMask(masks);
}
public boolean equals(Object arg0) {
if (arg0 == null) {
return false;
}
if (!(arg0 instanceof Permission)) {
return false;
}
Permission rhs = (Permission) arg0;
return (this.mask == rhs.getMask());
public final static Permission buildFromName(String name) {
return defaultPermissionFactory.buildFromName(name);
}
public int getMask() {
return mask;
public final static Permission[] buildFromName(String[] names) {
return defaultPermissionFactory.buildFromName(names);
}
public String getPattern() {
return AclFormattingUtils.printBinary(mask, code);
}
public String toString() {
return "BasePermission[" + getPattern() + "=" + mask + "]";
}
public int hashCode() {
return this.mask;
}
}
}
@@ -19,20 +19,21 @@ import org.springframework.security.acls.Permission;
/**
* Represents a <code>Permission</code> that is constructed at runtime from other permissions.<p>Methods return
* <code>this</code>, in order to facilitate method chaining.</p>
* Represents a <code>Permission</code> that is constructed at runtime from other permissions.
*
* <p>Methods return <code>this</code>, in order to facilitate method chaining.</p>
*
* @author Ben Alex
* @version $Id$
*/
public class CumulativePermission implements Permission {
//~ Instance fields ================================================================================================
public class CumulativePermission extends AbstractPermission {
private String pattern = THIRTY_TWO_RESERVED_OFF;
private int mask = 0;
//~ Methods ========================================================================================================
public CumulativePermission() {
super(0, ' ');
}
public CumulativePermission clear(Permission permission) {
this.mask &= ~permission.getMask();
this.pattern = AclFormattingUtils.demergePatterns(this.pattern, permission.getPattern());
@@ -46,43 +47,16 @@ public class CumulativePermission implements Permission {
return this;
}
public boolean equals(Object arg0) {
if (arg0 == null)
{
return false;
}
if (!(arg0 instanceof Permission)) {
return false;
}
Permission rhs = (Permission) arg0;
return (this.mask == rhs.getMask());
}
public int hashCode() {
return this.mask;
}
public int getMask() {
return this.mask;
}
public String getPattern() {
return this.pattern;
}
public CumulativePermission set(Permission permission) {
this.mask |= permission.getMask();
this.pattern = AclFormattingUtils.mergePatterns(this.pattern, permission.getPattern());
return this;
}
public String toString() {
return "CumulativePermission[" + pattern + "=" + this.mask + "]";
public String getPattern() {
return this.pattern;
}
}
@@ -0,0 +1,127 @@
package org.springframework.security.acls.domain;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import org.springframework.security.acls.Permission;
import org.springframework.security.acls.jdbc.LookupStrategy;
import org.springframework.util.Assert;
/**
* Default implementation of {@link PermissionFactory}.
*
* <p>
* Generally this class will be used by a {@link Permission} instance, as opposed to being dependency
* injected into a {@link LookupStrategy} or similar. Nevertheless, the latter mode of operation is
* fully supported (in which case your {@link Permission} implementations probably should extend
* {@link AbstractPermission} instead of {@link AbstractRegisteredPermission}).
* </p>
*
* @author Ben Alex
* @since 2.0.3
*
*/
public class DefaultPermissionFactory implements PermissionFactory {
private Map registeredPermissionsByInteger = new HashMap();
private Map registeredPermissionsByName = new HashMap();
/**
* Permit registration of a {@link DefaultPermissionFactory} class. The class must provide
* public static fields of type {@link Permission} to represent the possible permissions.
*
* @param clazz a {@link Permission} class with public static fields to register
*/
public void registerPublicPermissions(Class clazz) {
Assert.notNull(clazz, "Class required");
Assert.isAssignable(Permission.class, clazz);
Field[] fields = clazz.getFields();
for (int i = 0; i < fields.length; i++) {
try {
Object fieldValue = fields[i].get(null);
if (Permission.class.isAssignableFrom(fieldValue.getClass())) {
// Found a Permission static field
Permission perm = (Permission) fieldValue;
String permissionName = fields[i].getName();
registerPermission(perm, permissionName);
}
} catch (Exception ignore) {}
}
}
public void registerPermission(Permission perm, String permissionName) {
Assert.notNull(perm, "Permission required");
Assert.hasText(permissionName, "Permission name required");
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);
Assert.isTrue(!registeredPermissionsByName.containsKey(permissionName), "An existing Permission already provides name '" + permissionName + "'");
// Register the new Permission
registeredPermissionsByInteger.put(mask, perm);
registeredPermissionsByName.put(permissionName, perm);
}
public Permission buildFromMask(int mask) {
if (registeredPermissionsByInteger.containsKey(new Integer(mask))) {
// The requested mask has an exactly match against a statically-defined Permission, so return it
return (Permission) registeredPermissionsByInteger.get(new Integer(mask));
}
// To get this far, we have to use a CumulativePermission
CumulativePermission permission = new CumulativePermission();
for (int i = 0; i < 32; i++) {
int permissionToCheck = 1 << i;
if ((mask & permissionToCheck) == permissionToCheck) {
Permission p = (Permission) registeredPermissionsByInteger.get(new Integer(permissionToCheck));
Assert.state(p != null, "Mask " + permissionToCheck + " does not have a corresponding static Permission");
permission.set(p);
}
}
return permission;
}
public Permission[] buildFromMask(int[] masks) {
if ((masks == null) || (masks.length == 0)) {
return new Permission[0];
}
Permission[] permissions = new Permission[masks.length];
for (int i = 0; i < masks.length; i++) {
permissions[i] = buildFromMask(masks[i]);
}
return permissions;
}
public Permission buildFromName(String name) {
Assert.isTrue(registeredPermissionsByName.containsKey(name), "Unknown permission '" + name + "'");
return (Permission) registeredPermissionsByName.get(name);
}
public Permission[] buildFromName(String[] names) {
if ((names == null) || (names.length == 0)) {
return new Permission[0];
}
Permission[] permissions = new Permission[names.length];
for (int i = 0; i < names.length; i++) {
permissions[i] = buildFromName(names[i]);
}
return permissions;
}
}
@@ -0,0 +1,24 @@
package org.springframework.security.acls.domain;
import org.springframework.security.acls.Permission;
/**
* Provides a simple mechanism to retrieve {@link Permission} instances from integer masks.
*
* @author Ben Alex
* @since 2.0.3
*
*/
public interface PermissionFactory {
/**
* Dynamically creates a <code>CumulativePermission</code> or <code>BasePermission</code> representing the
* active bits in the passed mask.
*
* @param mask to build
*
* @return a Permission representing the requested object
*/
public abstract Permission buildFromMask(int mask);
}
@@ -18,12 +18,14 @@ import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import javax.sql.DataSource;
@@ -49,6 +51,7 @@ import org.springframework.security.acls.sid.PrincipalSid;
import org.springframework.security.acls.sid.Sid;
import org.springframework.security.util.FieldUtils;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
@@ -172,14 +175,33 @@ public final class BasicLookupStrategy implements LookupStrategy {
auditLogger, parent, null, inputAcl.isEntriesInheriting(), inputAcl.getOwner());
// Copy the "aces" from the input to the destination
Field field = FieldUtils.getField(AclImpl.class, "aces");
Field fieldAces = FieldUtils.getField(AclImpl.class, "aces");
Field fieldAcl = FieldUtils.getField(AccessControlEntryImpl.class, "acl");
try {
field.setAccessible(true);
field.set(result, field.get(inputAcl));
fieldAces.setAccessible(true);
fieldAcl.setAccessible(true);
// Obtain the "aces" from the input ACL
Iterator i = ((List) fieldAces.get(inputAcl)).iterator();
// Create a list in which to store the "aces" for the "result" AclImpl instance
List acesNew = new ArrayList();
// Iterate over the "aces" input and replace each nested AccessControlEntryImpl.getAcl() with the new "result" AclImpl instance
// This ensures StubAclParent instances are removed, as per SEC-951
while(i.hasNext()) {
AccessControlEntryImpl ace = (AccessControlEntryImpl) i.next();
fieldAcl.set(ace, result);
acesNew.add(ace);
}
// Finally, now that the "aces" have been converted to have the "result" AclImpl instance, modify the "result" AclImpl instance
fieldAces.set(result, acesNew);
} catch (IllegalAccessException ex) {
throw new IllegalStateException("Could not obtain or set AclImpl.ace field");
throw new IllegalStateException("Could not obtain or set AclImpl or AccessControlEntryImpl fields");
}
return result;
}
@@ -239,7 +261,8 @@ public final class BasicLookupStrategy implements LookupStrategy {
recipient = new GrantedAuthoritySid(rs.getString("ace_sid"));
}
Permission permission = BasePermission.buildFromMask(rs.getInt("mask"));
int mask = rs.getInt("mask");
Permission permission = convertMaskIntoPermission(mask);
boolean granting = rs.getBoolean("granting");
boolean auditSuccess = rs.getBoolean("audit_success");
boolean auditFailure = rs.getBoolean("audit_failure");
@@ -264,6 +287,10 @@ public final class BasicLookupStrategy implements LookupStrategy {
}
}
protected Permission convertMaskIntoPermission(int mask) {
return BasePermission.buildFromMask(mask);
}
/**
* Looks up a batch of <code>ObjectIdentity</code>s directly from the database.<p>The caller is responsible
* for optimization issues, such as selecting the identities to lookup, ensuring the cache doesn't contain them
@@ -293,10 +320,10 @@ public final class BasicLookupStrategy implements LookupStrategy {
for (int i = 0; i < objectIdentities.length; i++) {
// Determine prepared statement values for this iteration
String javaType = objectIdentities[i].getJavaType().getName();
Assert.isInstanceOf(Long.class, objectIdentities[i].getIdentifier(),
"This class requires ObjectIdentity.getIdentifier() to be a Long");
long id = ((Long) objectIdentities[i].getIdentifier()).longValue();
// No need to check for nulls, as guaranteed non-null by ObjectIdentity.getIdentifier() interface contract
String identifier = objectIdentities[i].getIdentifier().toString();
long id = (Long.valueOf(identifier)).longValue();
// Inject values
ps.setLong((2 * i) + 1, id);
@@ -53,7 +53,7 @@ public class JdbcAclService implements AclService {
//~ Static fields/initializers =====================================================================================
protected static final Log log = LogFactory.getLog(JdbcAclService.class);
private static final String selectAclObjectWithParent = "select obj.object_id_identity obj_id, class.class class "
private static final String selectAclObjectWithParent = "select obj.object_id_identity as obj_id, class.class as class "
+ "from acl_object_identity obj, acl_object_identity parent, acl_class class "
+ "where obj.parent_object = parent.id and obj.object_id_class = class.id "
+ "and parent.object_id_identity = ? and parent.object_id_class = ("
@@ -64,7 +64,8 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
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 identityQuery = "call identity()";
private String classIdentityQuery = "call identity()"; // should be overridden for postgres : select currval('acl_class_seq')
private String sidIdentityQuery = "call identity()"; // should be overridden for postgres : select currval('acl_siq_seq')
private String insertClass = "insert into acl_class (class) values (?)";
private String insertEntry = "insert into acl_entry "
+ "(acl_object_identity, ace_order, sid, mask, granting, audit_success, audit_failure)"
@@ -176,7 +177,7 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
jdbcTemplate.update(insertClass, new Object[] {clazz.getName()});
Assert.isTrue(TransactionSynchronizationManager.isSynchronizationActive(),
"Transaction must be running");
classId = new Long(jdbcTemplate.queryForLong(identityQuery));
classId = new Long(jdbcTemplate.queryForLong(classIdentityQuery));
}
} else {
classId = (Long) classIds.iterator().next();
@@ -221,7 +222,7 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
jdbcTemplate.update(insertSid, new Object[] {new Boolean(principal), sidName});
Assert.isTrue(TransactionSynchronizationManager.isSynchronizationActive(),
"Transaction must be running");
sidId = new Long(jdbcTemplate.queryForLong(identityQuery));
sidId = new Long(jdbcTemplate.queryForLong(sidIdentityQuery));
}
} else {
sidId = (Long) sidIds.iterator().next();
@@ -380,11 +381,15 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
}
}
public void setIdentityQuery(String identityQuery) {
public void setClassIdentityQuery(String identityQuery) {
Assert.hasText(identityQuery, "New identity query is required");
this.identityQuery = identityQuery;
this.classIdentityQuery = identityQuery;
}
public void setSidIdentityQuery(String identityQuery) {
Assert.hasText(identityQuery, "New identity query is required");
this.sidIdentityQuery = identityQuery;
}
/**
* @param foreignKeysInDatabase if false this class will perform additional FK constrain checking, which may
* cause deadlocks (the default is true, so deadlocks are avoided but the database is expected to enforce FKs)
@@ -15,6 +15,7 @@
package org.springframework.security.acls.objectidentity;
import org.springframework.security.acls.IdentityUnavailableException;
import org.springframework.security.acls.jdbc.LookupStrategy;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
@@ -97,6 +98,12 @@ public class ObjectIdentityImpl implements ObjectIdentity {
/**
* Important so caching operates properly.<P>Considers an object of the same class equal if it has the same
* <code>classname</code> and <code>id</code> properties.</p>
*
* <p>
* Note that this class uses string equality for the identifier field, which ensures it better supports
* differences between {@link LookupStrategy} requirements and the domain object represented by this
* <code>ObjectIdentityImpl</code>.
* </p>
*
* @param arg0 object to compare
*
@@ -113,7 +120,7 @@ public class ObjectIdentityImpl implements ObjectIdentity {
ObjectIdentityImpl other = (ObjectIdentityImpl) arg0;
if (this.getIdentifier().equals(other.getIdentifier()) && this.getJavaType().equals(other.getJavaType())) {
if (this.getIdentifier().toString().equals(other.getIdentifier().toString()) && this.getJavaType().equals(other.getJavaType())) {
return true;
}
@@ -34,20 +34,20 @@ import org.springframework.util.Assert;
/**
* DOCUMENT ME!
* Abstract {@link AfterInvocationProvider} which provides commonly-used ACL-related services.
*
* @author $author$
* @version $Revision$
* @author Ben Alex
* @version $Id$
*/
public abstract class AbstractAclProvider implements AfterInvocationProvider {
//~ Instance fields ================================================================================================
private AclService aclService;
private Class processDomainObjectClass = Object.class;
private ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy = new ObjectIdentityRetrievalStrategyImpl();
private SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();
private String processConfigAttribute;
private Permission[] requirePermission = {BasePermission.READ};
protected AclService aclService;
protected Class processDomainObjectClass = Object.class;
protected ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy = new ObjectIdentityRetrievalStrategyImpl();
protected SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();
protected String processConfigAttribute;
protected Permission[] requirePermission = {BasePermission.READ};
//~ Constructors ===================================================================================================
@@ -22,7 +22,7 @@ import org.springframework.security.acls.Permission;
/**
* Tests BasePermission and CumulativePermission.
* Tests classes associated with Permission.
*
* @author Ben Alex
* @version $Id${date}
@@ -32,6 +32,12 @@ public class PermissionTests {
//~ Methods ========================================================================================================
@Test
public void basePermissionTest() {
Permission p = BasePermission.buildFromName("WRITE");
assertNotNull(p);
}
@Test
public void expectedIntegerValues() {
assertEquals(1, BasePermission.READ.getMask());
@@ -63,9 +69,9 @@ public class PermissionTests {
assertEquals("CumulativePermission[...............................R=1]",
new CumulativePermission().set(BasePermission.READ).toString());
System.out.println("A = " + new CumulativePermission().set(BasePermission.ADMINISTRATION).toString());
assertEquals("CumulativePermission[...........................A....=16]",
new CumulativePermission().set(BasePermission.ADMINISTRATION).toString());
System.out.println("A = " + new CumulativePermission().set(SpecialPermission.ENTER).set(BasePermission.ADMINISTRATION).toString());
assertEquals("CumulativePermission[..........................EA....=48]",
new CumulativePermission().set(SpecialPermission.ENTER).set(BasePermission.ADMINISTRATION).toString());
System.out.println("RA = "
+ new CumulativePermission().set(BasePermission.ADMINISTRATION).set(BasePermission.READ).toString());
@@ -0,0 +1,40 @@
/* 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.acls.domain;
import org.springframework.security.acls.Permission;
/**
* A test permission.
*
* @author Ben Alex
* @version $Id$
*/
public class SpecialPermission extends BasePermission {
public static final Permission ENTER = new SpecialPermission(1 << 5, 'E'); // 32
/**
* Registers the public static permissions defined on this class. This is mandatory so
* that the static methods will operate correctly.
*/
static {
registerPermissionsFor(SpecialPermission.class);
}
protected SpecialPermission(int mask, char code) {
super(mask, code);
}
}
@@ -120,7 +120,8 @@ public class BasicLookupStrategyTests {
public void testAclsRetrievalWithDefaultBatchSize() throws Exception {
ObjectIdentity topParentOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(100));
ObjectIdentity middleParentOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(101));
ObjectIdentity childOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(102));
// Deliberately use an integer for the child, to reproduce bug report in SEC-819
ObjectIdentity childOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Integer(102));
Map map = this.strategy.readAclsById(new ObjectIdentity[] { topParentOid, middleParentOid, childOid }, null);
checkEntries(topParentOid, middleParentOid, childOid, map);
@@ -128,7 +129,7 @@ public class BasicLookupStrategyTests {
@Test
public void testAclsRetrievalFromCacheOnly() throws Exception {
ObjectIdentity topParentOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(100));
ObjectIdentity topParentOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Integer(100));
ObjectIdentity middleParentOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(101));
ObjectIdentity childOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(102));
@@ -145,7 +146,7 @@ public class BasicLookupStrategyTests {
@Test
public void testAclsRetrievalWithCustomBatchSize() throws Exception {
ObjectIdentity topParentOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(100));
ObjectIdentity middleParentOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(101));
ObjectIdentity middleParentOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Integer(101));
ObjectIdentity childOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(102));
// Set a batch size to allow multiple database queries in order to retrieve all acls
@@ -227,7 +228,7 @@ public class BasicLookupStrategyTests {
jdbcTemplate.execute(query);
ObjectIdentity topParentOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(100));
ObjectIdentity middleParentOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(101));
ObjectIdentity middleParentOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Integer(101));
ObjectIdentity childOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(102));
ObjectIdentity middleParent2Oid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(103));
@@ -260,8 +261,8 @@ public class BasicLookupStrategyTests {
ObjectIdentity grandParentOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(104));
ObjectIdentity parent1Oid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(105));
ObjectIdentity parent2Oid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(106));
ObjectIdentity childOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(107));
ObjectIdentity parent2Oid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Integer(106));
ObjectIdentity childOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Integer(107));
// First lookup only child, thus populating the cache with grandParent, parent1 and child
Permission[] checkPermission = new Permission[] { BasePermission.READ };
@@ -290,21 +291,4 @@ public class BasicLookupStrategyTests {
Assert.assertTrue(foundParent2Acl.isGranted(checkPermission, sids, false));
}
@Test
public void testAclsWithDifferentSerializableTypesAsObjectIdentities() throws Exception {
String query = "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (4,2,104,null,1,1);"
+ "INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (5,4,0,1,1,1,0,0)";
jdbcTemplate.execute(query);
ObjectIdentity oid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Integer(104));
Sid[] sids = new Sid[] { new PrincipalSid("ben") };
ObjectIdentity[] childOids = new ObjectIdentity[] { oid };
try {
Map foundAcls = strategy.readAclsById(childOids, sids);
Assert.fail("It should have thrown IllegalArgumentException");
} catch(IllegalArgumentException expected) {
Assert.assertTrue(true);
}
}
}
@@ -97,7 +97,7 @@ public class JdbcAclServiceTests extends AbstractTransactionalDataSourceSpringCo
ObjectIdentity topParentOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(100));
ObjectIdentity middleParentOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(101));
ObjectIdentity childOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(102));
ObjectIdentity childOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Integer(102));
MutableAcl topParent = jdbcMutableAclService.createAcl(topParentOid);
MutableAcl middleParent = jdbcMutableAclService.createAcl(middleParentOid);
@@ -337,7 +337,7 @@ public class JdbcAclServiceTests extends AbstractTransactionalDataSourceSpringCo
ObjectIdentity topParentOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(100));
ObjectIdentity middleParentOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(101));
ObjectIdentity childOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(102));
ObjectIdentity childOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Integer(102));
// Remove the child and check all related database rows were removed accordingly
jdbcMutableAclService.deleteAcl(childOid, false);
@@ -53,7 +53,7 @@ public class SidTests extends TestCase {
}
try {
Authentication authentication = new TestingAuthenticationToken(null, "password", null);
Authentication authentication = new TestingAuthenticationToken(null, "password");
Sid principalSid = new PrincipalSid(authentication);
Assert.fail("It should have thrown IllegalArgumentException");
}
@@ -62,7 +62,7 @@ public class SidTests extends TestCase {
}
try {
Authentication authentication = new TestingAuthenticationToken("johndoe", "password", null);
Authentication authentication = new TestingAuthenticationToken("johndoe", "password");
Sid principalSid = new PrincipalSid(authentication);
Assert.assertTrue(true);
}
@@ -128,15 +128,15 @@ public class SidTests extends TestCase {
}
public void testPrincipalSidEquals() throws Exception {
Authentication authentication = new TestingAuthenticationToken("johndoe", "password", null);
Authentication authentication = new TestingAuthenticationToken("johndoe", "password");
Sid principalSid = new PrincipalSid(authentication);
Assert.assertFalse(principalSid.equals(null));
Assert.assertFalse(principalSid.equals("DIFFERENT_TYPE_OBJECT"));
Assert.assertTrue(principalSid.equals(principalSid));
Assert.assertTrue(principalSid.equals(new PrincipalSid(authentication)));
Assert.assertTrue(principalSid.equals(new PrincipalSid(new TestingAuthenticationToken("johndoe", null, null))));
Assert.assertFalse(principalSid.equals(new PrincipalSid(new TestingAuthenticationToken("scott", null, null))));
Assert.assertTrue(principalSid.equals(new PrincipalSid(new TestingAuthenticationToken("johndoe", null))));
Assert.assertFalse(principalSid.equals(new PrincipalSid(new TestingAuthenticationToken("scott", null))));
Assert.assertTrue(principalSid.equals(new PrincipalSid("johndoe")));
Assert.assertFalse(principalSid.equals(new PrincipalSid("scott")));
}
@@ -156,14 +156,13 @@ public class SidTests extends TestCase {
}
public void testPrincipalSidHashCode() throws Exception {
Authentication authentication = new TestingAuthenticationToken("johndoe", "password", null);
Authentication authentication = new TestingAuthenticationToken("johndoe", "password");
Sid principalSid = new PrincipalSid(authentication);
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",
null)).hashCode());
Assert.assertTrue(principalSid.hashCode() != new PrincipalSid(new TestingAuthenticationToken("scott", "password")).hashCode());
}
public void testGrantedAuthoritySidHashCode() throws Exception {
@@ -177,7 +176,7 @@ public class SidTests extends TestCase {
}
public void testGetters() throws Exception {
Authentication authentication = new TestingAuthenticationToken("johndoe", "password", null);
Authentication authentication = new TestingAuthenticationToken("johndoe", "password");
PrincipalSid principalSid = new PrincipalSid(authentication);
GrantedAuthority ga = new GrantedAuthorityImpl("ROLE_TEST");
GrantedAuthoritySid gaSid = new GrantedAuthoritySid(ga);
+19
View File
@@ -0,0 +1,19 @@
Bundle-SymbolicName: org.springframework.security.acls
Bundle-Name: Spring Security Acls
Bundle-Vendor: SpringSource
Bundle-Version: ${version}
Bundle-ManifestVersion: 2
Ignored-Existing-Headers:
Import-Package,
Export-Package
Import-Template:
org.apache.commons.logging.*;version="[1.0.4, 2.0.0)",
org.springframework.security.*;version="[${version}, ${version}]",
org.springframework.context.*;version="[2.0.8, 3.1.0)";resolution:=optional,
org.springframework.dao.*;version="[2.0.8, 3.1.0)";resolution:=optional,
org.springframework.jdbc.core.*;version="[2.0.8, 3.1.0)";resolution:=optional,
org.springframework.transaction.support.*;version="[2.0.8, 3.1.0)";resolution:=optional,
org.springframework.util.*;version="[2.0.8, 3.1.0)";resolution:=optional,
net.sf.ehcache.*;version="[1.4.1, 2.0.0)";resolution:=optional,
javax.sql.*;version="0";resolution:=optional
+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-adapters</artifactId>
<version>2.0.2</version>
<version>2.0.5.CI-SNAPSHOT</version>
</parent>
<artifactId>spring-security-catalina</artifactId>
<name>Spring Security - Catalina adapter</name>
+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-adapters</artifactId>
<version>2.0.2</version>
<version>2.0.5.CI-SNAPSHOT</version>
</parent>
<artifactId>spring-security-jboss</artifactId>
<name>Spring Security - JBoss adapter</name>
+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-adapters</artifactId>
<version>2.0.2</version>
<version>2.0.5.CI-SNAPSHOT</version>
</parent>
<artifactId>spring-security-jetty</artifactId>
<name>Spring Security - Jetty adapter</name>
+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-parent</artifactId>
<version>2.0.2</version>
<version>2.0.5.CI-SNAPSHOT</version>
</parent>
<artifactId>spring-security-adapters</artifactId>
<name>Spring Security - Adapters</name>
+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-adapters</artifactId>
<version>2.0.2</version>
<version>2.0.5.CI-SNAPSHOT</version>
</parent>
<artifactId>spring-security-resin</artifactId>
<name>Spring Security - Resin adapter</name>
+18 -27
View File
@@ -3,11 +3,11 @@
<parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-parent</artifactId>
<version>2.0.2</version>
<version>2.0.8.RELEASE</version>
</parent>
<artifactId>spring-security-cas-client</artifactId>
<name>Spring Security - CAS support</name>
<packaging>bundle</packaging>
<packaging>jar</packaging>
<dependencies>
<dependency>
@@ -15,6 +15,13 @@
<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-dao</artifactId>
@@ -31,7 +38,7 @@
<dependency>
<groupId>org.jasig.cas</groupId>
<artifactId>cas-client-core</artifactId>
<version>3.1.1</version>
<version>3.1.5</version>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
@@ -40,28 +47,12 @@
</dependency>
</dependencies>
<properties>
<spring.osgi.export>
org.springframework.security.*;version=${pom.version.osgi}
</spring.osgi.export>
<spring.osgi.import>
org.springframework.security.*;version="[${pom.version.osgi},${pom.version.osgi}]",
org.springframework.beans.*;version="${spring.version.osgi}",
org.springframework.context.*;version="${spring.version.osgi}",
org.springframework.dao.*;version="${spring.version.osgi}";resolution:=optional,
org.springframework.util.*;version="${spring.version.osgi}",
javax.servlet.*;version="[2.4.0, 3.0.0)";resolution:=optional,
net.sf.ehcache.*;version="[1.4.1, 2.0.0)";resolution:=optional,
org.apache.commons.logging.*;version="[1.1.1, 2.0.0)",
org.jasig.cas.client.*;version="[3.1.1, 4.0.0)"
</spring.osgi.import>
<spring.osgi.private.pkg>
!org.springframework.security.*
</spring.osgi.private.pkg>
<spring.osgi.symbolic.name>org.springframework.security.cas</spring.osgi.symbolic.name>
</properties>
<build>
<plugins>
<plugin>
<groupId>com.springsource.bundlor</groupId>
<artifactId>com.springsource.bundlor.maven</artifactId>
</plugin>
</plugins>
</build>
</project>
@@ -76,7 +76,7 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
//~ Methods ========================================================================================================
public void afterPropertiesSet() throws Exception {
public void afterPropertiesSet() throws Exception {
Assert.notNull(this.userDetailsService, "A userDetailsService must be set");
Assert.notNull(this.ticketValidator, "A ticketValidator must be set");
Assert.notNull(this.statelessTicketCache, "A statelessTicketCache must be set");
@@ -140,29 +140,38 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
return result;
}
private CasAuthenticationToken authenticateNow(Authentication authentication) throws AuthenticationException {
try {
final Assertion assertion = this.ticketValidator.validate(authentication.getCredentials().toString(), serviceProperties.getService());
final UserDetails userDetails = userDetailsService.loadUserByUsername(assertion.getPrincipal().getName());
private final CasAuthenticationToken authenticateNow(final Authentication authentication) throws AuthenticationException {
try {
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(),
userDetails.getAuthorities(), userDetails, assertion);
} catch (final TicketValidationException e) {
// TODO get error message
throw new BadCredentialsException("", e);
}
return new CasAuthenticationToken(this.key, userDetails, authentication.getCredentials(), userDetails.getAuthorities(), userDetails, assertion);
} catch (final TicketValidationException e) {
throw new BadCredentialsException(e.getMessage(), e);
}
}
/**
* 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.
*
* @param assertion The CAS Assertion.
* @returns the UserDetails.
*/
protected UserDetails loadUserByAssertion(final Assertion assertion) {
return this.userDetailsService.loadUserByUsername(assertion.getPrincipal().getName());
}
protected UserDetailsService getUserDetailsService() {
return userDetailsService;
}
public void setUserDetailsService(UserDetailsService userDetailsService) {
public void setUserDetailsService(final UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
public void setServiceProperties(final ServiceProperties serviceProperties) {
this.serviceProperties = serviceProperties;
this.serviceProperties = serviceProperties;
}
protected String getKey() {
@@ -181,15 +190,15 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
return ticketValidator;
}
public void setMessageSource(MessageSource messageSource) {
public void setMessageSource(final MessageSource messageSource) {
this.messages = new MessageSourceAccessor(messageSource);
}
public void setStatelessTicketCache(StatelessTicketCache statelessTicketCache) {
public void setStatelessTicketCache(final StatelessTicketCache statelessTicketCache) {
this.statelessTicketCache = statelessTicketCache;
}
public void setTicketValidator(TicketValidator ticketValidator) {
public void setTicketValidator(final TicketValidator ticketValidator) {
this.ticketValidator = ticketValidator;
}
@@ -15,6 +15,11 @@
package org.springframework.security.ui.cas;
import java.io.IOException;
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;
import org.springframework.security.AuthenticationException;
@@ -24,6 +29,7 @@ import org.springframework.security.ui.AbstractProcessingFilter;
import org.springframework.security.ui.FilterChainOrder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
@@ -38,7 +44,11 @@ import javax.servlet.http.HttpServletRequest;
* <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.</p>
* <p><b>Do not use this class directly.</b> Instead configure <code>web.xml</code> to use the {@link
* <p>By configuring a shared {@link ProxyGrantingTicketStorage} between the {@link TicketValidator} and the CasProcessingFilter
* one can have the CasProcessingFilter 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><b>Do not use this class directly.</b> Instead configure <code>web.xml</code> to use the {@link
* org.springframework.security.util.FilterToBeanProxy}.</p>
*
* @author Ben Alex
@@ -57,8 +67,17 @@ public class CasProcessingFilter extends AbstractProcessingFilter {
*/
public static final String CAS_STATELESS_IDENTIFIER = "_cas_stateless_";
//~ Methods ========================================================================================================
/**
* The last portion of the receptor url, i.e. /proxy/receptor
*/
private String proxyReceptorUrl;
/**
* The backing storage to store ProxyGrantingTicket requests.
*/
private ProxyGrantingTicketStorage proxyGrantingTicketStorage;
//~ Methods ========================================================================================================
public Authentication attemptAuthentication(final HttpServletRequest request)
throws AuthenticationException {
final String username = CAS_STATEFUL_IDENTIFIER;
@@ -87,4 +106,35 @@ public class CasProcessingFilter extends AbstractProcessingFilter {
public int getOrder() {
return FilterChainOrder.CAS_PROCESSING_FILTER;
}
/**
* Overridden to provide proxying capabilities.
*/
protected boolean requiresAuthentication(final HttpServletRequest request,
final HttpServletResponse response) {
final String requestUri = request.getRequestURI();
if (CommonUtils.isEmpty(this.proxyReceptorUrl) || !requestUri.endsWith(this.proxyReceptorUrl) || this.proxyGrantingTicketStorage == null) {
return super.requiresAuthentication(request, response);
}
try {
CommonUtils.readAndRespondToProxyReceptorRequest(request, response, this.proxyGrantingTicketStorage);
return false;
} catch (final IOException e) {
return super.requiresAuthentication(request, response);
}
}
public final void setProxyReceptorUrl(final String proxyReceptorUrl) {
this.proxyReceptorUrl = proxyReceptorUrl;
}
public final void setProxyGrantingTicketStorage(
final ProxyGrantingTicketStorage proxyGrantingTicketStorage) {
this.proxyGrantingTicketStorage = proxyGrantingTicketStorage;
}
}
+19
View File
@@ -0,0 +1,19 @@
Bundle-SymbolicName: org.springframework.security.cas
Bundle-Name: Spring Security CAS
Bundle-Vendor: SpringSource
Bundle-Version: ${version}
Bundle-ManifestVersion: 2
Ignored-Existing-Headers:
Import-Package,
Export-Package
Import-Template:
org.apache.commons.logging.*;version="[1.0.4, 2.0.0)",
org.jasig.cas.client.*;version="[3.1.1,3.2)",
org.springframework.security.*;version="[${version}, ${version}]",
org.springframework.beans.factory;version="[2.0.8, 3.1.0)",
org.springframework.context.*;version="[2.0.8, 3.1.0)",
org.springframework.dao;version="[2.0.8, 3.1.0)",
org.springframework.util;version="[2.0.8, 3.1.0)",
net.sf.ehcache.*;version="[1.4.1, 2.0.0)";resolution:=optional,
javax.servlet.*;version="0"
+12 -50
View File
@@ -3,9 +3,9 @@
<parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-parent</artifactId>
<version>2.0.2</version>
<version>2.0.8.RELEASE</version>
</parent>
<packaging>bundle</packaging>
<packaging>jar</packaging>
<artifactId>spring-security-core-tiger</artifactId>
<name>Spring Security - Java 5 (Tiger)</name>
@@ -21,7 +21,12 @@
<version>${project.version}</version>
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-remoting</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
@@ -32,20 +37,6 @@
<artifactId>aspectjweaver</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-support</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
@@ -68,39 +59,10 @@
<target>1.5</target>
</configuration>
</plugin>
<plugin>
<groupId>com.springsource.bundlor</groupId>
<artifactId>com.springsource.bundlor.maven</artifactId>
</plugin>
</plugins>
</build>
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId>
<configuration>
<targetJdk>1.5</targetJdk>
</configuration>
</plugin>
</plugins>
</reporting>
<properties>
<spring.osgi.export>
org.springframework.security.*;version=${pom.version}
</spring.osgi.export>
<spring.osgi.import>
javax.annotation.*;version="[1.0.0, 2.0.0)",
org.springframework.security.*;version="[${pom.version.osgi},${pom.version.osgi}]",
org.springframework.core.*;version="${spring.version.osgi}"
</spring.osgi.import>
<spring.osgi.private.pkg>
!org.springframework.security.*
</spring.osgi.private.pkg>
<spring.osgi.include.res>
src/main/resources
</spring.osgi.include.res>
<spring.osgi.symbolic.name>org.springframework.security.annotation</spring.osgi.symbolic.name>
</properties>
</project>
@@ -39,8 +39,10 @@ public interface BusinessService {
public void someUserMethod1();
@Secured({"ROLE_USER"})
@RolesAllowed({"ROLE_USER"})
@RolesAllowed({"ROLE_USER"})
public void someUserMethod2();
public int someOther(String s);
public int someOther(int input);
}
@@ -26,7 +26,11 @@ public class BusinessServiceImpl<E extends Entity> implements BusinessService {
return entity;
}
public int someOther(int input) {
return input;
}
public int someOther(String s) {
return 0;
}
public int someOther(int input) {
return input;
}
}
@@ -27,7 +27,11 @@ public class Jsr250BusinessServiceImpl implements BusinessService {
public void someAdminMethod() {
}
public int someOther(String input) {
return 0;
}
public int someOther(int input) {
return input;
}
}
return input;
}
}
@@ -1,13 +1,12 @@
package org.springframework.security.config;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.*;
import static org.springframework.security.config.ConfigTestUtils.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
import org.springframework.context.support.AbstractXmlApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.security.AccessDeniedException;
import org.springframework.security.AuthenticationCredentialsNotFoundException;
import org.springframework.security.GrantedAuthority;
@@ -15,10 +14,12 @@ import org.springframework.security.GrantedAuthorityImpl;
import org.springframework.security.annotation.BusinessService;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
import org.springframework.security.userdetails.UserDetailsService;
import org.springframework.security.util.InMemoryXmlApplicationContext;
/**
* @author Ben Alex
* @author Luke Taylor
* @version $Id$
*/
public class GlobalMethodSecurityBeanDefinitionParserTests {
@@ -27,29 +28,36 @@ public class GlobalMethodSecurityBeanDefinitionParserTests {
private BusinessService target;
public void loadContext() {
appContext = new ClassPathXmlApplicationContext("org/springframework/security/config/global-method-security.xml");
setContext(
"<b:bean id='target' class='org.springframework.security.annotation.BusinessServiceImpl'/>" +
"<global-method-security>" +
" <protect-pointcut expression='execution(* *.someUser*(..))' access='ROLE_USER'/>" +
" <protect-pointcut expression='execution(* *.someAdmin*(..))' access='ROLE_ADMIN'/>" +
"</global-method-security>" + ConfigTestUtils.AUTH_PROVIDER_XML
);
target = (BusinessService) appContext.getBean("target");
}
}
@After
public void closeAppContext() {
if (appContext != null) {
appContext.close();
appContext = null;
}
SecurityContextHolder.clearContext();
target = null;
}
@Test(expected=AuthenticationCredentialsNotFoundException.class)
public void targetShouldPreventProtectedMethodInvocationWithNoContext() {
loadContext();
loadContext();
target.someUserMethod1();
}
@Test
public void targetShouldAllowProtectedMethodInvocationWithCorrectRole() {
loadContext();
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Test", "Password",
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_USER")});
loadContext();
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("user", "password");
SecurityContextHolder.getContext().setAuthentication(token);
target.someUserMethod1();
@@ -57,20 +65,19 @@ public class GlobalMethodSecurityBeanDefinitionParserTests {
@Test(expected=AccessDeniedException.class)
public void targetShouldPreventProtectedMethodInvocationWithIncorrectRole() {
loadContext();
loadContext();
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Test", "Password",
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_SOMEOTHERROLE")});
SecurityContextHolder.getContext().setAuthentication(token);
target.someAdminMethod();
}
@Test
public void doesntInterfereWithBeanPostProcessing() {
setContext(
"<b:bean id='myUserService' class='org.springframework.security.config.PostProcessedMockUserDetailsService'/>" +
"<global-method-security />" +
// "<http auto-config='true'/>" +
"<authentication-provider user-service-ref='myUserService'/>" +
"<b:bean id='beanPostProcessor' class='org.springframework.security.config.MockUserServiceBeanPostProcessor'/>"
);
@@ -79,7 +86,75 @@ public class GlobalMethodSecurityBeanDefinitionParserTests {
assertEquals("Hello from the post processor!", service.getPostProcessorWasHere());
}
@Test(expected=AccessDeniedException.class)
public void worksWithAspectJAutoproxy() {
setContext(
"<global-method-security>" +
" <protect-pointcut expression='execution(* org.springframework.security.config.*Service.*(..))'" +
" access='ROLE_SOMETHING' />" +
"</global-method-security>" +
"<b:bean id='myUserService' class='org.springframework.security.config.PostProcessedMockUserDetailsService'/>" +
"<aop:aspectj-autoproxy />" +
"<authentication-provider user-service-ref='myUserService'/>"
);
UserDetailsService service = (UserDetailsService) appContext.getBean("myUserService");
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Test", "Password",
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_SOMEOTHERROLE")});
SecurityContextHolder.getContext().setAuthentication(token);
service.loadUserByUsername("notused");
}
@Test
public void supportsMethodArgumentsInPointcut() {
setContext(
"<b:bean id='target' class='org.springframework.security.annotation.BusinessServiceImpl'/>" +
"<global-method-security>" +
" <protect-pointcut expression='execution(* org.springframework.security.annotation.BusinessService.someOther(String))' access='ROLE_ADMIN'/>" +
" <protect-pointcut expression='execution(* org.springframework.security.annotation.BusinessService.*(..))' access='ROLE_USER'/>" +
"</global-method-security>" + ConfigTestUtils.AUTH_PROVIDER_XML
);
SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken("user", "password"));
target = (BusinessService) appContext.getBean("target");
// someOther(int) should not be matched by someOther(String), but should require ROLE_USER
target.someOther(0);
try {
// String version should required admin role
target.someOther("somestring");
fail("Expected AccessDeniedException");
} catch (AccessDeniedException expected) {
}
}
@Test
public void supportsBooleanPointcutExpressions() {
setContext(
"<b:bean id='target' class='org.springframework.security.annotation.BusinessServiceImpl'/>" +
"<global-method-security>" +
" <protect-pointcut expression=" +
" 'execution(* org.springframework.security.annotation.BusinessService.*(..)) " +
" and not execution(* org.springframework.security.annotation.BusinessService.someOther(String)))' " +
" access='ROLE_USER'/>" +
"</global-method-security>" + ConfigTestUtils.AUTH_PROVIDER_XML
);
target = (BusinessService) appContext.getBean("target");
// String method should not be protected
target.someOther("somestring");
// All others should require ROLE_USER
try {
target.someOther(0);
fail("Expected AuthenticationCredentialsNotFoundException");
} catch (AuthenticationCredentialsNotFoundException expected) {
}
SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken("user", "password"));
target.someOther(0);
}
@Test(expected=BeanDefinitionParsingException.class)
public void duplicateElementCausesError() {
setContext(
@@ -87,8 +162,27 @@ public class GlobalMethodSecurityBeanDefinitionParserTests {
"<global-method-security />"
);
}
@Test(expected=AccessDeniedException.class)
public void worksWithoutTargetOrClass() {
setContext(
"<global-method-security secured-annotations='enabled'/>" +
"<b:bean id='businessService' class='org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean'>" +
" <b:property name='serviceUrl' value='http://localhost:8080/SomeService'/>" +
" <b:property name='serviceInterface' value='org.springframework.security.annotation.BusinessService'/>" +
"</b:bean>" + AUTH_PROVIDER_XML
);
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Test", "Password",
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_SOMEOTHERROLE")});
SecurityContextHolder.getContext().setAuthentication(token);
target = (BusinessService) appContext.getBean("businessService");
target.someUserMethod1();
}
private void setContext(String context) {
appContext = new InMemoryXmlApplicationContext(context);
}
}
@@ -3,7 +3,6 @@ package org.springframework.security.config;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.security.AccessDeniedException;
import org.springframework.security.AuthenticationCredentialsNotFoundException;
import org.springframework.security.GrantedAuthority;
@@ -11,19 +10,23 @@ import org.springframework.security.GrantedAuthorityImpl;
import org.springframework.security.annotation.BusinessService;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
import org.springframework.security.util.InMemoryXmlApplicationContext;
/**
* @author Luke Taylor
* @version $Id$
*/
public class Jsr250AnnotationDrivenBeanDefinitionParserTests {
private ClassPathXmlApplicationContext appContext;
private InMemoryXmlApplicationContext appContext;
private BusinessService target;
@Before
public void loadContext() {
appContext = new ClassPathXmlApplicationContext("/org/springframework/security/config/jsr250-annotated-method-security.xml");
appContext = new InMemoryXmlApplicationContext(
"<b:bean id='target' class='org.springframework.security.annotation.Jsr250BusinessServiceImpl'/>" +
"<global-method-security jsr250-annotations='enabled'/>" + ConfigTestUtils.AUTH_PROVIDER_XML
);
target = (BusinessService) appContext.getBean("target");
}
@@ -48,7 +51,7 @@ public class Jsr250AnnotationDrivenBeanDefinitionParserTests {
target.someOther(0);
}
@Test
public void targetShouldAllowProtectedMethodInvocationWithCorrectRole() {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Test", "Password",
@@ -66,4 +69,4 @@ public class Jsr250AnnotationDrivenBeanDefinitionParserTests {
target.someAdminMethod();
}
}
}
@@ -3,7 +3,6 @@ package org.springframework.security.config;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.security.AccessDeniedException;
import org.springframework.security.AuthenticationCredentialsNotFoundException;
import org.springframework.security.GrantedAuthority;
@@ -11,19 +10,23 @@ import org.springframework.security.GrantedAuthorityImpl;
import org.springframework.security.annotation.BusinessService;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
import org.springframework.security.util.InMemoryXmlApplicationContext;
/**
* @author Ben Alex
* @version $Id$
*/
public class SecuredAnnotationDrivenBeanDefinitionParserTests {
private ClassPathXmlApplicationContext appContext;
private InMemoryXmlApplicationContext appContext;
private BusinessService target;
@Before
public void loadContext() {
appContext = new ClassPathXmlApplicationContext("org/springframework/security/config/secured-annotated-method-security.xml");
appContext = new InMemoryXmlApplicationContext(
"<b:bean id='target' class='org.springframework.security.annotation.Jsr250BusinessServiceImpl'/>" +
"<global-method-security secured-annotations='enabled'/>" + ConfigTestUtils.AUTH_PROVIDER_XML
);
target = (BusinessService) appContext.getBean("target");
}
@@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<b:beans xmlns="http://www.springframework.org/schema/security"
xmlns:b="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.xsd">
<b:bean id="target" class="org.springframework.security.annotation.BusinessServiceImpl"/>
<global-method-security>
<protect-pointcut expression="execution(* *.someUser*(..))" access="ROLE_USER"/>
<protect-pointcut expression="execution(* *.someAdmin*(..))" access="ROLE_ADMIN"/>
</global-method-security>
<authentication-provider>
<user-service>
<user name="bob" password="bobspassword" authorities="ROLE_A,ROLE_B" />
<user name="bill" password="billspassword" authorities="ROLE_A,ROLE_B,AUTH_OTHER" />
</user-service>
</authentication-provider>
</b:beans>
@@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<b:beans xmlns="http://www.springframework.org/schema/security"
xmlns:b="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.xsd">
<b:bean id="target" class="org.springframework.security.annotation.Jsr250BusinessServiceImpl"/>
<global-method-security jsr250-annotations="enabled"/>
<authentication-provider>
<user-service>
<user name="bob" password="bobspassword" authorities="ROLE_A,ROLE_B" />
<user name="bill" password="billspassword" authorities="ROLE_A,ROLE_B,AUTH_OTHER" />
</user-service>
</authentication-provider>
</b:beans>
@@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<b:beans xmlns="http://www.springframework.org/schema/security"
xmlns:b="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.xsd">
<b:bean id="target" class="org.springframework.security.annotation.Jsr250BusinessServiceImpl"/>
<global-method-security secured-annotations="enabled"/>
<authentication-provider>
<user-service>
<user name="bob" password="bobspassword" authorities="ROLE_A,ROLE_B" />
<user name="bill" password="billspassword" authorities="ROLE_A,ROLE_B,AUTH_OTHER" />
</user-service>
</authentication-provider>
</b:beans>
+12
View File
@@ -0,0 +1,12 @@
Bundle-SymbolicName: org.springframework.security.annotation
Bundle-Name: Spring Security Annotations
Bundle-Vendor: SpringSource
Bundle-Version: ${version}
Bundle-ManifestVersion: 2
Ignored-Existing-Headers:
Import-Package,
Export-Package
Import-Template:
javax.annotation.security.*;version="0";resolution:=optional,
org.springframework.security.*;version="[${version},${version}]",
org.springframework.core.*;version="[2.0.8, 3.1.0)"
+11
View File
@@ -0,0 +1,11 @@
#! /bin/sh
pushd src/main/resources/org/springframework/security/config/
echo "Converting rnc file to xsd ..."
java -jar ~/bin/trang.jar spring-security-2.0.6.rnc spring-security-2.0.6.xsd
echo "Applying XSL transformation to xsd ..."
xsltproc --output spring-security-2.0.6.xsd spring-security.xsl spring-security-2.0.6.xsd
popd
+33 -72
View File
@@ -3,9 +3,9 @@
<parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-parent</artifactId>
<version>2.0.2</version>
<version>2.0.8.RELEASE</version>
</parent>
<packaging>bundle</packaging>
<packaging>jar</packaging>
<artifactId>spring-security-core</artifactId>
<name>Spring Security - Core</name>
@@ -47,27 +47,27 @@
<artifactId>spring-mock</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.ldap</groupId>
<artifactId>spring-ldap</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<scope>test</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<scope>test</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
@@ -95,7 +95,7 @@
<artifactId>jaxen</artifactId>
<version>1.1.1</version>
<optional>true</optional>
</dependency>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
@@ -135,64 +135,25 @@
<version>1.0.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.8.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<properties>
<spring.osgi.export>
org.springframework.security.*;version=${pom.version}
</spring.osgi.export>
<spring.osgi.import>
!com.ibm.websphere.security,
javax.servlet.*;version="[2.4.0, 3.0.0)";resolution:=optional,
net.sf.ehcache.*;version="[1.4.1, 2.0.0)";resolution:=optional,
org.aopalliance.*;version="[1.0.0, 2.0.0)",
org.apache.commons.codec.*;version="[1.3.0, 2.0.0)",
org.apache.commons.collections.*;version="[3.2.0, 4.0.0)",
org.apache.commons.lang.*;version="[2.1.0, 3.0.0)",
org.apache.commons.logging.*;version="[1.1.1, 2.0.0)",
org.apache.directory.server.configuration.*;version="[1.0.2, 2.0.0)";resolution:=optional,
org.apache.directory.server.core.*;version="[1.0.2, 2.0.0)";resolution:=optional,
org.apache.directory.server.protocol.*;version="[1.0.2, 2.0.0)";resolution:=optional,
org.aspectj.*;version="[1.5.4, 2.0.0)";resolution:=optional,
org.jaxen.*;version="[1.1.1, 2.0.0)";resolution:=optional,
org.springframework.aop.*;version="${spring.version.osgi}",
org.springframework.beans.*;version="${spring.version.osgi}",
org.springframework.context.*;version="${spring.version.osgi}",
org.springframework.core.*;version="${spring.version.osgi}",
org.springframework.dao.*;version="${spring.version.osgi}";resolution:=optional,
org.springframework.jdbc.*;version="${spring.version.osgi}";resolution:=optional,
org.springframework.ldap.*;version="[1.2.1.A, 2.0.0)";resolution:=optional,
org.springframework.metadata.*;version="${spring.version.osgi}",
org.springframework.mock.*;version="${spring.version.osgi}";resolution:=optional,
org.springframework.remoting.*;version="${spring.version.osgi}";resolution:=optional,
org.springframework.util.*;version="${spring.version.osgi}",
org.springframework.web.*;version="${spring.version.osgi}";resolution:=optional,
javax.crypto.*,
javax.naming.*,
javax.rmi.*,
javax.security.*,
javax.sql.*,
javax.xml.parsers.*,
org.w3c.dom.*,
org.xml.sax.*,
*;resolution:=optional
</spring.osgi.import>
<spring.osgi.private.pkg>
!org.springframework.security.*
</spring.osgi.private.pkg>
<!--
<spring.osgi.include.res>
src/main/resources
</spring.osgi.include.res>
-->
<spring.osgi.symbolic.name>org.springframework.security.core</spring.osgi.symbolic.name>
</properties>
<build>
<plugins>
<plugin>
<groupId>com.springsource.bundlor</groupId>
<artifactId>com.springsource.bundlor.maven</artifactId>
</plugin>
</plugins>
</build>
</project>
@@ -26,7 +26,7 @@ public abstract class AuthenticationException extends SpringSecurityException {
//~ Instance fields ================================================================================================
private Authentication authentication;
private Object extraInformation;
private transient Object extraInformation;
//~ Constructors ===================================================================================================
@@ -21,8 +21,13 @@ import org.springframework.util.Assert;
/**
* Basic concrete implementation of a {@link GrantedAuthority}.<p>Stores a <code>String</code> representation of an
* authority granted to the {@link Authentication} object.</p>
* Basic concrete implementation of a {@link GrantedAuthority}.
*
* <p>
* Stores a <code>String</code> representation of an authority granted to the {@link Authentication} object.
* <p>
* If compared to a custom authority which returns null from {@link #getAuthority}, the <tt>compareTo</tt>
* method will return -1, so the custom authority will take precedence.
*
* @author Ben Alex
* @version $Id$
@@ -36,7 +41,6 @@ public class GrantedAuthorityImpl implements GrantedAuthority, Serializable {
//~ Constructors ===================================================================================================
public GrantedAuthorityImpl(String role) {
super();
Assert.hasText(role, "A granted authority textual representation is required");
this.role = role;
}
@@ -71,8 +75,13 @@ public class GrantedAuthorityImpl implements GrantedAuthority, Serializable {
public int compareTo(Object o) {
if (o != null && o instanceof GrantedAuthority) {
GrantedAuthority rhs = (GrantedAuthority) o;
return this.role.compareTo(rhs.getAuthority());
String rhsRole = ((GrantedAuthority) o).getAuthority();
if (rhsRole == null) {
return -1;
}
return role.compareTo(rhsRole);
}
return -1;
}
@@ -0,0 +1,167 @@
package org.springframework.security.authoritymapping;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.GrantedAuthority;
import org.springframework.security.GrantedAuthorityImpl;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* <p>
* This class implements the Attributes2GrantedAuthoritiesMapper and
* MappableAttributesRetriever interfaces based on the supplied Map.
* It supports both one-to-one and one-to-many mappings. The granted
* authorities to map to can be supplied either as a String or as a
* GrantedAuthority object.
* </p>
* @author Ruud Senden
*/
public class MapBasedAttributes2GrantedAuthoritiesMapper implements Attributes2GrantedAuthoritiesMapper, MappableAttributesRetriever, InitializingBean {
private Map attributes2grantedAuthoritiesMap = null;
private String stringSeparator = ",";
private String[] mappableAttributes = null;
/**
* Check whether all properties have been set to correct values, and do some preprocessing.
*/
public void afterPropertiesSet() {
Assert.notEmpty(attributes2grantedAuthoritiesMap,"A non-empty attributes2grantedAuthoritiesMap must be supplied");
attributes2grantedAuthoritiesMap = preProcessMap(attributes2grantedAuthoritiesMap);
try {
mappableAttributes = (String[])attributes2grantedAuthoritiesMap.keySet().toArray(new String[]{});
} catch ( ArrayStoreException ase ) {
throw new IllegalArgumentException("attributes2grantedAuthoritiesMap contains non-String objects as keys");
}
}
/**
* Preprocess the given map
* @param orgMap The map to process
* @return the processed Map
*/
private Map preProcessMap(Map orgMap) {
Map result = new HashMap(orgMap.size());
Iterator it = orgMap.entrySet().iterator();
while ( it.hasNext() ) {
Map.Entry entry = (Map.Entry)it.next();
result.put(entry.getKey(),getGrantedAuthorityCollection(entry.getValue()));
}
return result;
}
/**
* Convert the given value to a collection of Granted Authorities
*
* @param value
* The value to convert to a GrantedAuthority Collection
* @return Collection containing the GrantedAuthority Collection
*/
private Collection getGrantedAuthorityCollection(Object value) {
Collection result = new ArrayList();
addGrantedAuthorityCollection(result,value);
return result;
}
/**
* Convert the given value to a collection of Granted Authorities,
* adding the result to the given result collection.
*
* @param value
* The value to convert to a GrantedAuthority Collection
* @return Collection containing the GrantedAuthority Collection
*/
private void addGrantedAuthorityCollection(Collection result, Object value) {
if ( value != null ) {
if ( value instanceof Collection ) {
addGrantedAuthorityCollection(result,(Collection)value);
} else if ( value instanceof Object[] ) {
addGrantedAuthorityCollection(result,(Object[])value);
} else if ( value instanceof String ) {
addGrantedAuthorityCollection(result,(String)value);
} else if ( value instanceof GrantedAuthority ) {
result.add(value);
} else {
throw new IllegalArgumentException("Invalid object type: "+value.getClass().getName());
}
}
}
private void addGrantedAuthorityCollection(Collection result, Collection value) {
Iterator it = value.iterator();
while ( it.hasNext() ) {
addGrantedAuthorityCollection(result,it.next());
}
}
private void addGrantedAuthorityCollection(Collection result, Object[] value) {
for ( int i = 0 ; i < value.length ; i++ ) {
addGrantedAuthorityCollection(result,value[i]);
}
}
private void addGrantedAuthorityCollection(Collection result, String value) {
StringTokenizer st = new StringTokenizer(value,stringSeparator,false);
while ( st.hasMoreTokens() ) {
String nextToken = st.nextToken();
if ( StringUtils.hasText(nextToken) ) {
result.add(new GrantedAuthorityImpl(nextToken));
}
}
}
/**
* Map the given array of attributes to Spring Security GrantedAuthorities.
*/
public GrantedAuthority[] getGrantedAuthorities(String[] attributes) {
List gaList = new ArrayList();
for (int i = 0; i < attributes.length; i++) {
Collection c = (Collection)attributes2grantedAuthoritiesMap.get(attributes[i]);
if ( c != null ) { gaList.addAll(c); }
}
GrantedAuthority[] result = new GrantedAuthority[gaList.size()];
result = (GrantedAuthority[])gaList.toArray(result);
return result;
}
/**
* @return Returns the attributes2grantedAuthoritiesMap.
*/
public Map getAttributes2grantedAuthoritiesMap() {
return attributes2grantedAuthoritiesMap;
}
/**
* @param attributes2grantedAuthoritiesMap The attributes2grantedAuthoritiesMap to set.
*/
public void setAttributes2grantedAuthoritiesMap(Map attributes2grantedAuthoritiesMap) {
this.attributes2grantedAuthoritiesMap = attributes2grantedAuthoritiesMap;
}
/**
*
* @see org.springframework.security.authoritymapping.MappableAttributesRetriever#getMappableAttributes()
*/
public String[] getMappableAttributes() {
return mappableAttributes;
}
/**
* @return Returns the stringSeparator.
*/
public String getStringSeparator() {
return stringSeparator;
}
/**
* @param stringSeparator The stringSeparator to set.
*/
public void setStringSeparator(String stringSeparator) {
this.stringSeparator = stringSeparator;
}
}
@@ -158,4 +158,8 @@ public class ConcurrentSessionControllerImpl implements ConcurrentSessionControl
public void setSessionRegistry(SessionRegistry sessionRegistry) {
this.sessionRegistry = sessionRegistry;
}
public SessionRegistry getSessionRegistry() {
return sessionRegistry;
}
}
@@ -61,14 +61,13 @@ public class AnonymousBeanDefinitionParser implements BeanDefinitionParser {
filter.getPropertyValues().addPropertyValue("userAttribute", username + "," + grantedAuthority);
filter.getPropertyValues().addPropertyValue(ATT_KEY, key);
BeanDefinition authManager = ConfigUtils.registerProviderManagerIfNecessary(parserContext);
RootBeanDefinition provider = new RootBeanDefinition(AnonymousAuthenticationProvider.class);
provider.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
provider.setSource(source);
provider.getPropertyValues().addPropertyValue(ATT_KEY, key);
ManagedList authMgrProviderList = (ManagedList) authManager.getPropertyValues().getPropertyValue("providers").getValue();
authMgrProviderList.add(provider);
parserContext.getRegistry().registerBeanDefinition(BeanIds.ANONYMOUS_AUTHENTICATION_PROVIDER, provider);
ConfigUtils.addAuthenticationProvider(parserContext, BeanIds.ANONYMOUS_AUTHENTICATION_PROVIDER);
parserContext.getRegistry().registerBeanDefinition(BeanIds.ANONYMOUS_PROCESSING_FILTER, filter);
ConfigUtils.addHttpFilter(parserContext, new RuntimeBeanReference(BeanIds.ANONYMOUS_PROCESSING_FILTER));
@@ -1,5 +1,6 @@
package org.springframework.security.config;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.beans.factory.config.BeanDefinition;
@@ -21,7 +22,7 @@ public class AuthenticationManagerBeanDefinitionParser implements BeanDefinition
private static final String ATT_ALIAS = "alias";
public BeanDefinition parse(Element element, ParserContext parserContext) {
BeanDefinition authManager = ConfigUtils.registerProviderManagerIfNecessary(parserContext);
ConfigUtils.registerProviderManagerIfNecessary(parserContext);
String alias = element.getAttribute(ATT_ALIAS);
@@ -32,14 +33,20 @@ public class AuthenticationManagerBeanDefinitionParser implements BeanDefinition
String sessionControllerRef = element.getAttribute(ATT_SESSION_CONTROLLER_REF);
if (StringUtils.hasText(sessionControllerRef)) {
BeanDefinition authManager = parserContext.getRegistry().getBeanDefinition(BeanIds.AUTHENTICATION_MANAGER);
ConfigUtils.setSessionControllerOnAuthenticationManager(parserContext,
BeanIds.CONCURRENT_SESSION_CONTROLLER, element);
authManager.getPropertyValues().addPropertyValue("sessionController",
new RuntimeBeanReference(sessionControllerRef));
RootBeanDefinition sessionRegistryInjector = new RootBeanDefinition(SessionRegistryInjectionBeanPostProcessor.class);
sessionRegistryInjector.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
sessionRegistryInjector.getConstructorArgumentValues().addGenericArgumentValue(sessionControllerRef);
parserContext.getRegistry().registerBeanDefinition(BeanIds.SESSION_REGISTRY_INJECTION_POST_PROCESSOR, sessionRegistryInjector);
}
parserContext.getRegistry().registerAlias(BeanIds.AUTHENTICATION_MANAGER, alias);
return null;
}
}
}
@@ -94,7 +94,7 @@ class AuthenticationProviderBeanDefinitionParser implements BeanDefinitionParser
parserContext.getRegistry().registerBeanDefinition(name , cacheResolver);
parserContext.registerComponent(new BeanComponentDefinition(cacheResolver, name));
ConfigUtils.getRegisteredProviders(parserContext).add(new RuntimeBeanReference(id));
ConfigUtils.addAuthenticationProvider(parserContext, id);
return null;
}
@@ -18,6 +18,7 @@ public abstract class BeanIds {
static final String CONTEXT_SOURCE_SETTING_POST_PROCESSOR = "_contextSettingPostProcessor";
static final String ENTRY_POINT_INJECTION_POST_PROCESSOR = "_entryPointInjectionBeanPostProcessor";
static final String USER_DETAILS_SERVICE_INJECTION_POST_PROCESSOR = "_userServiceInjectionPostProcessor";
static final String SESSION_REGISTRY_INJECTION_POST_PROCESSOR = "_sessionRegistryInjectionPostProcessor";
static final String FILTER_CHAIN_POST_PROCESSOR = "_filterChainProxyPostProcessor";
static final String FILTER_LIST = "_filterChainList";
@@ -41,6 +42,7 @@ public abstract class BeanIds {
public static final String MAIN_ENTRY_POINT = "_mainEntryPoint";
public static final String FILTER_CHAIN_PROXY = "_filterChainProxy";
public static final String HTTP_SESSION_CONTEXT_INTEGRATION_FILTER = "_httpSessionContextIntegrationFilter";
public static final String LDAP_AUTHENTICATION_PROVIDER = "_ldapAuthenticationProvider";
public static final String LOGOUT_FILTER = "_logoutFilter";
public static final String EXCEPTION_TRANSLATION_FILTER = "_exceptionTranslationFilter";
public static final String FILTER_SECURITY_INTERCEPTOR = "_filterSecurityInterceptor";
@@ -48,6 +50,7 @@ public abstract class BeanIds {
public static final String CHANNEL_DECISION_MANAGER = "_channelDecisionManager";
public static final String REMEMBER_ME_FILTER = "_rememberMeFilter";
public static final String REMEMBER_ME_SERVICES = "_rememberMeServices";
public static final String REMEMBER_ME_AUTHENTICATION_PROVIDER = "_rememberMeAuthenticationProvider";
public static final String DEFAULT_LOGIN_PAGE_GENERATING_FILTER = "_defaultLoginPageFilter";
public static final String SECURITY_CONTEXT_HOLDER_AWARE_REQUEST_FILTER = "_securityContextHolderAwareRequestFilter";
public static final String SESSION_FIXATION_PROTECTION_FILTER = "_sessionFixationProtectionFilter";
@@ -65,5 +68,4 @@ public abstract class BeanIds {
public static final String X509_AUTH_PROVIDER = "_x509AuthenticationProvider";
public static final String PRE_AUTH_ENTRY_POINT = "_preAuthenticatedProcessingFilterEntryPoint";
public static final String REMEMBER_ME_SERVICES_INJECTION_POST_PROCESSOR = "_rememberMeServicesInjectionBeanPostProcessor";
}
@@ -1,7 +1,7 @@
package org.springframework.security.config;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -9,15 +9,12 @@ import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.security.afterinvocation.AfterInvocationProviderManager;
import org.springframework.security.providers.ProviderManager;
import org.springframework.security.userdetails.UserDetailsService;
import org.springframework.security.util.UrlUtils;
import org.springframework.security.vote.AffirmativeBased;
import org.springframework.security.vote.AuthenticatedVoter;
@@ -80,21 +77,20 @@ public abstract class ConfigUtils {
* using the &lt;security:provider /> tag or by other beans which have a dependency on the
* authentication manager.
*/
static BeanDefinition registerProviderManagerIfNecessary(ParserContext parserContext) {
static void registerProviderManagerIfNecessary(ParserContext parserContext) {
if(parserContext.getRegistry().containsBeanDefinition(BeanIds.AUTHENTICATION_MANAGER)) {
return parserContext.getRegistry().getBeanDefinition(BeanIds.AUTHENTICATION_MANAGER);
return;
}
BeanDefinition authManager = new RootBeanDefinition(ProviderManager.class);
authManager.getPropertyValues().addPropertyValue("providers", new ManagedList());
BeanDefinition authManager = new RootBeanDefinition(NamespaceAuthenticationManager.class);
authManager.getPropertyValues().addPropertyValue("providerBeanNames", new ArrayList());
parserContext.getRegistry().registerBeanDefinition(BeanIds.AUTHENTICATION_MANAGER, authManager);
return authManager;
}
static ManagedList getRegisteredProviders(ParserContext parserContext) {
BeanDefinition authManager = registerProviderManagerIfNecessary(parserContext);
return (ManagedList) authManager.getPropertyValues().getPropertyValue("providers").getValue();
static void addAuthenticationProvider(ParserContext parserContext, String beanName) {
registerProviderManagerIfNecessary(parserContext);
BeanDefinition authManager = parserContext.getRegistry().getBeanDefinition(BeanIds.AUTHENTICATION_MANAGER);
((ArrayList) authManager.getPropertyValues().getPropertyValue("providerBeanNames").getValue()).add(beanName);
}
static ManagedList getRegisteredAfterInvocationProviders(ParserContext parserContext) {
@@ -172,7 +168,8 @@ public abstract class ConfigUtils {
}
static void setSessionControllerOnAuthenticationManager(ParserContext pc, String beanName, Element sourceElt) {
BeanDefinition authManager = registerProviderManagerIfNecessary(pc);
registerProviderManagerIfNecessary(pc);
BeanDefinition authManager = pc.getRegistry().getBeanDefinition(BeanIds.AUTHENTICATION_MANAGER);
PropertyValue pv = authManager.getPropertyValues().getPropertyValue("sessionController");
if (pv != null && pv.getValue() != null) {
@@ -17,7 +17,7 @@ import org.w3c.dom.Node;
*/
public class CustomAuthenticationProviderBeanDefinitionDecorator implements BeanDefinitionDecorator {
public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder holder, ParserContext parserContext) {
ConfigUtils.getRegisteredProviders(parserContext).add(new RuntimeBeanReference(holder.getBeanName()));
ConfigUtils.addAuthenticationProvider(parserContext, holder.getBeanName());
return holder;
}
@@ -10,34 +10,35 @@ abstract class Elements {
public static final String AUTHENTICATION_MANAGER = "authentication-manager";
public static final String USER_SERVICE = "user-service";
public static final String JDBC_USER_SERVICE = "jdbc-user-service";
public static final String FILTER_CHAIN_MAP = "filter-chain-map";
public static final String INTERCEPT_METHODS = "intercept-methods";
public static final String INTERCEPT_URL = "intercept-url";
public static final String AUTHENTICATION_PROVIDER = "authentication-provider";
public static final String HTTP = "http";
public static final String LDAP_PROVIDER = "ldap-authentication-provider";
public static final String LDAP_SERVER = "ldap-server";
public static final String JDBC_USER_SERVICE = "jdbc-user-service";
public static final String FILTER_CHAIN_MAP = "filter-chain-map";
public static final String INTERCEPT_METHODS = "intercept-methods";
public static final String INTERCEPT_URL = "intercept-url";
public static final String AUTHENTICATION_PROVIDER = "authentication-provider";
public static final String HTTP = "http";
public static final String LDAP_PROVIDER = "ldap-authentication-provider";
public static final String LDAP_SERVER = "ldap-server";
public static final String LDAP_USER_SERVICE = "ldap-user-service";
public static final String PROTECT_POINTCUT = "protect-pointcut";
public static final String PROTECT = "protect";
public static final String CONCURRENT_SESSIONS = "concurrent-session-control";
public static final String LOGOUT = "logout";
public static final String FORM_LOGIN = "form-login";
public static final String OPENID_LOGIN = "openid-login";
public static final String BASIC_AUTH = "http-basic";
public static final String REMEMBER_ME = "remember-me";
public static final String ANONYMOUS = "anonymous";
public static final String FILTER_CHAIN = "filter-chain";
public static final String GLOBAL_METHOD_SECURITY = "global-method-security";
public static final String PASSWORD_ENCODER = "password-encoder";
public static final String SALT_SOURCE = "salt-source";
public static final String PORT_MAPPINGS = "port-mappings";
public static final String CONCURRENT_SESSIONS = "concurrent-session-control";
public static final String LOGOUT = "logout";
public static final String FORM_LOGIN = "form-login";
public static final String OPENID_LOGIN = "openid-login";
public static final String BASIC_AUTH = "http-basic";
public static final String REMEMBER_ME = "remember-me";
public static final String ANONYMOUS = "anonymous";
public static final String FILTER_CHAIN = "filter-chain";
public static final String GLOBAL_METHOD_SECURITY = "global-method-security";
public static final String PASSWORD_ENCODER = "password-encoder";
public static final String SALT_SOURCE = "salt-source";
public static final String PORT_MAPPINGS = "port-mappings";
public static final String PORT_MAPPING = "port-mapping";
public static final String CUSTOM_FILTER = "custom-filter";
public static final String CUSTOM_AUTH_PROVIDER = "custom-authentication-provider";
public static final String CUSTOM_AFTER_INVOCATION_PROVIDER = "custom-after-invocation-provider";
public static final String CUSTOM_AFTER_INVOCATION_PROVIDER = "custom-after-invocation-provider";
public static final String X509 = "x509";
public static final String FILTER_INVOCATION_DEFINITION_SOURCE = "filter-invocation-definition-source";
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 HTTP_FIREWALL = "http-firewall";
}
@@ -66,7 +66,7 @@ public class FilterChainProxyPostProcessor implements BeanPostProcessor, BeanFac
unwrapFilter(previous) + "' have the same 'order' value. When using custom filters, " +
"please make sure the positions do not conflict with default filters. " +
"Alternatively you can disable the default filters by removing the corresponding " +
"child elements from <http> and not avoiding the use of <http auto-config='true'>.");
"child elements from <http> and avoiding the use of <http auto-config='true'>.");
}
}
}
@@ -20,14 +20,13 @@ import org.springframework.security.intercept.method.MapBasedMethodDefinitionSou
import org.springframework.security.intercept.method.ProtectPointcutPostProcessor;
import org.springframework.security.intercept.method.aopalliance.MethodDefinitionSourceAdvisor;
import org.springframework.security.intercept.method.aopalliance.MethodSecurityInterceptor;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
/**
* Processes the top-level "global-method-security" element.
*
*
* @author Ben Alex
* @version $Id$
*/
@@ -42,32 +41,26 @@ class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionParser {
private static final String ATT_USE_JSR250 = "jsr250-annotations";
private static final String ATT_USE_SECURED = "secured-annotations";
private void validatePresent(String className, Element element, ParserContext parserContext) {
if (!ClassUtils.isPresent(className, parserContext.getReaderContext().getBeanClassLoader())) {
parserContext.getReaderContext().error("Cannot locate '" + className + "'", element);
}
}
public BeanDefinition parse(Element element, ParserContext parserContext) {
Object source = parserContext.extractSource(element);
// The list of method metadata delegates
ManagedList delegates = new ManagedList();
boolean jsr250Enabled = registerAnnotationBasedMethodDefinitionSources(element, parserContext, delegates);
MapBasedMethodDefinitionSource mapBasedMethodDefinitionSource = new MapBasedMethodDefinitionSource();
delegates.add(mapBasedMethodDefinitionSource);
// Now create a Map<String, ConfigAttribute> for each <protect-pointcut> sub-element
Map pointcutMap = parseProtectPointcuts(parserContext,
// Now create a Map<String, ConfigAttribute> for each <protect-pointcut> sub-element
Map pointcutMap = parseProtectPointcuts(parserContext,
DomUtils.getChildElementsByTagName(element, Elements.PROTECT_POINTCUT));
if (pointcutMap.size() > 0) {
registerProtectPointcutPostProcessor(parserContext, pointcutMap, mapBasedMethodDefinitionSource, source);
}
registerDelegatingMethodDefinitionSource(parserContext, delegates, source);
// Register the applicable AccessDecisionManager, handling the special JSR 250 voter if being used
String accessManagerId = element.getAttribute(ATT_ACCESS_MGR);
@@ -75,45 +68,40 @@ class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionParser {
ConfigUtils.registerDefaultAccessManagerIfNecessary(parserContext);
if (jsr250Enabled) {
ConfigUtils.addVoter(new RootBeanDefinition(JSR_250_VOTER_CLASS, null, null), parserContext);
ConfigUtils.addVoter(new RootBeanDefinition(JSR_250_VOTER_CLASS, null, null), parserContext);
}
accessManagerId = BeanIds.ACCESS_MANAGER;
}
registerMethodSecurityInterceptor(parserContext, accessManagerId, source);
registerAdvisor(parserContext, source);
AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);
return null;
}
/**
* Checks whether JSR-250 and/or Secured annotations are enabled and adds the appropriate
* MethodDefinitionSource delegates if required.
* Checks whether JSR-250 and/or Secured annotations are enabled and adds the appropriate
* MethodDefinitionSource delegates if required.
*/
private boolean registerAnnotationBasedMethodDefinitionSources(Element element, ParserContext pc, ManagedList delegates) {
boolean useJsr250 = "enabled".equals(element.getAttribute(ATT_USE_JSR250));
boolean useSecured = "enabled".equals(element.getAttribute(ATT_USE_SECURED));
// Check the required classes are present
if (useSecured) {
validatePresent(SECURED_METHOD_DEFINITION_SOURCE_CLASS, element, pc);
validatePresent(SECURED_DEPENDENCY_CLASS, element, pc);
delegates.add(BeanDefinitionBuilder.rootBeanDefinition(SECURED_METHOD_DEFINITION_SOURCE_CLASS).getBeanDefinition());
}
if (useJsr250) {
validatePresent(JSR_250_SECURITY_METHOD_DEFINITION_SOURCE_CLASS, element, pc);
validatePresent(JSR_250_VOTER_CLASS, element, pc);
delegates.add(BeanDefinitionBuilder.rootBeanDefinition(JSR_250_SECURITY_METHOD_DEFINITION_SOURCE_CLASS).getBeanDefinition());
delegates.add(BeanDefinitionBuilder.rootBeanDefinition(JSR_250_SECURITY_METHOD_DEFINITION_SOURCE_CLASS).getBeanDefinition());
}
return useJsr250;
}
private void registerDelegatingMethodDefinitionSource(ParserContext parserContext, ManagedList delegates, Object source) {
if (parserContext.getRegistry().containsBeanDefinition(BeanIds.DELEGATING_METHOD_DEFINITION_SOURCE)) {
parserContext.getReaderContext().error("Duplicate <global-method-security> detected.", source);
@@ -122,9 +110,9 @@ class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionParser {
delegatingMethodDefinitionSource.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
delegatingMethodDefinitionSource.setSource(source);
delegatingMethodDefinitionSource.getPropertyValues().addPropertyValue("methodDefinitionSources", delegates);
parserContext.getRegistry().registerBeanDefinition(BeanIds.DELEGATING_METHOD_DEFINITION_SOURCE, delegatingMethodDefinitionSource);
parserContext.getRegistry().registerBeanDefinition(BeanIds.DELEGATING_METHOD_DEFINITION_SOURCE, delegatingMethodDefinitionSource);
}
private void registerProtectPointcutPostProcessor(ParserContext parserContext, Map pointcutMap,
MapBasedMethodDefinitionSource mapBasedMethodDefinitionSource, Object source) {
RootBeanDefinition ppbp = new RootBeanDefinition(ProtectPointcutPostProcessor.class);
@@ -162,13 +150,13 @@ class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionParser {
RootBeanDefinition interceptor = new RootBeanDefinition(MethodSecurityInterceptor.class);
interceptor.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
interceptor.setSource(source);
interceptor.getPropertyValues().addPropertyValue("accessDecisionManager", new RuntimeBeanReference(accessManagerId));
interceptor.getPropertyValues().addPropertyValue("authenticationManager", new RuntimeBeanReference(BeanIds.AUTHENTICATION_MANAGER));
interceptor.getPropertyValues().addPropertyValue("objectDefinitionSource", new RuntimeBeanReference(BeanIds.DELEGATING_METHOD_DEFINITION_SOURCE));
parserContext.getRegistry().registerBeanDefinition(BeanIds.METHOD_SECURITY_INTERCEPTOR, interceptor);
parserContext.registerComponent(new BeanComponentDefinition(interceptor, BeanIds.METHOD_SECURITY_INTERCEPTOR));
parserContext.getRegistry().registerBeanDefinition(BeanIds.METHOD_SECURITY_INTERCEPTOR_POST_PROCESSOR,
new RootBeanDefinition(MethodSecurityInterceptorPostProcessor.class));
}
@@ -180,6 +168,6 @@ class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionParser {
advisor.getConstructorArgumentValues().addGenericArgumentValue(BeanIds.METHOD_SECURITY_INTERCEPTOR);
advisor.getConstructorArgumentValues().addGenericArgumentValue(new RuntimeBeanReference(BeanIds.DELEGATING_METHOD_DEFINITION_SOURCE));
parserContext.getRegistry().registerBeanDefinition(BeanIds.METHOD_DEFINITION_SOURCE_ADVISOR, advisor);
}
parserContext.getRegistry().registerBeanDefinition(BeanIds.METHOD_DEFINITION_SOURCE_ADVISOR, advisor);
}
}
@@ -0,0 +1,31 @@
package org.springframework.security.config;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* Injects the supplied {@code HttpFirewall} bean reference into the {@code FilterChainProxy}.
*
* @author Luke Taylor
*/
public class HttpFirewallBeanDefinitionParser implements BeanDefinitionParser {
public BeanDefinition parse(Element element, ParserContext pc) {
String ref = element.getAttribute("ref");
if (!StringUtils.hasText(ref)) {
pc.getReaderContext().error("ref attribute is required", pc.extractSource(element));
}
BeanDefinitionBuilder injector = BeanDefinitionBuilder.rootBeanDefinition(HttpFirewallInjectionBeanPostProcessor.class);
injector.addConstructorArg(ref);
pc.getReaderContext().registerWithGeneratedName(injector.getBeanDefinition());
return null;
}
}
@@ -0,0 +1,40 @@
package org.springframework.security.config;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.security.config.BeanIds;
import org.springframework.security.util.FilterChainProxy;
import org.springframework.security.firewall.HttpFirewall;
/**
* @author Luke Taylor
*/
public class HttpFirewallInjectionBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
private ConfigurableListableBeanFactory beanFactory;
private String ref;
public HttpFirewallInjectionBeanPostProcessor(String ref) {
this.ref = ref;
}
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (BeanIds.FILTER_CHAIN_PROXY.equals(beanName)) {
HttpFirewall fw = (HttpFirewall) beanFactory.getBean(ref);
((FilterChainProxy)bean).setFirewall(fw);
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
}
}
@@ -50,16 +50,16 @@ import org.w3c.dom.Element;
* @version $Id$
*/
public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
protected final Log logger = LogFactory.getLog(getClass());
static final Log logger = LogFactory.getLog(HttpSecurityBeanDefinitionParser.class);
static final String ATT_REALM = "realm";
static final String DEF_REALM = "Spring Security Application";
static final String ATT_PATH_PATTERN = "pattern";
static final String ATT_SESSION_FIXATION_PROTECTION = "session-fixation-protection";
static final String OPT_SESSION_FIXATION_NO_PROTECTION = "none";
static final String OPT_SESSION_FIXATION_CLEAN_SESSION = "newSession";
static final String OPT_SESSION_FIXATION_CLEAN_SESSION = "newSession";
static final String OPT_SESSION_FIXATION_MIGRATE_SESSION = "migrateSession";
static final String ATT_PATH_TYPE = "path-type";
@@ -91,9 +91,9 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
static final String ATT_SERVLET_API_PROVISION = "servlet-api-provision";
static final String DEF_SERVLET_API_PROVISION = "true";
static final String ATT_ACCESS_MGR = "access-decision-manager-ref";
static final String ATT_ACCESS_MGR = "access-decision-manager-ref";
static final String ATT_USER_SERVICE_REF = "user-service-ref";
static final String ATT_ENTRY_POINT_REF = "entry-point-ref";
static final String ATT_ONCE_PER_REQUEST = "once-per-request";
static final String ATT_ACCESS_DENIED_PAGE = "access-denied-page";
@@ -106,20 +106,20 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
// SEC-501 - should paths stored in request maps be converted to lower case
// true if Ant path and using lower case
final boolean convertPathsToLowerCase = (matcher instanceof AntUrlPathMatcher) && matcher.requiresLowerCaseUrl();
final List interceptUrlElts = DomUtils.getChildElementsByTagName(element, Elements.INTERCEPT_URL);
final Map filterChainMap = new LinkedHashMap();
final LinkedHashMap channelRequestMap = new LinkedHashMap();
registerFilterChainProxy(parserContext, filterChainMap, matcher, source);
parseInterceptUrlsForChannelSecurityAndFilterChain(interceptUrlElts, filterChainMap, channelRequestMap,
parseInterceptUrlsForChannelSecurityAndFilterChain(interceptUrlElts, filterChainMap, channelRequestMap,
convertPathsToLowerCase, parserContext);
registerHttpSessionIntegrationFilter(element, parserContext);
boolean allowSessionCreation = registerHttpSessionIntegrationFilter(element, parserContext);
registerServletApiFilter(element, parserContext);
// Set up the access manager reference for http
String accessManagerId = element.getAttribute(ATT_ACCESS_MGR);
@@ -127,31 +127,31 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
ConfigUtils.registerDefaultAccessManagerIfNecessary(parserContext);
accessManagerId = BeanIds.ACCESS_MANAGER;
}
// Register the portMapper. A default will always be created, even if no element exists.
BeanDefinition portMapper = new PortMappingsBeanDefinitionParser().parse(
DomUtils.getChildElementByTagName(element, Elements.PORT_MAPPINGS), parserContext);
registry.registerBeanDefinition(BeanIds.PORT_MAPPER, portMapper);
registerExceptionTranslationFilter(element, parserContext);
registerExceptionTranslationFilter(element, parserContext, allowSessionCreation);
if (channelRequestMap.size() > 0) {
// At least one channel requirement has been specified
registerChannelProcessingBeans(parserContext, matcher, channelRequestMap);
}
registerFilterSecurityInterceptor(element, parserContext, matcher, accessManagerId,
}
registerFilterSecurityInterceptor(element, parserContext, matcher, accessManagerId,
parseInterceptUrlsForFilterInvocationRequestMap(interceptUrlElts, convertPathsToLowerCase, parserContext));
boolean sessionControlEnabled = registerConcurrentSessionControlBeansIfRequired(element, parserContext);
registerSessionFixationProtectionFilter(parserContext, element.getAttribute(ATT_SESSION_FIXATION_PROTECTION),
registerSessionFixationProtectionFilter(parserContext, element.getAttribute(ATT_SESSION_FIXATION_PROTECTION),
sessionControlEnabled);
boolean autoConfig = false;
if ("true".equals(element.getAttribute(ATT_AUTO_CONFIG))) {
autoConfig = true;
autoConfig = true;
}
Element anonymousElt = DomUtils.getChildElementByTagName(element, Elements.ANONYMOUS);
@@ -159,22 +159,9 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
new AnonymousBeanDefinitionParser().parse(anonymousElt, parserContext);
}
// Parse remember me before logout as RememberMeServices is also a LogoutHandler implementation.
Element rememberMeElt = DomUtils.getChildElementByTagName(element, Elements.REMEMBER_ME);
if (rememberMeElt != null || autoConfig) {
new RememberMeBeanDefinitionParser().parse(rememberMeElt, parserContext);
// Post processor to inject RememberMeServices into filters which need it
RootBeanDefinition rememberMeInjectionPostProcessor = new RootBeanDefinition(RememberMeServicesInjectionBeanPostProcessor.class);
rememberMeInjectionPostProcessor.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(BeanIds.REMEMBER_ME_SERVICES_INJECTION_POST_PROCESSOR, rememberMeInjectionPostProcessor);
}
Element logoutElt = DomUtils.getChildElementByTagName(element, Elements.LOGOUT);
if (logoutElt != null || autoConfig) {
new LogoutBeanDefinitionParser().parse(logoutElt, parserContext);
}
parseBasicFormLoginAndOpenID(element, parserContext, autoConfig);
parseRememberMeAndLogout(element, autoConfig, parserContext);
parseBasicFormLoginAndOpenID(element, parserContext, autoConfig, allowSessionCreation);
Element x509Elt = DomUtils.getChildElementByTagName(element, Elements.X509);
if (x509Elt != null) {
@@ -188,26 +175,49 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
RootBeanDefinition postProcessor2 = new RootBeanDefinition(UserDetailsServiceInjectionBeanPostProcessor.class);
postProcessor2.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(BeanIds.USER_DETAILS_SERVICE_INJECTION_POST_PROCESSOR, postProcessor2);
return null;
}
private void parseRememberMeAndLogout(Element elt, boolean autoConfig, ParserContext pc) {
// Parse remember me before logout as RememberMeServices is also a LogoutHandler implementation.
Element rememberMeElt = DomUtils.getChildElementByTagName(elt, Elements.REMEMBER_ME);
String rememberMeServices = null;
if (rememberMeElt != null || autoConfig) {
RememberMeBeanDefinitionParser rmbdp = new RememberMeBeanDefinitionParser();
rmbdp.parse(rememberMeElt, pc);
rememberMeServices = rmbdp.getServicesName();
// Post processor to inject RememberMeServices into filters which need it
RootBeanDefinition rememberMeInjectionPostProcessor = new RootBeanDefinition(RememberMeServicesInjectionBeanPostProcessor.class);
rememberMeInjectionPostProcessor.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
pc.getRegistry().registerBeanDefinition(BeanIds.REMEMBER_ME_SERVICES_INJECTION_POST_PROCESSOR, rememberMeInjectionPostProcessor);
}
Element logoutElt = DomUtils.getChildElementByTagName(elt, Elements.LOGOUT);
if (logoutElt != null || autoConfig) {
new LogoutBeanDefinitionParser(rememberMeServices).parse(logoutElt, pc);
}
}
private void registerFilterChainProxy(ParserContext pc, Map filterChainMap, UrlMatcher matcher, Object source) {
if (pc.getRegistry().containsBeanDefinition(BeanIds.FILTER_CHAIN_PROXY)) {
pc.getReaderContext().error("Duplicate <http> element detected", source);
}
RootBeanDefinition filterChainProxy = new RootBeanDefinition(FilterChainProxy.class);
filterChainProxy.setSource(source);
filterChainProxy.getPropertyValues().addPropertyValue("matcher", matcher);
filterChainProxy.getPropertyValues().addPropertyValue("stripQueryStringFromUrls", Boolean.valueOf(matcher instanceof AntUrlPathMatcher));
filterChainProxy.getPropertyValues().addPropertyValue("filterChainMap", filterChainMap);
pc.getRegistry().registerBeanDefinition(BeanIds.FILTER_CHAIN_PROXY, filterChainProxy);
pc.getRegistry().registerAlias(BeanIds.FILTER_CHAIN_PROXY, BeanIds.SPRING_SECURITY_FILTER_CHAIN);
pc.getRegistry().registerAlias(BeanIds.FILTER_CHAIN_PROXY, BeanIds.SPRING_SECURITY_FILTER_CHAIN);
}
private void registerHttpSessionIntegrationFilter(Element element, ParserContext pc) {
private boolean registerHttpSessionIntegrationFilter(Element element, ParserContext pc) {
RootBeanDefinition httpScif = new RootBeanDefinition(HttpSessionContextIntegrationFilter.class);
boolean sessionCreationAllowed = true;
String createSession = element.getAttribute(ATT_CREATE_SESSION);
if (OPT_CREATE_SESSION_ALWAYS.equals(createSession)) {
httpScif.getPropertyValues().addPropertyValue("allowSessionCreation", Boolean.TRUE);
@@ -215,6 +225,7 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
} else if (OPT_CREATE_SESSION_NEVER.equals(createSession)) {
httpScif.getPropertyValues().addPropertyValue("allowSessionCreation", Boolean.FALSE);
httpScif.getPropertyValues().addPropertyValue("forceEagerSessionCreation", Boolean.FALSE);
sessionCreationAllowed = false;
} else {
createSession = DEF_CREATE_SESSION_IF_REQUIRED;
httpScif.getPropertyValues().addPropertyValue("allowSessionCreation", Boolean.TRUE);
@@ -223,9 +234,11 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
pc.getRegistry().registerBeanDefinition(BeanIds.HTTP_SESSION_CONTEXT_INTEGRATION_FILTER, httpScif);
ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.HTTP_SESSION_CONTEXT_INTEGRATION_FILTER));
return sessionCreationAllowed;
}
// Adds the servlet-api integration filter if required
// Adds the servlet-api integration filter if required
private void registerServletApiFilter(Element element, ParserContext pc) {
String provideServletApi = element.getAttribute(ATT_SERVLET_API_PROVISION);
if (!StringUtils.hasText(provideServletApi)) {
@@ -238,56 +251,57 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.SECURITY_CONTEXT_HOLDER_AWARE_REQUEST_FILTER));
}
}
private boolean registerConcurrentSessionControlBeansIfRequired(Element element, ParserContext parserContext) {
Element sessionControlElt = DomUtils.getChildElementByTagName(element, Elements.CONCURRENT_SESSIONS);
if (sessionControlElt == null) {
return false;
}
new ConcurrentSessionsBeanDefinitionParser().parse(sessionControlElt, parserContext);
logger.info("Concurrent session filter in use, setting 'forceEagerSessionCreation' to true");
BeanDefinition sessionIntegrationFilter = parserContext.getRegistry().getBeanDefinition(BeanIds.HTTP_SESSION_CONTEXT_INTEGRATION_FILTER);
BeanDefinition sessionIntegrationFilter = parserContext.getRegistry().getBeanDefinition(BeanIds.HTTP_SESSION_CONTEXT_INTEGRATION_FILTER);
sessionIntegrationFilter.getPropertyValues().addPropertyValue("forceEagerSessionCreation", Boolean.TRUE);
return true;
}
private void registerExceptionTranslationFilter(Element element, ParserContext pc) {
String accessDeniedPage = element.getAttribute(ATT_ACCESS_DENIED_PAGE);
ConfigUtils.validateHttpRedirect(accessDeniedPage, pc, pc.extractSource(element));
private void registerExceptionTranslationFilter(Element element, ParserContext pc, boolean allowSessionCreation) {
String accessDeniedPage = element.getAttribute(ATT_ACCESS_DENIED_PAGE);
ConfigUtils.validateHttpRedirect(accessDeniedPage, pc, pc.extractSource(element));
BeanDefinitionBuilder exceptionTranslationFilterBuilder
= BeanDefinitionBuilder.rootBeanDefinition(ExceptionTranslationFilter.class);
exceptionTranslationFilterBuilder.addPropertyValue("createSessionAllowed", new Boolean(allowSessionCreation));
if (StringUtils.hasText(accessDeniedPage)) {
AccessDeniedHandlerImpl accessDeniedHandler = new AccessDeniedHandlerImpl();
accessDeniedHandler.setErrorPage(accessDeniedPage);
BeanDefinition accessDeniedHandler = new RootBeanDefinition(AccessDeniedHandlerImpl.class);
accessDeniedHandler.getPropertyValues().addPropertyValue("errorPage", accessDeniedPage);
exceptionTranslationFilterBuilder.addPropertyValue("accessDeniedHandler", accessDeniedHandler);
}
pc.getRegistry().registerBeanDefinition(BeanIds.EXCEPTION_TRANSLATION_FILTER, exceptionTranslationFilterBuilder.getBeanDefinition());
ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.EXCEPTION_TRANSLATION_FILTER));
}
private void registerFilterSecurityInterceptor(Element element, ParserContext pc, UrlMatcher matcher,
String accessManagerId, LinkedHashMap filterInvocationDefinitionMap) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(FilterSecurityInterceptor.class);
builder.addPropertyReference("accessDecisionManager", accessManagerId);
builder.addPropertyReference("authenticationManager", BeanIds.AUTHENTICATION_MANAGER);
if ("false".equals(element.getAttribute(ATT_ONCE_PER_REQUEST))) {
builder.addPropertyValue("observeOncePerRequest", Boolean.FALSE);
}
DefaultFilterInvocationDefinitionSource fids =
DefaultFilterInvocationDefinitionSource fids =
new DefaultFilterInvocationDefinitionSource(matcher, filterInvocationDefinitionMap);
fids.setStripQueryStringFromUrls(matcher instanceof AntUrlPathMatcher);
builder.addPropertyValue("objectDefinitionSource", fids);
pc.getRegistry().registerBeanDefinition(BeanIds.FILTER_SECURITY_INTERCEPTOR, builder.getBeanDefinition());
ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.FILTER_SECURITY_INTERCEPTOR));
}
private void registerChannelProcessingBeans(ParserContext pc, UrlMatcher matcher, LinkedHashMap channelRequestMap) {
RootBeanDefinition channelFilter = new RootBeanDefinition(ChannelProcessingFilter.class);
channelFilter.getPropertyValues().addPropertyValue("channelDecisionManager",
@@ -295,7 +309,7 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
DefaultFilterInvocationDefinitionSource channelFilterInvDefSource =
new DefaultFilterInvocationDefinitionSource(matcher, channelRequestMap);
channelFilterInvDefSource.setStripQueryStringFromUrls(matcher instanceof AntUrlPathMatcher);
channelFilter.getPropertyValues().addPropertyValue("filterInvocationDefinitionSource",
channelFilterInvDefSource);
RootBeanDefinition channelDecisionManager = new RootBeanDefinition(ChannelDecisionManagerImpl.class);
@@ -316,159 +330,161 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
pc.getRegistry().registerBeanDefinition(BeanIds.CHANNEL_PROCESSING_FILTER, channelFilter);
ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.CHANNEL_PROCESSING_FILTER));
pc.getRegistry().registerBeanDefinition(BeanIds.CHANNEL_DECISION_MANAGER, channelDecisionManager);
}
private void registerSessionFixationProtectionFilter(ParserContext pc, String sessionFixationAttribute, boolean sessionControlEnabled) {
if(!StringUtils.hasText(sessionFixationAttribute)) {
sessionFixationAttribute = OPT_SESSION_FIXATION_MIGRATE_SESSION;
}
if (!sessionFixationAttribute.equals(OPT_SESSION_FIXATION_NO_PROTECTION)) {
BeanDefinitionBuilder sessionFixationFilter =
BeanDefinitionBuilder sessionFixationFilter =
BeanDefinitionBuilder.rootBeanDefinition(SessionFixationProtectionFilter.class);
sessionFixationFilter.addPropertyValue("migrateSessionAttributes",
sessionFixationFilter.addPropertyValue("migrateSessionAttributes",
Boolean.valueOf(sessionFixationAttribute.equals(OPT_SESSION_FIXATION_MIGRATE_SESSION)));
if (sessionControlEnabled) {
sessionFixationFilter.addPropertyReference("sessionRegistry", BeanIds.SESSION_REGISTRY);
}
pc.getRegistry().registerBeanDefinition(BeanIds.SESSION_FIXATION_PROTECTION_FILTER,
pc.getRegistry().registerBeanDefinition(BeanIds.SESSION_FIXATION_PROTECTION_FILTER,
sessionFixationFilter.getBeanDefinition());
ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.SESSION_FIXATION_PROTECTION_FILTER));
}
}
private void parseBasicFormLoginAndOpenID(Element element, ParserContext pc, boolean autoConfig) {
private void parseBasicFormLoginAndOpenID(Element element, ParserContext pc, boolean autoConfig, boolean allowSessionCreation) {
RootBeanDefinition formLoginFilter = null;
RootBeanDefinition formLoginEntryPoint = null;
String formLoginPage = null;
String formLoginPage = null;
RootBeanDefinition openIDFilter = null;
RootBeanDefinition openIDEntryPoint = null;
String openIDLoginPage = null;
String realm = element.getAttribute(ATT_REALM);
if (!StringUtils.hasText(realm)) {
realm = DEF_REALM;
realm = DEF_REALM;
}
Element basicAuthElt = DomUtils.getChildElementByTagName(element, Elements.BASIC_AUTH);
if (basicAuthElt != null || autoConfig) {
new BasicAuthenticationBeanDefinitionParser(realm).parse(basicAuthElt, pc);
}
Element formLoginElt = DomUtils.getChildElementByTagName(element, Elements.FORM_LOGIN);
Element formLoginElt = DomUtils.getChildElementByTagName(element, Elements.FORM_LOGIN);
if (formLoginElt != null || autoConfig) {
FormLoginBeanDefinitionParser parser = new FormLoginBeanDefinitionParser("/j_spring_security_check",
"org.springframework.security.ui.webapp.AuthenticationProcessingFilter");
FormLoginBeanDefinitionParser parser = new FormLoginBeanDefinitionParser("/j_spring_security_check",
"org.springframework.security.ui.webapp.AuthenticationProcessingFilter");
parser.parse(formLoginElt, pc);
formLoginFilter = parser.getFilterBean();
formLoginEntryPoint = parser.getEntryPointBean();
formLoginPage = parser.getLoginPage();
}
Element openIDLoginElt = DomUtils.getChildElementByTagName(element, Elements.OPENID_LOGIN);
if (openIDLoginElt != null) {
FormLoginBeanDefinitionParser parser = new FormLoginBeanDefinitionParser("/j_spring_openid_security_check",
"org.springframework.security.ui.openid.OpenIDAuthenticationProcessingFilter");
FormLoginBeanDefinitionParser parser = new FormLoginBeanDefinitionParser("/j_spring_openid_security_check",
"org.springframework.security.ui.openid.OpenIDAuthenticationProcessingFilter");
parser.parse(openIDLoginElt, pc);
openIDFilter = parser.getFilterBean();
openIDEntryPoint = parser.getEntryPointBean();
openIDLoginPage = parser.getLoginPage();
BeanDefinitionBuilder openIDProviderBuilder =
BeanDefinitionBuilder openIDProviderBuilder =
BeanDefinitionBuilder.rootBeanDefinition("org.springframework.security.providers.openid.OpenIDAuthenticationProvider");
String userService = openIDLoginElt.getAttribute(ATT_USER_SERVICE_REF);
if (StringUtils.hasText(userService)) {
openIDProviderBuilder.addPropertyReference("userDetailsService", userService);
}
BeanDefinition openIDProvider = openIDProviderBuilder.getBeanDefinition();
pc.getRegistry().registerBeanDefinition(BeanIds.OPEN_ID_PROVIDER, openIDProvider);
ConfigUtils.getRegisteredProviders(pc).add(new RuntimeBeanReference(BeanIds.OPEN_ID_PROVIDER));
ConfigUtils.addAuthenticationProvider(pc, BeanIds.OPEN_ID_PROVIDER);
}
boolean needLoginPage = false;
if (formLoginFilter != null) {
needLoginPage = true;
pc.getRegistry().registerBeanDefinition(BeanIds.FORM_LOGIN_FILTER, formLoginFilter);
ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.FORM_LOGIN_FILTER));
pc.getRegistry().registerBeanDefinition(BeanIds.FORM_LOGIN_ENTRY_POINT, formLoginEntryPoint);
}
needLoginPage = true;
formLoginFilter.getPropertyValues().addPropertyValue("allowSessionCreation", new Boolean(allowSessionCreation));
pc.getRegistry().registerBeanDefinition(BeanIds.FORM_LOGIN_FILTER, formLoginFilter);
ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.FORM_LOGIN_FILTER));
pc.getRegistry().registerBeanDefinition(BeanIds.FORM_LOGIN_ENTRY_POINT, formLoginEntryPoint);
}
if (openIDFilter != null) {
needLoginPage = true;
pc.getRegistry().registerBeanDefinition(BeanIds.OPEN_ID_FILTER, openIDFilter);
ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.OPEN_ID_FILTER));
pc.getRegistry().registerBeanDefinition(BeanIds.OPEN_ID_ENTRY_POINT, openIDEntryPoint);
needLoginPage = true;
openIDFilter.getPropertyValues().addPropertyValue("allowSessionCreation", new Boolean(allowSessionCreation));
pc.getRegistry().registerBeanDefinition(BeanIds.OPEN_ID_FILTER, openIDFilter);
ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.OPEN_ID_FILTER));
pc.getRegistry().registerBeanDefinition(BeanIds.OPEN_ID_ENTRY_POINT, openIDEntryPoint);
}
// If no login page has been defined, add in the default page generator.
if (needLoginPage && formLoginPage == null && openIDLoginPage == null) {
logger.info("No login page configured. The default internal one will be used. Use the '"
+ FormLoginBeanDefinitionParser.ATT_LOGIN_PAGE + "' attribute to set the URL of the login page.");
BeanDefinitionBuilder loginPageFilter =
BeanDefinitionBuilder.rootBeanDefinition(DefaultLoginPageGeneratingFilter.class);
BeanDefinitionBuilder loginPageFilter =
BeanDefinitionBuilder.rootBeanDefinition(DefaultLoginPageGeneratingFilter.class);
if (formLoginFilter != null) {
loginPageFilter.addConstructorArg(new RuntimeBeanReference(BeanIds.FORM_LOGIN_FILTER));
}
if (openIDFilter != null) {
loginPageFilter.addConstructorArg(new RuntimeBeanReference(BeanIds.OPEN_ID_FILTER));
loginPageFilter.addConstructorArg(new RuntimeBeanReference(BeanIds.FORM_LOGIN_FILTER));
}
pc.getRegistry().registerBeanDefinition(BeanIds.DEFAULT_LOGIN_PAGE_GENERATING_FILTER,
loginPageFilter.getBeanDefinition());
if (openIDFilter != null) {
loginPageFilter.addConstructorArg(new RuntimeBeanReference(BeanIds.OPEN_ID_FILTER));
}
pc.getRegistry().registerBeanDefinition(BeanIds.DEFAULT_LOGIN_PAGE_GENERATING_FILTER,
loginPageFilter.getBeanDefinition());
ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.DEFAULT_LOGIN_PAGE_GENERATING_FILTER));
}
// We need to establish the main entry point.
// First check if a custom entry point bean is set
String customEntryPoint = element.getAttribute(ATT_ENTRY_POINT_REF);
if (StringUtils.hasText(customEntryPoint)) {
pc.getRegistry().registerAlias(customEntryPoint, BeanIds.MAIN_ENTRY_POINT);
return;
}
// Basic takes precedence if explicit element is used and no others are configured
if (basicAuthElt != null && formLoginElt == null && openIDLoginElt == null) {
pc.getRegistry().registerAlias(BeanIds.BASIC_AUTHENTICATION_ENTRY_POINT, BeanIds.MAIN_ENTRY_POINT);
return;
pc.getRegistry().registerAlias(BeanIds.BASIC_AUTHENTICATION_ENTRY_POINT, BeanIds.MAIN_ENTRY_POINT);
return;
}
// If formLogin has been enabled either through an element or auto-config, then it is used if no openID login page
// has been set
if (formLoginFilter != null && openIDLoginPage == null) {
pc.getRegistry().registerAlias(BeanIds.FORM_LOGIN_ENTRY_POINT, BeanIds.MAIN_ENTRY_POINT);
return;
pc.getRegistry().registerAlias(BeanIds.FORM_LOGIN_ENTRY_POINT, BeanIds.MAIN_ENTRY_POINT);
return;
}
// Otherwise use OpenID if enabled
if (openIDFilter != null && formLoginFilter == null) {
pc.getRegistry().registerAlias(BeanIds.OPEN_ID_ENTRY_POINT, BeanIds.MAIN_ENTRY_POINT);
return;
pc.getRegistry().registerAlias(BeanIds.OPEN_ID_ENTRY_POINT, BeanIds.MAIN_ENTRY_POINT);
return;
}
// If X.509 has been enabled, use the preauth entry point.
if (DomUtils.getChildElementByTagName(element, Elements.X509) != null) {
pc.getRegistry().registerAlias(BeanIds.PRE_AUTH_ENTRY_POINT, BeanIds.MAIN_ENTRY_POINT);
return;
}
pc.getReaderContext().error("No AuthenticationEntryPoint could be established. Please " +
"make sure you have a login mechanism configured through the namespace (such as form-login) or " +
"specify a custom AuthenticationEntryPoint with the custom-entry-point-ref attribute ",
"make sure you have a login mechanism configured through the namespace (such as form-login) or " +
"specify a custom AuthenticationEntryPoint with the custom-entry-point-ref attribute ",
pc.extractSource(element));
}
static UrlMatcher createUrlMatcher(Element element) {
String patternType = element.getAttribute(ATT_PATH_TYPE);
if (!StringUtils.hasText(patternType)) {
@@ -502,8 +518,8 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
}
// Default for regex is no change
}
return matcher;
return matcher;
}
/**
@@ -547,7 +563,7 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
editor.setAsText(channelConfigAttribute);
channelRequestMap.put(new RequestKey(path), (ConfigAttributeDefinition) editor.getValue());
}
String filters = urlElt.getAttribute(ATT_FILTERS);
if (StringUtils.hasText(filters)) {
@@ -560,10 +576,10 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
}
}
}
static LinkedHashMap parseInterceptUrlsForFilterInvocationRequestMap(List urlElts, boolean useLowerCasePaths, ParserContext parserContext) {
LinkedHashMap filterInvocationDefinitionMap = new LinkedHashMap();
Iterator urlEltsIterator = urlElts.iterator();
ConfigAttributeEditor editor = new ConfigAttributeEditor();
@@ -590,11 +606,17 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
// Convert the comma-separated list of access attributes to a ConfigAttributeDefinition
if (StringUtils.hasText(access)) {
editor.setAsText(access);
filterInvocationDefinitionMap.put(new RequestKey(path, method), editor.getValue());
Object key = new RequestKey(path, method);
if (filterInvocationDefinitionMap.containsKey(key)) {
logger.warn("Duplicate URL defined: " + key + ". The original attribute values will be overwritten");
}
filterInvocationDefinitionMap.put(key, editor.getValue());
}
}
return filterInvocationDefinitionMap;
}
}
@@ -50,7 +50,7 @@ public class JdbcUserServiceBeanDefinitionParser extends AbstractUserDetailsServ
if (StringUtils.hasText(groupAuthoritiesQuery)) {
builder.addPropertyValue("enableGroups", Boolean.TRUE);
builder.addPropertyValue("authoritiesByUsernameQuery", groupAuthoritiesQuery);
builder.addPropertyValue("groupAuthoritiesByUsernameQuery", groupAuthoritiesQuery);
}
}
}
@@ -100,8 +100,9 @@ public class LdapProviderBeanDefinitionParser implements BeanDefinitionParser {
ldapProvider.addConstructorArg(LdapUserServiceBeanDefinitionParser.parseAuthoritiesPopulator(elt, parserContext));
ldapProvider.addPropertyValue("userDetailsContextMapper",
LdapUserServiceBeanDefinitionParser.parseUserDetailsClass(elt, parserContext));
parserContext.getRegistry().registerBeanDefinition(BeanIds.LDAP_AUTHENTICATION_PROVIDER, ldapProvider.getBeanDefinition());
ConfigUtils.getRegisteredProviders(parserContext).add(ldapProvider.getBeanDefinition());
ConfigUtils.addAuthenticationProvider(parserContext, BeanIds.LDAP_AUTHENTICATION_PROVIDER);
return null;
}
@@ -14,22 +14,22 @@ import org.w3c.dom.Element;
* @since 2.0
*/
public class LdapUserServiceBeanDefinitionParser extends AbstractUserDetailsServiceBeanDefinitionParser {
public static final String ATT_SERVER = "server-ref";
public static final String ATT_SERVER = "server-ref";
public static final String ATT_USER_SEARCH_FILTER = "user-search-filter";
public static final String ATT_USER_SEARCH_BASE = "user-search-base";
public static final String DEF_USER_SEARCH_BASE = "";
public static final String ATT_GROUP_SEARCH_FILTER = "group-search-filter";
public static final String ATT_GROUP_SEARCH_BASE = "group-search-base";
public static final String ATT_GROUP_ROLE_ATTRIBUTE = "group-role-attribute";
public static final String ATT_GROUP_ROLE_ATTRIBUTE = "group-role-attribute";
public static final String DEF_GROUP_SEARCH_FILTER = "(uniqueMember={0})";
public static final String DEF_GROUP_SEARCH_BASE = "ou=groups";
public static final String DEF_GROUP_SEARCH_BASE = "";
static final String ATT_ROLE_PREFIX = "role-prefix";
static final String ATT_USER_CLASS = "user-details-class";
static final String OPT_PERSON = "person";
static final String OPT_INETORGPERSON = "inetOrgPerson";
public static final String LDAP_SEARCH_CLASS = "org.springframework.security.ldap.search.FilterBasedLdapUserSearch";
public static final String PERSON_MAPPER_CLASS = "org.springframework.security.userdetails.ldap.PersonContextMapper";
public static final String INET_ORG_PERSON_MAPPER_CLASS = "org.springframework.security.userdetails.ldap.InetOrgPersonContextMapper";
@@ -45,42 +45,42 @@ public class LdapUserServiceBeanDefinitionParser extends AbstractUserDetailsServ
if (!StringUtils.hasText(elt.getAttribute(ATT_USER_SEARCH_FILTER))) {
parserContext.getReaderContext().error("User search filter must be supplied", elt);
}
builder.addConstructorArg(parseSearchBean(elt, parserContext));
builder.addConstructorArg(parseAuthoritiesPopulator(elt, parserContext));
builder.addPropertyValue("userDetailsMapper", parseUserDetailsClass(elt, parserContext));
}
static RootBeanDefinition parseSearchBean(Element elt, ParserContext parserContext) {
String userSearchFilter = elt.getAttribute(ATT_USER_SEARCH_FILTER);
String userSearchBase = elt.getAttribute(ATT_USER_SEARCH_BASE);
Object source = parserContext.extractSource(elt);
if (StringUtils.hasText(userSearchBase)) {
if(!StringUtils.hasText(userSearchFilter)) {
parserContext.getReaderContext().error(ATT_USER_SEARCH_BASE + " cannot be used without a " + ATT_USER_SEARCH_FILTER, source);
}
} else {
userSearchBase = DEF_USER_SEARCH_BASE;
}
}
if (!StringUtils.hasText(userSearchFilter)) {
return null;
}
BeanDefinitionBuilder searchBuilder = BeanDefinitionBuilder.rootBeanDefinition(LDAP_SEARCH_CLASS);
searchBuilder.setSource(source);
searchBuilder.addConstructorArg(userSearchBase);
searchBuilder.addConstructorArg(userSearchFilter);
searchBuilder.addConstructorArg(parseServerReference(elt, parserContext));
return (RootBeanDefinition) searchBuilder.getBeanDefinition();
}
static RuntimeBeanReference parseServerReference(Element elt, ParserContext parserContext) {
String server = elt.getAttribute(ATT_SERVER);
boolean requiresDefaultName = false;
if (!StringUtils.hasText(server)) {
server = BeanIds.CONTEXT_SOURCE;
requiresDefaultName = true;
@@ -89,27 +89,27 @@ public class LdapUserServiceBeanDefinitionParser extends AbstractUserDetailsServ
RuntimeBeanReference contextSource = new RuntimeBeanReference(server);
contextSource.setSource(parserContext.extractSource(elt));
LdapConfigUtils.registerPostProcessorIfNecessary(parserContext.getRegistry(), requiresDefaultName);
return contextSource;
}
static RootBeanDefinition parseUserDetailsClass(Element elt, ParserContext parserContext) {
String userDetailsClass = elt.getAttribute(ATT_USER_CLASS);
if (OPT_PERSON.equals(userDetailsClass)) {
return new RootBeanDefinition(PERSON_MAPPER_CLASS, null, null);
} else if (OPT_INETORGPERSON.equals(userDetailsClass)) {
return new RootBeanDefinition(INET_ORG_PERSON_MAPPER_CLASS, null, null);
}
return new RootBeanDefinition(LDAP_USER_MAPPER_CLASS, null, null);
String userDetailsClass = elt.getAttribute(ATT_USER_CLASS);
if (OPT_PERSON.equals(userDetailsClass)) {
return new RootBeanDefinition(PERSON_MAPPER_CLASS, null, null);
} else if (OPT_INETORGPERSON.equals(userDetailsClass)) {
return new RootBeanDefinition(INET_ORG_PERSON_MAPPER_CLASS, null, null);
}
return new RootBeanDefinition(LDAP_USER_MAPPER_CLASS, null, null);
}
static RootBeanDefinition parseAuthoritiesPopulator(Element elt, ParserContext parserContext) {
String groupSearchFilter = elt.getAttribute(ATT_GROUP_SEARCH_FILTER);
String groupSearchBase = elt.getAttribute(ATT_GROUP_SEARCH_BASE);
String groupRoleAttribute = elt.getAttribute(ATT_GROUP_ROLE_ATTRIBUTE);
String rolePrefix = elt.getAttribute(ATT_ROLE_PREFIX);
if (!StringUtils.hasText(groupSearchFilter)) {
groupSearchFilter = DEF_GROUP_SEARCH_FILTER;
}
@@ -117,25 +117,25 @@ public class LdapUserServiceBeanDefinitionParser extends AbstractUserDetailsServ
if (!StringUtils.hasText(groupSearchBase)) {
groupSearchBase = DEF_GROUP_SEARCH_BASE;
}
BeanDefinitionBuilder populator = BeanDefinitionBuilder.rootBeanDefinition(LDAP_AUTHORITIES_POPULATOR_CLASS);
populator.setSource(parserContext.extractSource(elt));
populator.addConstructorArg(parseServerReference(elt, parserContext));
populator.addConstructorArg(groupSearchBase);
populator.addPropertyValue("groupSearchFilter", groupSearchFilter);
populator.addPropertyValue("searchSubtree", Boolean.TRUE);
if (StringUtils.hasText(rolePrefix)) {
if ("none".equals(rolePrefix)) {
rolePrefix = "";
}
populator.addPropertyValue("rolePrefix", rolePrefix);
}
if (StringUtils.hasLength(groupRoleAttribute)) {
populator.addPropertyValue("groupRoleAttribute", groupRoleAttribute);
}
return (RootBeanDefinition) populator.getBeanDefinition();
}
}
@@ -25,6 +25,12 @@ public class LogoutBeanDefinitionParser implements BeanDefinitionParser {
static final String ATT_LOGOUT_URL = "logout-url";
static final String DEF_LOGOUT_URL = "/j_spring_security_logout";
String rememberMeServices;
public LogoutBeanDefinitionParser(String rememberMeServices) {
this.rememberMeServices = rememberMeServices;
}
public BeanDefinition parse(Element element, ParserContext parserContext) {
String logoutUrl = null;
@@ -66,8 +72,8 @@ public class LogoutBeanDefinitionParser implements BeanDefinitionParser {
}
handlers.add(sclh);
if (parserContext.getRegistry().containsBeanDefinition(BeanIds.REMEMBER_ME_SERVICES)) {
handlers.add(new RuntimeBeanReference(BeanIds.REMEMBER_ME_SERVICES));
if (rememberMeServices != null) {
handlers.add(new RuntimeBeanReference(rememberMeServices));
}
builder.addConstructorArg(handlers);
@@ -0,0 +1,59 @@
package org.springframework.security.config;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.security.providers.ProviderManager;
import org.springframework.util.Assert;
/**
* Extended version of {@link ProviderManager the default authentication manager} which lazily initializes
* the list of {@link AuthenticationProvider}s. This prevents some of the issues that have occurred with
* namespace configuration where early instantiation of a security interceptor has caused the AuthenticationManager
* and thus dependent beans (typically UserDetailsService implementations or DAOs) to be initialized too early.
*
* @author Luke Taylor
* @since 2.0.4
*/
public class NamespaceAuthenticationManager extends ProviderManager implements BeanFactoryAware {
BeanFactory beanFactory;
List providerBeanNames;
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(providerBeanNames, "provideBeanNames has not been set");
Assert.notEmpty(providerBeanNames, "No authentication providers were found in the application context");
super.afterPropertiesSet();
}
/**
* Overridden to lazily-initialize the list of providers on first use.
*/
public List getProviders() {
// We use the names array to determine whether the list has been set yet.
if (providerBeanNames != null) {
List providers = new ArrayList();
Iterator beanNames = providerBeanNames.iterator();
while (beanNames.hasNext()) {
providers.add(beanFactory.getBean((String) beanNames.next()));
}
providerBeanNames = null;
setProviders(providers);
}
return super.getProviders();
}
public void setProviderBeanNames(List provideBeanNames) {
this.providerBeanNames = provideBeanNames;
}
}
@@ -22,7 +22,7 @@ import org.w3c.dom.Element;
import org.w3c.dom.Node;
/**
* Adds the decorated "Filter" bean into the standard filter chain maintained by the FilterChainProxy.
* Adds the decorated "Filter" bean into the standard filter chain maintained by the FilterChainProxy.
* This allows user to add their own custom filters to the security chain. If the user's filter
* already implements Ordered, and no "order" attribute is specified, the filter's default order will be used.
*
@@ -33,7 +33,7 @@ public class OrderedFilterBeanDefinitionDecorator implements BeanDefinitionDecor
public static final String ATT_AFTER = "after";
public static final String ATT_BEFORE = "before";
public static final String ATT_POSITION = "position";
public static final String ATT_POSITION = "position";
public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder holder, ParserContext parserContext) {
Element elt = (Element)node;
@@ -48,7 +48,7 @@ public class OrderedFilterBeanDefinitionDecorator implements BeanDefinitionDecor
}
ConfigUtils.addHttpFilter(parserContext, wrapper.getBeanDefinition());
return holder;
}
@@ -59,22 +59,26 @@ public class OrderedFilterBeanDefinitionDecorator implements BeanDefinitionDecor
String after = elt.getAttribute(ATT_AFTER);
String before = elt.getAttribute(ATT_BEFORE);
String position = elt.getAttribute(ATT_POSITION);
if(ConfigUtils.countNonEmpty(new String[] {after, before, position}) != 1) {
pc.getReaderContext().error("A single '" + ATT_AFTER + "', '" + ATT_BEFORE + "', or '" +
ATT_POSITION + "' attribute must be supplied", pc.extractSource(elt));
pc.getReaderContext().error("A single '" + ATT_AFTER + "', '" + ATT_BEFORE + "', or '" +
ATT_POSITION + "' attribute must be supplied", pc.extractSource(elt));
}
if (StringUtils.hasText(position)) {
return Integer.toString(FilterChainOrder.getOrder(position));
return Integer.toString(FilterChainOrder.getOrder(position));
}
if (StringUtils.hasText(after)) {
return Integer.toString(FilterChainOrder.getOrder(after) + 1);
int order = FilterChainOrder.getOrder(after);
return Integer.toString(order == Integer.MAX_VALUE ? order : order + 1);
}
if (StringUtils.hasText(before)) {
return Integer.toString(FilterChainOrder.getOrder(before) - 1);
int order = FilterChainOrder.getOrder(before);
return Integer.toString(order == Integer.MIN_VALUE ? order : order - 1);
}
return null;
@@ -121,12 +125,12 @@ public class OrderedFilterBeanDefinitionDecorator implements BeanDefinitionDecor
return beanName;
}
public String toString() {
return "OrderedFilterDecorator[ delegate=" + delegate + "; order=" + getOrder() + "]";
}
Filter getDelegate() {
return delegate;
}
public String toString() {
return "OrderedFilterDecorator[ delegate=" + delegate + "; order=" + getOrder() + "]";
}
Filter getDelegate() {
return delegate;
}
}
}
@@ -32,6 +32,7 @@ public class RememberMeBeanDefinitionParser implements BeanDefinitionParser {
static final String ATT_TOKEN_VALIDITY = "token-validity-seconds";
protected final Log logger = LogFactory.getLog(getClass());
private String servicesName;
public BeanDefinition parse(Element element, ParserContext parserContext) {
String tokenRepository = null;
@@ -84,7 +85,7 @@ public class RememberMeBeanDefinitionParser implements BeanDefinitionParser {
tokenRepo = new RuntimeBeanReference(tokenRepository);
} else {
tokenRepo = new RootBeanDefinition(JdbcTokenRepositoryImpl.class);
((BeanDefinition)tokenRepo).getPropertyValues().addPropertyValue(ATT_DATA_SOURCE,
((BeanDefinition)tokenRepo).getPropertyValues().addPropertyValue("dataSource",
new RuntimeBeanReference(dataSource));
}
services.getPropertyValues().addPropertyValue("tokenRepository", tokenRepo);
@@ -102,8 +103,10 @@ public class RememberMeBeanDefinitionParser implements BeanDefinitionParser {
}
services.setSource(source);
services.getPropertyValues().addPropertyValue(ATT_KEY, key);
parserContext.getRegistry().registerBeanDefinition(BeanIds.REMEMBER_ME_SERVICES, services);
parserContext.getRegistry().registerBeanDefinition(BeanIds.REMEMBER_ME_SERVICES, services);
servicesName = BeanIds.REMEMBER_ME_SERVICES;
} else {
servicesName = rememberMeServicesRef;
parserContext.getRegistry().registerAlias(rememberMeServicesRef, BeanIds.REMEMBER_ME_SERVICES);
}
@@ -114,13 +117,17 @@ public class RememberMeBeanDefinitionParser implements BeanDefinitionParser {
return null;
}
private void registerProvider(ParserContext pc, Object source, String key) {
BeanDefinition authManager = ConfigUtils.registerProviderManagerIfNecessary(pc);
String getServicesName() {
return servicesName;
}
private void registerProvider(ParserContext pc, Object source, String key) {
//BeanDefinition authManager = ConfigUtils.registerProviderManagerIfNecessary(pc);
RootBeanDefinition provider = new RootBeanDefinition(RememberMeAuthenticationProvider.class);
provider.setSource(source);
provider.getPropertyValues().addPropertyValue(ATT_KEY, key);
ManagedList providers = (ManagedList) authManager.getPropertyValues().getPropertyValue("providers").getValue();
providers.add(provider);
pc.getRegistry().registerBeanDefinition(BeanIds.REMEMBER_ME_AUTHENTICATION_PROVIDER, provider);
ConfigUtils.addAuthenticationProvider(pc, BeanIds.REMEMBER_ME_AUTHENTICATION_PROVIDER);
}
private void registerFilter(ParserContext pc, Object source) {
@@ -51,7 +51,8 @@ public class RememberMeServicesInjectionBeanPostProcessor implements BeanPostPro
Map beans = beanFactory.getBeansOfType(RememberMeServices.class);
Assert.isTrue(beans.size() > 0, "No RememberMeServices configured");
Assert.isTrue(beans.size() == 1, "More than one RememberMeServices bean found.");
Assert.isTrue(beans.size() == 1, "Use of '<remember-me />' requires a single instance of RememberMeServices " +
"in the application context, but more than one was found.");
return (RememberMeServices) beans.values().toArray()[0];
}
@@ -24,6 +24,7 @@ public class SecurityNamespaceHandler extends NamespaceHandlerSupport {
registerBeanDefinitionParser(Elements.GLOBAL_METHOD_SECURITY, new GlobalMethodSecurityBeanDefinitionParser());
registerBeanDefinitionParser(Elements.AUTHENTICATION_MANAGER, new AuthenticationManagerBeanDefinitionParser());
registerBeanDefinitionParser(Elements.FILTER_INVOCATION_DEFINITION_SOURCE, new FilterInvocationDefinitionSourceBeanDefinitionParser());
registerBeanDefinitionParser(Elements.HTTP_FIREWALL, new HttpFirewallBeanDefinitionParser());
// Decorators
registerBeanDefinitionDecorator(Elements.INTERCEPT_METHODS, new InterceptMethodsBeanDefinitionDecorator());
@@ -0,0 +1,94 @@
package org.springframework.security.config;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.security.concurrent.ConcurrentSessionController;
import org.springframework.security.concurrent.ConcurrentSessionControllerImpl;
import org.springframework.security.concurrent.SessionRegistry;
import org.springframework.security.ui.AbstractProcessingFilter;
import org.springframework.security.ui.SessionFixationProtectionFilter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Registered by the <tt>AuthenticationManagerBeanDefinitionParser</tt> if an external
* ConcurrentSessionController is set (and hence an external SessionRegistry).
* Its responsibility is to set the SessionRegistry on namespace-registered beans which require access
* to it.
* <p>
* It will attempt to read the registry directly from the registered controller. If that fails, it will look in
* the application context for a registered SessionRegistry bean.
*
* See SEC-879.
*
* @author Luke Taylor
* @since 2.0.3
*/
class SessionRegistryInjectionBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
private final Log logger = LogFactory.getLog(getClass());
private ListableBeanFactory beanFactory;
private SessionRegistry sessionRegistry;
private final String controllerBeanName;
SessionRegistryInjectionBeanPostProcessor(String controllerBeanName) {
this.controllerBeanName = controllerBeanName;
}
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (BeanIds.FORM_LOGIN_FILTER.equals(beanName) ||
BeanIds.OPEN_ID_FILTER.equals(beanName)) {
((AbstractProcessingFilter) bean).setSessionRegistry(getSessionRegistry());
} else if (BeanIds.SESSION_FIXATION_PROTECTION_FILTER.equals(beanName)) {
((SessionFixationProtectionFilter)bean).setSessionRegistry(getSessionRegistry());
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
private SessionRegistry getSessionRegistry() {
if (sessionRegistry != null) {
return sessionRegistry;
}
logger.info("Attempting to read SessionRegistry from registered ConcurrentSessionController bean");
ConcurrentSessionController controller = (ConcurrentSessionController) beanFactory.getBean(controllerBeanName);
if (controller instanceof ConcurrentSessionControllerImpl) {
sessionRegistry = ((ConcurrentSessionControllerImpl)controller).getSessionRegistry();
return sessionRegistry;
}
logger.info("ConcurrentSessionController is not a standard implementation. SessionRegistry could not be read from it. Looking for it in the context.");
List sessionRegs = new ArrayList(beanFactory.getBeansOfType(SessionRegistry.class).values());
if (sessionRegs.size() == 0) {
throw new SecurityConfigurationException("concurrent-session-controller-ref was set but no SessionRegistry could be obtained from the application context.");
}
if (sessionRegs.size() > 1) {
logger.warn("More than one SessionRegistry instance in application context. Possible configuration errors may result.");
}
sessionRegistry = (SessionRegistry) sessionRegs.get(0);
return sessionRegistry;
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = (ListableBeanFactory) beanFactory;
}
}
@@ -2,8 +2,6 @@ package org.springframework.security.config;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValue;
@@ -21,91 +19,91 @@ import org.springframework.security.userdetails.UserDetailsService;
import org.springframework.util.Assert;
/**
*
* Registered by {@link HttpSecurityBeanDefinitionParser} to inject a UserDetailsService into
* the X509Provider, RememberMeServices and OpenIDAuthenticationProvider instances created by
* the namespace.
*
* @author Luke Taylor
* @since 2.0.2
*/
public class UserDetailsServiceInjectionBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
private final Log logger = LogFactory.getLog(getClass());
private ConfigurableListableBeanFactory beanFactory;
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (BeanIds.X509_AUTH_PROVIDER.equals(beanName)) {
injectUserDetailsServiceIntoX509Provider((PreAuthenticatedAuthenticationProvider) bean);
} else if (BeanIds.REMEMBER_ME_SERVICES.equals(beanName)) {
injectUserDetailsServiceIntoRememberMeServices((AbstractRememberMeServices)bean);
} else if (BeanIds.OPEN_ID_PROVIDER.equals(beanName)) {
injectUserDetailsServiceIntoOpenIDProvider(bean);
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
private ConfigurableListableBeanFactory beanFactory;
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (BeanIds.X509_AUTH_PROVIDER.equals(beanName)) {
injectUserDetailsServiceIntoX509Provider((PreAuthenticatedAuthenticationProvider) bean);
} else if (BeanIds.REMEMBER_ME_SERVICES.equals(beanName)) {
injectUserDetailsServiceIntoRememberMeServices((AbstractRememberMeServices)bean);
} else if (BeanIds.OPEN_ID_PROVIDER.equals(beanName)) {
injectUserDetailsServiceIntoOpenIDProvider(bean);
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
private void injectUserDetailsServiceIntoRememberMeServices(AbstractRememberMeServices services) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(BeanIds.REMEMBER_ME_SERVICES);
PropertyValue pv = beanDefinition.getPropertyValues().getPropertyValue("userDetailsService");
if (pv == null) {
services.setUserDetailsService(getUserDetailsService());
services.setUserDetailsService(getUserDetailsService());
} else {
UserDetailsService cachingUserService = getCachingUserService(pv.getValue());
if (cachingUserService != null) {
services.setUserDetailsService(cachingUserService);
}
UserDetailsService cachingUserService = getCachingUserService(pv.getValue());
if (cachingUserService != null) {
services.setUserDetailsService(cachingUserService);
}
}
}
private void injectUserDetailsServiceIntoX509Provider(PreAuthenticatedAuthenticationProvider provider) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(BeanIds.X509_AUTH_PROVIDER);
PropertyValue pv = beanDefinition.getPropertyValues().getPropertyValue("preAuthenticatedUserDetailsService");
UserDetailsByNameServiceWrapper wrapper = new UserDetailsByNameServiceWrapper();
if (pv == null) {
wrapper.setUserDetailsService(getUserDetailsService());
provider.setPreAuthenticatedUserDetailsService(wrapper);
wrapper.setUserDetailsService(getUserDetailsService());
provider.setPreAuthenticatedUserDetailsService(wrapper);
} else {
RootBeanDefinition preAuthUserService = (RootBeanDefinition) pv.getValue();
Object userService =
preAuthUserService.getPropertyValues().getPropertyValue("userDetailsService").getValue();
UserDetailsService cachingUserService = getCachingUserService(userService);
if (cachingUserService != null) {
wrapper.setUserDetailsService(cachingUserService);
provider.setPreAuthenticatedUserDetailsService(wrapper);
}
RootBeanDefinition preAuthUserService = (RootBeanDefinition) pv.getValue();
Object userService =
preAuthUserService.getPropertyValues().getPropertyValue("userDetailsService").getValue();
UserDetailsService cachingUserService = getCachingUserService(userService);
if (cachingUserService != null) {
wrapper.setUserDetailsService(cachingUserService);
provider.setPreAuthenticatedUserDetailsService(wrapper);
}
}
}
private void injectUserDetailsServiceIntoOpenIDProvider(Object bean) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(BeanIds.OPEN_ID_PROVIDER);
PropertyValue pv = beanDefinition.getPropertyValues().getPropertyValue("userDetailsService");
if (pv == null) {
BeanWrapperImpl beanWrapper = new BeanWrapperImpl(bean);
BeanWrapperImpl beanWrapper = new BeanWrapperImpl(bean);
beanWrapper.setPropertyValue("userDetailsService", getUserDetailsService());
}
}
/**
* Obtains a user details service for use in RememberMeServices etc. Will return a caching version
* if available so should not be used for beans which need to separate the two.
* if available so should not be used for beans which need to separate the two.
*/
UserDetailsService getUserDetailsService() {
Map beans = beanFactory.getBeansOfType(CachingUserDetailsService.class);
if (beans.size() == 0) {
beans = beanFactory.getBeansOfType(UserDetailsService.class);
}
Map beans = beanFactory.getBeansOfType(CachingUserDetailsService.class);
if (beans.size() == 0) {
beans = beanFactory.getBeansOfType(UserDetailsService.class);
}
if (beans.size() == 0) {
throw new SecurityConfigurationException("No UserDetailsService registered.");
@@ -116,24 +114,24 @@ public class UserDetailsServiceInjectionBeanPostProcessor implements BeanPostPro
return (UserDetailsService) beans.values().toArray()[0];
}
private UserDetailsService getCachingUserService(Object userServiceRef) {
Assert.isInstanceOf(RuntimeBeanReference.class, userServiceRef,
"userDetailsService property value must be a RuntimeBeanReference");
String id = ((RuntimeBeanReference)userServiceRef).getBeanName();
// Overwrite with the caching version if available
String cachingId = id + AbstractUserDetailsServiceBeanDefinitionParser.CACHING_SUFFIX;
if (beanFactory.containsBeanDefinition(cachingId)) {
return (UserDetailsService) beanFactory.getBean(cachingId);
}
return null;
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
}
private UserDetailsService getCachingUserService(Object userServiceRef) {
Assert.isInstanceOf(RuntimeBeanReference.class, userServiceRef,
"userDetailsService property value must be a RuntimeBeanReference");
String id = ((RuntimeBeanReference)userServiceRef).getBeanName();
// Overwrite with the caching version if available
String cachingId = id + AbstractUserDetailsServiceBeanDefinitionParser.CACHING_SUFFIX;
if (beanFactory.containsBeanDefinition(cachingId)) {
return (UserDetailsService) beanFactory.getBean(cachingId);
}
return null;
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
}
}
@@ -46,7 +46,7 @@ public class X509BeanDefinitionParser implements BeanDefinitionParser {
BeanDefinition provider = new RootBeanDefinition(PreAuthenticatedAuthenticationProvider.class);
parserContext.getRegistry().registerBeanDefinition(BeanIds.X509_AUTH_PROVIDER, provider);
ConfigUtils.getRegisteredProviders(parserContext).add(new RuntimeBeanReference(BeanIds.X509_AUTH_PROVIDER));
ConfigUtils.addAuthenticationProvider(parserContext, BeanIds.X509_AUTH_PROVIDER);
String userServiceRef = element.getAttribute(ATT_USER_SERVICE_REF);
@@ -15,33 +15,30 @@
package org.springframework.security.context.rmi;
import org.springframework.security.context.SecurityContext;
import org.springframework.security.context.SecurityContextHolder;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.remoting.support.RemoteInvocation;
import org.springframework.security.Authentication;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
import java.lang.reflect.InvocationTargetException;
/**
* The actual <code>RemoteInvocation</code> that is passed from the client to the server, which contains the
* contents of {@link SecurityContextHolder}, being a {@link SecurityContext} object.<p>When constructed on the
* client via {@link org.springframework.security.context.rmi.ContextPropagatingRemoteInvocationFactory}, the contents of the
* <code>SecurityContext</code> are stored inside the object. The object is then passed to the server that is
* processing the remote invocation. Upon the server invoking the remote invocation, it will retrieve the passed
* contents of the <code>SecurityContextHolder</code> and set them to the server-side
* <code>SecurityContextHolder</code> whilst the target object is invoked. When the target invocation has been
* completed, the server-side <code>SecurityContextHolder</code> will be reset to a new instance of
* <code>SecurityContextImpl</code>.</p>
* The actual {@code RemoteInvocation} that is passed from the client to the server.
* <p>
* The principal and credentials information will be extracted from the current
* security context and passed to the server as part of the invocation object.
* <p>
* To avoid potential serialization-based attacks, this implementation interprets the values as {@code String}s
* and creates a {@code UsernamePasswordAuthenticationToken} on the server side to hold them. If a different
* token type is required you can override the {@code createAuthenticationRequest} method.
*
* @author James Monaghan
* @author Ben Alex
* @version $Id$
* @author Luke Taylor
*/
public class ContextPropagatingRemoteInvocation extends RemoteInvocation {
//~ Static fields/initializers =====================================================================================
@@ -50,33 +47,40 @@ public class ContextPropagatingRemoteInvocation extends RemoteInvocation {
//~ Instance fields ================================================================================================
private SecurityContext securityContext;
private final String principal;
private final String credentials;
//~ Constructors ===================================================================================================
/**
* Constructs the object, storing the value of the client-side
* <code>SecurityContextHolder</code> inside the object.
/**
* Constructs the object, storing the principal and credentials extracted from the client-side
* security context.
*
* @param methodInvocation the method to invoke
*/
public ContextPropagatingRemoteInvocation(MethodInvocation methodInvocation) {
super(methodInvocation);
securityContext = SecurityContextHolder.getContext();
Authentication currentUser = SecurityContextHolder.getContext().getAuthentication();
if (currentUser != null) {
principal = currentUser.getPrincipal().toString();
credentials = currentUser.getCredentials().toString();
} else {
principal = credentials = null;
}
if (logger.isDebugEnabled()) {
logger.debug("RemoteInvocation now has SecurityContext: " + securityContext);
logger.debug("RemoteInvocation now has principal: " + principal);
}
}
//~ Methods ========================================================================================================
/**
* Invoked on the server-side as described in the class JavaDocs.<p>Invocations will always have their
* {@link org.springframework.security.Authentication#setAuthenticated(boolean)} set to <code>false</code>, which is
* guaranteed to always be accepted by <code>Authentication</code> implementations. This ensures that even
* remotely authenticated <code>Authentication</code>s will be untrusted by the server-side, which is an
* appropriate security measure.</p>
* Invoked on the server-side.
* <p>
* The transmitted principal and credentials will be used to create an unauthenticated {@code Authentication}
* instance for processing by the {@code AuthenticationManager}.
*
* @param targetObject the target object to apply the invocation to
*
@@ -87,16 +91,16 @@ public class ContextPropagatingRemoteInvocation extends RemoteInvocation {
* @throws InvocationTargetException if the method invocation resulted in an exception
*/
public Object invoke(Object targetObject)
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
SecurityContextHolder.setContext(securityContext);
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
if ((SecurityContextHolder.getContext() != null)
&& (SecurityContextHolder.getContext().getAuthentication() != null)) {
SecurityContextHolder.getContext().getAuthentication().setAuthenticated(false);
}
if (principal != null) {
Authentication request = createAuthenticationRequest(principal, credentials);
request.setAuthenticated(false);
SecurityContextHolder.getContext().setAuthentication(request);
if (logger.isDebugEnabled()) {
logger.debug("Set SecurityContextHolder to contain: " + securityContext);
if (logger.isDebugEnabled()) {
logger.debug("Set SecurityContextHolder to contain: " + request);
}
}
try {
@@ -105,8 +109,15 @@ public class ContextPropagatingRemoteInvocation extends RemoteInvocation {
SecurityContextHolder.clearContext();
if (logger.isDebugEnabled()) {
logger.debug("Set SecurityContext to new instance of SecurityContextImpl");
logger.debug("Cleared SecurityContextHolder.");
}
}
}
/**
* Creates the server-side authentication request object.
*/
protected Authentication createAuthenticationRequest(String principal, String credentials) {
return new UsernamePasswordAuthenticationToken(principal, credentials);
}
}
@@ -0,0 +1,67 @@
package org.springframework.security.firewall;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Default implementation which wraps requests in order to provide consistent values of the {@code servletPath} and
* {@code pathInfo}, which do not contain path parameters (as defined in
* <a href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>). Different servlet containers
* interpret the servlet spec differently as to how path parameters are treated and it is possible they might be added
* in order to bypass particular security constraints. When using this implementation, they will be removed for all
* requests as the request passes through the security filter chain. Note that this means that any segments in the
* decoded path which contain a semi-colon, will have the part following the semi-colon removed for
* request matching. Your application should not contain any valid paths which contain semi-colons.
* <p>
* If any un-normalized paths are found (containing directory-traversal character sequences), the request will be
* rejected immediately. Most containers normalize the paths before performing the servlet-mapping, but again this is
* not guaranteed by the servlet spec.
*
* @author Luke Taylor
*/
public class DefaultHttpFirewall implements HttpFirewall {
public FirewalledRequest getFirewalledRequest(HttpServletRequest request) throws RequestRejectedException {
FirewalledRequest fwr = new RequestWrapper(request);
if (!isNormalized(fwr.getServletPath()) || !isNormalized(fwr.getPathInfo())) {
throw new RequestRejectedException("Un-normalized paths are not supported: " + fwr.getServletPath() +
(fwr.getPathInfo() != null ? fwr.getPathInfo() : ""));
}
return fwr;
}
public HttpServletResponse getFirewalledResponse(HttpServletResponse response) {
return new FirewalledResponse(response);
}
/**
* Checks whether a path is normalized (doesn't contain path traversal sequences like "./", "/../" or "/.")
*
* @param path the path to test
* @return true if the path doesn't contain any path-traversal character sequences.
*/
private boolean isNormalized(String path) {
if (path == null) {
return true;
}
for (int j = path.length(); j > 0;) {
int i = path.lastIndexOf('/', j - 1);
int gap = j - i;
if (gap == 2 && path.charAt(i+1) == '.') {
// ".", "/./" or "/."
return false;
} else if (gap == 3 && path.charAt(i+1) == '.'&& path.charAt(i+2) == '.') {
return false;
}
j = i;
}
return true;
}
}
@@ -0,0 +1,34 @@
package org.springframework.security.firewall;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
/**
* Request wrapper which is returned by the {@code HttpFirewall} interface.
* <p>
* The only difference is the {@code reset} method which allows some
* or all of the state to be reset by the {@code FilterChainProxy} when the
* request leaves the security filter chain.
*
* @author Luke Taylor
*/
public abstract class FirewalledRequest extends HttpServletRequestWrapper {
/**
* Constructs a request object wrapping the given request.
*
* @throws IllegalArgumentException if the request is null
*/
public FirewalledRequest(HttpServletRequest request) {
super(request);
}
/**
* This method will be called once the request has passed through the
* security filter chain, when it is about to proceed to the application
* proper.
* <p>
* An implementation can thus choose to modify the state of the request
* for the security infrastructure, while still maintaining the
*/
public abstract void reset();
}
@@ -0,0 +1,26 @@
package org.springframework.security.firewall;
import javax.servlet.http.HttpServletResponseWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.regex.Pattern;
/**
* @author Luke Taylor
*/
class FirewalledResponse extends HttpServletResponseWrapper {
Pattern CR_OR_LF = Pattern.compile("\\r|\\n");
public FirewalledResponse(HttpServletResponse response) {
super(response);
}
public void sendRedirect(String location) throws IOException {
// TODO: implement pluggable validation, instead of simple blacklisting.
// SEC-1790. Prevent redirects containing CRLF
if (CR_OR_LF.matcher(location).find()) {
throw new IllegalArgumentException("Invalid characters (CR/LF) in redirect location");
}
super.sendRedirect(location);
}
}
@@ -0,0 +1,32 @@
package org.springframework.security.firewall;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Interface which can be used to reject potentially dangerous requests and/or wrap them to
* control their behaviour.
* <p>
* The implementation is injected into the {@code FilterChainProxy} and will be invoked before
* sending any request through the filter chain. It can also provide a response wrapper if the response
* behaviour should also be restricted.
*
* @author Luke Taylor
*/
public interface HttpFirewall {
/**
* Provides the request object which will be passed through the filter chain.
*
* @throws RequestRejectedException if the request should be rejected immediately
*/
FirewalledRequest getFirewalledRequest(HttpServletRequest request) throws RequestRejectedException;
/**
* Provides the response which will be passed through the filter chain.
*
* @param response the original response
* @return either the original response or a replacement/wrapper.
*/
HttpServletResponse getFirewalledResponse(HttpServletResponse response);
}
@@ -0,0 +1,10 @@
package org.springframework.security.firewall;
/**
* @author Luke Taylor
*/
public class RequestRejectedException extends RuntimeException {
public RequestRejectedException(String message) {
super(message);
}
}
@@ -0,0 +1,140 @@
package org.springframework.security.firewall;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.*;
/**
* Request wrapper which ensures values of {@code servletPath} and {@code pathInfo} are returned which are suitable for
* pattern matching against. It strips out path parameters and extra consecutive '/' characters.
*
* <h3>Path Parameters</h3>
* Parameters (as defined in <a href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>) are stripped from the path
* segments of the {@code servletPath} and {@code pathInfo} values of the request.
* <p>
* The parameter sequence is demarcated by a semi-colon, so each segment is checked for the occurrence of a ";"
* character and truncated at that point if it is present.
* <p>
* The behaviour differs between servlet containers in how they interpret the servlet spec, which
* does not clearly state what the behaviour should be. For consistency, we make sure they are always removed, to
* avoid the risk of URL matching rules being bypassed by the malicious addition of parameters to the path component.
*
* @author Luke Taylor
*/
final class RequestWrapper extends FirewalledRequest {
private final String strippedServletPath;
private final String strippedPathInfo;
private boolean stripPaths = true;
public RequestWrapper(HttpServletRequest request) {
super(request);
strippedServletPath = strip(request.getServletPath());
String pathInfo = strip(request.getPathInfo());
if (pathInfo != null && pathInfo.length() == 0) {
pathInfo = null;
}
strippedPathInfo = pathInfo;
}
/**
* Removes path parameters from each path segment in the supplied path and truncates sequences of multiple '/'
* characters to a single '/'.
*
* @param path either the {@code servletPath} and {@code pathInfo} from the original request
*
* @return the supplied value, with path parameters removed and sequences of multiple '/' characters truncated,
* or null if the supplied path was null.
*/
private String strip(String path) {
if (path == null) {
return null;
}
int scIndex = path.indexOf(';');
if (scIndex < 0) {
int doubleSlashIndex = path.indexOf("//");
if (doubleSlashIndex < 0) {
// Most likely case, no parameters in any segment and no '//', so no stripping required
return path;
}
}
StringTokenizer st = new StringTokenizer(path, "/");
StringBuilder stripped = new StringBuilder(path.length());
if (path.charAt(0) == '/') {
stripped.append('/');
}
while(st.hasMoreTokens()) {
String segment = st.nextToken();
scIndex = segment.indexOf(';');
if (scIndex >= 0) {
segment = segment.substring(0, scIndex);
}
stripped.append(segment).append('/');
}
// Remove the trailing slash if the original path didn't have one
if (path.charAt(path.length() - 1) != '/') {
stripped.deleteCharAt(stripped.length() - 1);
}
return stripped.toString();
}
public String getPathInfo() {
return stripPaths ? strippedPathInfo : super.getPathInfo();
}
public String getServletPath() {
return stripPaths ? strippedServletPath : super.getServletPath();
}
public RequestDispatcher getRequestDispatcher(String path) {
return this.stripPaths ? new FirewalledRequestAwareRequestDispatcher(path) : super.getRequestDispatcher(path);
}
public void reset() {
this.stripPaths = false;
}
/**
* Ensures {@link FirewalledRequest#reset()} is called prior to performing a forward. It then delegates work to the
* {@link RequestDispatcher} from the original {@link HttpServletRequest}.
*
* @author Rob Winch
*/
private class FirewalledRequestAwareRequestDispatcher implements RequestDispatcher {
private final String path;
/**
*
* @param path the {@code path} that will be used to obtain the delegate {@link RequestDispatcher} from the
* original {@link HttpServletRequest}.
*/
public FirewalledRequestAwareRequestDispatcher(String path) {
this.path = path;
}
public void forward(ServletRequest request, ServletResponse response) throws ServletException, IOException {
reset();
getDelegateDispatcher().forward(request, response);
}
public void include(ServletRequest request, ServletResponse response) throws ServletException, IOException {
getDelegateDispatcher().include(request, response);
}
private RequestDispatcher getDelegateDispatcher() {
return RequestWrapper.super.getRequestDispatcher(path);
}
}
}
@@ -26,8 +26,10 @@ import org.springframework.security.ConfigAttribute;
import org.springframework.security.ConfigAttributeDefinition;
import org.springframework.security.RunAsManager;
import org.springframework.security.context.SecurityContext;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.security.context.SecurityContextImpl;
import org.springframework.security.event.authorization.AuthenticationCredentialsNotFoundEvent;
import org.springframework.security.event.authorization.AuthorizationFailureEvent;
import org.springframework.security.event.authorization.AuthorizedEvent;
@@ -110,7 +112,7 @@ public abstract class AbstractSecurityInterceptor implements InitializingBean, A
protected static final Log logger = LogFactory.getLog(AbstractSecurityInterceptor.class);
//~ Instance fields ================================================================================================
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
private ApplicationEventPublisher eventPublisher;
private AccessDecisionManager accessDecisionManager;
@@ -140,21 +142,22 @@ public abstract class AbstractSecurityInterceptor implements InitializingBean, A
if (token.isContextHolderRefreshRequired()) {
if (logger.isDebugEnabled()) {
logger.debug("Reverting to original Authentication: " + token.getAuthentication().toString());
logger.debug("Reverting to original Authentication: " + token.getSecurityContext().getAuthentication());
}
SecurityContextHolder.getContext().setAuthentication(token.getAuthentication());
SecurityContextHolder.setContext(token.getSecurityContext());
}
if (afterInvocationManager != null) {
// Attempt after invocation handling
try {
returnedObject = afterInvocationManager.decide(token.getAuthentication(), token.getSecureObject(),
returnedObject = afterInvocationManager.decide(token.getSecurityContext().getAuthentication(),
token.getSecureObject(),
token.getAttr(), returnedObject);
}
catch (AccessDeniedException accessDeniedException) {
AuthorizationFailureEvent event = new AuthorizationFailureEvent(token.getSecureObject(), token
.getAttr(), token.getAuthentication(), accessDeniedException);
.getAttr(), token.getSecurityContext().getAuthentication(), accessDeniedException);
publishEvent(event);
throw accessDeniedException;
@@ -285,16 +288,20 @@ public abstract class AbstractSecurityInterceptor implements InitializingBean, A
}
// no further work post-invocation
return new InterceptorStatusToken(authenticated, false, attr, object);
return new InterceptorStatusToken(SecurityContextHolder.getContext(), false, attr, object);
} else {
if (logger.isDebugEnabled()) {
logger.debug("Switching to RunAs Authentication: " + runAs);
}
SecurityContextHolder.getContext().setAuthentication(runAs);
SecurityContext originalContext = SecurityContextHolder.getContext();
SecurityContext runAsContext = new SecurityContextImpl();
runAsContext.setAuthentication(runAs);
// revert to token.Authenticated post-invocation
return new InterceptorStatusToken(authenticated, true, attr, object);
SecurityContextHolder.setContext(runAsContext);
// revert to original context post-invocation
return new InterceptorStatusToken(originalContext, true, attr, object);
}
}
@@ -17,6 +17,7 @@ package org.springframework.security.intercept;
import org.springframework.security.Authentication;
import org.springframework.security.ConfigAttributeDefinition;
import org.springframework.security.context.SecurityContext;
/**
@@ -32,16 +33,16 @@ import org.springframework.security.ConfigAttributeDefinition;
public class InterceptorStatusToken {
//~ Instance fields ================================================================================================
private Authentication authentication;
private SecurityContext context;
private ConfigAttributeDefinition attr;
private Object secureObject;
private boolean contextHolderRefreshRequired;
//~ Constructors ===================================================================================================
public InterceptorStatusToken(Authentication authentication, boolean contextHolderRefreshRequired,
public InterceptorStatusToken(SecurityContext context, boolean contextHolderRefreshRequired,
ConfigAttributeDefinition attr, Object secureObject) {
this.authentication = authentication;
this.context = context;
this.contextHolderRefreshRequired = contextHolderRefreshRequired;
this.attr = attr;
this.secureObject = secureObject;
@@ -53,8 +54,8 @@ public class InterceptorStatusToken {
return attr;
}
public Authentication getAuthentication() {
return authentication;
public SecurityContext getSecurityContext() {
return context;
}
public Object getSecureObject() {
@@ -18,11 +18,11 @@ import org.springframework.util.ObjectUtils;
* Abstract implementation of {@link MethodDefinitionSource} that supports both Spring AOP and AspectJ and
* caches configuration attribute resolution from: 1. specific target method; 2. target class; 3. declaring method;
* 4. declaring class/interface.
*
*
* <p>
* This class mimics the behaviour of Spring's AbstractFallbackTransactionAttributeSource class.
* </p>
*
*
* <p>
* Note that this class cannot extract security metadata where that metadata is expressed by way of
* a target method/class (ie #1 and #2 above) AND the target method/class is encapsulated in another
@@ -31,7 +31,7 @@ import org.springframework.util.ObjectUtils;
* another proxy), move the metadata to declared methods or interfaces the proxy implements, or provide
* your own replacement <tt>MethodDefinitionSource</tt>.
* </p>
*
*
* @author Ben Alex
* @version $Id$
* @since 2.0
@@ -39,15 +39,16 @@ import org.springframework.util.ObjectUtils;
public abstract class AbstractFallbackMethodDefinitionSource implements MethodDefinitionSource {
private static final Log logger = LogFactory.getLog(AbstractFallbackMethodDefinitionSource.class);
private final static Object NULL_CONFIG_ATTRIBUTE = new Object();
private final static Object NULL_CONFIG_ATTRIBUTE = new Object();
private final Map attributeCache = new HashMap();
public ConfigAttributeDefinition getAttributes(Object object) throws IllegalArgumentException {
Assert.notNull(object, "Object cannot be null");
if (object instanceof MethodInvocation) {
MethodInvocation mi = (MethodInvocation) object;
return getAttributes(mi.getMethod(), mi.getThis().getClass());
MethodInvocation mi = (MethodInvocation) object;
Object target = mi.getThis();
return getAttributes(mi.getMethod(), target == null ? null : target.getClass());
}
if (object instanceof JoinPoint) {
@@ -59,143 +60,143 @@ public abstract class AbstractFallbackMethodDefinitionSource implements MethodDe
Method method = ClassUtils.getMethodIfAvailable(declaringType, targetMethodName, types);
Assert.notNull(method, "Could not obtain target method from JoinPoint: '"+ jp + "'");
return getAttributes(method, targetClass);
}
throw new IllegalArgumentException("Object must be a MethodInvocation or JoinPoint");
}
public final boolean supports(Class clazz) {
return (MethodInvocation.class.isAssignableFrom(clazz) || JoinPoint.class.isAssignableFrom(clazz));
}
public ConfigAttributeDefinition getAttributes(Method method, Class targetClass) {
// First, see if we have a cached value.
Object cacheKey = new DefaultCacheKey(method, targetClass);
synchronized (this.attributeCache) {
Object cached = this.attributeCache.get(cacheKey);
if (cached != null) {
// Value will either be canonical value indicating there is no config attribute,
// or an actual config attribute.
if (cached == NULL_CONFIG_ATTRIBUTE) {
return null;
}
else {
return (ConfigAttributeDefinition) cached;
}
}
else {
// We need to work it out.
ConfigAttributeDefinition cfgAtt = computeAttributes(method, targetClass);
// Put it in the cache.
if (cfgAtt == null) {
this.attributeCache.put(cacheKey, NULL_CONFIG_ATTRIBUTE);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Adding security method [" + cacheKey + "] with attribute [" + cfgAtt + "]");
}
this.attributeCache.put(cacheKey, cfgAtt);
}
return cfgAtt;
}
}
// First, see if we have a cached value.
Object cacheKey = new DefaultCacheKey(method, targetClass);
synchronized (this.attributeCache) {
Object cached = this.attributeCache.get(cacheKey);
if (cached != null) {
// Value will either be canonical value indicating there is no config attribute,
// or an actual config attribute.
if (cached == NULL_CONFIG_ATTRIBUTE) {
return null;
}
else {
return (ConfigAttributeDefinition) cached;
}
}
else {
// We need to work it out.
ConfigAttributeDefinition cfgAtt = computeAttributes(method, targetClass);
// Put it in the cache.
if (cfgAtt == null) {
this.attributeCache.put(cacheKey, NULL_CONFIG_ATTRIBUTE);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Adding security method [" + cacheKey + "] with attribute [" + cfgAtt + "]");
}
this.attributeCache.put(cacheKey, cfgAtt);
}
return cfgAtt;
}
}
}
/**
*
* @param method the method for the current invocation (never <code>null</code>)
* @param targetClass the target class for this invocation (may be <code>null</code>)
*
* @param method the method for the current invocation (never <code>null</code>)
* @param targetClass the target class for this invocation (may be <code>null</code>)
* @return
*/
private ConfigAttributeDefinition computeAttributes(Method method, Class targetClass) {
// The method may be on an interface, but we need attributes from the target class.
// If the target class is null, the method will be unchanged.
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
// First try is the method in the target class.
ConfigAttributeDefinition attr = findAttributes(specificMethod, targetClass);
if (attr != null) {
return attr;
}
// The method may be on an interface, but we need attributes from the target class.
// If the target class is null, the method will be unchanged.
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
// First try is the method in the target class.
ConfigAttributeDefinition attr = findAttributes(specificMethod, targetClass);
if (attr != null) {
return attr;
}
// Second try is the config attribute on the target class.
attr = findAttributes(specificMethod.getDeclaringClass());
if (attr != null) {
return attr;
}
// Second try is the config attribute on the target class.
attr = findAttributes(specificMethod.getDeclaringClass());
if (attr != null) {
return attr;
}
if (specificMethod != method) {
// Fallback is to look at the original method.
attr = findAttributes(method, method.getDeclaringClass());
if (attr != null) {
return attr;
}
// Last fallback is the class of the original method.
return findAttributes(method.getDeclaringClass());
}
return null;
if (specificMethod != method || targetClass == null) {
// Fallback is to look at the original method.
attr = findAttributes(method, method.getDeclaringClass());
if (attr != null) {
return attr;
}
// Last fallback is the class of the original method.
return findAttributes(method.getDeclaringClass());
}
return null;
}
/**
* Obtains the security metadata applicable to the specified method invocation.
*
*
* <p>
* Note that the {@link Method#getDeclaringClass()} may not equal the <code>targetClass</code>.
* Both parameters are provided to assist subclasses which may wish to provide advanced
* capabilities related to method metadata being "registered" against a method even if the
* target class does not declare the method (i.e. the subclass may only inherit the method).
*
*
* @param method the method for the current invocation (never <code>null</code>)
* @param targetClass the target class for the invocation (may be <code>null</code>)
* @return the security metadata (or null if no metadata applies)
*/
protected abstract ConfigAttributeDefinition findAttributes(Method method, Class targetClass);
/**
* Obtains the security metadata registered against the specified class.
*
*
* <p>
* Subclasses should only return metadata expressed at a class level. Subclasses should NOT
* aggregate metadata for each method registered against a class, as the abstract superclass
* will separate invoke {@link #findAttributes(Method, Class)} for individual methods as
* appropriate.
*
* appropriate.
*
* @param clazz the target class for the invocation (never <code>null</code>)
* @return the security metadata (or null if no metadata applies)
*/
protected abstract ConfigAttributeDefinition findAttributes(Class clazz);
private static class DefaultCacheKey {
private final Method method;
private final Class targetClass;
private static class DefaultCacheKey {
public DefaultCacheKey(Method method, Class targetClass) {
this.method = method;
this.targetClass = targetClass;
}
private final Method method;
private final Class targetClass;
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof DefaultCacheKey)) {
return false;
}
DefaultCacheKey otherKey = (DefaultCacheKey) other;
return (this.method.equals(otherKey.method) &&
ObjectUtils.nullSafeEquals(this.targetClass, otherKey.targetClass));
}
public DefaultCacheKey(Method method, Class targetClass) {
this.method = method;
this.targetClass = targetClass;
}
public int hashCode() {
return this.method.hashCode() * 21 + (this.targetClass != null ? this.targetClass.hashCode() : 0);
}
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof DefaultCacheKey)) {
return false;
}
DefaultCacheKey otherKey = (DefaultCacheKey) other;
return (this.method.equals(otherKey.method) &&
ObjectUtils.nullSafeEquals(this.targetClass, otherKey.targetClass));
}
public int hashCode() {
return this.method.hashCode() * 21 + (this.targetClass != null ? this.targetClass.hashCode() : 0);
}
public String toString() {
return "CacheKey[" + (targetClass == null ? "-" : targetClass.getName()) + "; " + method + "]";
}
}
public String toString() {
return "CacheKey[" + (targetClass == null ? "-" : targetClass.getName()) + "; " + method + "]";
}
}
}
@@ -34,13 +34,13 @@ import org.springframework.util.ClassUtils;
/**
* Stores a {@link ConfigAttributeDefinition} for a method or class signature.
*
*
* <p>
* This class is the preferred implementation of {@link MethodDefinitionSource} for XML-based
* definition of method security metadata. To assist in XML-based definition, wildcard support
* is provided.
* </p>
*
*
* @author Ben Alex
* @version $Id$
* @since 2.0
@@ -51,9 +51,9 @@ public class MapBasedMethodDefinitionSource extends AbstractFallbackMethodDefini
private static final Log logger = LogFactory.getLog(MapBasedMethodDefinitionSource.class);
//~ Instance fields ================================================================================================
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
/** Map from RegisteredMethod to ConfigAttributeDefinition */
/** Map from RegisteredMethod to ConfigAttributeDefinition */
protected Map methodMap = new HashMap();
/** Map from RegisteredMethod to name pattern used for registration */
@@ -74,48 +74,37 @@ public class MapBasedMethodDefinitionSource extends AbstractFallbackMethodDefini
while (iterator.hasNext()) {
Map.Entry entry = (Map.Entry) iterator.next();
addSecureMethod((String)entry.getKey(), (ConfigAttributeDefinition)entry.getValue());
}
}
}
/**
* Implementation does not support class-level attributes.
*/
protected ConfigAttributeDefinition findAttributes(Class clazz) {
return null;
}
/**
* Will walk the method inheritance tree to find the most specific declaration applicable.
*/
protected ConfigAttributeDefinition findAttributes(Method method, Class targetClass) {
return findAttributesSpecifiedAgainst(method, targetClass);
}
private ConfigAttributeDefinition findAttributesSpecifiedAgainst(Method method, Class clazz) {
RegisteredMethod registeredMethod = new RegisteredMethod(method, clazz);
if (methodMap.containsKey(registeredMethod)) {
return (ConfigAttributeDefinition) methodMap.get(registeredMethod);
}
// Search superclass
if (clazz.getSuperclass() != null) {
return findAttributesSpecifiedAgainst(method, clazz.getSuperclass());
}
return null;
}
/**
* Implementation does not support class-level attributes.
*/
protected ConfigAttributeDefinition findAttributes(Class clazz) {
return null;
}
/**
* Add configuration attributes for a secure method.
*
* @param method the method to be secured
* @param attr required authorities associated with the method
* Will walk the method inheritance tree to find the most specific declaration applicable.
*/
private void addSecureMethod(RegisteredMethod method, ConfigAttributeDefinition attr) {
Assert.notNull(method, "RegisteredMethod required");
Assert.notNull(attr, "Configuration attribute required");
if (logger.isInfoEnabled()) {
logger.info("Adding secure method [" + method + "] with attributes [" + attr + "]");
}
this.methodMap.put(method, attr);
protected ConfigAttributeDefinition findAttributes(Method method, Class targetClass) {
if (targetClass == null) {
return null;
}
return findAttributesSpecifiedAgainst(method, targetClass);
}
private ConfigAttributeDefinition findAttributesSpecifiedAgainst(Method method, Class clazz) {
RegisteredMethod registeredMethod = new RegisteredMethod(method, clazz);
if (methodMap.containsKey(registeredMethod)) {
return (ConfigAttributeDefinition) methodMap.get(registeredMethod);
}
// Search superclass
if (clazz.getSuperclass() != null) {
return findAttributesSpecifiedAgainst(method, clazz.getSuperclass());
}
return null;
}
/**
@@ -126,7 +115,7 @@ public class MapBasedMethodDefinitionSource extends AbstractFallbackMethodDefini
* @param attr required authorities associated with the method
*/
public void addSecureMethod(String name, ConfigAttributeDefinition attr) {
int lastDotIndex = name.lastIndexOf(".");
int lastDotIndex = name.lastIndexOf(".");
if (lastDotIndex == -1) {
throw new IllegalArgumentException("'" + name + "' is not a valid method name: format is FQN.methodName");
@@ -134,17 +123,17 @@ public class MapBasedMethodDefinitionSource extends AbstractFallbackMethodDefini
String methodName = name.substring(lastDotIndex + 1);
Assert.hasText(methodName, "Method not found for '" + name + "'");
String typeName = name.substring(0, lastDotIndex);
Class type = ClassUtils.resolveClassName(typeName, this.beanClassLoader);
addSecureMethod(type, methodName, attr);
}
/**
* Add configuration attributes for a secure method. Mapped method names can end or start with <code>&#42</code>
* for matching multiple methods.
*
*
* @param javaType target interface or class the security configuration attribute applies to
* @param mappedName mapped method name, which the javaType has declared or inherited
* @param attr required authorities associated with the method
@@ -192,6 +181,38 @@ public class MapBasedMethodDefinitionSource extends AbstractFallbackMethodDefini
}
}
/**
* Adds configuration attributes for a specific method, for example where the method has been
* matched using a pointcut expression. If a match already exists in the map for the method, then
* the existing match will be retained, so that if this method is called for a more general pointcut
* it will not override a more specific one which has already been added. This
*/
public void addSecureMethod(Class javaType, Method method, ConfigAttributeDefinition attr) {
RegisteredMethod key = new RegisteredMethod(method, javaType);
if (methodMap.containsKey(key)) {
logger.debug("Method [" + method + "] is already registered with attributes [" + methodMap.get(key) + "]");
return;
}
methodMap.put(key, attr);
}
/**
* Add configuration attributes for a secure method.
*
* @param method the method to be secured
* @param attr required authorities associated with the method
*/
private void addSecureMethod(RegisteredMethod method, ConfigAttributeDefinition attr) {
Assert.notNull(method, "RegisteredMethod required");
Assert.notNull(attr, "Configuration attribute required");
if (logger.isInfoEnabled()) {
logger.info("Adding secure method [" + method + "] with attributes [" + attr + "]");
}
this.methodMap.put(method, attr);
}
/**
* Obtains the configuration attributes explicitly defined against this bean.
*
@@ -215,93 +236,54 @@ public class MapBasedMethodDefinitionSource extends AbstractFallbackMethodDefini
|| (mappedName.startsWith("*") && methodName.endsWith(mappedName.substring(1, mappedName.length())));
}
protected ConfigAttributeDefinition lookupAttributes(Method method) {
List attributesToReturn = new ArrayList();
public void setBeanClassLoader(ClassLoader beanClassLoader) {
Assert.notNull(beanClassLoader, "Bean class loader required");
this.beanClassLoader = beanClassLoader;
}
// Add attributes explicitly defined for this method invocation
merge(attributesToReturn, (ConfigAttributeDefinition) this.methodMap.get(method));
/**
* @return map size (for unit tests and diagnostics)
*/
public int getMethodMapSize() {
return methodMap.size();
}
// Add attributes explicitly defined for this method invocation's interfaces
Class[] interfaces = method.getDeclaringClass().getInterfaces();
/**
* Stores both the Java Method as well as the Class we obtained the Method from. This is necessary because Method only
* provides us access to the declaring class. It doesn't provide a way for us to introspect which Class the Method
* was registered against. If a given Class inherits and redeclares a method (i.e. calls super();) the registered Class
* and declaring Class are the same. If a given class merely inherits but does not redeclare a method, the registered
* Class will be the Class we're invoking against and the Method will provide details of the declared class.
*/
private class RegisteredMethod {
private Method method;
private Class registeredJavaType;
for (int i = 0; i < interfaces.length; i++) {
Class clazz = interfaces[i];
public RegisteredMethod(Method method, Class registeredJavaType) {
Assert.notNull(method, "Method required");
Assert.notNull(registeredJavaType, "Registered Java Type required");
this.method = method;
this.registeredJavaType = registeredJavaType;
}
try {
// Look for the method on the current interface
Method interfaceMethod = clazz.getDeclaredMethod(method.getName(), (Class[]) method.getParameterTypes());
ConfigAttributeDefinition interfaceAssigned =
(ConfigAttributeDefinition) this.methodMap.get(interfaceMethod);
merge(attributesToReturn, interfaceAssigned);
} catch (Exception e) {
// skip this interface
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj != null && obj instanceof RegisteredMethod) {
RegisteredMethod rhs = (RegisteredMethod) obj;
return method.equals(rhs.method) && registeredJavaType.equals(rhs.registeredJavaType);
}
return false;
}
// Return null if empty, as per abstract superclass contract
if (attributesToReturn.size() == 0) {
return null;
public int hashCode() {
return method.hashCode() * registeredJavaType.hashCode();
}
return new ConfigAttributeDefinition(attributesToReturn);
public String toString() {
return "RegisteredMethod[" + registeredJavaType.getName() + "; " + method + "]";
}
}
private void merge(List attributes, ConfigAttributeDefinition toMerge) {
if (toMerge == null) {
return;
}
attributes.addAll(toMerge.getConfigAttributes());
}
public void setBeanClassLoader(ClassLoader beanClassLoader) {
Assert.notNull(beanClassLoader, "Bean class loader required");
this.beanClassLoader = beanClassLoader;
}
/**
* @return map size (for unit tests and diagnostics)
*/
public int getMethodMapSize() {
return methodMap.size();
}
/**
* Stores both the Java Method as well as the Class we obtained the Method from. This is necessary because Method only
* provides us access to the declaring class. It doesn't provide a way for us to introspect which Class the Method
* was registered against. If a given Class inherits and redeclares a method (i.e. calls super();) the registered Class
* and declaring Class are the same. If a given class merely inherits but does not redeclare a method, the registered
* Class will be the Class we're invoking against and the Method will provide details of the declared class.
*/
private class RegisteredMethod {
private Method method;
private Class registeredJavaType;
public RegisteredMethod(Method method, Class registeredJavaType) {
Assert.notNull(method, "Method required");
Assert.notNull(registeredJavaType, "Registered Java Type required");
this.method = method;
this.registeredJavaType = registeredJavaType;
}
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj != null && obj instanceof RegisteredMethod) {
RegisteredMethod rhs = (RegisteredMethod) obj;
return method.equals(rhs.method) && registeredJavaType.equals(rhs.registeredJavaType);
}
return false;
}
public int hashCode() {
return method.hashCode() * registeredJavaType.hashCode();
}
public String toString() {
return "RegisteredMethod[" + registeredJavaType.getName() + "; " + method + "]";
}
}
}
@@ -17,29 +17,30 @@ import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.security.ConfigAttributeDefinition;
import org.springframework.security.intercept.method.aopalliance.MethodDefinitionSourceAdvisor;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Parses AspectJ pointcut expressions, registering methods that match the pointcut with a
* traditional {@link MapBasedMethodDefinitionSource}.
*
*
* <p>
* This class provides a convenient way of declaring a list of pointcuts, and then
* having every method of every bean defined in the Spring application context compared with
* those pointcuts. Where a match is found, the matching method will be registered with the
* {@link MapBasedMethodDefinitionSource}.
* </p>
*
*
* <p>
* It is very important to understand that only the <b>first</b> pointcut that matches a given
* method will be taken as authoritative for that method. This is why pointcuts should be provided
* as a <tt>LinkedHashMap</tt>, because their order is very important.
* </p>
*
*
* <p>
* Note also that only beans defined in the Spring application context will be examined by this
* class.
* class.
* </p>
*
*
* <p>
* Because this class registers method security metadata with {@link MapBasedMethodDefinitionSource},
* normal Spring Security capabilities such as {@link MethodDefinitionSourceAdvisor} can be used.
@@ -57,18 +58,19 @@ public final class ProtectPointcutPostProcessor implements BeanPostProcessor {
private static final Log logger = LogFactory.getLog(ProtectPointcutPostProcessor.class);
private Map pointcutMap = new LinkedHashMap(); /** Key: string-based pointcut, value: ConfigAttributeDefinition */
private MapBasedMethodDefinitionSource mapBasedMethodDefinitionSource;
private PointcutParser parser;
public ProtectPointcutPostProcessor(MapBasedMethodDefinitionSource mapBasedMethodDefinitionSource) {
Assert.notNull(mapBasedMethodDefinitionSource, "MapBasedMethodDefinitionSource to populate is required");
this.mapBasedMethodDefinitionSource = mapBasedMethodDefinitionSource;
// Setup AspectJ pointcut expression parser
Set supportedPrimitives = new HashSet();
supportedPrimitives.add(PointcutPrimitive.EXECUTION);
supportedPrimitives.add(PointcutPrimitive.ARGS);
supportedPrimitives.add(PointcutPrimitive.REFERENCE);
private MapBasedMethodDefinitionSource mapBasedMethodDefinitionSource;
private PointcutParser parser;
private final Set processedBeans = new HashSet();
public ProtectPointcutPostProcessor(MapBasedMethodDefinitionSource mapBasedMethodDefinitionSource) {
Assert.notNull(mapBasedMethodDefinitionSource, "MapBasedMethodDefinitionSource to populate is required");
this.mapBasedMethodDefinitionSource = mapBasedMethodDefinitionSource;
// Setup AspectJ pointcut expression parser
Set supportedPrimitives = new HashSet();
supportedPrimitives.add(PointcutPrimitive.EXECUTION);
supportedPrimitives.add(PointcutPrimitive.ARGS);
supportedPrimitives.add(PointcutPrimitive.REFERENCE);
// supportedPrimitives.add(PointcutPrimitive.THIS);
// supportedPrimitives.add(PointcutPrimitive.TARGET);
// supportedPrimitives.add(PointcutPrimitive.WITHIN);
@@ -76,79 +78,97 @@ public final class ProtectPointcutPostProcessor implements BeanPostProcessor {
// supportedPrimitives.add(PointcutPrimitive.AT_WITHIN);
// supportedPrimitives.add(PointcutPrimitive.AT_ARGS);
// supportedPrimitives.add(PointcutPrimitive.AT_TARGET);
parser = PointcutParser.getPointcutParserSupportingSpecifiedPrimitivesAndUsingContextClassloaderForResolution(supportedPrimitives);
}
parser = PointcutParser.getPointcutParserSupportingSpecifiedPrimitivesAndUsingContextClassloaderForResolution(supportedPrimitives);
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// Obtain methods for the present bean
Method[] methods;
try {
methods = bean.getClass().getMethods();
} catch (Exception e) {
throw new IllegalStateException(e.getMessage());
}
// Check to see if any of those methods are compatible with our pointcut expressions
for (int i = 0; i < methods.length; i++) {
Iterator iter = pointcutMap.keySet().iterator();
while (iter.hasNext()) {
String ex = iter.next().toString();
// Parse the presented AspectJ pointcut expression
PointcutExpression expression = parser.parsePointcutExpression(ex);
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (processedBeans.contains(beanName)) {
// We already have the metadata for this bean
return bean;
}
// Try for the bean class directly
if (attemptMatch(bean.getClass(), methods[i], expression, beanName)) {
// We've found the first expression that matches this method, so move onto the next method now
break; // the "while" loop, not the "for" loop
}
}
}
return bean;
}
private boolean attemptMatch(Class targetClass, Method method, PointcutExpression expression, String beanName) {
// Determine if the presented AspectJ pointcut expression matches this method
boolean matches = expression.matchesMethodExecution(method).alwaysMatches();
// Handle accordingly
if (matches) {
ConfigAttributeDefinition attr = (ConfigAttributeDefinition) pointcutMap.get(expression.getPointcutExpression());
if (logger.isDebugEnabled()) {
logger.debug("AspectJ pointcut expression '" + expression.getPointcutExpression() + "' matches target class '" + targetClass.getName() + "' (bean ID '" + beanName + "') for method '" + method + "'; registering security configuration attribute '" + attr + "'");
}
mapBasedMethodDefinitionSource.addSecureMethod(targetClass, method.getName(), attr);
}
return matches;
}
public void setPointcutMap(Map map) {
Assert.notEmpty(map);
Iterator i = map.keySet().iterator();
while (i.hasNext()) {
String expression = i.next().toString();
Object value = map.get(expression);
Assert.isInstanceOf(ConfigAttributeDefinition.class, value, "Map keys must be instances of ConfigAttributeDefinition");
addPointcut(expression, (ConfigAttributeDefinition) value);
}
}
// Obtain methods for the present bean
Method[] methods;
try {
methods = bean.getClass().getMethods();
} catch (Exception e) {
throw new IllegalStateException(e.getMessage());
}
// Check to see if any of those methods are compatible with our pointcut expressions
for (int i = 0; i < methods.length; i++) {
Iterator iter = pointcutMap.keySet().iterator();
while (iter.hasNext()) {
String ex = iter.next().toString();
// Parse the presented AspectJ pointcut expression
PointcutExpression expression = parser.parsePointcutExpression(ex);
// Try for the bean class directly
if (attemptMatch(bean.getClass(), methods[i], expression, beanName)) {
// We've found the first expression that matches this method, so move onto the next method now
break; // the "while" loop, not the "for" loop
}
}
}
processedBeans.add(beanName);
return bean;
}
private boolean attemptMatch(Class targetClass, Method method, PointcutExpression expression, String beanName) {
// Determine if the presented AspectJ pointcut expression matches this method
boolean matches = expression.matchesMethodExecution(method).alwaysMatches();
// Handle accordingly
if (matches) {
ConfigAttributeDefinition attr = (ConfigAttributeDefinition) pointcutMap.get(expression.getPointcutExpression());
if (logger.isDebugEnabled()) {
logger.debug("AspectJ pointcut expression '" + expression.getPointcutExpression() + "' matches target class '" + targetClass.getName() + "' (bean ID '" + beanName + "') for method '" + method + "'; registering security configuration attribute '" + attr + "'");
}
mapBasedMethodDefinitionSource.addSecureMethod(targetClass, method, attr);
}
return matches;
}
public void setPointcutMap(Map map) {
Assert.notEmpty(map);
Iterator i = map.keySet().iterator();
while (i.hasNext()) {
String expression = i.next().toString();
Object value = map.get(expression);
Assert.isInstanceOf(ConfigAttributeDefinition.class, value, "Map keys must be instances of ConfigAttributeDefinition");
addPointcut(expression, (ConfigAttributeDefinition) value);
}
}
private void addPointcut(String pointcutExpression, ConfigAttributeDefinition definition) {
Assert.hasText(pointcutExpression, "An AspectJ pointcut expression is required");
Assert.notNull(definition, "ConfigAttributeDefinition required");
pointcutExpression = replaceBooleanOperators(pointcutExpression);
pointcutMap.put(pointcutExpression, definition);
if (logger.isDebugEnabled()) {
logger.debug("AspectJ pointcut expression '" + pointcutExpression + "' registered for security configuration attribute '" + definition + "'");
}
}
/**
* @see org.springframework.aop.aspectj.AspectJExpressionPointcut#replaceBooleanOperators
*/
private String replaceBooleanOperators(String pcExpr) {
pcExpr = StringUtils.replace(pcExpr," and "," && ");
pcExpr = StringUtils.replace(pcExpr, " or ", " || ");
pcExpr = StringUtils.replace(pcExpr, " not ", " ! ");
return pcExpr;
}
public void addPointcut(String pointcutExpression, ConfigAttributeDefinition definition) {
Assert.hasText(pointcutExpression, "An AspectJ pointcut expression is required");
Assert.notNull(definition, "ConfigAttributeDefinition required");
pointcutMap.put(pointcutExpression, definition);
if (logger.isDebugEnabled()) {
logger.debug("AspectJ pointcut expression '" + pointcutExpression + "' registered for security configuration attribute '" + definition + "'");
}
}
}
@@ -33,7 +33,7 @@ import org.springframework.util.Assert;
* Advisor driven by a {@link MethodDefinitionSource}, used to exclude a {@link MethodSecurityInterceptor} from
* public (ie non-secure) methods.
* <p>
* Because the AOP framework caches advice calculations, this is normally faster than just letting the
* Because the AOP framework caches advice calculations, this is normally faster than just letting the
* <code>MethodSecurityInterceptor</code> run and find out itself that it has no work to do.
* <p>
* This class also allows the use of Spring's
@@ -63,64 +63,63 @@ public class MethodDefinitionSourceAdvisor extends AbstractPointcutAdvisor imple
* @deprecated use the decoupled approach instead
*/
public MethodDefinitionSourceAdvisor(MethodSecurityInterceptor advice) {
Assert.notNull(advice.getObjectDefinitionSource(), "Cannot construct a MethodDefinitionSourceAdvisor using a " +
"MethodSecurityInterceptor that has no ObjectDefinitionSource configured");
Assert.notNull(advice.getObjectDefinitionSource(), "Cannot construct a MethodDefinitionSourceAdvisor using a " +
"MethodSecurityInterceptor that has no ObjectDefinitionSource configured");
this.interceptor = advice;
this.interceptor = advice;
this.attributeSource = advice.getObjectDefinitionSource();
}
/**
* Alternative constructor for situations where we want the advisor decoupled from the advice. Instead the advice
* bean name should be set. This prevents eager instantiation of the interceptor
* bean name should be set. This prevents eager instantiation of the interceptor
* (and hence the AuthenticationManager). See SEC-773, for example.
* <p>
* This is essentially the approach taken by subclasses of {@link AbstractBeanFactoryPointcutAdvisor}, which this
* class should extend in future. The original hierarchy and constructor have been retained for backwards
* compatibilty.
*
* class should extend in future. The original hierarchy and constructor have been retained for backwards
* compatibility.
*
* @param adviceBeanName name of the MethodSecurityInterceptor bean
* @param attributeSource the attribute source (should be the same as the one used on the interceptor)
*/
public MethodDefinitionSourceAdvisor(String adviceBeanName, MethodDefinitionSource attributeSource) {
Assert.notNull(adviceBeanName, "The adviceBeanName cannot be null");
Assert.notNull(attributeSource, "The attributeSource cannot be null");
this.adviceBeanName = adviceBeanName;
this.attributeSource = attributeSource;
Assert.notNull(adviceBeanName, "The adviceBeanName cannot be null");
Assert.notNull(attributeSource, "The attributeSource cannot be null");
this.adviceBeanName = adviceBeanName;
this.attributeSource = attributeSource;
}
//~ Methods ========================================================================================================
public Pointcut getPointcut() {
return pointcut;
}
public Pointcut getPointcut() {
return pointcut;
}
public Advice getAdvice() {
synchronized (this.adviceMonitor) {
if (interceptor == null) {
Assert.notNull(adviceBeanName, "'adviceBeanName' must be set for use with bean factory lookup.");
Assert.state(beanFactory != null, "BeanFactory must be set to resolve 'adviceBeanName'");
interceptor = (MethodSecurityInterceptor)
beanFactory.getBean(this.adviceBeanName, MethodSecurityInterceptor.class);
attributeSource = interceptor.getObjectDefinitionSource();
}
return interceptor;
}
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
public Advice getAdvice() {
synchronized (this.adviceMonitor) {
if (interceptor == null) {
Assert.notNull(adviceBeanName, "'adviceBeanName' must be set for use with bean factory lookup.");
Assert.state(beanFactory != null, "BeanFactory must be set to resolve 'adviceBeanName'");
interceptor = (MethodSecurityInterceptor)
beanFactory.getBean(this.adviceBeanName, MethodSecurityInterceptor.class);
}
return interceptor;
}
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
//~ Inner Classes ==================================================================================================
class MethodDefinitionSourcePointcut extends StaticMethodMatcherPointcut {
public boolean matches(Method m, Class targetClass) {
return attributeSource.getAttributes(m, targetClass) != null;
}
}
/**
* Represents a <code>MethodInvocation</code>.
* <p>
@@ -153,7 +152,7 @@ public class MethodDefinitionSourceAdvisor extends AbstractPointcutAdvisor imple
}
public Object getThis() {
return this.targetClass;
return this.targetClass;
}
public Object proceed() throws Throwable {
@@ -12,5 +12,5 @@ package org.springframework.security.intercept.method.aspectj;
public interface AspectJAnnotationCallback {
//~ Methods ========================================================================================================
Object proceedWithObject();
Object proceedWithObject() throws Throwable;
}
@@ -54,4 +54,16 @@ public class RequestKey {
return method.equals(key.method);
}
public String toString() {
StringBuffer sb = new StringBuffer(url.length() + 7);
sb.append("[");
if (method != null) {
sb.append(method).append(",");
}
sb.append(url);
sb.append("]");
return sb.toString();
}
}
@@ -71,6 +71,7 @@ public class DefaultSpringSecurityContextSource extends LdapContextSource implem
env.put(Context.SECURITY_PRINCIPAL, userDn);
env.put(Context.SECURITY_CREDENTIALS, credentials);
env.remove(SUN_LDAP_POOLING_FLAG);
if (logger.isDebugEnabled()) {
logger.debug("Creating context with principal: '" + userDn + "'");
@@ -158,7 +158,7 @@ public final class LdapUtils {
if (url.startsWith("ldap:") || url.startsWith("ldaps:")) {
URI uri = parseLdapUrl(url);
urlRootDn = uri.getPath();
urlRootDn = uri.getRawPath();
} else {
// Assume it's an embedded server
urlRootDn = url;
@@ -15,6 +15,21 @@
package org.springframework.security.ldap;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.PartialResultException;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.ldap.core.ContextExecutor;
import org.springframework.ldap.core.ContextMapper;
@@ -23,33 +38,18 @@ import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.ldap.core.DistinguishedName;
import org.springframework.ldap.core.LdapEncoder;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.util.Assert;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import java.text.MessageFormat;
import java.util.HashSet;
import java.util.Set;
import java.util.Arrays;
/**
* LDAP equivalent of the Spring JdbcTemplate class.
* <p>
* This is mainly intended to simplify Ldap access within Spring Security's LDAP-related services.
* </p>
* Extension of Spring LDAP's LdapTemplate class which adds extra functionality required by Spring Security.
*
* @author Ben Alex
* @author Luke Taylor
* @since 2.0
*/
public class SpringSecurityLdapTemplate extends org.springframework.ldap.core.LdapTemplate {
public class SpringSecurityLdapTemplate extends LdapTemplate {
//~ Static fields/initializers =====================================================================================
private static final Log logger = LogFactory.getLog(SpringSecurityLdapTemplate.class);
@@ -136,14 +136,14 @@ public class SpringSecurityLdapTemplate extends org.springframework.ldap.core.Ld
* @return the set of String values for the attribute as a union of the values found in all the matching entries.
*/
public Set searchForSingleAttributeValues(final String base, final String filter, final Object[] params,
final String attributeName) {
// Escape the params acording to RFC2254
Object[] encodedParams = new String[params.length];
for (int i=0; i < params.length; i++) {
encodedParams[i] = LdapEncoder.filterEncode(params[i].toString());
}
final String attributeName) {
// Escape the params acording to RFC2254
Object[] encodedParams = new String[params.length];
for (int i=0; i < params.length; i++) {
encodedParams[i] = LdapEncoder.filterEncode(params[i].toString());
}
String formattedFilter = MessageFormat.format(filter, encodedParams);
logger.debug("Using filter: " + formattedFilter);
@@ -175,12 +175,15 @@ public class SpringSecurityLdapTemplate extends org.springframework.ldap.core.Ld
/**
* Performs a search, with the requirement that the search shall return a single directory entry, and uses
* the supplied mapper to create the object from that entry.
* <p>
* Ignores <tt>PartialResultException</tt> if thrown, for compatibility with Active Directory
* (see {@link LdapTemplate#setIgnorePartialResultException(boolean)}).
*
* @param base
* @param filter
* @param params
* @param base the search base, relative to the base context supplied by the context source.
* @param filter the LDAP search filter
* @param params parameters to be substituted in the search.
*
* @return the object created by the mapper from the matching entry
* @return a DirContextOperations instance created from the matching entry.
*
* @throws IncorrectResultSizeDataAccessException if no results are found or the search returns more than one
* result.
@@ -188,32 +191,43 @@ public class SpringSecurityLdapTemplate extends org.springframework.ldap.core.Ld
public DirContextOperations searchForSingleEntry(final String base, final String filter, final Object[] params) {
return (DirContextOperations) executeReadOnly(new ContextExecutor() {
public Object executeWithContext(DirContext ctx)
throws NamingException {
public Object executeWithContext(DirContext ctx) throws NamingException {
DistinguishedName ctxBaseDn = new DistinguishedName(ctx.getNameInNamespace());
if (logger.isDebugEnabled()) {
logger.debug("Searching for entry in under DN '" + ctxBaseDn
+ "', base = '" + base + "', filter = '" + filter + "'");
}
NamingEnumeration results = ctx.search(base, filter, params, searchControls);
NamingEnumeration resultsEnum = ctx.search(base, filter, params, searchControls);
Set results = new HashSet();
try {
while (resultsEnum.hasMore()) {
SearchResult searchResult = (SearchResult) resultsEnum.next();
// Work out the DN of the matched entry
DistinguishedName dn = new DistinguishedName(searchResult.getName());
if (!results.hasMore()) {
if (base.length() > 0) {
dn.prepend(new DistinguishedName(base));
}
if (logger.isDebugEnabled()) {
logger.debug("Found DN: " + dn);
}
results.add(new DirContextAdapter(searchResult.getAttributes(), dn, ctxBaseDn));
}
} catch (PartialResultException e) {
logger.info("Ignoring PartialResultException");
}
if (results.size() == 0) {
throw new IncorrectResultSizeDataAccessException(1, 0);
}
SearchResult searchResult = (SearchResult) results.next();
if (results.hasMore()) {
// We don't know how many results but set to 2 which is good enough
throw new IncorrectResultSizeDataAccessException(1, 2);
if (results.size() > 1) {
throw new IncorrectResultSizeDataAccessException(1, results.size());
}
// Work out the DN of the matched entry
StringBuffer dn = new StringBuffer(searchResult.getName());
if (base.length() > 0) {
dn.append(",");
dn.append(base);
}
return new DirContextAdapter(searchResult.getAttributes(),
new DistinguishedName(dn.toString()), new DistinguishedName(ctx.getNameInNamespace()));
return results.toArray()[0];
}
});
}
@@ -99,8 +99,6 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
*/
private GrantedAuthority defaultRole;
private ContextSource contextSource;
private SpringSecurityLdapTemplate ldapTemplate;
/**
@@ -143,8 +141,10 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
* context factory.
*/
public DefaultLdapAuthoritiesPopulator(ContextSource contextSource, String groupSearchBase) {
this.setContextSource(contextSource);
this.setGroupSearchBase(groupSearchBase);
Assert.notNull(contextSource, "contextSource must not be null");
ldapTemplate = new SpringSecurityLdapTemplate(contextSource);
ldapTemplate.setSearchControls(searchControls);
setGroupSearchBase(groupSearchBase);
}
//~ Methods ========================================================================================================
@@ -226,20 +226,7 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
}
protected ContextSource getContextSource() {
return contextSource;
}
/**
* Set the {@link ContextSource}
*
* @param contextSource supplies the contexts used to search for user roles.
*/
private void setContextSource(ContextSource contextSource) {
Assert.notNull(contextSource, "contextSource must not be null");
this.contextSource = contextSource;
ldapTemplate = new SpringSecurityLdapTemplate(contextSource);
ldapTemplate.setSearchControls(searchControls);
return ldapTemplate.getContextSource();
}
/**
@@ -144,17 +144,10 @@ public class ProviderManager extends AbstractAuthenticationManager implements In
//~ Methods ========================================================================================================
public void afterPropertiesSet() throws Exception {
checkIfValidList(this.providers);
Assert.notNull(this.messages, "A message source must be set");
exceptionMappings.putAll(additionalExceptionMappings);
}
private void checkIfValidList(List listToCheck) {
if ((listToCheck == null) || (listToCheck.size() == 0)) {
throw new IllegalArgumentException("A list of AuthenticationProviders is required");
}
}
/**
* Attempts to authenticate the passed {@link Authentication} object.
* <p>
@@ -174,7 +167,7 @@ public class ProviderManager extends AbstractAuthenticationManager implements In
* @throws AuthenticationException if authentication fails.
*/
public Authentication doAuthentication(Authentication authentication) throws AuthenticationException {
Iterator iter = providers.iterator();
Iterator iter = getProviders().iterator();
Class toTest = authentication.getClass();
@@ -273,7 +266,11 @@ public class ProviderManager extends AbstractAuthenticationManager implements In
}
public List getProviders() {
return this.providers;
if (providers == null || providers.size() == 0) {
throw new IllegalArgumentException("A list of AuthenticationProviders is required");
}
return providers;
}
/**
@@ -303,8 +300,7 @@ public class ProviderManager extends AbstractAuthenticationManager implements In
* AuthenticationProvider instance.
*/
public void setProviders(List providers) {
checkIfValidList(providers);
Assert.notEmpty(providers, "A list of AuthenticationProviders is required");
Iterator iter = providers.iterator();
while (iter.hasNext()) {
@@ -15,30 +15,28 @@
package org.springframework.security.providers.anonymous;
import org.springframework.security.SpringSecurityMessageSource;
import org.springframework.security.Authentication;
import org.springframework.security.AuthenticationException;
import org.springframework.security.BadCredentialsException;
import org.springframework.security.providers.AuthenticationProvider;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.security.Authentication;
import org.springframework.security.AuthenticationException;
import org.springframework.security.BadCredentialsException;
import org.springframework.security.SpringSecurityMessageSource;
import org.springframework.security.providers.AuthenticationProvider;
import org.springframework.util.Assert;
/**
* An {@link AuthenticationProvider} implementation that validates {@link
* org.springframework.security.providers.anonymous.AnonymousAuthenticationToken}s.<p>To be successfully validated, the
* {@link org.springframework.security.providers.anonymous.AnonymousAuthenticationToken#getKeyHash()} must match this class'
* {@link #getKey()}.</p>
* An {@link AuthenticationProvider} implementation that validates {@link AnonymousAuthenticationToken}s.
* <p>
* To be successfully validated, the {@link AnonymousAuthenticationToken#getKeyHash()} must match this class'
* {@link #getKey()}.
*
* @author Ben Alex
* @version $Id$
*/
public class AnonymousAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware {
//~ Static fields/initializers =====================================================================================
private static final Log logger = LogFactory.getLog(AnonymousAuthenticationProvider.class);
//~ Instance fields ================================================================================================
@@ -47,7 +45,7 @@ public class AnonymousAuthenticationProvider implements AuthenticationProvider,
//~ Methods ========================================================================================================
public void afterPropertiesSet() throws Exception {
public void afterPropertiesSet() throws Exception {
Assert.hasLength(key, "A Key is required");
Assert.notNull(this.messages, "A message source must be set");
}
@@ -24,6 +24,7 @@ import org.springframework.security.providers.encoding.PasswordEncoder;
import org.springframework.security.providers.encoding.PlaintextPasswordEncoder;
import org.springframework.security.userdetails.UserDetails;
import org.springframework.security.userdetails.UserDetailsService;
import org.springframework.security.userdetails.UsernameNotFoundException;
import org.springframework.dao.DataAccessException;
import org.springframework.util.Assert;
@@ -35,10 +36,24 @@ import org.springframework.util.Assert;
* @version $Id$
*/
public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
//~ Static fields/initializers =====================================================================================
/**
* The plaintext password used to perform {@link PasswordEncoder#isPasswordValid(String, String, Object)} on when the user is
* not found to avoid SEC-2056.
*/
private static final String USER_NOT_FOUND_PASSWORD = "userNotFoundPassword";
//~ Instance fields ================================================================================================
private PasswordEncoder passwordEncoder = new PlaintextPasswordEncoder();
private PasswordEncoder passwordEncoder;
/**
* The password used to perform {@link PasswordEncoder#isPasswordValid(String, String, Object)} on when the user is
* not found to avoid SEC-2056. This is necessary, because some {@link PasswordEncoder} implementations will short circuit if the
* password is not in a valid format.
*/
private String userNotFoundEncodedPassword;
private SaltSource saltSource;
@@ -46,6 +61,10 @@ public class DaoAuthenticationProvider extends AbstractUserDetailsAuthentication
private boolean includeDetailsObject = true;
public DaoAuthenticationProvider() {
setPasswordEncoder(new PlaintextPasswordEncoder());
}
//~ Methods ========================================================================================================
protected void additionalAuthenticationChecks(UserDetails userDetails,
@@ -85,6 +104,13 @@ public class DaoAuthenticationProvider extends AbstractUserDetailsAuthentication
catch (DataAccessException repositoryProblem) {
throw new AuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem);
}
catch (UsernameNotFoundException notFound) {
if(authentication.getCredentials() != null) {
String presentedPassword = authentication.getCredentials().toString();
passwordEncoder.isPasswordValid(userNotFoundEncodedPassword, presentedPassword, null);
}
throw notFound;
}
if (loadedUser == null) {
throw new AuthenticationServiceException(
@@ -100,6 +126,7 @@ public class DaoAuthenticationProvider extends AbstractUserDetailsAuthentication
* @param passwordEncoder The passwordEncoder to use
*/
public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
this.userNotFoundEncodedPassword = passwordEncoder.encodePassword(USER_NOT_FOUND_PASSWORD, null);
this.passwordEncoder = passwordEncoder;
}
@@ -143,6 +170,6 @@ public class DaoAuthenticationProvider extends AbstractUserDetailsAuthentication
*/
public void setIncludeDetailsObject(boolean includeDetailsObject) {
this.includeDetailsObject = includeDetailsObject;
}
}
}
@@ -189,13 +189,9 @@ public class JaasAuthenticationProvider implements AuthenticationProvider, Appli
//Attempt to login the user, the LoginContext will call our InternalCallbackHandler at this point.
loginContext.login();
//create a set to hold the authorities, and add any that have already been applied.
//create a set to hold the authorities
Set authorities = new HashSet();
if (request.getAuthorities() != null) {
authorities.addAll(Arrays.asList(request.getAuthorities()));
}
//get the subject principals and pass them to each of the AuthorityGranters
Set principals = loginContext.getSubject().getPrincipals();
@@ -246,6 +242,9 @@ public class JaasAuthenticationProvider implements AuthenticationProvider, Appli
*/
protected void configureJaas(Resource loginConfig) throws IOException {
configureJaasUsingLoop();
// Overcome issue in SEC-760
Configuration.getConfiguration().refresh();
}
/**
@@ -375,7 +374,9 @@ public class JaasAuthenticationProvider implements AuthenticationProvider, Appli
* @param token The {@link UsernamePasswordAuthenticationToken} being processed
*/
protected void publishSuccessEvent(UsernamePasswordAuthenticationToken token) {
applicationEventPublisher.publishEvent(new JaasAuthenticationSuccessEvent(token));
if (applicationEventPublisher != null) {
applicationEventPublisher.publishEvent(new JaasAuthenticationSuccessEvent(token));
}
}
/**
@@ -15,22 +15,26 @@
package org.springframework.security.providers.ldap.authenticator;
import org.springframework.security.Authentication;
import org.springframework.security.BadCredentialsException;
import org.springframework.security.ldap.SpringSecurityContextSource;
import org.springframework.security.ldap.SpringSecurityLdapTemplate;
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
import org.springframework.dao.DataAccessException;
import org.springframework.ldap.core.ContextSource;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.ldap.core.DistinguishedName;
import org.springframework.util.Assert;
import java.util.Iterator;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.naming.directory.DirContext;
import java.util.Iterator;
import org.springframework.dao.DataAccessException;
import org.springframework.ldap.NamingException;
import org.springframework.ldap.core.ContextSource;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.ldap.core.DistinguishedName;
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
import org.springframework.ldap.support.LdapUtils;
import org.springframework.security.Authentication;
import org.springframework.security.BadCredentialsException;
import org.springframework.security.ldap.SpringSecurityContextSource;
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
import org.springframework.util.Assert;
/**
@@ -91,21 +95,42 @@ public class BindAuthenticator extends AbstractLdapAuthenticator {
return user;
}
private DirContextOperations bindWithDn(String userDn, String username, String password) {
SpringSecurityLdapTemplate template = new SpringSecurityLdapTemplate(
new BindWithSpecificDnContextSource((SpringSecurityContextSource) getContextSource(), userDn, password));
private DirContextOperations bindWithDn(String userDnStr, String username, String password) {
BaseLdapPathContextSource ctxSource = (BaseLdapPathContextSource) getContextSource();
DistinguishedName userDn = new DistinguishedName(userDnStr);
DistinguishedName fullDn = new DistinguishedName(userDn);
fullDn.prepend(ctxSource.getBaseLdapPath());
BindWithSpecificDnContextSource specificDnContextSource = new BindWithSpecificDnContextSource(
(SpringSecurityContextSource) getContextSource(), fullDn,
password);
logger.debug("Attemptimg to bind as " + fullDn);
DirContext ctx = null;
try {
ctx = specificDnContextSource.getReadOnlyContext();
try {
return template.retrieveEntry(userDn, getUserAttributes());
Attributes attrs = ctx.getAttributes(userDn, getUserAttributes());
} catch (BadCredentialsException e) {
// This will be thrown if an invalid user name is used and the method may
// be called multiple times to try different names, so we trap the exception
// unless a subclass wishes to implement more specialized behaviour.
handleBindException(userDn, username, e.getCause());
}
DirContextAdapter result = new DirContextAdapter(attrs, userDn, ctxSource.getBaseLdapPath());
return result;
} catch (NamingException e) {
// This will be thrown if an invalid user name is used and the method may
// be called multiple times to try different names, so we trap the exception
// unless a subclass wishes to implement more specialized behaviour.
if ((e instanceof org.springframework.ldap.AuthenticationException)
|| (e instanceof org.springframework.ldap.OperationNotSupportedException)) {
handleBindException(userDnStr, username, e);
} else {
throw e;
}
} catch (javax.naming.NamingException e) {
throw LdapUtils.convertLdapException(e);
} finally {
LdapUtils.closeContext(ctx);
}
return null;
return null;
}
/**
@@ -120,13 +145,12 @@ public class BindAuthenticator extends AbstractLdapAuthenticator {
private class BindWithSpecificDnContextSource implements ContextSource {
private SpringSecurityContextSource ctxFactory;
DistinguishedName userDn;
private DistinguishedName userDn;
private String password;
public BindWithSpecificDnContextSource(SpringSecurityContextSource ctxFactory, String userDn, String password) {
public BindWithSpecificDnContextSource(SpringSecurityContextSource ctxFactory, DistinguishedName userDn, String password) {
this.ctxFactory = ctxFactory;
this.userDn = new DistinguishedName(userDn);
this.userDn.prepend(ctxFactory.getBaseLdapPath());
this.userDn = userDn;
this.password = password;
}
@@ -86,9 +86,9 @@ public class LdapShaPasswordEncoder implements PasswordEncoder {
sha.update(rawPass.getBytes("UTF-8"));
} catch (java.security.NoSuchAlgorithmException e) {
throw new IllegalStateException("No SHA implementation available!");
} catch (UnsupportedEncodingException ue) {
throw new IllegalStateException("UTF-8 not supported!");
}
} catch (UnsupportedEncodingException ue) {
throw new IllegalStateException("UTF-8 not supported!");
}
if (salt != null) {
Assert.isInstanceOf(byte[].class, salt, "Salt value must be a byte array");
@@ -131,7 +131,7 @@ public class LdapShaPasswordEncoder implements PasswordEncoder {
*/
public boolean isPasswordValid(final String encPass, final String rawPass, Object salt) {
String prefix = extractPrefix(encPass);
if (prefix == null) {
return encPass.equals(rawPass);
}
@@ -141,32 +141,32 @@ public class LdapShaPasswordEncoder implements PasswordEncoder {
} else if (!prefix.equals(SHA_PREFIX) && !prefix.equals(SHA_PREFIX_LC)) {
throw new IllegalArgumentException("Unsupported password prefix '" + prefix + "'");
} else {
// Standard SHA
salt = null;
// Standard SHA
salt = null;
}
int startOfHash = prefix.length() + 1;
int startOfHash = prefix.length();
String encodedRawPass = encodePassword(rawPass, salt).substring(startOfHash);
return encodedRawPass.equals(encPass.substring(startOfHash));
}
/**
* Returns the hash prefix or null if there isn't one.
* Returns the hash prefix or null if there isn't one.
*/
private String extractPrefix(String encPass) {
if (!encPass.startsWith("{")) {
return null;
return null;
}
int secondBrace = encPass.lastIndexOf('}');
if (secondBrace < 0) {
throw new IllegalArgumentException("Couldn't find closing brace for SHA prefix");
}
return encPass.substring(0, secondBrace + 1);
int secondBrace = encPass.lastIndexOf('}');
if (secondBrace < 0) {
throw new IllegalArgumentException("Couldn't find closing brace for SHA prefix");
}
return encPass.substring(0, secondBrace + 1);
}
public void setForceLowerCasePrefix(boolean forceLowerCasePrefix) {
@@ -88,7 +88,6 @@ public class PreAuthenticatedAuthenticationProvider implements AuthenticationPro
result.setDetails(authentication.getDetails());
return result;
}
/**
@@ -123,4 +122,14 @@ public class PreAuthenticatedAuthenticationProvider implements AuthenticationPro
public void setThrowExceptionWhenTokenRejected(boolean throwExceptionWhenTokenRejected) {
this.throwExceptionWhenTokenRejected = throwExceptionWhenTokenRejected;
}
/**
* Sets the strategy which will be used to validate the loaded <tt>UserDetails</tt> object
* for the user. Defaults to an {@link AccountStatusUserDetailsChecker}.
* @param userDetailsChecker
*/
public void setUserDetailsChecker(UserDetailsChecker userDetailsChecker) {
Assert.notNull(userDetailsChecker, "userDetailsChacker cannot be null");
this.userDetailsChecker = userDetailsChecker;
}
}
@@ -45,7 +45,7 @@ import java.security.cert.X509Certificate;
*
* @author Luke Taylor
* @deprecated superceded by the preauth provider. Use the X.509 authentication support in org.springframework.security.ui.preauth.x509 instead
* or namespace support via the &lt;x509 /&gt; element.
* or namespace support via the &lt;x509 /&gt; element.
* @version $Id$
*/
public class X509AuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware {
@@ -61,7 +61,7 @@ public class X509AuthenticationProvider implements AuthenticationProvider, Initi
//~ Methods ========================================================================================================
public void afterPropertiesSet() throws Exception {
public void afterPropertiesSet() throws Exception {
Assert.notNull(userCache, "An x509UserCache must be set");
Assert.notNull(x509AuthoritiesPopulator, "An X509AuthoritiesPopulator must be set");
Assert.notNull(this.messages, "A message source must be set");
@@ -101,7 +101,9 @@ public class X509AuthenticationProvider implements AuthenticationProvider, Initi
UserDetails user = userCache.getUserFromCache(clientCertificate);
if (user == null) {
logger.debug("Authenticating with certificate " + clientCertificate);
if (logger.isDebugEnabled()) {
logger.debug("Authenticating with certificate " + clientCertificate);
}
user = x509AuthoritiesPopulator.getUserDetails(clientCertificate);
userCache.putUserInCache(clientCertificate, user);
}
@@ -122,7 +122,8 @@ import javax.servlet.http.HttpSession;
* The behaviour is turned off by default. Additionally there is a property <tt>migrateInvalidatedSessionAttributes</tt>
* which tells if on session invalidation we are to migrate all session attributes from the old session to a newly
* created one. This is turned on by default, but not used unless <tt>invalidateSessionOnSuccessfulAuthentication</tt>
* is true.
* is true. If you are using this feature in combination with concurrent session control, you should set the
* <tt>sessionRegistry</tt> property to make sure that the session information is updated consistently.
*
* @author Ben Alex
* @version $Id$
@@ -147,10 +148,14 @@ public abstract class AbstractProcessingFilter extends SpringSecurityFilter impl
private Properties exceptionMappings = new Properties();
private RememberMeServices rememberMeServices = new NullRememberMeServices();
/**
* Delay use of NullRememberMeServices until initialization so that namespace has a chance to inject
* the RememberMeServices implementation into custom implementations.
*/
private RememberMeServices rememberMeServices = null;
private TargetUrlResolver targetUrlResolver = new TargetUrlResolverImpl();
/** Where to redirect the browser to if authentication fails */
private String authenticationFailureUrl;
@@ -206,23 +211,25 @@ public abstract class AbstractProcessingFilter extends SpringSecurityFilter impl
private boolean migrateInvalidatedSessionAttributes = true;
private boolean allowSessionCreation = true;
private boolean serverSideRedirect = false;
private SessionRegistry sessionRegistry;
//~ Methods ========================================================================================================
public void afterPropertiesSet() throws Exception {
Assert.hasLength(filterProcessesUrl, "filterProcessesUrl must be specified");
Assert.isTrue(UrlUtils.isValidRedirectUrl(filterProcessesUrl), filterProcessesUrl + " isn't a valid redirect URL");
Assert.isTrue(UrlUtils.isValidRedirectUrl(filterProcessesUrl), filterProcessesUrl + " isn't a valid redirect URL");
Assert.hasLength(defaultTargetUrl, "defaultTargetUrl must be specified");
Assert.isTrue(UrlUtils.isValidRedirectUrl(defaultTargetUrl), defaultTargetUrl + " isn't a valid redirect URL");
// Assert.hasLength(authenticationFailureUrl, "authenticationFailureUrl must be specified");
Assert.isTrue(UrlUtils.isValidRedirectUrl(defaultTargetUrl), defaultTargetUrl + " isn't a valid redirect URL");
Assert.isTrue(UrlUtils.isValidRedirectUrl(authenticationFailureUrl), authenticationFailureUrl + " isn't a valid redirect URL");
Assert.notNull(authenticationManager, "authenticationManager must be specified");
Assert.notNull(rememberMeServices, "rememberMeServices cannot be null");
Assert.notNull(targetUrlResolver, "targetUrlResolver cannot be null");
if (rememberMeServices == null) {
rememberMeServices = new NullRememberMeServices();
}
}
/**
@@ -272,8 +279,8 @@ public abstract class AbstractProcessingFilter extends SpringSecurityFilter impl
}
public static String obtainFullSavedRequestUrl(HttpServletRequest request) {
SavedRequest savedRequest = getSavedRequest(request);
SavedRequest savedRequest = getSavedRequest(request);
return savedRequest == null ? null : savedRequest.getFullRequestUrl();
}
@@ -286,9 +293,9 @@ public abstract class AbstractProcessingFilter extends SpringSecurityFilter impl
SavedRequest savedRequest = (SavedRequest) session.getAttribute(SPRING_SECURITY_SAVED_REQUEST_KEY);
return savedRequest;
}
return savedRequest;
}
protected void onPreAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException, IOException {
}
@@ -381,8 +388,8 @@ public abstract class AbstractProcessingFilter extends SpringSecurityFilter impl
protected String determineTargetUrl(HttpServletRequest request) {
// Don't attempt to obtain the url from the saved request if alwaysUsedefaultTargetUrl is set
String targetUrl = alwaysUseDefaultTargetUrl ? null :
targetUrlResolver.determineTargetUrl(getSavedRequest(request), request, SecurityContextHolder.getContext().getAuthentication());
String targetUrl = alwaysUseDefaultTargetUrl ? null :
targetUrlResolver.determineTargetUrl(getSavedRequest(request), request, SecurityContextHolder.getContext().getAuthentication());
if (targetUrl == null) {
targetUrl = getDefaultTargetUrl();
@@ -418,13 +425,13 @@ public abstract class AbstractProcessingFilter extends SpringSecurityFilter impl
onUnsuccessfulAuthentication(request, response, failed);
rememberMeServices.loginFail(request, response);
if (failureUrl == null) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication Failed:" + failed.getMessage());
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication Failed:" + failed.getMessage());
} else if (serverSideRedirect){
request.getRequestDispatcher(failureUrl).forward(request, response);
request.getRequestDispatcher(failureUrl).forward(request, response);
} else {
sendRedirect(request, response, failureUrl);
sendRedirect(request, response, failureUrl);
}
}
@@ -475,7 +482,7 @@ public abstract class AbstractProcessingFilter extends SpringSecurityFilter impl
this.defaultTargetUrl = defaultTargetUrl;
}
Properties getExceptionMappings() {
protected Properties getExceptionMappings() {
return new Properties(exceptionMappings);
}
@@ -549,33 +556,33 @@ public abstract class AbstractProcessingFilter extends SpringSecurityFilter impl
this.allowSessionCreation = allowSessionCreation;
}
/**
* @return the targetUrlResolver
*/
protected TargetUrlResolver getTargetUrlResolver() {
return targetUrlResolver;
}
/**
* @return the targetUrlResolver
*/
protected TargetUrlResolver getTargetUrlResolver() {
return targetUrlResolver;
}
/**
* @param targetUrlResolver the targetUrlResolver to set
*/
public void setTargetUrlResolver(TargetUrlResolver targetUrlResolver) {
this.targetUrlResolver = targetUrlResolver;
}
/**
* @param targetUrlResolver the targetUrlResolver to set
*/
public void setTargetUrlResolver(TargetUrlResolver targetUrlResolver) {
this.targetUrlResolver = targetUrlResolver;
}
/**
* Tells if we are to do a server side include of the error URL instead of a 302 redirect.
*
* @param serverSideRedirect
*/
public void setServerSideRedirect(boolean serverSideRedirect) {
this.serverSideRedirect = serverSideRedirect;
}
*/
public void setServerSideRedirect(boolean serverSideRedirect) {
this.serverSideRedirect = serverSideRedirect;
}
/**
* The session registry needs to be set if session fixation attack protection is in use (and concurrent
* session control is enabled).
*/
/**
* The session registry needs to be set if session fixation attack protection is in use (and concurrent
* session control is enabled).
*/
public void setSessionRegistry(SessionRegistry sessionRegistry) {
this.sessionRegistry = sessionRegistry;
}
@@ -58,7 +58,7 @@ public class AuthenticationDetailsSourceImpl implements AuthenticationDetailsSou
Constructor constructor = null;
for (int i = 0; i < constructors.length; i++) {
Class[] parameterTypes = constructors[i].getParameterTypes();
if (parameterTypes.length == 1 && (object == null || parameterTypes[i].isInstance(object))) {
if (parameterTypes.length == 1 && (object == null || parameterTypes[0].isInstance(object))) {
constructor = constructors[i];
break;
}
@@ -48,7 +48,7 @@ public class SessionFixationProtectionFilter extends SpringSecurityFilter {
protected void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
// Session fixation isn't a problem if there's no session
if(request.getSession(false) == null || request.getAttribute(FILTER_APPLIED) != null) {
if(request.getSession(false) == null || request.getAttribute(FILTER_APPLIED) != null || !request.isRequestedSessionIdValid()) {
chain.doFilter(request, response);
return;
}
@@ -28,38 +28,37 @@ import org.springframework.util.StringUtils;
/**
* Default implementation for {@link TargetUrlResolver}
* <p>
* Returns a target URL based from the contents of the configured <tt>targetUrlParameter</tt> if present on
* the current request. Failing that, the SavedRequest in the session will be used.
*
* <p/>
* Returns a target URL based from the contents of the configured <tt>targetUrlParameter</tt> if present on
* the current request. Failing that, the SavedRequest in the session will be used.
*
* @author Martino Piccinato
* @author Luke Taylor
* @version $Id$
* @since 2.0
*
*/
public class TargetUrlResolverImpl implements TargetUrlResolver {
public static String DEFAULT_TARGET_PARAMETER = "spring-security-redirect";
/* SEC-213 */
private String targetUrlParameter = DEFAULT_TARGET_PARAMETER;
/**
* If <code>true</code>, will only use <code>SavedRequest</code> to determine the target URL on successful
/**
* If <code>true</code>, will only use <code>SavedRequest</code> to determine the target URL on successful
* authentication if the request that caused the authentication request was a GET.
* It will then return null for a POST/PUT request.
* Defaults to false.
*/
private boolean justUseSavedRequestOnGet = false;
*/
private boolean justUseSavedRequestOnGet = false;
/* (non-Javadoc)
* @see org.acegisecurity.ui.TargetUrlResolver#determineTargetUrl(org.acegisecurity.ui.savedrequest.SavedRequest, javax.servlet.http.HttpServletRequest, org.acegisecurity.Authentication)
*/
public String determineTargetUrl(SavedRequest savedRequest, HttpServletRequest currentRequest,
Authentication auth) {
* @see org.acegisecurity.ui.TargetUrlResolver#determineTargetUrl(org.acegisecurity.ui.savedrequest.SavedRequest, javax.servlet.http.HttpServletRequest, org.acegisecurity.Authentication)
*/
public String determineTargetUrl(SavedRequest savedRequest, HttpServletRequest currentRequest,
Authentication auth) {
String targetUrl = currentRequest.getParameter(targetUrlParameter);
if (StringUtils.hasText(targetUrl)) {
try {
return URLDecoder.decode(targetUrl, "UTF-8");
@@ -75,35 +74,34 @@ public class TargetUrlResolverImpl implements TargetUrlResolver {
}
return targetUrl;
}
}
/**
* @return <code>true</code> if just GET request will be used
* to determine target URLs, <code>false</code> otherwise.
*/
protected boolean isJustUseSavedRequestOnGet() {
return justUseSavedRequestOnGet;
}
/**
* @return <code>true</code> if just GET request will be used
* to determine target URLs, <code>false</code> otherwise.
*/
protected boolean isJustUseSavedRequestOnGet() {
return justUseSavedRequestOnGet;
}
/**
* @param justUseSavedRequestOnGet set to <code>true</code> if
* just GET request will be used to determine target URLs,
* <code>false</code> otherwise.
*/
public void setJustUseSavedRequestOnGet(boolean justUseSavedRequestOnGet) {
this.justUseSavedRequestOnGet = justUseSavedRequestOnGet;
}
/**
* @param justUseSavedRequestOnGet set to <code>true</code> if
* just GET request will be used to determine target URLs,
* <code>false</code> otherwise.
*/
public void setJustUseSavedRequestOnGet(boolean justUseSavedRequestOnGet) {
this.justUseSavedRequestOnGet = justUseSavedRequestOnGet;
}
/**
* Before checking the SavedRequest, the current request will be checked for this parameter
* and the value used as the target URL if resent.
*
* @param targetUrlParameter the name of the parameter containing the encoded target URL. Defaults
* to "redirect".
*/
public void setTargetUrlParameter(String targetUrlParameter) {
Assert.hasText("targetUrlParamete canot be null or empty");
/**
* Before checking the SavedRequest, the current request will be checked for this parameter
* and the value used as the target URL if resent.
*
* @param targetUrlParameter the name of the parameter containing the encoded target URL. Defaults
* to "redirect".
*/
public void setTargetUrlParameter(String targetUrlParameter) {
Assert.hasText("targetUrlParameter cannot be null or empty");
this.targetUrlParameter = targetUrlParameter;
}
}
@@ -35,6 +35,7 @@ import org.springframework.security.ui.WebAuthenticationDetailsSource;
import org.springframework.security.ui.AuthenticationEntryPoint;
import org.springframework.security.ui.FilterChainOrder;
import org.springframework.security.ui.SpringSecurityFilter;
import org.springframework.security.ui.rememberme.NullRememberMeServices;
import org.springframework.security.ui.rememberme.RememberMeServices;
import org.springframework.util.Assert;
@@ -91,7 +92,7 @@ public class BasicProcessingFilter extends SpringSecurityFilter implements Initi
private AuthenticationDetailsSource authenticationDetailsSource = new WebAuthenticationDetailsSource();
private AuthenticationEntryPoint authenticationEntryPoint;
private AuthenticationManager authenticationManager;
private RememberMeServices rememberMeServices;
private RememberMeServices rememberMeServices = new NullRememberMeServices();
private boolean ignoreFailure = false;
private String credentialsCharset = "UTF-8";
@@ -105,10 +106,10 @@ public class BasicProcessingFilter extends SpringSecurityFilter implements Initi
}
}
public void doFilterHttp(HttpServletRequest httpRequest, HttpServletResponse httpResponse, FilterChain chain)
public void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
String header = httpRequest.getHeader("Authorization");
String header = request.getHeader("Authorization");
if (logger.isDebugEnabled()) {
logger.debug("Authorization header: " + header);
@@ -116,7 +117,7 @@ public class BasicProcessingFilter extends SpringSecurityFilter implements Initi
if ((header != null) && header.startsWith("Basic ")) {
byte[] base64Token = header.substring(6).getBytes("UTF-8");
String token = new String(Base64.decodeBase64(base64Token), getCredentialsCharset(httpRequest));
String token = new String(Base64.decodeBase64(base64Token), getCredentialsCharset(request));
String username = "";
String password = "";
@@ -130,7 +131,7 @@ public class BasicProcessingFilter extends SpringSecurityFilter implements Initi
if (authenticationIsRequired(username)) {
UsernamePasswordAuthenticationToken authRequest =
new UsernamePasswordAuthenticationToken(username, password);
authRequest.setDetails(authenticationDetailsSource.buildDetails(httpRequest));
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
Authentication authResult;
@@ -144,14 +145,14 @@ public class BasicProcessingFilter extends SpringSecurityFilter implements Initi
SecurityContextHolder.getContext().setAuthentication(null);
if (rememberMeServices != null) {
rememberMeServices.loginFail(httpRequest, httpResponse);
}
rememberMeServices.loginFail(request, response);
onUnsuccessfulAuthentication(request, response, failed);
if (ignoreFailure) {
chain.doFilter(httpRequest, httpResponse);
chain.doFilter(request, response);
} else {
authenticationEntryPoint.commence(httpRequest, httpResponse, failed);
authenticationEntryPoint.commence(request, response, failed);
}
return;
@@ -164,13 +165,13 @@ public class BasicProcessingFilter extends SpringSecurityFilter implements Initi
SecurityContextHolder.getContext().setAuthentication(authResult);
if (rememberMeServices != null) {
rememberMeServices.loginSuccess(httpRequest, httpResponse, authResult);
}
rememberMeServices.loginSuccess(request, response, authResult);
onSuccessfulAuthentication(request, response, authResult);
}
}
chain.doFilter(httpRequest, httpResponse);
chain.doFilter(request, response);
}
private boolean authenticationIsRequired(String username) {
@@ -202,6 +203,14 @@ public class BasicProcessingFilter extends SpringSecurityFilter implements Initi
return false;
}
protected void onSuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
Authentication authResult) throws IOException {
}
protected void onUnsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
AuthenticationException failed) throws IOException {
}
protected AuthenticationEntryPoint getAuthenticationEntryPoint() {
return authenticationEntryPoint;
@@ -233,6 +242,7 @@ public class BasicProcessingFilter extends SpringSecurityFilter implements Initi
}
public void setRememberMeServices(RememberMeServices rememberMeServices) {
Assert.notNull(rememberMeServices, "rememberMeServices cannot be null");
this.rememberMeServices = rememberMeServices;
}

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