1
0
mirror of synced 2026-07-08 20:30:04 +00:00

Compare commits

..

191 Commits

Author SHA1 Message Date
Luke Taylor d6d9bc9d6b [maven-release-plugin] copy for tag spring-security-parent-2.0.3 2008-06-23 14:08:17 +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
Luke Taylor 122e1c47ed Changed rnc filename prior to 2.0.2 release 2008-06-01 19:34:50 +00:00
Luke Taylor 64ab7e534c Spelling corrections in Javadoc. 2008-06-01 17:26:27 +00:00
Luke Taylor ab6d29d927 SEC-862: Make logoutSuccessUrl accessible to sub-classes. 2008-06-01 16:15:09 +00:00
Luke Taylor 2a510f3539 Added question on login with multiple fields to faq 2008-06-01 15:25:39 +00:00
Luke Taylor 7c0f8b9756 Corrected typo in docbook. 2008-05-30 20:48:05 +00:00
Luke Taylor 1d9d7eb9a7 Removed accidental commit of SavedRequest clearing code in TargetUrlResolverImpl 2008-05-30 17:53:09 +00:00
Luke Taylor 0cfcc0e9f1 Tidying Javadoc 2008-05-30 17:51:23 +00:00
Luke Taylor 373f7b648b Corrected outdated filter name in Javadoc 2008-05-30 17:49:03 +00:00
Luke Taylor a5cae70949 SEC-800: Removed references to outdated method configuration classes. 2008-05-30 16:35:09 +00:00
Luke Taylor ecd2cc6da7 Added some Assert calls to setters and improved comments. 2008-05-30 15:29:51 +00:00
Luke Taylor f228d013d8 SEC-861: Change default value of justUseSavedRequestOnGet to false 2008-05-30 15:09:51 +00:00
Luke Taylor 4de4bb8e87 SEC-860: Added setter for authenticationDetailsSource to AbstractRememberMeServices 2008-05-30 14:29:32 +00:00
Luke Taylor f8cded10ee Typo. 2008-05-30 11:20:16 +00:00
Luke Taylor c031588975 SEC-606: Added support for customizable credentials character set. 2008-05-29 18:00:15 +00:00
Luke Taylor 36a192b70f SEC-858: Replaced integer properties in schema with strings to allow use of placeholders. 2008-05-29 16:13:14 +00:00
Luke Taylor 980a72f9a0 Removed TODO (done). 2008-05-29 15:54:50 +00:00
Luke Taylor 517a7f117a SEC-857: Make request wrapper getParameterValues() consistent with getParameterMap() etc. 2008-05-29 15:49:43 +00:00
Luke Taylor 244579faf4 OPEN - issue SEC-856: GroupManager JdbcUserDetailsManager implementation: addGroupAuthority() method doesn't work.
http://jira.springframework.org/browse/SEC-856. Refactored class to remove the JDBC-related inner classes.
2008-05-28 16:25:28 +00:00
Luke Taylor 87ba871605 Minor doc updates 2008-05-28 13:38:33 +00:00
Luke Taylor d63536cc0d SEC-821: Added support for eternal session registry and concurrent session controller to the 2.0.2 namespace. 2008-05-27 13:14:21 +00:00
Luke Taylor 8b5bbe3800 SEC-830: Changed SavedRequestAwareWrapper to make wrapped request parameters take precedence over saved request ones. 2008-05-25 22:57:03 +00:00
Luke Taylor cf4072c517 Context file improvements (based on sts suggestions) 2008-05-25 20:57:07 +00:00
Luke Taylor 4b45e5d7c2 Fixed OSGi version numbers in ranges [x,y] by adding a property pom.version.osgi 2008-05-25 20:55:17 +00:00
Luke Taylor 45c3084502 SEC-836: Made LDAP namespace elements use subtree group searching by default. 2008-05-23 23:57:01 +00:00
Luke Taylor 871e529840 SEC-850: custom-authentication-provider Registering Separate Bean Definitions in App Context and Providers List
http://jira.springframework.org/browse/SEC-850. Added extra test.
2008-05-23 23:32:57 +00:00
Luke Taylor d1005e4cfb SEC-850: custom-authentication-provider Registering Separate Bean Definitions in App Context and Providers List
http://jira.springframework.org/browse/SEC-850. Changed bean decorator to add a bean reference to the ProviderManager rather than a bean definition.
2008-05-23 23:25:09 +00:00
Luke Taylor 9ce0270226 Fixed typo in test name 2008-05-23 22:57:30 +00:00
Luke Taylor 7603ce2f97 SEC-848: Remove all Spring LDAP dependecy loading from namespace parsers
http://jira.springframework.org/browse/SEC-848. Replaced class references with class names.
2008-05-23 21:30:57 +00:00
Luke Taylor 859e99edf4 SEC-851: Fix port number in LDAP sample. 2008-05-23 21:24:48 +00:00
Luke Taylor 25ba269db0 SEC-835: use setContentType on response for J2EE 1.3 compatibility. 2008-05-23 20:55:10 +00:00
Luke Taylor 11b448c0e0 SEC-847: Updated the xsl file to inline openid-login and other elements 2008-05-23 16:29:44 +00:00
Luke Taylor 08c5fe8925 Fixed autoboxing issue 2008-05-22 12:19:00 +00:00
Luke Taylor fbe3ca48f4 SEC-823, SEC-843: Allow setting of custom RememberMeServices and token validity periodon remember-me namespace element 2008-05-21 16:03:05 +00:00
Luke Taylor 3e33b8a880 Update InMemoryXmlApplicationContext to use 2.0.2 schema 2008-05-20 22:46:37 +00:00
Luke Taylor b60c578b25 SEC-844: Support for SHA-256 hashing. 2008-05-20 22:45:02 +00:00
Luke Taylor 03981ab6a0 SEC-844: Added sec-256 to namespace schema 2008-05-20 22:32:03 +00:00
Luke Taylor e9adbd4d62 SEC-844, SEC-843, SEC-823: Added support for sha-256, custom remember-me services and setting of remember me token validity period to namespace schema. Also added 2.0.2 XSD file 2008-05-20 19:48:32 +00:00
Luke Taylor 29d31b72d0 SEC-837: Add special character filtering to LDAP search filters 2008-05-20 19:25:37 +00:00
Luke Taylor 3fb1f59fde SEC-837: Add special character filtering to LDAP search filterscore/src/test/java/org/springframework/security/ldap 2008-05-20 19:22:49 +00:00
Luke Taylor 219d2e8962 Corrected link 2008-05-20 10:57:17 +00:00
Luke Taylor ff215e6750 Minor doc fixes 2008-05-20 10:54:29 +00:00
Luke Taylor 6ae81d553b SEC-842: Minor doc fixes 2008-05-20 10:48:59 +00:00
Luke Taylor 5af53da106 Improved doc for'filters' attribute 2008-05-18 11:09:50 +00:00
Luke Taylor 2329dadf48 Removed jalopy parameter comments 2008-05-15 17:58:15 +00:00
Luke Taylor fb5eefeea5 SEC-740: Finished preauth chapter 2008-05-15 17:00:45 +00:00
Luke Taylor f269373442 IDE-791: Remove explicit Spring LDAP class dependencies from LdapServerBDP. 2008-05-15 14:33:42 +00:00
Luke Taylor 4f6b4e4bfd Make sample login pages use c:out for data output 2008-05-15 12:48:13 +00:00
Luke Taylor 8b2c0468ff OPEN - issue SEC-834: Session fixation attack protection will cause problems with URL rewriting
http://jira.springframework.org/browse/SEC-834. Modified HttpSecurityBDP to add session-fixation parameters to openId and form-login filters. Also added sessionRegistry property to AbstractProcessingFilter so that it doesn't conflict with concurrent session control.
2008-05-15 01:34:14 +00:00
Luke Taylor d17a2da9e0 SEC-834: Session fixation attack protection will cause problems with URL rewriting
http://jira.springframework.org/browse/SEC-834. Changed position of SessionFixationProtectionFilter and modified it to make a decision about whether authentication has taken place prior to calling doFilter(). Previously it did this on the return through the filter chain, which caused the problem described in this issue.
2008-05-15 00:26:27 +00:00
Luke Taylor 7f38c656ca SEC-820: Expand regular expression used in hierarchical roles. 2008-05-14 22:59:33 +00:00
Luke Taylor 6493df13f8 SEC-803: Removed use of websphere SubjectHelper class. 2008-05-14 22:51:39 +00:00
Luke Taylor d4defb10fe SEC-833: Fixed login-failure-url in contacts sample app. 2008-05-14 22:41:13 +00:00
Luke Taylor 59543af4fb SEC-826: Support for JPA PersistenceContext annotation broken
http://jira.springframework.org/browse/SEC-826 Moved all injection post-processing to BeanPostProcessors (and deleted bean factory post-processor) to prevent early instantiation problems. Beas should now all be instantiated before the injection takes place.
2008-05-14 16:41:52 +00:00
Luke Taylor 332f8fe5a1 SEC-624: Minor updates to docs 2008-05-13 17:16:19 +00:00
Luke Taylor 7a8eec11da SEC-765: Brief outline of preauth sample 2008-05-13 17:14:45 +00:00
Luke Taylor ff61644219 SEC-740: More on preauth 2008-05-13 17:13:47 +00:00
Luke Taylor 1fee538c7e Fixed typo in setter method (uses of). 2008-05-13 15:32:30 +00:00
Luke Taylor ae2470127c Fixed typo in setter method "seAttributePrefix" 2008-05-13 13:51:49 +00:00
Luke Taylor 2a4d859812 SEC-829: Minor doc fix 2008-05-13 10:18:09 +00:00
Luke Taylor 15b893f9ae SEC-809: OpenIDProcessingFilter updated to set authentication details (to make compatible with concurrent session control). 2008-05-12 20:05:24 +00:00
Luke Taylor de886e36fa SEC-624: Expanded general info on obtaining samples and added pointers to ldap and cas versions 2008-05-10 17:21:18 +00:00
Luke Taylor ad9a667b75 SEC-624: Inserted sub-sections for key class definitions so they appear in toc 2008-05-10 17:19:46 +00:00
Luke Taylor f70701d55a SEC-624: Added section on 'getting the source' for reference from samples chapter 2008-05-10 17:18:29 +00:00
Luke Taylor e1b226ee57 Added 2.0.2 namespace file 2008-05-10 17:16:46 +00:00
Luke Taylor af0153d833 Extended intro to Authentication part to include pointers to tech overview and namespace config 2008-05-10 17:10:49 +00:00
Luke Taylor d78a021fe1 Added basic intro to preauth 2008-05-10 16:07:39 +00:00
Luke Taylor e1c17450b3 Updated faqs to add infinite loop and access denied debug message 2008-05-10 12:31:14 +00:00
Luke Taylor add2649397 Javadoc typo. 2008-05-09 18:09:56 +00:00
Luke Taylor 781d88bd30 OPEN - issue SEC-825: Query string isn't beig stripped from URLs when ant matcher is in use (regression issue)
http://jira.springframework.org/browse/SEC-825. Make sure the property is set on DefaultFilterInvocationDefinitionSource when ant paths are in use.
2008-05-09 18:08:32 +00:00
Luke Taylor 1030dca353 SEC-786: Added information on the need ofor a UserDetailsService if using auto-config/remember-me 2008-05-09 15:01:39 +00:00
Luke Taylor b99f9d343d SEC-624: Added some info on use of role-prefix 2008-05-09 14:25:42 +00:00
Luke Taylor c0e829a41d SEC-700: Added info on new remember-me imlementation and namespace config examples 2008-05-08 15:59:01 +00:00
Luke Taylor 5a1258a4ca Corrected references to parts and reading order 2008-05-08 15:54:27 +00:00
Luke Taylor 883b92e7bd SEC-822: Converted to long arithmetic to prevent integer overflowing with long token validity periods 2008-05-08 15:07:40 +00:00
Luke Taylor 301d021bf5 SEC-817: NPE in org.springframework.security.config.FilterChainProxyPostProcessor
Reversed order of beanName.equals() call as suggested.
2008-05-07 13:58:53 +00:00
Luke Taylor 8ad2d681ab SEC-818: Changed redirect URL validation to ignore potential property placeholders at parsing time and report a warning through the parser context rather than an error. Also validated the URLs in the beans themselves using Asserts, so an exception will occur later when the beans have been created rather than while assembling the bean definitions. 2008-05-07 13:49:20 +00:00
Luke Taylor 5cf0c84e2f SEC-814: Added standard bean config to ldap example and updated doc to provide some pointers to DefaultLdapAuthoritiesPopulator 2008-05-06 14:50:14 +00:00
Luke Taylor afc757e618 Removed reference to LdapDataAccessException since it isn't actually mentioned except in javadoc 2008-05-06 14:43:52 +00:00
Luke Taylor c333070fe3 Javadoc tidying 2008-05-06 13:59:46 +00:00
Luke Taylor fca3a2a709 SEC-812: Added missing TextUtils file 2008-05-05 19:09:09 +00:00
Luke Taylor fa44c74993 SEC-812: Added entity-escaping of username stored under last username key, to prevent problems if it is rendered in a page without escaping the text. 2008-05-05 18:37:02 +00:00
Luke Taylor 06719053f1 Removed commons lang dependency. 2008-05-05 17:18:47 +00:00
Luke Taylor e7b6fe09e1 Corrected css for 'poweredBy' 2008-05-03 16:18:19 +00:00
Ben Alex 9961c7f867 Moved to correct build location. 2008-05-02 10:52:57 +00:00
Ben Alex 7a2e1e13d3 SEC-811: Provide a mechanism to allocate and rebuild cryptographically strong, randomised tokens. 2008-05-02 10:38:56 +00:00
Luke Taylor a599ef5398 [maven-release-plugin] prepare for next development iteration 2008-05-01 20:09:03 +00:00
Luke Taylor 3e808335a4 [maven-release-plugin] prepare release spring-security-parent-2.0.1 2008-05-01 20:07:46 +00:00
Luke Taylor 054e2f6c38 SEC-624: Start of preauth document 2008-05-01 19:51:35 +00:00
Luke Taylor 79ca0d1612 Set correct 'test' scope on core-tests dependency 2008-05-01 19:47:47 +00:00
Luke Taylor 18a9965b80 Moved dummy file out of default package for easy exclusion from javadoc 2008-05-01 19:45:36 +00:00
Luke Taylor de179c3e46 Fixed javadoc links 2008-05-01 19:44:34 +00:00
Luke Taylor fc498954c6 Updated sample context files to point at 2.0.1 schema 2008-05-01 17:51:48 +00:00
Luke Taylor 014f21ee85 Deleted attributes sample 2008-05-01 17:50:47 +00:00
Luke Taylor 6ecfa0541f SEC-806: Osgi-ified more modules 2008-05-01 17:11:31 +00:00
Luke Taylor 4984d4be65 OPEN - issue SEC-757: Add validation of redirect URLs on namespace
http://jira.springframework.org/browse/SEC-757. Added validation method to ConfigUtils and calls to it for url attributes.
2008-05-01 16:39:31 +00:00
Luke Taylor af3dc22586 added Ruud S. to contributors list 2008-05-01 16:37:33 +00:00
Luke Taylor 26d2b03667 Updates to heavyduty sample 2008-05-01 16:36:16 +00:00
Ben Alex e7e256a9d5 SEC-787: Consistently use lowercase in all ACL module SQL statements. 2008-05-01 07:48:10 +00:00
Luke Taylor 0df9dee9dd SEC-806: Improved OSGi bundle version information support 2008-04-30 18:02:47 +00:00
Luke Taylor 81ebd094ff OPEN - issue SEC-808: Switch namespace schema version to 2.0.1 and update spring.schemas
http://jira.springframework.org/browse/SEC-808. Replaced 2.0 text with that from the 2.0 release, rather than the website schema.
2008-04-29 18:59:25 +00:00
Luke Taylor 473f6a32c6 OPEN - issue SEC-808: Switch namespace schema version to 2.0.1 and update spring.schemas
http://jira.springframework.org/browse/SEC-808. Created new 2.0.1 schema files and updated tests to use them.
2008-04-29 18:53:33 +00:00
Luke Taylor 8281aeb0da SEC-807: Allow mapping to a standard Ldap UserDetails through the namespace
http://jira.springframework.org/browse/SEC-807. Added extra test for Ldap provider parser.
2008-04-29 18:01:59 +00:00
Luke Taylor e4b32b8d29 OPEN - issue SEC-807: Allow mapping to a standard Ldap UserDetails through the namespace
http://jira.springframework.org/browse/SEC-807. Added support for user-details-class attribute to ldap-authentication-provider and ldap-user-service.
2008-04-29 16:53:24 +00:00
Luke Taylor 104716fedb SEC-805: Add extra fields to InetOrgPerson
http://jira.springframework.org/browse/SEC-805. Added a substantial number of new fields to the class.
2008-04-29 14:39:58 +00:00
Luke Taylor 49bec559a9 SEC-804: Added notes to LDAP section to explain how to customize returned UserDetails 2008-04-29 10:57:52 +00:00
Luke Taylor f96fa66a60 Added Michael Mayr to contributors list 2008-04-29 10:56:47 +00:00
Luke Taylor ef112f7967 Fixed autoboxing problem. 2008-04-28 15:26:20 +00:00
Luke Taylor 341455cde4 SEC-799: Import cleaning following other changes. 2008-04-28 15:19:25 +00:00
Luke Taylor 2d692718e0 SEC-799: Add better detection of missing server-ref element for <ldap-user-service> and <ldap-authentication-provider />
http://jira.springframework.org/browse/SEC-799. Updated ContextSourceSettingPostProcessor to set the standard ContextSource as an alias if it is needed by a bean but has not been set (because the user specified their own server id on <ldap-server />).
2008-04-28 15:01:20 +00:00
Luke Taylor 270fa92780 Improved Javadoc comment 2008-04-28 09:20:37 +00:00
Luke Taylor 0c28845d4e SEC-787: Converted SQL in BasicLookupStrategy to lower case to make it consistent with other classes. 2008-04-26 13:08:31 +00:00
Luke Taylor d3a0f05de9 SEC-783: GlobalMethodSecurityBeanDefinitionParser should support AfterInvocationProviders
http://jira.springframework.org/browse/SEC-783. Added support for custom-after-invocation-provider
2008-04-25 12:28:30 +00:00
Luke Taylor 348d211b8c SEC-797: Minor javadoc correction. 2008-04-24 23:12:55 +00:00
Luke Taylor d1e23b3d2c SEC-783: Added custom-after-invocation-provider element to namespace. 2008-04-24 02:02:23 +00:00
Luke Taylor 1090072fff SEC-795: Add check for protected login page when using namespace
http://jira.springframework.org/browse/SEC-795. I've added checks for the various scenarios which will result in a protected login page and suitable warning messages.
2008-04-24 01:59:19 +00:00
Luke Taylor 882509fb2a Renamed context file 2008-04-24 00:27:37 +00:00
Luke Taylor 5d51b35cfa SEC-792: Filters should only be added to the default stack if they are labelled using custom-filter.
http://jira.springframework.org/browse/SEC-792. Updated FilterChainProxyPostProcessor to raise an exception if two filters have the same order, and also to unwrap wrapped filters once the sorting by order has been performed.
2008-04-23 23:19:44 +00:00
Luke Taylor eba18675fc Removed old acegi file from tutorial sample as it's causing confusion with users. 2008-04-23 21:08:41 +00:00
Luke Taylor 38774ec94f SEC-792: Filters should only be added to the default stack if they are labelled using custom-filter.
http://jira.springframework.org/browse/SEC-792. The filters are now maintained as a list in the context and have to be stored there explicitly on registration.
2008-04-23 16:06:54 +00:00
Luke Taylor 80cd7f4acc Removed accidental commit of tutorial context file 2008-04-23 13:13:56 +00:00
Luke Taylor c184d2d8c5 Added 'heavyduty' sample to sandbox for testing 2008-04-23 13:11:26 +00:00
Luke Taylor 01185475a1 OPEN - issue SEC-793: ldap-authentication-provider element parser ignores hash attribute.
http://jira.springframework.org/browse/SEC-793. Added support for hash attribute. password-encoder still takes precendence with a warning if both are present.
2008-04-23 12:50:09 +00:00
Luke Taylor 7e63fe7357 SEC-790: DefaultLoginPageGeneratingFilter should be a better HTTP citizen
http://jira.springframework.org/browse/SEC-790. Applied submitted patch.
2008-04-23 00:41:52 +00:00
Luke Taylor 8ea7487ec3 Removed unused method. 2008-04-22 23:20:49 +00:00
Luke Taylor ec81e780b2 Import cleaning. 2008-04-22 22:27:51 +00:00
Luke Taylor 599d9fea04 Minor improvements to toString() methods for logging. 2008-04-22 22:21:20 +00:00
Luke Taylor a845a69cb7 Updated surefire plugin to 2.4.2 2008-04-22 22:01:28 +00:00
Luke Taylor 0cf745b85f Updated clean plugin to 2.2 2008-04-22 21:59:40 +00:00
Luke Taylor b2e9e82727 Fixed typo in message. 2008-04-22 21:54:54 +00:00
Luke Taylor 63decfeb93 SEC-761: HttpSessionContextIntegrationFilter.contextObject should be created in afterPropertiesSet(), not the constructor
http://jira.springframework.org/browse/SEC-761. Added call to generateNewContext() in the afterPropertiesSet() method to take account of custom security context classes.
2008-04-22 21:51:12 +00:00
Luke Taylor 1ae167434a SEC-756: Add checks for duplicate use of namespace elements such as global-method-security
http://jira.springframework.org/browse/SEC-756. Refactored HttpSecurityBDP and added check for duplicate usage of the element.
2008-04-22 21:25:35 +00:00
Luke Taylor 083644f2fe SEC-756: Refactored GlobalMethodSecurityDefinitionParser and added check for duplicate registration. 2008-04-22 18:25:35 +00:00
Luke Taylor eec62e9760 Removed reference in petclinic tutorial to acegisecurity.org 2008-04-22 18:17:20 +00:00
Luke Taylor c5f6cbb8f5 Removed corrupt character in author name which was causing build problems with bamboo. 2008-04-22 15:10:11 +00:00
Luke Taylor 1258fa854e SEC-788: x509 authentication does not work properly
http://jira.springframework.org/browse/SEC-788. Added check for X509 element when choosing entry point, if nothing else is available.
2008-04-22 14:53:11 +00:00
Luke Taylor e12b6afefa SEC-776: Http Session created for Anonymous request
http://jira.springframework.org/browse/SEC-776. Added AuthenticationtrustResolver to HttpSCIF to check for anonymous authentication.
2008-04-22 13:22:38 +00:00
Luke Taylor 88ea87642a SEC-791: RequestKey.equals throws NPE if method is null
http://jira.springframework.org/browse/SEC-791. Fixed handling of equals when one http method is null.
2008-04-22 12:32:33 +00:00
Luke Taylor 9eaa1cbbdd OPEN - issue SEC-789: Add support for optional role-prefix attribute to namespace
http://jira.springframework.org/browse/SEC-789. Added role-prefix attribute to ldap provider and jdbc/ldap user-service elements.
2008-04-21 18:29:54 +00:00
Luke Taylor aba5a22b6c SEC-789: Add support for optional role-prefix attribute to namespace
http://jira.springframework.org/browse/SEC-789. Added support for role-prefix to jdbc-user-service element.
2008-04-21 17:44:32 +00:00
Luke Taylor 1a4130528a SEC-782: Incorrect UrlMatcher initialization in FilterChainProxy results in wrong lowercase/uppercase matching
http://jira.springframework.org/browse/SEC-782. I've updated FilterChainProxy to make sure the same UrlMatcher is used throughout when converting a legacy configuration.
2008-04-21 16:51:06 +00:00
Luke Taylor 5bb558bd6a SEC-777: The disabled status cannot be set in <user-service>
http://jira.springframework.org/browse/SEC-777. Added the disabled flag to the relax grammar file.
2008-04-21 15:59:08 +00:00
Luke Taylor 993fdd7a32 Added better toString() method to OrderedFilterDecorator to make it report the delegate filter information. 2008-04-21 12:53:54 +00:00
Luke Taylor 1663142cf1 SEC-784: removed 'optional' tag on dependencies 2008-04-19 12:40:17 +00:00
Luke Taylor 469f55ce05 SEC-773: global-method-security fails with JPA
http://jira.springframework.org/browse/SEC-773. Added extra constructor to MethodDefinitionSourceAdvisor to allow for lazy initialization of the advice (MethodSecurityInterceptor), and in turn the AuthenticationManager and ay referenced UserDetailsService implementations.
2008-04-18 13:15:56 +00:00
Luke Taylor 4d347cfdb5 Updated surefire plugin versions 2008-04-15 19:59:15 +00:00
Luke Taylor 2cc3068de7 Minor site css adjustments 2008-04-15 19:58:53 +00:00
Luke Taylor 09ccc35119 Updated index file to include release features 2008-04-15 18:45:09 +00:00
Luke Taylor 7238097310 OPEN - issue SEC-775: CLONE -impossible to specify "observeOncePerRequest" property in the namespace based configuration.
http://jira.springframework.org/browse/SEC-775. Corrected check for value of observe-once-per-request attribute. Should be a check for "false" as it is true by default.
2008-04-15 16:57:47 +00:00
Luke Taylor 31a9fa553d added section on maven repo downloads 2008-04-14 14:20:11 +00:00
Luke Taylor 680b2d3b74 Updated version number for reference manual 2008-04-14 14:09:06 +00:00
Luke Taylor d2ddbc1ee3 Removed references to packaged docs 2008-04-14 14:05:42 +00:00
Luke Taylor 35698edc04 Website URL updates in readme file 2008-04-14 13:00:57 +00:00
Ben Alex b5dc523041 [maven-release-plugin] prepare for next development iteration 2008-04-14 07:06:44 +00:00
Ben Alex 0c42670431 [maven-release-plugin] prepare release spring-security-parent-2.0.0 2008-04-14 07:05:46 +00:00
272 changed files with 10818 additions and 2904 deletions
+28 -2
View File
@@ -3,13 +3,14 @@
<parent>
<artifactId>spring-security-parent</artifactId>
<groupId>org.springframework.security</groupId>
<version>2.0.0</version>
<version>2.0.3</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.0</version>
<version>2.0.3</version>
<packaging>bundle</packaging>
<dependencies>
<dependency>
@@ -44,4 +45,29 @@
<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>
</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;
}
}
@@ -0,0 +1,39 @@
package org.springframework.security.acls.domain;
import org.springframework.security.acls.Permission;
/**
* Provides an abstract base for standard {@link Permission} instances that wish to offer static convenience
* methods to callers via delegation to {@link DefaultPermissionFactory}.
*
* @author Ben Alex
* @since 2.0.3
*
*/
public abstract class AbstractRegisteredPermission extends AbstractPermission {
protected static DefaultPermissionFactory defaultPermissionFactory = new DefaultPermissionFactory();
protected AbstractRegisteredPermission(int mask, char code) {
super(mask, code);
}
protected final static void registerPermissionsFor(Class subClass) {
defaultPermissionFactory.registerPublicPermissions(subClass);
}
public final static Permission buildFromMask(int mask) {
return defaultPermissionFactory.buildFromMask(mask);
}
public final static Permission[] buildFromMask(int[] masks) {
return defaultPermissionFactory.buildFromMask(masks);
}
public final static Permission buildFromName(String name) {
return defaultPermissionFactory.buildFromName(name);
}
public final static Permission[] buildFromName(String[] names) {
return defaultPermissionFactory.buildFromName(names);
}
}
@@ -14,157 +14,36 @@
*/
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 AbstractRegisteredPermission {
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();
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);
}
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 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 getMask() {
return mask;
}
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);
}
@@ -49,6 +49,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;
/**
@@ -73,7 +74,7 @@ public final class BasicLookupStrategy implements LookupStrategy {
//~ Constructors ===================================================================================================
/**
/**
* Constructor accepting mandatory arguments
*
* @param dataSource to access the database
@@ -97,30 +98,30 @@ public final class BasicLookupStrategy implements LookupStrategy {
private static String computeRepeatingSql(String repeatingSql, int requiredRepetitions) {
Assert.isTrue(requiredRepetitions >= 1, "Must be => 1");
String startSql = "select ACL_OBJECT_IDENTITY.OBJECT_ID_IDENTITY, "
+ "ACL_ENTRY.ACE_ORDER, "
+ "ACL_OBJECT_IDENTITY.ID as ACL_ID, "
+ "ACL_OBJECT_IDENTITY.PARENT_OBJECT, "
+ "ACL_OBJECT_IDENTITY.ENTRIES_INHERITING, "
+ "ACL_ENTRY.ID as ACE_ID, "
+ "ACL_ENTRY.MASK, "
+ "ACL_ENTRY.GRANTING, "
+ "ACL_ENTRY.AUDIT_SUCCESS, "
+ "ACL_ENTRY.AUDIT_FAILURE, "
+ "ACL_SID.PRINCIPAL as ACE_PRINCIPAL, "
+ "ACL_SID.SID as ACE_SID, "
+ "ACLI_SID.PRINCIPAL as ACL_PRINCIPAL, "
+ "ACLI_SID.SID as ACL_SID, "
+ "ACL_CLASS.CLASS "
+ "from ACL_OBJECT_IDENTITY "
+ "left join ACL_SID ACLI_SID on ACLI_SID.ID = ACL_OBJECT_IDENTITY.OWNER_SID "
+ "left join ACL_CLASS on ACL_CLASS.ID = ACL_OBJECT_IDENTITY.OBJECT_ID_CLASS "
+ "left join ACL_ENTRY on ACL_OBJECT_IDENTITY.ID = ACL_ENTRY.ACL_OBJECT_IDENTITY "
+ "left join ACL_SID on ACL_ENTRY.SID = ACL_SID.ID "
String startSql = "select acl_object_identity.object_id_identity, "
+ "acl_entry.ace_order, "
+ "acl_object_identity.id as acl_id, "
+ "acl_object_identity.parent_object, "
+ "acl_object_identity.entries_inheriting, "
+ "acl_entry.id as ace_id, "
+ "acl_entry.mask, "
+ "acl_entry.granting, "
+ "acl_entry.audit_success, "
+ "acl_entry.audit_failure, "
+ "acl_sid.principal as ace_principal, "
+ "acl_sid.sid as ace_sid, "
+ "acli_sid.principal as acl_principal, "
+ "acli_sid.sid as acl_sid, "
+ "acl_class.class "
+ "from acl_object_identity "
+ "left join acl_sid acli_sid on acli_sid.id = acl_object_identity.owner_sid "
+ "left join acl_class on acl_class.id = acl_object_identity.object_id_class "
+ "left join acl_entry on acl_object_identity.id = acl_entry.acl_object_identity "
+ "left join acl_sid on acl_entry.sid = acl_sid.id "
+ "where ( ";
String endSql = ") order by ACL_OBJECT_IDENTITY.OBJECT_ID_IDENTITY"
+ " asc, ACL_ENTRY.ACE_ORDER asc";
String endSql = ") order by acl_object_identity.object_id_identity"
+ " asc, acl_entry.ace_order asc";
StringBuffer sqlStringBuffer = new StringBuffer();
sqlStringBuffer.append(startSql);
@@ -196,30 +197,30 @@ public final class BasicLookupStrategy implements LookupStrategy {
*/
private void convertCurrentResultIntoObject(Map acls, ResultSet rs)
throws SQLException {
Long id = new Long(rs.getLong("ACL_ID"));
Long id = new Long(rs.getLong("acl_id"));
// If we already have an ACL for this ID, just create the ACE
AclImpl acl = (AclImpl) acls.get(id);
if (acl == null) {
// Make an AclImpl and pop it into the Map
ObjectIdentity objectIdentity = new ObjectIdentityImpl(rs.getString("CLASS"),
new Long(rs.getLong("OBJECT_ID_IDENTITY")));
ObjectIdentity objectIdentity = new ObjectIdentityImpl(rs.getString("class"),
new Long(rs.getLong("object_id_identity")));
Acl parentAcl = null;
long parentAclId = rs.getLong("PARENT_OBJECT");
long parentAclId = rs.getLong("parent_object");
if (parentAclId != 0) {
parentAcl = new StubAclParent(new Long(parentAclId));
}
boolean entriesInheriting = rs.getBoolean("ENTRIES_INHERITING");
boolean entriesInheriting = rs.getBoolean("entries_inheriting");
Sid owner;
if (rs.getBoolean("ACL_PRINCIPAL")) {
owner = new PrincipalSid(rs.getString("ACL_SID"));
if (rs.getBoolean("acl_principal")) {
owner = new PrincipalSid(rs.getString("acl_sid"));
} else {
owner = new GrantedAuthoritySid(rs.getString("ACL_SID"));
owner = new GrantedAuthoritySid(rs.getString("acl_sid"));
}
acl = new AclImpl(objectIdentity, id, aclAuthorizationStrategy, auditLogger, parentAcl, null,
@@ -229,20 +230,21 @@ public final class BasicLookupStrategy implements LookupStrategy {
// Add an extra ACE to the ACL (ORDER BY maintains the ACE list order)
// It is permissable to have no ACEs in an ACL (which is detected by a null ACE_SID)
if (rs.getString("ACE_SID") != null) {
Long aceId = new Long(rs.getLong("ACE_ID"));
if (rs.getString("ace_sid") != null) {
Long aceId = new Long(rs.getLong("ace_id"));
Sid recipient;
if (rs.getBoolean("ACE_PRINCIPAL")) {
recipient = new PrincipalSid(rs.getString("ACE_SID"));
if (rs.getBoolean("ace_principal")) {
recipient = new PrincipalSid(rs.getString("ace_sid"));
} else {
recipient = new GrantedAuthoritySid(rs.getString("ACE_SID"));
recipient = new GrantedAuthoritySid(rs.getString("ace_sid"));
}
Permission permission = BasePermission.buildFromMask(rs.getInt("MASK"));
boolean granting = rs.getBoolean("GRANTING");
boolean auditSuccess = rs.getBoolean("AUDIT_SUCCESS");
boolean auditFailure = rs.getBoolean("AUDIT_FAILURE");
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");
AccessControlEntryImpl ace = new AccessControlEntryImpl(aceId, acl, recipient, permission, granting,
auditSuccess, auditFailure);
@@ -264,6 +266,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
@@ -283,7 +289,7 @@ public final class BasicLookupStrategy implements LookupStrategy {
// Make the "acls" map contain all requested objectIdentities
// (including markers to each parent in the hierarchy)
String sql = computeRepeatingSql("(ACL_OBJECT_IDENTITY.OBJECT_ID_IDENTITY = ? and ACL_CLASS.CLASS = ?)",
String sql = computeRepeatingSql("(acl_object_identity.object_id_identity = ? and acl_class.class = ?)",
objectIdentities.length);
Set parentsToLookup = (Set) jdbcTemplate.query(sql,
@@ -293,10 +299,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);
@@ -338,7 +344,7 @@ public final class BasicLookupStrategy implements LookupStrategy {
Assert.notNull(acls, "ACLs are required");
Assert.notEmpty(findNow, "Items to find now required");
String sql = computeRepeatingSql("(ACL_OBJECT_IDENTITY.ID = ?)", findNow.size());
String sql = computeRepeatingSql("(acl_object_identity.id = ?)", findNow.size());
Set parentsToLookup = (Set) jdbcTemplate.query(sql,
new PreparedStatementSetter() {
@@ -473,7 +479,7 @@ public final class BasicLookupStrategy implements LookupStrategy {
convertCurrentResultIntoObject(acls, rs);
// Figure out if this row means we need to lookup another parent
long parentId = rs.getLong("PARENT_OBJECT");
long parentId = rs.getLong("parent_object");
if (parentId != 0) {
// See if it's already in the "acls"
@@ -53,11 +53,11 @@ 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 "
+ "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 = ("
+ "SELECT id FROM acl_class WHERE acl_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 = ("
+ "select id FROM acl_class where acl_class.class = ?)";
//~ Instance fields ================================================================================================
@@ -62,23 +62,23 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
private boolean foreignKeysInDatabase = true;
private AclCache aclCache;
private String deleteClassByClassNameString = "DELETE FROM acl_class WHERE class=?";
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 insertClass = "INSERT INTO acl_class (class) VALUES (?)";
private String insertEntry = "INSERT INTO acl_entry "
private String deleteEntryByObjectIdentityForeignKey = "delete from acl_entry where acl_object_identity=?";
private String deleteObjectIdentityByPrimaryKey = "delete from acl_object_identity where id=?";
private String classIdentityQuery = "call identity()"; // 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)"
+ "VALUES (?, ?, ?, ?, ?, ?, ?)";
private String insertObjectIdentity = "INSERT INTO acl_object_identity "
+ "(object_id_class, object_id_identity, owner_sid, entries_inheriting) " + "VALUES (?, ?, ?, ?)";
private String insertSid = "INSERT INTO acl_sid (principal, sid) VALUES (?, ?)";
private String selectClassPrimaryKey = "SELECT id FROM acl_class WHERE class=?";
private String selectObjectIdentityPrimaryKey = "SELECT acl_object_identity.id FROM acl_object_identity, acl_class "
+ "WHERE acl_object_identity.object_id_class = acl_class.id and acl_class.class=? "
+ "values (?, ?, ?, ?, ?, ?, ?)";
private String insertObjectIdentity = "insert into acl_object_identity "
+ "(object_id_class, object_id_identity, owner_sid, entries_inheriting) " + "values (?, ?, ?, ?)";
private String insertSid = "insert into acl_sid (principal, sid) values (?, ?)";
private String selectClassPrimaryKey = "select id from acl_class where class=?";
private String selectObjectIdentityPrimaryKey = "select acl_object_identity.id from acl_object_identity, acl_class "
+ "where acl_object_identity.object_id_class = acl_class.id and acl_class.class=? "
+ "and acl_object_identity.object_id_identity = ?";
private String selectSidPrimaryKey = "SELECT id FROM acl_sid WHERE principal=? AND sid=?";
private String updateObjectIdentity = "UPDATE acl_object_identity SET "
private String selectSidPrimaryKey = "select id from acl_sid where principal=? and sid=?";
private String updateObjectIdentity = "update acl_object_identity set "
+ "parent_object = ?, owner_sid = ?, entries_inheriting = ?" + " where id = ?";
//~ Constructors ===================================================================================================
@@ -177,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();
@@ -222,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();
@@ -381,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}
@@ -63,9 +63,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());
@@ -12,24 +12,29 @@
* 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;
package sample.attributes;
/**
* DOCUMENT ME!
*
* @author Cameron Braid
* A test permission.
*
* @author Ben Alex
* @version $Id$
*/
public class BankServiceImpl implements BankService {
//~ Methods ========================================================================================================
public float balance(String accountNumber) {
return 42000000;
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);
}
public String[] listAccounts() {
return new String[] {"1", "2", "3"};
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);
+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-adapters</artifactId>
<version>2.0.0</version>
<version>2.0.3</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.0</version>
<version>2.0.3</version>
</parent>
<artifactId>spring-security-jboss</artifactId>
<name>Spring Security - JBoss adapter</name>
@@ -57,7 +57,7 @@ import javax.security.auth.login.LoginException;
* which is subsequently available from <code>java:comp/env/security/subject</code>.</p>
*
* @author Ben Alex
* @author Sergio Bern
* @author Sergio Bern
* @version $Id:JbossSpringSecurityLoginModule.java 2151 2007-09-22 11:54:13Z luke_t $
*/
public class JbossSpringSecurityLoginModule extends AbstractServerLoginModule {
+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-adapters</artifactId>
<version>2.0.0</version>
<version>2.0.3</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.0</version>
<version>2.0.3</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.0</version>
<version>2.0.3</version>
</parent>
<artifactId>spring-security-resin</artifactId>
<name>Spring Security - Resin adapter</name>
+28 -3
View File
@@ -3,11 +3,11 @@
<parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-parent</artifactId>
<version>2.0.0</version>
<version>2.0.3</version>
</parent>
<artifactId>spring-security-cas-client</artifactId>
<name>Spring Security - CAS support</name>
<packaging>jar</packaging>
<packaging>bundle</packaging>
<dependencies>
<dependency>
@@ -31,7 +31,7 @@
<dependency>
<groupId>org.jasig.cas</groupId>
<artifactId>cas-client-core</artifactId>
<version>3.1.1</version>
<version>3.1.3</version>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
@@ -39,4 +39,29 @@
<optional>true</optional>
</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.3, 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>
</project>
@@ -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;
}
}
@@ -30,12 +30,14 @@ import org.springframework.util.Assert;
/**
* Used by the <code>SecurityEnforcementFilter</code> to commence authentication via the JA-SIG Central
* Authentication Service (CAS).<P>The user's browser will be redirected to the JA-SIG CAS enterprise-wide login
* page. This page is specified by the <code>loginUrl</code> property. Once login is complete, the CAS login page will
* Used by the <code>ExceptionTranslationFilter</code> to commence authentication via the JA-SIG Central
* Authentication Service (CAS).
* <p>
* The user's browser will be redirected to the JA-SIG CAS enterprise-wide login page.
* This page is specified by the <code>loginUrl</code> property. Once login is complete, the CAS login page will
* redirect to the page indicated by the <code>service</code> property. The <code>service</code> is a HTTP URL
* belonging to the current application. The <code>service</code> URL is monitored by the {@link CasProcessingFilter},
* which will validate the CAS login was successful.</p>
* which will validate the CAS login was successful.
*
* @author Ben Alex
* @author Scott Battaglia
@@ -65,8 +67,8 @@ public class CasProcessingFilterEntryPoint implements AuthenticationEntryPoint,
}
public void commence(final ServletRequest servletRequest, final ServletResponse servletResponse,
final AuthenticationException authenticationException)
throws IOException, ServletException {
final AuthenticationException authenticationException) throws IOException, ServletException {
final HttpServletResponse response = (HttpServletResponse) servletResponse;
final String urlEncodedService = CommonUtils.constructServiceUrl(null, response, this.serviceProperties.getService(), null, "ticket", this.encodeServiceUrlWithSessionId);
final String redirectUrl = CommonUtils.constructRedirectUrl(this.loginUrl, "service", urlEncodedService, this.serviceProperties.isSendRenew(), false);
@@ -21,9 +21,10 @@ import org.springframework.util.Assert;
/**
* Stores properties related to this CAS service.
* <p>Each web application capable of processing CAS tickets is known as a service.
* <p>
* Each web application capable of processing CAS tickets is known as a service.
* This class stores the properties that are relevant to the local CAS service, being the application
* that is being secured by Spring Security.</p>
* that is being secured by Spring Security.
*
* @author Ben Alex
* @version $Id$
@@ -42,7 +43,8 @@ public class ServiceProperties implements InitializingBean {
/**
* Represents the service the user is authenticating to.
* <p>This service is the callback URL belonging to the local Spring Security System for Spring secured application.
* <p>
* This service is the callback URL belonging to the local Spring Security System for Spring secured application.
* For example,
* <pre>
* https://www.mycompany.com/application/j_spring_cas_security_check
@@ -56,10 +58,12 @@ public class ServiceProperties implements InitializingBean {
/**
* Indicates whether the <code>renew</code> parameter should be sent to the CAS login URL and CAS
* validation URL.<p>If <code>true</code>, it will force CAS to authenticate the user again (even if the
* validation URL.
* <p>
* If <code>true</code>, it will force CAS to authenticate the user again (even if the
* user has previously authenticated). During ticket validation it will require the ticket was generated as a
* consequence of an explicit login. High security applications would probably set this to <code>true</code>.
* Defaults to <code>false</code>, providing automated single sign on.</p>
* Defaults to <code>false</code>, providing automated single sign on.
*
* @return whether to send the <code>renew</code> parameter to CAS
*/
+30 -35
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-parent</artifactId>
<version>2.0.0</version>
<version>2.0.3</version>
</parent>
<packaging>bundle</packaging>
<artifactId>spring-security-core-tiger</artifactId>
@@ -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.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
@@ -25,20 +32,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>
@@ -61,26 +54,6 @@
<target>1.5</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>${felix.version}</version>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>org.springframework.bundle.security.core.tiger</Bundle-SymbolicName>
<Export-Package>org.springframework.security.*;version=${pom.version}</Export-Package>
<Private-Package>!org.springframework.security.*</Private-Package>
<Implementation-Title>${pom.name}</Implementation-Title>
<Implementation-Version>${pom.version}</Implementation-Version>
<Import-Package>
org.springframework*;resolution:=optional;version="[2.0,2.6)",
*;resolution:=optional
</Import-Package>
</instructions>
<excludeDependencies>true</excludeDependencies>
</configuration>
</plugin>
</plugins>
</build>
<reporting>
@@ -94,4 +67,26 @@
</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>
@@ -1,8 +1,12 @@
package org.springframework.security.config;
import static org.junit.Assert.assertEquals;
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;
@@ -11,17 +15,17 @@ 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 GlobalMethodSecurityBeanDefinitionParserTests {
private ClassPathXmlApplicationContext appContext;
private AbstractXmlApplicationContext appContext;
private BusinessService target;
@Before
public void loadContext() {
appContext = new ClassPathXmlApplicationContext("org/springframework/security/config/global-method-security.xml");
target = (BusinessService) appContext.getBean("target");
@@ -37,11 +41,13 @@ public class GlobalMethodSecurityBeanDefinitionParserTests {
@Test(expected=AuthenticationCredentialsNotFoundException.class)
public void targetShouldPreventProtectedMethodInvocationWithNoContext() {
loadContext();
target.someUserMethod1();
}
@Test
public void targetShouldAllowProtectedMethodInvocationWithCorrectRole() {
loadContext();
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Test", "Password",
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_USER")});
SecurityContextHolder.getContext().setAuthentication(token);
@@ -51,10 +57,38 @@ public class GlobalMethodSecurityBeanDefinitionParserTests {
@Test(expected=AccessDeniedException.class)
public void targetShouldPreventProtectedMethodInvocationWithIncorrectRole() {
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'/>"
);
PostProcessedMockUserDetailsService service = (PostProcessedMockUserDetailsService)appContext.getBean("myUserService");
assertEquals("Hello from the post processor!", service.getPostProcessorWasHere());
}
@Test(expected=BeanDefinitionParsingException.class)
public void duplicateElementCausesError() {
setContext(
"<global-method-security />" +
"<global-method-security />"
);
}
private void setContext(String context) {
appContext = new InMemoryXmlApplicationContext(context);
}
}
@@ -0,0 +1,12 @@
# Logging
#
# $Id: log4j.properties 2385 2007-12-20 20:53:26Z luke_t $
log4j.rootCategory=DEBUG, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p %c - %m%n
log4j.category.org.springframework.security=DEBUG
+53 -29
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-parent</artifactId>
<version>2.0.0</version>
<version>2.0.3</version>
</parent>
<packaging>bundle</packaging>
<artifactId>spring-security-core</artifactId>
@@ -141,34 +141,58 @@
<optional>true</optional>
</dependency>
</dependencies>
<properties>
<spring.osgi.export>
org.springframework.security.*;version=${pom.version}
</spring.osgi.export>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>${felix.version}</version>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>org.springframework.bundle.security.core</Bundle-SymbolicName>
<Export-Package>org.springframework.security.*;version=${pom.version}</Export-Package>
<Private-Package>!org.springframework.security.*</Private-Package>
<Implementation-Title>${pom.name}</Implementation-Title>
<Implementation-Version>${pom.version}</Implementation-Version>
<Import-Package>
org.springframework*;resolution:=optional;version="[2.0,2.6)",
*;resolution:=optional
</Import-Package>
<!--
<Embed-Dependency>
*;scope=compile|runtime;inline=true
</Embed-Dependency>
-->
</instructions>
</configuration>
</plugin>
</plugins>
</build>
<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>
</project>
@@ -158,8 +158,7 @@ public class ConfigAttributeDefinition implements Serializable {
* Allows <code>AccessDecisionManager</code>s and other classes to loop through every configuration attribute
* associated with a target secure object.
*
* @return all the configuration attributes stored by the instance, or <code>null</code> if an
* <code>Iterator</code> is unavailable
* @return the configuration attributes stored in this instance.
*/
public Collection getConfigAttributes() {
return this.configAttributes;
@@ -90,7 +90,7 @@ public class SimpleAttributes2GrantedAuthoritiesMapper implements Attributes2Gra
return attributePrefix == null ? "" : attributePrefix;
}
public void seAttributePrefix(String string) {
public void setAttributePrefix(String string) {
attributePrefix = string;
}
@@ -158,4 +158,8 @@ public class ConcurrentSessionControllerImpl implements ConcurrentSessionControl
public void setSessionRegistry(SessionRegistry sessionRegistry) {
this.sessionRegistry = sessionRegistry;
}
public SessionRegistry getSessionRegistry() {
return sessionRegistry;
}
}
@@ -21,6 +21,7 @@ import org.springframework.security.ui.FilterChainOrder;
import org.springframework.security.ui.SpringSecurityFilter;
import org.springframework.security.ui.logout.LogoutHandler;
import org.springframework.security.ui.logout.SecurityContextLogoutHandler;
import org.springframework.security.util.UrlUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
@@ -60,6 +61,7 @@ public class ConcurrentSessionFilter extends SpringSecurityFilter implements Ini
public void afterPropertiesSet() throws Exception {
Assert.notNull(sessionRegistry, "SessionRegistry required");
Assert.isTrue(UrlUtils.isValidRedirectUrl(expiredUrl), expiredUrl + " isn't a valid redirect URL");
}
public void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
@@ -23,12 +23,12 @@ public abstract class AbstractUserDetailsServiceBeanDefinitionParser implements
/** UserDetailsService bean Id. For use in a stateful context (i.e. in AuthenticationProviderBDP) */
private String id;
protected abstract Class getBeanClass(Element element);
protected abstract String getBeanClassName(Element element);
protected abstract void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder);
public BeanDefinition parse(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(getBeanClass(element));
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(getBeanClassName(element));
doParse(element, parserContext, builder);
@@ -3,6 +3,7 @@ package org.springframework.security.config;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.support.RootBeanDefinition;
@@ -70,6 +71,7 @@ public class AnonymousBeanDefinitionParser implements BeanDefinitionParser {
authMgrProviderList.add(provider);
parserContext.getRegistry().registerBeanDefinition(BeanIds.ANONYMOUS_PROCESSING_FILTER, filter);
ConfigUtils.addHttpFilter(parserContext, new RuntimeBeanReference(BeanIds.ANONYMOUS_PROCESSING_FILTER));
parserContext.registerComponent(new BeanComponentDefinition(filter, BeanIds.ANONYMOUS_PROCESSING_FILTER));
return null;
@@ -1,32 +1,51 @@
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;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* Just registers an alias name for the default ProviderManager used by the namespace
* Registers an alias name for the default ProviderManager used by the namespace
* configuration, allowing users to reference it in their beans and clearly see where the name is
* coming from.
* coming from. Also allows the ConcurrentSessionController to be set on the ProviderManager.
*
* @author Luke Taylor
* @version $Id$
*/
public class AuthenticationManagerBeanDefinitionParser implements BeanDefinitionParser {
private static final String ATT_ALIAS = "alias";
private static final String ATT_SESSION_CONTROLLER_REF = "session-controller-ref";
private static final String ATT_ALIAS = "alias";
public BeanDefinition parse(Element element, ParserContext parserContext) {
BeanDefinition authManager = ConfigUtils.registerProviderManagerIfNecessary(parserContext);
String alias = element.getAttribute(ATT_ALIAS);
if (!StringUtils.hasText(alias)) {
parserContext.getReaderContext().error(ATT_ALIAS + " is required.", element );
}
String sessionControllerRef = element.getAttribute(ATT_SESSION_CONTROLLER_REF);
if (StringUtils.hasText(sessionControllerRef)) {
ConfigUtils.setSessionControllerOnAuthenticationManager(parserContext,
BeanIds.CONCURRENT_SESSION_CONTROLLER, element);
authManager.getPropertyValues().addPropertyValue("sessionController",
new RuntimeBeanReference(sessionControllerRef));
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;
}
}
}
@@ -41,6 +41,7 @@ public class BasicAuthenticationBeanDefinitionParser implements BeanDefinitionPa
parserContext.getRegistry().registerBeanDefinition(BeanIds.BASIC_AUTHENTICATION_FILTER,
filterBuilder.getBeanDefinition());
ConfigUtils.addHttpFilter(parserContext, new RuntimeBeanReference(BeanIds.BASIC_AUTHENTICATION_FILTER));
parserContext.registerComponent(new BeanComponentDefinition(filterBuilder.getBeanDefinition(),
BeanIds.BASIC_AUTHENTICATION_FILTER));
return null;
@@ -16,8 +16,11 @@ public abstract class BeanIds {
/** Package protected as end users shouldn't really be using this BFPP directly */
static final String INTERCEPT_METHODS_BEAN_FACTORY_POST_PROCESSOR = "_interceptMethodsBeanfactoryPP";
static final String CONTEXT_SOURCE_SETTING_POST_PROCESSOR = "_contextSettingPostProcessor";
static final String HTTP_POST_PROCESSOR = "_httpConfigBeanFactoryPostProcessor";
static final String ENTRY_POINT_INJECTION_POST_PROCESSOR = "_entryPointInjectionBeanPostProcessor";
static final String USER_DETAILS_SERVICE_INJECTION_POST_PROCESSOR = "_userServiceInjectionPostProcessor";
static final String SESSION_REGISTRY_INJECTION_POST_PROCESSOR = "_sessionRegistryInjectionPostProcessor";
static final String FILTER_CHAIN_POST_PROCESSOR = "_filterChainProxyPostProcessor";
static final String FILTER_LIST = "_filterChainList";
public static final String JDBC_USER_DETAILS_MANAGER = "_jdbcUserDetailsManager";
public static final String USER_DETAILS_SERVICE = "_userDetailsService";
@@ -30,6 +33,7 @@ public abstract class BeanIds {
public static final String CONCURRENT_SESSION_CONTROLLER = "_concurrentSessionController";
public static final String ACCESS_MANAGER = "_accessManager";
public static final String AUTHENTICATION_MANAGER = "_authenticationManager";
public static final String AFTER_INVOCATION_MANAGER = "_afterInvocationManager";
public static final String FORM_LOGIN_FILTER = "_formLoginFilter";
public static final String FORM_LOGIN_ENTRY_POINT = "_formLoginEntryPoint";
public static final String OPEN_ID_FILTER = "_openIDFilter";
@@ -49,6 +53,7 @@ public abstract class BeanIds {
public static final String SECURITY_CONTEXT_HOLDER_AWARE_REQUEST_FILTER = "_securityContextHolderAwareRequestFilter";
public static final String SESSION_FIXATION_PROTECTION_FILTER = "_sessionFixationProtectionFilter";
public static final String METHOD_SECURITY_INTERCEPTOR = "_methodSecurityInterceptor";
public static final String METHOD_SECURITY_INTERCEPTOR_POST_PROCESSOR = "_methodSecurityInterceptorPostProcessor";
public static final String METHOD_DEFINITION_SOURCE_ADVISOR = "_methodDefinitionSourceAdvisor";
public static final String PROTECT_POINTCUT_POST_PROCESSOR = "_protectPointcutPostProcessor";
public static final String DELEGATING_METHOD_DEFINITION_SOURCE = "_delegatingMethodDefinitionSource";
@@ -58,7 +63,8 @@ public abstract class BeanIds {
public static final String CONTEXT_SOURCE = "_securityContextSource";
public static final String PORT_MAPPER = "_portMapper";
public static final String X509_FILTER = "_x509ProcessingFilter";
public static final String X509_AUTH_PROVIDER = "_x509AuthenitcationProvider";
public static final String X509_AUTH_PROVIDER = "_x509AuthenticationProvider";
public static final String PRE_AUTH_ENTRY_POINT = "_preAuthenticatedProcessingFilterEntryPoint";
public static final String REMEMBER_ME_SERVICES_INJECTION_POST_PROCESSOR = "_rememberMeServicesInjectionBeanPostProcessor";
}
@@ -29,7 +29,8 @@ public class ConcurrentSessionsBeanDefinitionParser implements BeanDefinitionPar
static final String ATT_EXPIRY_URL = "expired-url";
static final String ATT_MAX_SESSIONS = "max-sessions";
static final String ATT_EXCEPTION_IF_MAX_EXCEEDED = "exception-if-maximum-exceeded";
static final String ATT_SESSION_REGISTRY_ALIAS = "session-registry-alias";
static final String ATT_SESSION_REGISTRY_ALIAS = "session-registry-alias";
static final String ATT_SESSION_REGISTRY_REF = "session-registry-ref";
public BeanDefinition parse(Element element, ParserContext parserContext) {
CompositeComponentDefinition compositeDef =
@@ -38,25 +39,43 @@ public class ConcurrentSessionsBeanDefinitionParser implements BeanDefinitionPar
BeanDefinitionRegistry beanRegistry = parserContext.getRegistry();
RootBeanDefinition sessionRegistry = new RootBeanDefinition(SessionRegistryImpl.class);
String sessionRegistryId = element.getAttribute(ATT_SESSION_REGISTRY_REF);
if (!StringUtils.hasText(sessionRegistryId)) {
RootBeanDefinition sessionRegistry = new RootBeanDefinition(SessionRegistryImpl.class);
beanRegistry.registerBeanDefinition(BeanIds.SESSION_REGISTRY, sessionRegistry);
parserContext.registerComponent(new BeanComponentDefinition(sessionRegistry, BeanIds.SESSION_REGISTRY));
sessionRegistryId = BeanIds.SESSION_REGISTRY;
} else {
// Register the default ID as an alias so that things like session fixation filter can access it
beanRegistry.registerAlias(sessionRegistryId, BeanIds.SESSION_REGISTRY);
}
String registryAlias = element.getAttribute(ATT_SESSION_REGISTRY_ALIAS);
if (StringUtils.hasText(registryAlias)) {
beanRegistry.registerAlias(sessionRegistryId, registryAlias);
}
BeanDefinitionBuilder filterBuilder =
BeanDefinitionBuilder.rootBeanDefinition(ConcurrentSessionFilter.class);
BeanDefinitionBuilder controllerBuilder
= BeanDefinitionBuilder.rootBeanDefinition(ConcurrentSessionControllerImpl.class);
controllerBuilder.addPropertyValue("sessionRegistry", new RuntimeBeanReference(BeanIds.SESSION_REGISTRY));
filterBuilder.addPropertyValue("sessionRegistry", new RuntimeBeanReference(BeanIds.SESSION_REGISTRY));
filterBuilder.addPropertyValue("sessionRegistry", new RuntimeBeanReference(sessionRegistryId));
Object source = parserContext.extractSource(element);
filterBuilder.setSource(source);
filterBuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
controllerBuilder.setSource(source);
controllerBuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
String expiryUrl = element.getAttribute(ATT_EXPIRY_URL);
if (StringUtils.hasText(expiryUrl)) {
ConfigUtils.validateHttpRedirect(expiryUrl, parserContext, source);
filterBuilder.addPropertyValue("expiredUrl", expiryUrl);
}
}
BeanDefinitionBuilder controllerBuilder
= BeanDefinitionBuilder.rootBeanDefinition(ConcurrentSessionControllerImpl.class);
controllerBuilder.setSource(source);
controllerBuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
controllerBuilder.addPropertyValue("sessionRegistry", new RuntimeBeanReference(sessionRegistryId));
String maxSessions = element.getAttribute(ATT_MAX_SESSIONS);
@@ -71,22 +90,14 @@ public class ConcurrentSessionsBeanDefinitionParser implements BeanDefinitionPar
}
BeanDefinition controller = controllerBuilder.getBeanDefinition();
beanRegistry.registerBeanDefinition(BeanIds.SESSION_REGISTRY, sessionRegistry);
parserContext.registerComponent(new BeanComponentDefinition(sessionRegistry, BeanIds.SESSION_REGISTRY));
String registryAlias = element.getAttribute(ATT_SESSION_REGISTRY_ALIAS);
if (StringUtils.hasText(registryAlias)) {
beanRegistry.registerAlias(BeanIds.SESSION_REGISTRY, registryAlias);
}
beanRegistry.registerBeanDefinition(BeanIds.CONCURRENT_SESSION_CONTROLLER, controller);
parserContext.registerComponent(new BeanComponentDefinition(controller, BeanIds.CONCURRENT_SESSION_CONTROLLER));
beanRegistry.registerBeanDefinition(BeanIds.CONCURRENT_SESSION_FILTER, filterBuilder.getBeanDefinition());
parserContext.registerComponent(new BeanComponentDefinition(filterBuilder.getBeanDefinition(), BeanIds.CONCURRENT_SESSION_FILTER));
ConfigUtils.addHttpFilter(parserContext, new RuntimeBeanReference(BeanIds.CONCURRENT_SESSION_FILTER));
BeanDefinition providerManager = ConfigUtils.registerProviderManagerIfNecessary(parserContext);
providerManager.getPropertyValues().addPropertyValue("sessionController", controller);
ConfigUtils.setSessionControllerOnAuthenticationManager(parserContext, BeanIds.CONCURRENT_SESSION_CONTROLLER, element);
parserContext.popAndRegisterContainingComponent();
@@ -1,9 +1,13 @@
package org.springframework.security.config;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.RuntimeBeanReference;
@@ -11,13 +15,15 @@ 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.AuthenticationManager;
import org.springframework.security.afterinvocation.AfterInvocationProviderManager;
import org.springframework.security.providers.ProviderManager;
import org.springframework.security.userdetails.UserDetailsService;
import org.springframework.security.util.UrlUtils;
import org.springframework.security.vote.AffirmativeBased;
import org.springframework.security.vote.AuthenticatedVoter;
import org.springframework.security.vote.RoleVoter;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* Utility methods used internally by the Spring Security namespace configuration code.
@@ -86,45 +92,95 @@ public abstract class ConfigUtils {
return authManager;
}
/**
* Obtains a user details service for use in RememberMeServices etc. Will return a caching version
* if available so should not be used for beans which need to separate the two.
*/
static RuntimeBeanReference getUserDetailsService(ConfigurableListableBeanFactory bf) {
String[] services = bf.getBeanNamesForType(CachingUserDetailsService.class, false, false);
if (services.length == 0) {
services = bf.getBeanNamesForType(UserDetailsService.class);
}
if (services.length == 0) {
throw new IllegalArgumentException("No UserDetailsService registered.");
} else if (services.length > 1) {
throw new IllegalArgumentException("More than one UserDetailsService registered. Please" +
"use a specific Id in your configuration");
}
return new RuntimeBeanReference(services[0]);
}
private static AuthenticationManager getAuthenticationManager(ConfigurableListableBeanFactory bf) {
Map authManagers = bf.getBeansOfType(AuthenticationManager.class);
if (authManagers.size() == 0) {
throw new IllegalArgumentException("No AuthenticationManager registered. " +
"Make sure you have configured at least one AuthenticationProvider?");
} else if (authManagers.size() > 1) {
throw new IllegalArgumentException("More than one AuthenticationManager registered.");
}
return (AuthenticationManager) authManagers.values().toArray()[0];
}
static ManagedList getRegisteredProviders(ParserContext parserContext) {
BeanDefinition authManager = registerProviderManagerIfNecessary(parserContext);
return (ManagedList) authManager.getPropertyValues().getPropertyValue("providers").getValue();
}
static ManagedList getRegisteredAfterInvocationProviders(ParserContext parserContext) {
BeanDefinition manager = registerAfterInvocationProviderManagerIfNecessary(parserContext);
return (ManagedList) manager.getPropertyValues().getPropertyValue("providers").getValue();
}
private static BeanDefinition registerAfterInvocationProviderManagerIfNecessary(ParserContext parserContext) {
if(parserContext.getRegistry().containsBeanDefinition(BeanIds.AFTER_INVOCATION_MANAGER)) {
return parserContext.getRegistry().getBeanDefinition(BeanIds.AFTER_INVOCATION_MANAGER);
}
BeanDefinition manager = new RootBeanDefinition(AfterInvocationProviderManager.class);
manager.getPropertyValues().addPropertyValue("providers", new ManagedList());
parserContext.getRegistry().registerBeanDefinition(BeanIds.AFTER_INVOCATION_MANAGER, manager);
return manager;
}
private static void registerFilterChainPostProcessorIfNecessary(ParserContext pc) {
if (pc.getRegistry().containsBeanDefinition(BeanIds.FILTER_CHAIN_POST_PROCESSOR)) {
return;
}
// Post processor specifically to assemble and order the filter chain immediately before the FilterChainProxy is initialized.
RootBeanDefinition filterChainPostProcessor = new RootBeanDefinition(FilterChainProxyPostProcessor.class);
filterChainPostProcessor.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
pc.getRegistry().registerBeanDefinition(BeanIds.FILTER_CHAIN_POST_PROCESSOR, filterChainPostProcessor);
RootBeanDefinition filterList = new RootBeanDefinition(FilterChainList.class);
filterList.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
pc.getRegistry().registerBeanDefinition(BeanIds.FILTER_LIST, filterList);
}
static void addHttpFilter(ParserContext pc, BeanMetadataElement filter) {
registerFilterChainPostProcessorIfNecessary(pc);
RootBeanDefinition filterList = (RootBeanDefinition) pc.getRegistry().getBeanDefinition(BeanIds.FILTER_LIST);
ManagedList filters;
MutablePropertyValues pvs = filterList.getPropertyValues();
if (pvs.contains("filters")) {
filters = (ManagedList) pvs.getPropertyValue("filters").getValue();
} else {
filters = new ManagedList();
pvs.addPropertyValue("filters", filters);
}
filters.add(filter);
}
/**
* Bean which holds the list of filters which are maintained in the context and modified by calls to
* addHttpFilter. The post processor retrieves these before injecting the list into the FilterChainProxy.
*/
public static class FilterChainList {
List filters;
public List getFilters() {
return filters;
}
public void setFilters(List filters) {
this.filters = filters;
}
}
/**
* Checks the value of an XML attribute which represents a redirect URL.
* If not empty or starting with "$" (potential placeholder), "/" or "http" it will raise an error.
*/
static void validateHttpRedirect(String url, ParserContext pc, Object source) {
if (UrlUtils.isValidRedirectUrl(url) || url.startsWith("$")) {
return;
}
pc.getReaderContext().warning(url + " is not a valid redirect URL (must start with '/' or http(s))", source);
}
static void setSessionControllerOnAuthenticationManager(ParserContext pc, String beanName, Element sourceElt) {
BeanDefinition authManager = registerProviderManagerIfNecessary(pc);
PropertyValue pv = authManager.getPropertyValues().getPropertyValue("sessionController");
if (pv != null && pv.getValue() != null) {
pc.getReaderContext().error("A session controller has already been set on the authentication manager. " +
"The <concurrent-session-control> element isn't compatible with a custom session controller",
pc.extractSource(sourceElt));
}
authManager.getPropertyValues().addPropertyValue("sessionController", new RuntimeBeanReference(beanName));
}
}
@@ -0,0 +1,24 @@
package org.springframework.security.config;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.xml.BeanDefinitionDecorator;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Node;
/**
* Adds the decorated {@link org.springframework.security.afterinvocation.AfterInvocationProvider} to the
* AfterInvocationProviderManager's list.
*
* @author Luke Taylor
* @version $Id$
* @since 2.0
*/
public class CustomAfterInvocationProviderBeanDefinitionDecorator implements BeanDefinitionDecorator {
public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder holder, ParserContext parserContext) {
ConfigUtils.getRegisteredAfterInvocationProviders(parserContext).add(holder.getBeanDefinition());
return holder;
}
}
@@ -3,6 +3,7 @@ package org.springframework.security.config;
import org.springframework.beans.factory.xml.BeanDefinitionDecorator;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.w3c.dom.Node;
@@ -16,7 +17,7 @@ import org.w3c.dom.Node;
*/
public class CustomAuthenticationProviderBeanDefinitionDecorator implements BeanDefinitionDecorator {
public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder holder, ParserContext parserContext) {
ConfigUtils.getRegisteredProviders(parserContext).add(holder.getBeanDefinition());
ConfigUtils.getRegisteredProviders(parserContext).add(new RuntimeBeanReference(holder.getBeanName()));
return holder;
}
@@ -13,6 +13,7 @@ abstract class Elements {
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";
@@ -35,6 +36,7 @@ abstract class Elements {
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 X509 = "x509";
public static final String FILTER_INVOCATION_DEFINITION_SOURCE = "filter-invocation-definition-source";
public static final String LDAP_PASSWORD_COMPARE = "password-compare";
@@ -0,0 +1,59 @@
package org.springframework.security.config;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.security.ui.AuthenticationEntryPoint;
import org.springframework.security.ui.ExceptionTranslationFilter;
import org.springframework.util.Assert;
/**
*
* @author Luke Taylor
* @since 2.0.2
*/
public class EntryPointInjectionBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
private final Log logger = LogFactory.getLog(getClass());
private ConfigurableListableBeanFactory beanFactory;
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (!BeanIds.EXCEPTION_TRANSLATION_FILTER.equals(beanName)) {
return bean;
}
logger.info("Selecting AuthenticationEntryPoint for use in ExceptionTranslationFilter");
ExceptionTranslationFilter etf = (ExceptionTranslationFilter) beanFactory.getBean(BeanIds.EXCEPTION_TRANSLATION_FILTER);
Object entryPoint = null;
if (beanFactory.containsBean(BeanIds.MAIN_ENTRY_POINT)) {
entryPoint = beanFactory.getBean(BeanIds.MAIN_ENTRY_POINT);
logger.info("Using main configured AuthenticationEntryPoint.");
} else {
Map entryPoints = beanFactory.getBeansOfType(AuthenticationEntryPoint.class);
Assert.isTrue(entryPoints.size() != 0, "No AuthenticationEntryPoint instances defined");
Assert.isTrue(entryPoints.size() == 1, "More than one AuthenticationEntryPoint defined in context");
entryPoint = entryPoints.values().toArray()[0];
}
logger.info("Using bean '" + entryPoint + "' as the entry point.");
etf.setAuthenticationEntryPoint((AuthenticationEntryPoint) entryPoint);
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
}
}
@@ -2,7 +2,6 @@ package org.springframework.security.config;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -17,8 +16,21 @@ import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.OrderComparator;
import org.springframework.core.Ordered;
import org.springframework.security.ConfigAttributeDefinition;
import org.springframework.security.config.ConfigUtils.FilterChainList;
import org.springframework.security.context.HttpSessionContextIntegrationFilter;
import org.springframework.security.intercept.web.DefaultFilterInvocationDefinitionSource;
import org.springframework.security.intercept.web.FilterSecurityInterceptor;
import org.springframework.security.providers.anonymous.AnonymousAuthenticationToken;
import org.springframework.security.providers.anonymous.AnonymousProcessingFilter;
import org.springframework.security.ui.ExceptionTranslationFilter;
import org.springframework.security.ui.SessionFixationProtectionFilter;
import org.springframework.security.ui.basicauth.BasicProcessingFilter;
import org.springframework.security.ui.webapp.AuthenticationProcessingFilter;
import org.springframework.security.ui.webapp.AuthenticationProcessingFilterEntryPoint;
import org.springframework.security.ui.webapp.DefaultLoginPageGeneratingFilter;
import org.springframework.security.util.FilterChainProxy;
import org.springframework.util.Assert;
import org.springframework.security.wrapper.SecurityContextHolderAwareRequestFilter;
/**
*
@@ -29,70 +41,161 @@ import org.springframework.util.Assert;
public class FilterChainProxyPostProcessor implements BeanPostProcessor, BeanFactoryAware {
private Log logger = LogFactory.getLog(getClass());
private ListableBeanFactory beanFactory;
private ListableBeanFactory beanFactory;
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if(!beanName.equals(BeanIds.FILTER_CHAIN_PROXY)) {
if(!BeanIds.FILTER_CHAIN_PROXY.equals(beanName)) {
return bean;
}
FilterChainProxy filterChainProxy = (FilterChainProxy) bean;
// Set the default match
List defaultFilterChain = orderFilters(beanFactory);
FilterChainList filterList = (FilterChainList) beanFactory.getBean(BeanIds.FILTER_LIST);
List filters = new ArrayList(filterList.getFilters());
Collections.sort(filters, new OrderComparator());
logger.info("Checking sorted filter chain: " + filters);
for(int i=0; i < filters.size(); i++) {
Ordered filter = (Ordered)filters.get(i);
if (i > 0) {
Ordered previous = (Ordered)filters.get(i-1);
if (filter.getOrder() == previous.getOrder()) {
throw new SecurityConfigurationException("Filters '" + unwrapFilter(filter) + "' and '" +
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 avoiding the use of <http auto-config='true'>.");
}
}
}
logger.info("Filter chain...");
for (int i=0; i < filters.size(); i++) {
// Remove the ordered wrapper from the filter and put it back in the chain at the same position.
Filter filter = unwrapFilter(filters.get(i));
logger.info("[" + i + "] - " + filter);
filters.set(i, filter);
}
checkFilterStack(filters);
// Note that this returns a copy
Map filterMap = filterChainProxy.getFilterChainMap();
String allUrlsMatch = filterChainProxy.getMatcher().getUniversalMatchPattern();
filterMap.put(allUrlsMatch, defaultFilterChain);
filterMap.put(filterChainProxy.getMatcher().getUniversalMatchPattern(), filters);
filterChainProxy.setFilterChainMap(filterMap);
logger.info("Configured filter chain(s): " + filterChainProxy);
checkLoginPageIsntProtected(filterChainProxy);
logger.info("FilterChainProxy: " + filterChainProxy);
return bean;
}
private List orderFilters(ListableBeanFactory beanFactory) {
Map filters = beanFactory.getBeansOfType(Filter.class);
Assert.notEmpty(filters, "No filters found in app context!");
Iterator ids = filters.keySet().iterator();
List orderedFilters = new ArrayList();
while (ids.hasNext()) {
String id = (String) ids.next();
Filter filter = (Filter) filters.get(id);
if (filter instanceof FilterChainProxy) {
continue;
/**
* Checks the filter list for possible errors and logs them
*/
private void checkFilterStack(List filters) {
checkForDuplicates(HttpSessionContextIntegrationFilter.class, filters);
checkForDuplicates(AuthenticationProcessingFilter.class, filters);
checkForDuplicates(SessionFixationProtectionFilter.class, filters);
checkForDuplicates(BasicProcessingFilter.class, filters);
checkForDuplicates(SecurityContextHolderAwareRequestFilter.class, filters);
checkForDuplicates(ExceptionTranslationFilter.class, filters);
checkForDuplicates(FilterSecurityInterceptor.class, filters);
}
private void checkForDuplicates(Class clazz, List filters) {
for (int i=0; i < filters.size(); i++) {
Filter f1 = (Filter)filters.get(i);
if (clazz.isAssignableFrom(f1.getClass())) {
// Found the first one, check remaining for another
for (int j=i+1; j < filters.size(); j++) {
Filter f2 = (Filter)filters.get(j);
if (clazz.isAssignableFrom(f2.getClass())) {
logger.warn("Possible error: Filters at position " + i + " and " + j + " are both " +
"instances of " + clazz.getName());
return;
}
}
}
// Filters must be Spring security filters or wrapped using <custom-filter>
if (!filter.getClass().getName().startsWith("org.springframework.security")) {
continue;
}
if (!(filter instanceof Ordered)) {
logger.info("Filter " + id + " doesn't implement the Ordered interface, skipping it.");
continue;
}
orderedFilters.add(filter);
}
}
/* Checks for the common error of having a login page URL protected by the security interceptor */
private void checkLoginPageIsntProtected(FilterChainProxy fcp) {
ExceptionTranslationFilter etf = (ExceptionTranslationFilter) beanFactory.getBean(BeanIds.EXCEPTION_TRANSLATION_FILTER);
if (etf.getAuthenticationEntryPoint() instanceof AuthenticationProcessingFilterEntryPoint) {
String loginPage =
((AuthenticationProcessingFilterEntryPoint)etf.getAuthenticationEntryPoint()).getLoginFormUrl();
List filters = fcp.getFilters(loginPage);
logger.info("Checking whether login URL '" + loginPage + "' is accessible with your configuration");
if (filters == null || filters.isEmpty()) {
logger.debug("Filter chain is empty for the login page");
return;
}
if (loginPage.equals(DefaultLoginPageGeneratingFilter.DEFAULT_LOGIN_PAGE_URL) &&
beanFactory.containsBean(BeanIds.DEFAULT_LOGIN_PAGE_GENERATING_FILTER)) {
logger.debug("Default generated login page is in use");
return;
}
FilterSecurityInterceptor fsi =
((FilterSecurityInterceptor)beanFactory.getBean(BeanIds.FILTER_SECURITY_INTERCEPTOR));
DefaultFilterInvocationDefinitionSource fids =
(DefaultFilterInvocationDefinitionSource) fsi.getObjectDefinitionSource();
ConfigAttributeDefinition cad = fids.lookupAttributes(loginPage, "POST");
if (cad == null) {
logger.debug("No access attributes defined for login page URL");
if (fsi.isRejectPublicInvocations()) {
logger.warn("FilterSecurityInterceptor is configured to reject public invocations." +
" Your login page may not be accessible.");
}
return;
}
Collections.sort(orderedFilters, new OrderComparator());
return orderedFilters;
}
if (!beanFactory.containsBean(BeanIds.ANONYMOUS_PROCESSING_FILTER)) {
logger.warn("The login page is being protected by the filter chain, but you don't appear to have" +
" anonymous authentication enabled. This is almost certainly an error.");
return;
}
// Simulate an anonymous access with the supplied attributes.
AnonymousProcessingFilter anonPF = (AnonymousProcessingFilter) beanFactory.getBean(BeanIds.ANONYMOUS_PROCESSING_FILTER);
AnonymousAuthenticationToken token =
new AnonymousAuthenticationToken("key", anonPF.getUserAttribute().getPassword(),
anonPF.getUserAttribute().getAuthorities());
try {
fsi.getAccessDecisionManager().decide(token, new Object(), cad);
} catch (Exception e) {
logger.warn("Anonymous access to the login page doesn't appear to be enabled. This is almost certainly " +
"an error. Please check your configuration allows unauthenticated access to the configured " +
"login page. (Simulated access was rejected: " + e + ")");
}
}
}
/**
* Returns the delegate filter of a wrapper, or the unchanged filter if it isn't wrapped.
*/
private Filter unwrapFilter(Object filter) {
if (filter instanceof OrderedFilterBeanDefinitionDecorator.OrderedFilterDecorator) {
return ((OrderedFilterBeanDefinitionDecorator.OrderedFilterDecorator)filter).getDelegate();
}
return (Filter) filter;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = (ListableBeanFactory) beanFactory;
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = (ListableBeanFactory) beanFactory;
}
}
@@ -44,8 +44,8 @@ public class FilterInvocationDefinitionSourceBeanDefinitionParser extends Abstra
UrlMatcher matcher = HttpSecurityBeanDefinitionParser.createUrlMatcher(element);
boolean convertPathsToLowerCase = (matcher instanceof AntUrlPathMatcher) && matcher.requiresLowerCaseUrl();
LinkedHashMap requestMap = new LinkedHashMap();
HttpSecurityBeanDefinitionParser.parseInterceptUrlsForFilterInvocationRequestMap(interceptUrls, requestMap,
LinkedHashMap requestMap =
HttpSecurityBeanDefinitionParser.parseInterceptUrlsForFilterInvocationRequestMap(interceptUrls,
convertPathsToLowerCase, parserContext);
builder.addConstructorArg(matcher);
@@ -1,5 +1,6 @@
package org.springframework.security.config;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
@@ -46,7 +47,7 @@ public class FormLoginBeanDefinitionParser implements BeanDefinitionParser {
this.filterClassName = filterClassName;
}
public BeanDefinition parse(Element elt, ParserContext parserContext) {
public BeanDefinition parse(Element elt, ParserContext pc) {
String loginUrl = null;
String defaultTargetUrl = null;
String authenticationFailureUrl = null;
@@ -54,30 +55,57 @@ public class FormLoginBeanDefinitionParser implements BeanDefinitionParser {
Object source = null;
// Copy values from the session fixation protection filter
final Boolean sessionFixationProtectionEnabled =
new Boolean(pc.getRegistry().containsBeanDefinition(BeanIds.SESSION_FIXATION_PROTECTION_FILTER));
Boolean migrateSessionAttributes = Boolean.FALSE;
if (sessionFixationProtectionEnabled.booleanValue()) {
PropertyValue pv =
pc.getRegistry().getBeanDefinition(BeanIds.SESSION_FIXATION_PROTECTION_FILTER)
.getPropertyValues().getPropertyValue("migrateSessionAttributes");
migrateSessionAttributes = (Boolean)pv.getValue();
}
if (elt != null) {
source = pc.extractSource(elt);
loginUrl = elt.getAttribute(ATT_LOGIN_URL);
ConfigUtils.validateHttpRedirect(loginUrl, pc, source);
defaultTargetUrl = elt.getAttribute(ATT_FORM_LOGIN_TARGET_URL);
ConfigUtils.validateHttpRedirect(defaultTargetUrl, pc, source);
authenticationFailureUrl = elt.getAttribute(ATT_FORM_LOGIN_AUTHENTICATION_FAILURE_URL);
ConfigUtils.validateHttpRedirect(authenticationFailureUrl, pc, source);
alwaysUseDefault = elt.getAttribute(ATT_ALWAYS_USE_DEFAULT_TARGET_URL);
loginPage = elt.getAttribute(ATT_LOGIN_PAGE);
if (!StringUtils.hasText(loginPage)) {
loginPage = null;
}
source = parserContext.extractSource(elt);
ConfigUtils.validateHttpRedirect(loginPage, pc, source);
}
ConfigUtils.registerProviderManagerIfNecessary(parserContext);
ConfigUtils.registerProviderManagerIfNecessary(pc);
filterBean = createFilterBean(loginUrl, defaultTargetUrl, alwaysUseDefault, loginPage, authenticationFailureUrl);
filterBean.setSource(source);
filterBean.getPropertyValues().addPropertyValue("authenticationManager",
new RuntimeBeanReference(BeanIds.AUTHENTICATION_MANAGER));
if (parserContext.getRegistry().containsBeanDefinition(BeanIds.REMEMBER_ME_SERVICES)) {
filterBean.getPropertyValues().addPropertyValue("invalidateSessionOnSuccessfulAuthentication",
sessionFixationProtectionEnabled);
filterBean.getPropertyValues().addPropertyValue("migrateInvalidatedSessionAttributes",
migrateSessionAttributes);
if (pc.getRegistry().containsBeanDefinition(BeanIds.REMEMBER_ME_SERVICES)) {
filterBean.getPropertyValues().addPropertyValue("rememberMeServices",
new RuntimeBeanReference(BeanIds.REMEMBER_ME_SERVICES) );
}
if (pc.getRegistry().containsBeanDefinition(BeanIds.SESSION_REGISTRY)) {
filterBean.getPropertyValues().addPropertyValue("sessionRegistry",
new RuntimeBeanReference(BeanIds.SESSION_REGISTRY));
}
BeanDefinitionBuilder entryPointBuilder =
BeanDefinitionBuilder.rootBeanDefinition(AuthenticationProcessingFilterEntryPoint.class);
@@ -20,7 +20,6 @@ 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.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
@@ -48,67 +47,26 @@ class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionParser {
parserContext.getReaderContext().error("Cannot locate '" + className + "'", element);
}
}
public BeanDefinition parse(Element element, ParserContext parserContext) {
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, parserContext);
validatePresent(SECURED_DEPENDENCY_CLASS, element, parserContext);
}
if (useJsr250) {
validatePresent(JSR_250_SECURITY_METHOD_DEFINITION_SOURCE_CLASS, element, parserContext);
validatePresent(JSR_250_VOTER_CLASS, element, parserContext);
}
// Now create a Map<String, ConfigAttribute> for each <protect-pointcut> sub-element
Map pointcutMap = new LinkedHashMap();
List protect = DomUtils.getChildElementsByTagName(element, Elements.PROTECT_POINTCUT);
for (Iterator i = protect.iterator(); i.hasNext();) {
Element childElt = (Element) i.next();
String accessConfig = childElt.getAttribute(ATT_ACCESS);
String expression = childElt.getAttribute(ATT_EXPRESSION);
Assert.hasText(accessConfig, "Access configuration required for '" + childElt + "'");
Assert.hasText(expression, "Expression required for '" + childElt + "'");
ConfigAttributeDefinition def = new ConfigAttributeDefinition(StringUtils.commaDelimitedListToStringArray(accessConfig));
pointcutMap.put(expression, def);
}
MapBasedMethodDefinitionSource mapBasedMethodDefinitionSource = new MapBasedMethodDefinitionSource();
// Now create and populate our ProtectPointcutBeanPostProcessor, if needed
if (pointcutMap.size() > 0) {
RootBeanDefinition ppbp = new RootBeanDefinition(ProtectPointcutPostProcessor.class);
ppbp.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
ppbp.setSource(parserContext.extractSource(element));
ppbp.getConstructorArgumentValues().addGenericArgumentValue(mapBasedMethodDefinitionSource);
ppbp.getPropertyValues().addPropertyValue("pointcutMap", pointcutMap);
parserContext.getRegistry().registerBeanDefinition(BeanIds.PROTECT_POINTCUT_POST_PROCESSOR, ppbp);
}
// Create our list of method metadata delegates
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);
if (useSecured) {
delegates.add(BeanDefinitionBuilder.rootBeanDefinition(SECURED_METHOD_DEFINITION_SOURCE_CLASS).getBeanDefinition());
// 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);
}
if (useJsr250) {
delegates.add(BeanDefinitionBuilder.rootBeanDefinition(JSR_250_SECURITY_METHOD_DEFINITION_SOURCE_CLASS).getBeanDefinition());
}
// Register our DelegatingMethodDefinitionSource
RootBeanDefinition delegatingMethodDefinitionSource = new RootBeanDefinition(DelegatingMethodDefinitionSource.class);
delegatingMethodDefinitionSource.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
delegatingMethodDefinitionSource.setSource(parserContext.extractSource(element));
delegatingMethodDefinitionSource.getPropertyValues().addPropertyValue("methodDefinitionSources", delegates);
parserContext.getRegistry().registerBeanDefinition(BeanIds.DELEGATING_METHOD_DEFINITION_SOURCE, delegatingMethodDefinitionSource);
registerDelegatingMethodDefinitionSource(parserContext, delegates, source);
// Register the applicable AccessDecisionManager, handling the special JSR 250 voter if being used
String accessManagerId = element.getAttribute(ATT_ACCESS_MGR);
@@ -116,17 +74,94 @@ class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionParser {
if (!StringUtils.hasText(accessManagerId)) {
ConfigUtils.registerDefaultAccessManagerIfNecessary(parserContext);
if (useJsr250) {
if (jsr250Enabled) {
ConfigUtils.addVoter(new RootBeanDefinition(JSR_250_VOTER_CLASS, null, null), parserContext);
}
accessManagerId = BeanIds.ACCESS_MANAGER;
}
registerMethodSecurityInterceptor(parserContext, accessManagerId, source);
registerAdvisor(parserContext, source);
// MethodSecurityInterceptor
AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);
return null;
}
/**
* 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());
}
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);
}
RootBeanDefinition delegatingMethodDefinitionSource = new RootBeanDefinition(DelegatingMethodDefinitionSource.class);
delegatingMethodDefinitionSource.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
delegatingMethodDefinitionSource.setSource(source);
delegatingMethodDefinitionSource.getPropertyValues().addPropertyValue("methodDefinitionSources", delegates);
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);
ppbp.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
ppbp.setSource(source);
ppbp.getConstructorArgumentValues().addGenericArgumentValue(mapBasedMethodDefinitionSource);
ppbp.getPropertyValues().addPropertyValue("pointcutMap", pointcutMap);
parserContext.getRegistry().registerBeanDefinition(BeanIds.PROTECT_POINTCUT_POST_PROCESSOR, ppbp);
}
private Map parseProtectPointcuts(ParserContext parserContext, List protectPointcutElts) {
Map pointcutMap = new LinkedHashMap();
for (Iterator i = protectPointcutElts.iterator(); i.hasNext();) {
Element childElt = (Element) i.next();
String accessConfig = childElt.getAttribute(ATT_ACCESS);
String expression = childElt.getAttribute(ATT_EXPRESSION);
if (!StringUtils.hasText(accessConfig)) {
parserContext.getReaderContext().error("Access configuration required", parserContext.extractSource(childElt));
}
if (!StringUtils.hasText(expression)) {
parserContext.getReaderContext().error("Pointcut expression required", parserContext.extractSource(childElt));
}
ConfigAttributeDefinition def = new ConfigAttributeDefinition(StringUtils.commaDelimitedListToStringArray(accessConfig));
pointcutMap.put(expression, def);
}
return pointcutMap;
}
private void registerMethodSecurityInterceptor(ParserContext parserContext, String accessManagerId, Object source) {
RootBeanDefinition interceptor = new RootBeanDefinition(MethodSecurityInterceptor.class);
interceptor.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
interceptor.setSource(parserContext.extractSource(element));
interceptor.setSource(source);
interceptor.getPropertyValues().addPropertyValue("accessDecisionManager", new RuntimeBeanReference(accessManagerId));
interceptor.getPropertyValues().addPropertyValue("authenticationManager", new RuntimeBeanReference(BeanIds.AUTHENTICATION_MANAGER));
@@ -134,15 +169,17 @@ class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionParser {
parserContext.getRegistry().registerBeanDefinition(BeanIds.METHOD_SECURITY_INTERCEPTOR, interceptor);
parserContext.registerComponent(new BeanComponentDefinition(interceptor, BeanIds.METHOD_SECURITY_INTERCEPTOR));
// MethodDefinitionSourceAdvisor
parserContext.getRegistry().registerBeanDefinition(BeanIds.METHOD_SECURITY_INTERCEPTOR_POST_PROCESSOR,
new RootBeanDefinition(MethodSecurityInterceptorPostProcessor.class));
}
private void registerAdvisor(ParserContext parserContext, Object source) {
RootBeanDefinition advisor = new RootBeanDefinition(MethodDefinitionSourceAdvisor.class);
advisor.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
advisor.setSource(parserContext.extractSource(element));
advisor.getConstructorArgumentValues().addGenericArgumentValue(interceptor);
parserContext.getRegistry().registerBeanDefinition(BeanIds.METHOD_DEFINITION_SOURCE_ADVISOR, advisor);
advisor.setSource(source);
advisor.getConstructorArgumentValues().addGenericArgumentValue(BeanIds.METHOD_SECURITY_INTERCEPTOR);
advisor.getConstructorArgumentValues().addGenericArgumentValue(new RuntimeBeanReference(BeanIds.DELEGATING_METHOD_DEFINITION_SOURCE));
AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);
return null;
}
parserContext.getRegistry().registerBeanDefinition(BeanIds.METHOD_DEFINITION_SOURCE_ADVISOR, advisor);
}
}
@@ -97,154 +97,57 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
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";
public BeanDefinition parse(Element element, ParserContext parserContext) {
BeanDefinitionRegistry registry = parserContext.getRegistry();
RootBeanDefinition filterChainProxy = new RootBeanDefinition(FilterChainProxy.class);
RootBeanDefinition httpScif = new RootBeanDefinition(HttpSessionContextIntegrationFilter.class);
BeanDefinition portMapper = new PortMappingsBeanDefinitionParser().parse(
DomUtils.getChildElementByTagName(element, Elements.PORT_MAPPINGS), parserContext);
registry.registerBeanDefinition(BeanIds.PORT_MAPPER, portMapper);
RuntimeBeanReference portMapperRef = new RuntimeBeanReference(BeanIds.PORT_MAPPER);
String createSession = element.getAttribute(ATT_CREATE_SESSION);
if (OPT_CREATE_SESSION_ALWAYS.equals(createSession)) {
httpScif.getPropertyValues().addPropertyValue("allowSessionCreation", Boolean.TRUE);
httpScif.getPropertyValues().addPropertyValue("forceEagerSessionCreation", Boolean.TRUE);
} else if (OPT_CREATE_SESSION_NEVER.equals(createSession)) {
httpScif.getPropertyValues().addPropertyValue("allowSessionCreation", Boolean.FALSE);
httpScif.getPropertyValues().addPropertyValue("forceEagerSessionCreation", Boolean.FALSE);
} else {
createSession = DEF_CREATE_SESSION_IF_REQUIRED;
httpScif.getPropertyValues().addPropertyValue("allowSessionCreation", Boolean.TRUE);
httpScif.getPropertyValues().addPropertyValue("forceEagerSessionCreation", Boolean.FALSE);
}
BeanDefinitionBuilder filterSecurityInterceptorBuilder
= BeanDefinitionBuilder.rootBeanDefinition(FilterSecurityInterceptor.class);
BeanDefinitionBuilder exceptionTranslationFilterBuilder
= BeanDefinitionBuilder.rootBeanDefinition(ExceptionTranslationFilter.class);
ConfigUtils.registerProviderManagerIfNecessary(parserContext);
final BeanDefinitionRegistry registry = parserContext.getRegistry();
final UrlMatcher matcher = createUrlMatcher(element);
final Object source = parserContext.extractSource(element);
// 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();
String accessDeniedPage = element.getAttribute(ATT_ACCESS_DENIED_PAGE);
if (StringUtils.hasText(accessDeniedPage)) {
AccessDeniedHandlerImpl accessDeniedHandler = new AccessDeniedHandlerImpl();
accessDeniedHandler.setErrorPage(accessDeniedPage);
exceptionTranslationFilterBuilder.addPropertyValue("accessDeniedHandler", accessDeniedHandler);
}
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,
convertPathsToLowerCase, parserContext);
Map filterChainMap = new LinkedHashMap();
boolean allowSessionCreation = registerHttpSessionIntegrationFilter(element, parserContext);
UrlMatcher matcher = createUrlMatcher(element);
filterChainProxy.getPropertyValues().addPropertyValue("matcher", matcher);
// Add servlet-api integration filter if required
String provideServletApi = element.getAttribute(ATT_SERVLET_API_PROVISION);
if (!StringUtils.hasText(provideServletApi)) {
provideServletApi = DEF_SERVLET_API_PROVISION;
}
if ("true".equals(provideServletApi)) {
parserContext.getRegistry().registerBeanDefinition(BeanIds.SECURITY_CONTEXT_HOLDER_AWARE_REQUEST_FILTER,
new RootBeanDefinition(SecurityContextHolderAwareRequestFilter.class));
}
filterChainProxy.getPropertyValues().addPropertyValue("filterChainMap", filterChainMap);
// Set up the access manager and authentication manager references for http
registerServletApiFilter(element, parserContext);
// Set up the access manager reference for http
String accessManagerId = element.getAttribute(ATT_ACCESS_MGR);
if (!StringUtils.hasText(accessManagerId)) {
ConfigUtils.registerDefaultAccessManagerIfNecessary(parserContext);
accessManagerId = BeanIds.ACCESS_MANAGER;
}
filterSecurityInterceptorBuilder.addPropertyValue("accessDecisionManager",
new RuntimeBeanReference(accessManagerId));
filterSecurityInterceptorBuilder.addPropertyValue("authenticationManager",
ConfigUtils.registerProviderManagerIfNecessary(parserContext));
if ("true".equals(element.getAttribute(ATT_ONCE_PER_REQUEST))) {
filterSecurityInterceptorBuilder.addPropertyValue("observeOncePerRequest", Boolean.TRUE);
}
// 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);
// SEC-501 - should paths stored in request maps be converted to lower case
// true if Ant path and using lower case
boolean convertPathsToLowerCase = (matcher instanceof AntUrlPathMatcher) && matcher.requiresLowerCaseUrl();
LinkedHashMap channelRequestMap = new LinkedHashMap();
LinkedHashMap filterInvocationDefinitionMap = new LinkedHashMap();
List interceptUrlElts = DomUtils.getChildElementsByTagName(element, "intercept-url");
parseInterceptUrlsForChannelSecurityAndFilterChain(interceptUrlElts, filterChainMap, channelRequestMap,
convertPathsToLowerCase, parserContext);
parseInterceptUrlsForFilterInvocationRequestMap(interceptUrlElts, filterInvocationDefinitionMap,
convertPathsToLowerCase, parserContext);
registerExceptionTranslationFilter(element, parserContext, allowSessionCreation);
DefaultFilterInvocationDefinitionSource interceptorFilterInvDefSource =
new DefaultFilterInvocationDefinitionSource(matcher, filterInvocationDefinitionMap);
DefaultFilterInvocationDefinitionSource channelFilterInvDefSource =
new DefaultFilterInvocationDefinitionSource(matcher, channelRequestMap);
filterSecurityInterceptorBuilder.addPropertyValue("objectDefinitionSource", interceptorFilterInvDefSource);
// Check if we need to register the channel processing beans
if (channelRequestMap.size() > 0) {
// At least one channel requirement has been specified
RootBeanDefinition channelFilter = new RootBeanDefinition(ChannelProcessingFilter.class);
channelFilter.getPropertyValues().addPropertyValue("channelDecisionManager",
new RuntimeBeanReference(BeanIds.CHANNEL_DECISION_MANAGER));
channelFilter.getPropertyValues().addPropertyValue("filterInvocationDefinitionSource",
channelFilterInvDefSource);
RootBeanDefinition channelDecisionManager = new RootBeanDefinition(ChannelDecisionManagerImpl.class);
ManagedList channelProcessors = new ManagedList(3);
RootBeanDefinition secureChannelProcessor = new RootBeanDefinition(SecureChannelProcessor.class);
RootBeanDefinition retryWithHttp = new RootBeanDefinition(RetryWithHttpEntryPoint.class);
RootBeanDefinition retryWithHttps = new RootBeanDefinition(RetryWithHttpsEntryPoint.class);
retryWithHttp.getPropertyValues().addPropertyValue("portMapper", portMapperRef);
retryWithHttps.getPropertyValues().addPropertyValue("portMapper", portMapperRef);
secureChannelProcessor.getPropertyValues().addPropertyValue("entryPoint", retryWithHttps);
RootBeanDefinition inSecureChannelProcessor = new RootBeanDefinition(InsecureChannelProcessor.class);
inSecureChannelProcessor.getPropertyValues().addPropertyValue("entryPoint", retryWithHttp);
channelProcessors.add(secureChannelProcessor);
channelProcessors.add(inSecureChannelProcessor);
channelDecisionManager.getPropertyValues().addPropertyValue("channelProcessors", channelProcessors);
registry.registerBeanDefinition(BeanIds.CHANNEL_PROCESSING_FILTER, channelFilter);
registry.registerBeanDefinition(BeanIds.CHANNEL_DECISION_MANAGER, channelDecisionManager);
}
Element sessionControlElt = DomUtils.getChildElementByTagName(element, Elements.CONCURRENT_SESSIONS);
if (sessionControlElt != null) {
new ConcurrentSessionsBeanDefinitionParser().parse(sessionControlElt, parserContext);
logger.info("Concurrent session filter in use, setting 'forceEagerSessionCreation' to true");
httpScif.getPropertyValues().addPropertyValue("forceEagerSessionCreation", Boolean.TRUE);
}
String sessionFixationAttribute = element.getAttribute(ATT_SESSION_FIXATION_PROTECTION);
if(!StringUtils.hasText(sessionFixationAttribute)) {
sessionFixationAttribute = OPT_SESSION_FIXATION_MIGRATE_SESSION;
}
if (!sessionFixationAttribute.equals(OPT_SESSION_FIXATION_NO_PROTECTION)) {
BeanDefinitionBuilder sessionFixationFilter =
BeanDefinitionBuilder.rootBeanDefinition(SessionFixationProtectionFilter.class);
sessionFixationFilter.addPropertyValue("migrateSessionAttributes",
Boolean.valueOf(sessionFixationAttribute.equals(OPT_SESSION_FIXATION_MIGRATE_SESSION)));
if (sessionControlElt != null) {
sessionFixationFilter.addPropertyReference("sessionRegistry", BeanIds.SESSION_REGISTRY);
}
parserContext.getRegistry().registerBeanDefinition(BeanIds.SESSION_FIXATION_PROTECTION_FILTER,
sessionFixationFilter.getBeanDefinition());
registerChannelProcessingBeans(parserContext, matcher, channelRequestMap);
}
registerFilterSecurityInterceptor(element, parserContext, matcher, accessManagerId,
parseInterceptUrlsForFilterInvocationRequestMap(interceptUrlElts, convertPathsToLowerCase, parserContext));
boolean sessionControlEnabled = registerConcurrentSessionControlBeansIfRequired(element, parserContext);
registerSessionFixationProtectionFilter(parserContext, element.getAttribute(ATT_SESSION_FIXATION_PROTECTION),
sessionControlEnabled);
boolean autoConfig = false;
if ("true".equals(element.getAttribute(ATT_AUTO_CONFIG))) {
@@ -271,33 +174,176 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
new LogoutBeanDefinitionParser().parse(logoutElt, parserContext);
}
parseBasicFormLoginAndOpenID(element, parserContext, autoConfig);
parseBasicFormLoginAndOpenID(element, parserContext, autoConfig, allowSessionCreation);
Element x509Elt = DomUtils.getChildElementByTagName(element, Elements.X509);
if (x509Elt != null) {
new X509BeanDefinitionParser().parse(x509Elt, parserContext);
}
registry.registerBeanDefinition(BeanIds.FILTER_CHAIN_PROXY, filterChainProxy);
registry.registerAlias(BeanIds.FILTER_CHAIN_PROXY, BeanIds.SPRING_SECURITY_FILTER_CHAIN);
registry.registerBeanDefinition(BeanIds.HTTP_SESSION_CONTEXT_INTEGRATION_FILTER, httpScif);
registry.registerBeanDefinition(BeanIds.EXCEPTION_TRANSLATION_FILTER, exceptionTranslationFilterBuilder.getBeanDefinition());
registry.registerBeanDefinition(BeanIds.FILTER_SECURITY_INTERCEPTOR, filterSecurityInterceptorBuilder.getBeanDefinition());
// Register the post processor which will tie up the loose ends in the configuration once the app context has been created and all beans are available.
RootBeanDefinition postProcessor = new RootBeanDefinition(HttpSecurityConfigPostProcessor.class);
// Register the post processors which will tie up the loose ends in the configuration once the app context has been created and all beans are available.
RootBeanDefinition postProcessor = new RootBeanDefinition(EntryPointInjectionBeanPostProcessor.class);
postProcessor.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(BeanIds.HTTP_POST_PROCESSOR, postProcessor);
// Post processor specifically to assemble and order the filter chain immediately before the FilterChainProxy is initialized.
RootBeanDefinition filterChainPostProcessor = new RootBeanDefinition(FilterChainProxyPostProcessor.class);
filterChainPostProcessor.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(BeanIds.FILTER_CHAIN_POST_PROCESSOR, filterChainPostProcessor);
registry.registerBeanDefinition(BeanIds.ENTRY_POINT_INJECTION_POST_PROCESSOR, postProcessor);
RootBeanDefinition postProcessor2 = new RootBeanDefinition(UserDetailsServiceInjectionBeanPostProcessor.class);
postProcessor2.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(BeanIds.USER_DETAILS_SERVICE_INJECTION_POST_PROCESSOR, postProcessor2);
return null;
}
private void parseBasicFormLoginAndOpenID(Element element, ParserContext parserContext, boolean autoConfig) {
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("filterChainMap", filterChainMap);
pc.getRegistry().registerBeanDefinition(BeanIds.FILTER_CHAIN_PROXY, filterChainProxy);
pc.getRegistry().registerAlias(BeanIds.FILTER_CHAIN_PROXY, BeanIds.SPRING_SECURITY_FILTER_CHAIN);
}
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);
httpScif.getPropertyValues().addPropertyValue("forceEagerSessionCreation", Boolean.TRUE);
} 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);
httpScif.getPropertyValues().addPropertyValue("forceEagerSessionCreation", Boolean.FALSE);
}
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
private void registerServletApiFilter(Element element, ParserContext pc) {
String provideServletApi = element.getAttribute(ATT_SERVLET_API_PROVISION);
if (!StringUtils.hasText(provideServletApi)) {
provideServletApi = DEF_SERVLET_API_PROVISION;
}
if ("true".equals(provideServletApi)) {
pc.getRegistry().registerBeanDefinition(BeanIds.SECURITY_CONTEXT_HOLDER_AWARE_REQUEST_FILTER,
new RootBeanDefinition(SecurityContextHolderAwareRequestFilter.class));
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);
sessionIntegrationFilter.getPropertyValues().addPropertyValue("forceEagerSessionCreation", Boolean.TRUE);
return true;
}
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);
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 =
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",
new RuntimeBeanReference(BeanIds.CHANNEL_DECISION_MANAGER));
DefaultFilterInvocationDefinitionSource channelFilterInvDefSource =
new DefaultFilterInvocationDefinitionSource(matcher, channelRequestMap);
channelFilterInvDefSource.setStripQueryStringFromUrls(matcher instanceof AntUrlPathMatcher);
channelFilter.getPropertyValues().addPropertyValue("filterInvocationDefinitionSource",
channelFilterInvDefSource);
RootBeanDefinition channelDecisionManager = new RootBeanDefinition(ChannelDecisionManagerImpl.class);
ManagedList channelProcessors = new ManagedList(3);
RootBeanDefinition secureChannelProcessor = new RootBeanDefinition(SecureChannelProcessor.class);
RootBeanDefinition retryWithHttp = new RootBeanDefinition(RetryWithHttpEntryPoint.class);
RootBeanDefinition retryWithHttps = new RootBeanDefinition(RetryWithHttpsEntryPoint.class);
RuntimeBeanReference portMapper = new RuntimeBeanReference(BeanIds.PORT_MAPPER);
retryWithHttp.getPropertyValues().addPropertyValue("portMapper", portMapper);
retryWithHttps.getPropertyValues().addPropertyValue("portMapper", portMapper);
secureChannelProcessor.getPropertyValues().addPropertyValue("entryPoint", retryWithHttps);
RootBeanDefinition inSecureChannelProcessor = new RootBeanDefinition(InsecureChannelProcessor.class);
inSecureChannelProcessor.getPropertyValues().addPropertyValue("entryPoint", retryWithHttp);
channelProcessors.add(secureChannelProcessor);
channelProcessors.add(inSecureChannelProcessor);
channelDecisionManager.getPropertyValues().addPropertyValue("channelProcessors", channelProcessors);
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.rootBeanDefinition(SessionFixationProtectionFilter.class);
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,
sessionFixationFilter.getBeanDefinition());
ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.SESSION_FIXATION_PROTECTION_FILTER));
}
}
private void parseBasicFormLoginAndOpenID(Element element, ParserContext pc, boolean autoConfig, boolean allowSessionCreation) {
RootBeanDefinition formLoginFilter = null;
RootBeanDefinition formLoginEntryPoint = null;
String formLoginPage = null;
@@ -308,12 +354,12 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
String realm = element.getAttribute(ATT_REALM);
if (!StringUtils.hasText(realm)) {
realm = DEF_REALM;
}
}
Element basicAuthElt = DomUtils.getChildElementByTagName(element, Elements.BASIC_AUTH);
if (basicAuthElt != null || autoConfig) {
new BasicAuthenticationBeanDefinitionParser(realm).parse(basicAuthElt, parserContext);
}
new BasicAuthenticationBeanDefinitionParser(realm).parse(basicAuthElt, pc);
}
Element formLoginElt = DomUtils.getChildElementByTagName(element, Elements.FORM_LOGIN);
@@ -321,7 +367,7 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
FormLoginBeanDefinitionParser parser = new FormLoginBeanDefinitionParser("/j_spring_security_check",
"org.springframework.security.ui.webapp.AuthenticationProcessingFilter");
parser.parse(formLoginElt, parserContext);
parser.parse(formLoginElt, pc);
formLoginFilter = parser.getFilterBean();
formLoginEntryPoint = parser.getEntryPointBean();
formLoginPage = parser.getLoginPage();
@@ -333,7 +379,7 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
FormLoginBeanDefinitionParser parser = new FormLoginBeanDefinitionParser("/j_spring_openid_security_check",
"org.springframework.security.ui.openid.OpenIDAuthenticationProcessingFilter");
parser.parse(openIDLoginElt, parserContext);
parser.parse(openIDLoginElt, pc);
openIDFilter = parser.getFilterBean();
openIDEntryPoint = parser.getEntryPointBean();
openIDLoginPage = parser.getLoginPage();
@@ -348,23 +394,26 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
}
BeanDefinition openIDProvider = openIDProviderBuilder.getBeanDefinition();
ConfigUtils.getRegisteredProviders(parserContext).add(openIDProvider);
parserContext.getRegistry().registerBeanDefinition(BeanIds.OPEN_ID_PROVIDER, openIDProvider);
pc.getRegistry().registerBeanDefinition(BeanIds.OPEN_ID_PROVIDER, openIDProvider);
ConfigUtils.getRegisteredProviders(pc).add(new RuntimeBeanReference(BeanIds.OPEN_ID_PROVIDER));
}
boolean needLoginPage = false;
if (formLoginFilter != null) {
needLoginPage = true;
parserContext.getRegistry().registerBeanDefinition(BeanIds.FORM_LOGIN_FILTER, formLoginFilter);
parserContext.getRegistry().registerBeanDefinition(BeanIds.FORM_LOGIN_ENTRY_POINT, formLoginEntryPoint);
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;
parserContext.getRegistry().registerBeanDefinition(BeanIds.OPEN_ID_FILTER, openIDFilter);
parserContext.getRegistry().registerBeanDefinition(BeanIds.OPEN_ID_ENTRY_POINT, openIDEntryPoint);
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.
@@ -382,8 +431,9 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
loginPageFilter.addConstructorArg(new RuntimeBeanReference(BeanIds.OPEN_ID_FILTER));
}
parserContext.getRegistry().registerBeanDefinition(BeanIds.DEFAULT_LOGIN_PAGE_GENERATING_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.
@@ -391,33 +441,39 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
String customEntryPoint = element.getAttribute(ATT_ENTRY_POINT_REF);
if (StringUtils.hasText(customEntryPoint)) {
parserContext.getRegistry().registerAlias(customEntryPoint, BeanIds.MAIN_ENTRY_POINT);
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) {
parserContext.getRegistry().registerAlias(BeanIds.BASIC_AUTHENTICATION_ENTRY_POINT, BeanIds.MAIN_ENTRY_POINT);
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) {
parserContext.getRegistry().registerAlias(BeanIds.FORM_LOGIN_ENTRY_POINT, BeanIds.MAIN_ENTRY_POINT);
pc.getRegistry().registerAlias(BeanIds.FORM_LOGIN_ENTRY_POINT, BeanIds.MAIN_ENTRY_POINT);
return;
}
// Otherwise use OpenID
// Otherwise use OpenID if enabled
if (openIDFilter != null && formLoginFilter == null) {
parserContext.getRegistry().registerAlias(BeanIds.OPEN_ID_ENTRY_POINT, BeanIds.MAIN_ENTRY_POINT);
pc.getRegistry().registerAlias(BeanIds.OPEN_ID_ENTRY_POINT, BeanIds.MAIN_ENTRY_POINT);
return;
}
parserContext.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 ",
parserContext.extractSource(element));
// 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 ",
pc.extractSource(element));
}
static UrlMatcher createUrlMatcher(Element element) {
@@ -512,9 +568,9 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
}
}
static void parseInterceptUrlsForFilterInvocationRequestMap(List urlElts, Map filterInvocationDefinitionMap,
boolean useLowerCasePaths, ParserContext parserContext) {
static LinkedHashMap parseInterceptUrlsForFilterInvocationRequestMap(List urlElts, boolean useLowerCasePaths, ParserContext parserContext) {
LinkedHashMap filterInvocationDefinitionMap = new LinkedHashMap();
Iterator urlEltsIterator = urlElts.iterator();
ConfigAttributeEditor editor = new ConfigAttributeEditor();
@@ -544,6 +600,8 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
filterInvocationDefinitionMap.put(new RequestKey(path, method), editor.getValue());
}
}
return filterInvocationDefinitionMap;
}
}
@@ -1,150 +0,0 @@
package org.springframework.security.config;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.core.Ordered;
import org.springframework.security.ui.AuthenticationEntryPoint;
import org.springframework.security.userdetails.UserDetailsByNameServiceWrapper;
import org.springframework.util.Assert;
/**
* Responsible for tying up the HTTP security configuration once all the beans are registered.
* This class does not actually instantiate any beans (for example, it should not call {@link BeanFactory#getBean(String)}).
* All the wiring up should be done using bean definitions or bean references to avoid this. This approach should avoid any
* conflict with other processors.
*
* @author Luke Taylor
* @author Ben Alex
* @version $Id$
* @since 2.0
*/
public class HttpSecurityConfigPostProcessor implements BeanFactoryPostProcessor, Ordered {
private Log logger = LogFactory.getLog(getClass());
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
injectUserDetailsServiceIntoRememberMeServices(beanFactory);
injectUserDetailsServiceIntoX509Provider(beanFactory);
injectUserDetailsServiceIntoOpenIDProvider(beanFactory);
injectAuthenticationEntryPointIntoExceptionTranslationFilter(beanFactory);
}
private void injectUserDetailsServiceIntoRememberMeServices(ConfigurableListableBeanFactory bf) {
try {
BeanDefinition rememberMeServices = bf.getBeanDefinition(BeanIds.REMEMBER_ME_SERVICES);
PropertyValue pv = rememberMeServices.getPropertyValues().getPropertyValue("userDetailsService");
if (pv == null) {
rememberMeServices.getPropertyValues().addPropertyValue("userDetailsService",
ConfigUtils.getUserDetailsService(bf));
} else {
RuntimeBeanReference cachingUserService = getCachingUserService(bf, pv.getValue());
if (cachingUserService != null) {
rememberMeServices.getPropertyValues().addPropertyValue("userDetailsService", cachingUserService);
}
}
} catch (NoSuchBeanDefinitionException e) {
// ignore
}
}
private void injectUserDetailsServiceIntoX509Provider(ConfigurableListableBeanFactory bf) {
try {
BeanDefinition x509AuthProvider = bf.getBeanDefinition(BeanIds.X509_AUTH_PROVIDER);
PropertyValue pv = x509AuthProvider.getPropertyValues().getPropertyValue("preAuthenticatedUserDetailsService");
if (pv == null) {
BeanDefinitionBuilder preAuthUserService = BeanDefinitionBuilder.rootBeanDefinition(UserDetailsByNameServiceWrapper.class);
preAuthUserService.addPropertyValue("userDetailsService", ConfigUtils.getUserDetailsService(bf));
x509AuthProvider.getPropertyValues().addPropertyValue("preAuthenticatedUserDetailsService",
preAuthUserService.getBeanDefinition());
} else {
RootBeanDefinition preAuthUserService = (RootBeanDefinition) pv.getValue();
Object userService =
preAuthUserService.getPropertyValues().getPropertyValue("userDetailsService").getValue();
RuntimeBeanReference cachingUserService = getCachingUserService(bf, userService);
if (cachingUserService != null) {
preAuthUserService.getPropertyValues().addPropertyValue("userDetailsService", cachingUserService);
}
}
} catch (NoSuchBeanDefinitionException e) {
// ignore
}
}
private void injectUserDetailsServiceIntoOpenIDProvider(ConfigurableListableBeanFactory beanFactory) {
try {
BeanDefinition openIDProvider = beanFactory.getBeanDefinition(BeanIds.OPEN_ID_PROVIDER);
PropertyValue pv = openIDProvider.getPropertyValues().getPropertyValue("userDetailsService");
if (pv == null) {
openIDProvider.getPropertyValues().addPropertyValue("userDetailsService",
ConfigUtils.getUserDetailsService(beanFactory));
}
} catch (NoSuchBeanDefinitionException e) {
// ignore
}
}
private RuntimeBeanReference getCachingUserService(ConfigurableListableBeanFactory bf, Object userServiceRef) {
Assert.isInstanceOf(RuntimeBeanReference.class, userServiceRef,
"userDetailsService property value must be a RuntimeBeanReference");
String id = ((RuntimeBeanReference)userServiceRef).getBeanName();
// Overwrite with the caching version if available
String cachingId = id + AbstractUserDetailsServiceBeanDefinitionParser.CACHING_SUFFIX;
if (bf.containsBeanDefinition(cachingId)) {
return new RuntimeBeanReference(cachingId);
}
return null;
}
/**
* Selects the entry point that should be used in ExceptionTranslationFilter. If an entry point has been
* set during parsing of form, openID and basic authentication information, or via a custom reference
* (using <tt>custom-entry-point</tt>, then that will be used. Otherwise there
* must be a single entry point bean and that will be used.
*
* Todo: this could probably be more easily be done in a BeanPostProcessor for ExceptionTranslationFilter.
*
*/
private void injectAuthenticationEntryPointIntoExceptionTranslationFilter(ConfigurableListableBeanFactory beanFactory) {
logger.info("Selecting AuthenticationEntryPoint for use in ExceptionTranslationFilter");
BeanDefinition etf =
beanFactory.getBeanDefinition(BeanIds.EXCEPTION_TRANSLATION_FILTER);
String entryPoint = null;
if (beanFactory.containsBean(BeanIds.MAIN_ENTRY_POINT)) {
entryPoint = BeanIds.MAIN_ENTRY_POINT;
logger.info("Using main configured AuthenticationEntryPoint set to " + BeanIds.MAIN_ENTRY_POINT);
} else {
String[] entryPoints = beanFactory.getBeanNamesForType(AuthenticationEntryPoint.class);
Assert.isTrue(entryPoints.length != 0, "No AuthenticationEntryPoint instances defined");
Assert.isTrue(entryPoints.length == 1, "More than one AuthenticationEntryPoint defined in context");
entryPoint = entryPoints[0];
}
logger.info("Using bean '" + entryPoint + "' as the entry point.");
etf.getPropertyValues().addPropertyValue("authenticationEntryPoint", new RuntimeBeanReference(entryPoint));
}
public int getOrder() {
return HIGHEST_PRECEDENCE + 1;
}
}
@@ -1,10 +1,8 @@
package org.springframework.security.config;
import org.springframework.security.userdetails.jdbc.JdbcUserDetailsManager;
import org.springframework.util.StringUtils;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.w3c.dom.Element;
@@ -17,26 +15,30 @@ public class JdbcUserServiceBeanDefinitionParser extends AbstractUserDetailsServ
static final String ATT_USERS_BY_USERNAME_QUERY = "users-by-username-query";
static final String ATT_AUTHORITIES_BY_USERNAME_QUERY = "authorities-by-username-query";
static final String ATT_GROUP_AUTHORITIES_QUERY = "group-authorities-by-username-query";
static final String ATT_ROLE_PREFIX = "role-prefix";
protected Class getBeanClass(Element element) {
return JdbcUserDetailsManager.class;
protected String getBeanClassName(Element element) {
return "org.springframework.security.userdetails.jdbc.JdbcUserDetailsManager";
}
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
// TODO: Set authenticationManager property
String dataSource = element.getAttribute(ATT_DATA_SOURCE);
// An explicit dataSource was specified, so use it
if (dataSource != null) {
builder.addPropertyReference("dataSource", dataSource);
} else {
// TODO: Have some sensible fallback if dataSource not specified, eg autowire
throw new BeanDefinitionStoreException(ATT_DATA_SOURCE + " is required for "
+ Elements.JDBC_USER_SERVICE );
parserContext.getReaderContext().error(ATT_DATA_SOURCE + " is required for "
+ Elements.JDBC_USER_SERVICE, parserContext.extractSource(element));
}
String usersQuery = element.getAttribute(ATT_USERS_BY_USERNAME_QUERY);
String authoritiesQuery = element.getAttribute(ATT_AUTHORITIES_BY_USERNAME_QUERY);
String groupAuthoritiesQuery = element.getAttribute(ATT_GROUP_AUTHORITIES_QUERY);
String rolePrefix = element.getAttribute(ATT_ROLE_PREFIX);
if (StringUtils.hasText(rolePrefix)) {
builder.addPropertyValue("rolePrefix", rolePrefix);
}
if (StringUtils.hasText(usersQuery)) {
builder.addPropertyValue("usersByUsernameQuery", usersQuery);
@@ -1,14 +1,13 @@
package org.springframework.security.config;
import org.springframework.security.ldap.SpringSecurityContextSource;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.BeansException;
import org.springframework.core.Ordered;
import java.util.Map;
import org.springframework.security.ldap.SpringSecurityContextSource;
/**
* @author Luke Taylor
@@ -17,35 +16,56 @@ import java.util.Map;
*/
class LdapConfigUtils {
/** Checks for the presence of a ContextSource instance */
/**
* Checks for the presence of a ContextSource instance. Also supplies the standard reference to any
* unconfigured <ldap-authentication-provider> or <ldap-user-service> beans. This is
* necessary in cases where the user has given the server a specific Id, but hasn't used
* the server-ref attribute to link this to the other ldap definitions. See SEC-799.
*/
private static class ContextSourceSettingPostProcessor implements BeanFactoryPostProcessor, Ordered {
/** If set to true, a bean parser has indicated that the default context source name needs to be set */
private boolean defaultNameRequired;
public void postProcessBeanFactory(ConfigurableListableBeanFactory bf) throws BeansException {
Map beans = bf.getBeansOfType(SpringSecurityContextSource.class);
String[] sources = bf.getBeanNamesForType(SpringSecurityContextSource.class);
if (beans.size() == 0) {
if (sources.length == 0) {
throw new SecurityConfigurationException("No SpringSecurityContextSource instances found. Have you " +
"added an <" + Elements.LDAP_SERVER + " /> element to your application context?");
}
// else if (beans.size() > 1) {
// throw new SecurityConfigurationException("More than one SpringSecurityContextSource instance found. " +
// "Please specify a specific server id when configuring your <" + Elements.LDAP_PROVIDER + "> " +
// "or <" + Elements.LDAP_USER_SERVICE + ">.");
// }
if (!bf.containsBean(BeanIds.CONTEXT_SOURCE) && defaultNameRequired) {
if (sources.length > 1) {
throw new SecurityConfigurationException("More than one SpringSecurityContextSource instance found. " +
"Please specify a specific server id using the 'server-ref' attribute when configuring your <" +
Elements.LDAP_PROVIDER + "> " + "or <" + Elements.LDAP_USER_SERVICE + ">.");
}
bf.registerAlias(sources[0], BeanIds.CONTEXT_SOURCE);
}
}
public void setDefaultNameRequired(boolean defaultNameRequired) {
this.defaultNameRequired = defaultNameRequired;
}
public int getOrder() {
return LOWEST_PRECEDENCE;
}
}
static void registerPostProcessorIfNecessary(BeanDefinitionRegistry registry) {
static void registerPostProcessorIfNecessary(BeanDefinitionRegistry registry, boolean defaultNameRequired) {
if (registry.containsBeanDefinition(BeanIds.CONTEXT_SOURCE_SETTING_POST_PROCESSOR)) {
if (defaultNameRequired) {
BeanDefinition bd = registry.getBeanDefinition(BeanIds.CONTEXT_SOURCE_SETTING_POST_PROCESSOR);
bd.getPropertyValues().addPropertyValue("defaultNameRequired", Boolean.valueOf(defaultNameRequired));
}
return;
}
registry.registerBeanDefinition(BeanIds.CONTEXT_SOURCE_SETTING_POST_PROCESSOR,
new RootBeanDefinition(ContextSourceSettingPostProcessor.class));
BeanDefinition bd = new RootBeanDefinition(ContextSourceSettingPostProcessor.class);
registry.registerBeanDefinition(BeanIds.CONTEXT_SOURCE_SETTING_POST_PROCESSOR, bd);
bd.getPropertyValues().addPropertyValue("defaultNameRequired", Boolean.valueOf(defaultNameRequired));
}
}
@@ -1,11 +1,8 @@
package org.springframework.security.config;
import org.springframework.security.ldap.search.FilterBasedLdapUserSearch;
import org.springframework.security.providers.ldap.LdapAuthenticationProvider;
import org.springframework.security.providers.ldap.authenticator.BindAuthenticator;
import org.springframework.security.providers.ldap.authenticator.PasswordComparisonAuthenticator;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
@@ -27,14 +24,19 @@ public class LdapProviderBeanDefinitionParser implements BeanDefinitionParser {
private Log logger = LogFactory.getLog(getClass());
private static final String ATT_USER_DN_PATTERN = "user-dn-pattern";
private static final String ATT_USER_PASSWORD= "password-attribute";
private static final String ATT_USER_PASSWORD = "password-attribute";
private static final String ATT_HASH = PasswordEncoderParser.ATT_HASH;
private static final String DEF_USER_SEARCH_FILTER="uid={0}";
private static final String DEF_USER_SEARCH_FILTER = "uid={0}";
private static final String PROVIDER_CLASS = "org.springframework.security.providers.ldap.LdapAuthenticationProvider";
private static final String BIND_AUTH_CLASS = "org.springframework.security.providers.ldap.authenticator.BindAuthenticator";
private static final String PASSWD_AUTH_CLASS = "org.springframework.security.providers.ldap.authenticator.PasswordComparisonAuthenticator";
public BeanDefinition parse(Element elt, ParserContext parserContext) {
RuntimeBeanReference contextSource = LdapUserServiceBeanDefinitionParser.parseServerReference(elt, parserContext);
RootBeanDefinition searchBean = LdapUserServiceBeanDefinitionParser.parseSearchBean(elt, parserContext);
BeanDefinition searchBean = LdapUserServiceBeanDefinitionParser.parseSearchBean(elt, parserContext);
String userDnPattern = elt.getAttribute(ATT_USER_DN_PATTERN);
String[] userDnPatternArray = new String[0];
@@ -44,49 +46,62 @@ public class LdapProviderBeanDefinitionParser implements BeanDefinitionParser {
// TODO: Validate the pattern and make sure it is a valid DN.
} else if (searchBean == null) {
logger.info("No search information or DN pattern specified. Using default search filter '" + DEF_USER_SEARCH_FILTER + "'");
searchBean = new RootBeanDefinition(FilterBasedLdapUserSearch.class);
searchBean.setSource(elt);
searchBean.getConstructorArgumentValues().addIndexedArgumentValue(0, "");
searchBean.getConstructorArgumentValues().addIndexedArgumentValue(1, DEF_USER_SEARCH_FILTER);
searchBean.getConstructorArgumentValues().addIndexedArgumentValue(2, contextSource);
BeanDefinitionBuilder searchBeanBuilder = BeanDefinitionBuilder.rootBeanDefinition(LdapUserServiceBeanDefinitionParser.LDAP_SEARCH_CLASS);
searchBeanBuilder.setSource(elt);
searchBeanBuilder.addConstructorArg("");
searchBeanBuilder.addConstructorArg(DEF_USER_SEARCH_FILTER);
searchBeanBuilder.addConstructorArg(contextSource);
searchBean = searchBeanBuilder.getBeanDefinition();
}
RootBeanDefinition authenticator = new RootBeanDefinition(BindAuthenticator.class);
BeanDefinitionBuilder authenticatorBuilder =
BeanDefinitionBuilder.rootBeanDefinition(BIND_AUTH_CLASS);
Element passwordCompareElt = DomUtils.getChildElementByTagName(elt, Elements.LDAP_PASSWORD_COMPARE);
if (passwordCompareElt != null) {
authenticator = new RootBeanDefinition(PasswordComparisonAuthenticator.class);
authenticatorBuilder =
BeanDefinitionBuilder.rootBeanDefinition(PASSWD_AUTH_CLASS);
String passwordAttribute = passwordCompareElt.getAttribute(ATT_USER_PASSWORD);
if (StringUtils.hasText(passwordAttribute)) {
authenticator.getPropertyValues().addPropertyValue("passwordAttributeName", passwordAttribute);
authenticatorBuilder.addPropertyValue("passwordAttributeName", passwordAttribute);
}
Element passwordEncoderElement = DomUtils.getChildElementByTagName(passwordCompareElt, Elements.PASSWORD_ENCODER);
String hash = passwordCompareElt.getAttribute(ATT_HASH);
if (passwordEncoderElement != null) {
if (StringUtils.hasText(hash)) {
parserContext.getReaderContext().warning("Attribute 'hash' cannot be used with 'password-encoder' and " +
"will be ignored.", parserContext.extractSource(elt));
}
PasswordEncoderParser pep = new PasswordEncoderParser(passwordEncoderElement, parserContext);
authenticator.getPropertyValues().addPropertyValue("passwordEncoder", pep.getPasswordEncoder());
authenticatorBuilder.addPropertyValue("passwordEncoder", pep.getPasswordEncoder());
if (pep.getSaltSource() != null) {
parserContext.getReaderContext().warning("Salt source information isn't valid when used with LDAP", passwordEncoderElement);
parserContext.getReaderContext().warning("Salt source information isn't valid when used with LDAP",
passwordEncoderElement);
}
} else if (StringUtils.hasText(hash)) {
Class encoderClass = (Class) PasswordEncoderParser.ENCODER_CLASSES.get(hash);
authenticatorBuilder.addPropertyValue("passwordEncoder", new RootBeanDefinition(encoderClass));
}
}
authenticator.getConstructorArgumentValues().addGenericArgumentValue(contextSource);
authenticator.getPropertyValues().addPropertyValue("userDnPatterns", userDnPatternArray);
authenticatorBuilder.addConstructorArg(contextSource);
authenticatorBuilder.addPropertyValue("userDnPatterns", userDnPatternArray);
if (searchBean != null) {
authenticator.getPropertyValues().addPropertyValue("userSearch", searchBean);
authenticatorBuilder.addPropertyValue("userSearch", searchBean);
}
RootBeanDefinition ldapProvider = new RootBeanDefinition(LdapAuthenticationProvider.class);
ldapProvider.getConstructorArgumentValues().addGenericArgumentValue(authenticator);
ldapProvider.getConstructorArgumentValues().addGenericArgumentValue(LdapUserServiceBeanDefinitionParser.parseAuthoritiesPopulator(elt, parserContext));
LdapConfigUtils.registerPostProcessorIfNecessary(parserContext.getRegistry());
ConfigUtils.getRegisteredProviders(parserContext).add(ldapProvider);
BeanDefinitionBuilder ldapProvider = BeanDefinitionBuilder.rootBeanDefinition(PROVIDER_CLASS);
ldapProvider.addConstructorArg(authenticatorBuilder.getBeanDefinition());
ldapProvider.addConstructorArg(LdapUserServiceBeanDefinitionParser.parseAuthoritiesPopulator(elt, parserContext));
ldapProvider.addPropertyValue("userDetailsContextMapper",
LdapUserServiceBeanDefinitionParser.parseUserDetailsClass(elt, parserContext));
ConfigUtils.getRegisteredProviders(parserContext).add(ldapProvider.getBeanDefinition());
return null;
}
@@ -1,6 +1,10 @@
package org.springframework.security.config;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
@@ -8,7 +12,6 @@ import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedSet;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
@@ -20,7 +23,9 @@ import org.apache.commons.logging.LogFactory;
* @version $Id$
*/
public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
private Log logger = LogFactory.getLog(getClass());
private static final String CONTEXT_SOURCE_CLASS="org.springframework.security.ldap.DefaultSpringSecurityContextSource";
private final Log logger = LogFactory.getLog(getClass());
/** Defines the Url of the ldap server to use. If not specified, an embedded apache DS instance will be created */
private static final String ATT_URL = "url";
@@ -53,7 +58,8 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
if (!StringUtils.hasText(url)) {
contextSource = createEmbeddedServer(elt, parserContext);
} else {
contextSource = new RootBeanDefinition(DefaultSpringSecurityContextSource.class);
contextSource = new RootBeanDefinition();
contextSource.setBeanClassName(CONTEXT_SOURCE_CLASS);
contextSource.getConstructorArgumentValues().addIndexedArgumentValue(0, url);
}
@@ -92,17 +98,22 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
*/
private RootBeanDefinition createEmbeddedServer(Element element, ParserContext parserContext) {
Object source = parserContext.extractSource(element);
BeanDefinitionBuilder configuration = BeanDefinitionBuilder.rootBeanDefinition("org.apache.directory.server.configuration.MutableServerStartupConfiguration");
BeanDefinitionBuilder partition = BeanDefinitionBuilder.rootBeanDefinition("org.apache.directory.server.core.partition.impl.btree.MutableBTreePartitionConfiguration");
BeanDefinitionBuilder configuration =
BeanDefinitionBuilder.rootBeanDefinition("org.apache.directory.server.configuration.MutableServerStartupConfiguration");
BeanDefinitionBuilder partition =
BeanDefinitionBuilder.rootBeanDefinition("org.apache.directory.server.core.partition.impl.btree.MutableBTreePartitionConfiguration");
configuration.setSource(source);
partition.setSource(source);
DirContextAdapter rootContext = new DirContextAdapter();
rootContext.setAttributeValues("objectClass", new String[] {"top", "domain", "extensibleObject"});
rootContext.setAttributeValue("dc", "springsecurity");
Attributes rootAttributes = new BasicAttributes("dc", "springsecurity");
Attribute a = new BasicAttribute("objectClass");
a.add("top");
a.add("domain");
a.add("extensibleObject");
rootAttributes.put(a);
partition.addPropertyValue("name", "springsecurity");
partition.addPropertyValue("contextEntry", rootContext.getAttributes());
partition.addPropertyValue("contextEntry", rootAttributes);
String suffix = element.getAttribute(ATT_ROOT_SUFFIX);
@@ -131,15 +142,15 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
String url = "ldap://127.0.0.1:" + port + "/" + suffix;
RootBeanDefinition contextSource = new RootBeanDefinition(DefaultSpringSecurityContextSource.class);
contextSource.getConstructorArgumentValues().addIndexedArgumentValue(0, url);
contextSource.getPropertyValues().addPropertyValue("userDn", "uid=admin,ou=system");
contextSource.getPropertyValues().addPropertyValue("password", "secret");
BeanDefinitionBuilder contextSource = BeanDefinitionBuilder.rootBeanDefinition(CONTEXT_SOURCE_CLASS);
contextSource.addConstructorArg(url);
contextSource.addPropertyValue("userDn", "uid=admin,ou=system");
contextSource.addPropertyValue("password", "secret");
RootBeanDefinition apacheContainer = new RootBeanDefinition("org.springframework.security.config.ApacheDSContainer", null, null);
apacheContainer.setSource(source);
apacheContainer.getConstructorArgumentValues().addGenericArgumentValue(configuration.getBeanDefinition());
apacheContainer.getConstructorArgumentValues().addGenericArgumentValue(contextSource);
apacheContainer.getConstructorArgumentValues().addGenericArgumentValue(contextSource.getBeanDefinition());
String ldifs = element.getAttribute(ATT_LDIF_FILE);
if (!StringUtils.hasText(ldifs)) {
@@ -157,6 +168,6 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
parserContext.getRegistry().registerBeanDefinition(BeanIds.EMBEDDED_APACHE_DS, apacheContainer);
return contextSource;
return (RootBeanDefinition) contextSource.getBeanDefinition();
}
}
@@ -1,8 +1,5 @@
package org.springframework.security.config;
import org.springframework.security.userdetails.ldap.LdapUserDetailsService;
import org.springframework.security.ldap.search.FilterBasedLdapUserSearch;
import org.springframework.security.ldap.populator.DefaultLdapAuthoritiesPopulator;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.RootBeanDefinition;
@@ -17,7 +14,7 @@ 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 = "";
@@ -27,9 +24,20 @@ public class LdapUserServiceBeanDefinitionParser extends AbstractUserDetailsServ
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";
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";
public static final String LDAP_USER_MAPPER_CLASS = "org.springframework.security.userdetails.ldap.LdapUserDetailsMapper";
public static final String LDAP_AUTHORITIES_POPULATOR_CLASS = "org.springframework.security.ldap.populator.DefaultLdapAuthoritiesPopulator";
protected Class getBeanClass(Element element) {
return LdapUserDetailsService.class;
protected String getBeanClassName(Element element) {
return "org.springframework.security.userdetails.ldap.LdapUserDetailsService";
}
protected void doParse(Element elt, ParserContext parserContext, BeanDefinitionBuilder builder) {
@@ -40,8 +48,7 @@ public class LdapUserServiceBeanDefinitionParser extends AbstractUserDetailsServ
builder.addConstructorArg(parseSearchBean(elt, parserContext));
builder.addConstructorArg(parseAuthoritiesPopulator(elt, parserContext));
LdapConfigUtils.registerPostProcessorIfNecessary(parserContext.getRegistry());
builder.addPropertyValue("userDetailsMapper", parseUserDetailsClass(elt, parserContext));
}
static RootBeanDefinition parseSearchBean(Element elt, ParserContext parserContext) {
@@ -61,33 +68,48 @@ public class LdapUserServiceBeanDefinitionParser extends AbstractUserDetailsServ
return null;
}
RootBeanDefinition search = new RootBeanDefinition(FilterBasedLdapUserSearch.class);
search.setSource(source);
search.getConstructorArgumentValues().addIndexedArgumentValue(0, userSearchBase);
search.getConstructorArgumentValues().addIndexedArgumentValue(1, userSearchFilter);
search.getConstructorArgumentValues().addIndexedArgumentValue(2, parseServerReference(elt, parserContext));
BeanDefinitionBuilder searchBuilder = BeanDefinitionBuilder.rootBeanDefinition(LDAP_SEARCH_CLASS);
searchBuilder.setSource(source);
searchBuilder.addConstructorArg(userSearchBase);
searchBuilder.addConstructorArg(userSearchFilter);
searchBuilder.addConstructorArg(parseServerReference(elt, parserContext));
return search;
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;
}
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);
}
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;
}
@@ -96,16 +118,24 @@ public class LdapUserServiceBeanDefinitionParser extends AbstractUserDetailsServ
groupSearchBase = DEF_GROUP_SEARCH_BASE;
}
RootBeanDefinition populator = new RootBeanDefinition(DefaultLdapAuthoritiesPopulator.class);
BeanDefinitionBuilder populator = BeanDefinitionBuilder.rootBeanDefinition(LDAP_AUTHORITIES_POPULATOR_CLASS);
populator.setSource(parserContext.extractSource(elt));
populator.getConstructorArgumentValues().addIndexedArgumentValue(0, parseServerReference(elt, parserContext));
populator.getConstructorArgumentValues().addIndexedArgumentValue(1, groupSearchBase);
populator.getPropertyValues().addPropertyValue("groupSearchFilter", groupSearchFilter);
populator.addConstructorArg(parseServerReference(elt, parserContext));
populator.addConstructorArg(groupSearchBase);
populator.addPropertyValue("groupSearchFilter", groupSearchFilter);
populator.addPropertyValue("searchSubtree", Boolean.TRUE);
if (StringUtils.hasLength(groupRoleAttribute)) {
populator.getPropertyValues().addPropertyValue("groupRoleAttribute", groupRoleAttribute);
if (StringUtils.hasText(rolePrefix)) {
if ("none".equals(rolePrefix)) {
rolePrefix = "";
}
populator.addPropertyValue("rolePrefix", rolePrefix);
}
return populator;
if (StringUtils.hasLength(groupRoleAttribute)) {
populator.addPropertyValue("groupRoleAttribute", groupRoleAttribute);
}
return (RootBeanDefinition) populator.getBeanDefinition();
}
}
@@ -31,15 +31,18 @@ public class LogoutBeanDefinitionParser implements BeanDefinitionParser {
String logoutSuccessUrl = null;
String invalidateSession = null;
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(LogoutFilter.class);
if (element != null) {
Object source = parserContext.extractSource(element);
builder.setSource(source);
logoutUrl = element.getAttribute(ATT_LOGOUT_URL);
ConfigUtils.validateHttpRedirect(logoutUrl, parserContext, source);
logoutSuccessUrl = element.getAttribute(ATT_LOGOUT_SUCCESS_URL);
ConfigUtils.validateHttpRedirect(logoutSuccessUrl, parserContext, source);
invalidateSession = element.getAttribute(ATT_INVALIDATE_SESSION);
}
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(LogoutFilter.class);
builder.setSource(parserContext.extractSource(element));
if (!StringUtils.hasText(logoutUrl)) {
logoutUrl = DEF_LOGOUT_URL;
}
@@ -70,7 +73,8 @@ public class LogoutBeanDefinitionParser implements BeanDefinitionParser {
builder.addConstructorArg(handlers);
parserContext.getRegistry().registerBeanDefinition(BeanIds.LOGOUT_FILTER, builder.getBeanDefinition());
ConfigUtils.addHttpFilter(parserContext, new RuntimeBeanReference(BeanIds.LOGOUT_FILTER));
return null;
}
}
@@ -0,0 +1,48 @@
package org.springframework.security.config;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.security.AfterInvocationManager;
import org.springframework.security.intercept.method.aopalliance.MethodSecurityInterceptor;
/**
* BeanPostProcessor which sets the AfterInvocationManager on the default MethodSecurityInterceptor,
* if one has been configured.
*
* @author Luke Taylor
* @version $Id$
*
*/
public class MethodSecurityInterceptorPostProcessor implements BeanPostProcessor, BeanFactoryAware{
private Log logger = LogFactory.getLog(getClass());
private BeanFactory beanFactory;
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if(!BeanIds.METHOD_SECURITY_INTERCEPTOR.equals(beanName)) {
return bean;
}
MethodSecurityInterceptor interceptor = (MethodSecurityInterceptor) bean;
if (beanFactory.containsBean(BeanIds.AFTER_INVOCATION_MANAGER)) {
logger.debug("Setting AfterInvocationManaer on MethodSecurityInterceptor");
interceptor.setAfterInvocationManager((AfterInvocationManager)
beanFactory.getBean(BeanIds.AFTER_INVOCATION_MANAGER));
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) {
return bean;
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
}
@@ -9,8 +9,8 @@ import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.BeanDefinitionDecorator;
import org.springframework.beans.factory.xml.ParserContext;
@@ -22,8 +22,8 @@ import org.w3c.dom.Element;
import org.w3c.dom.Node;
/**
* Replaces a Spring bean of type "Filter" with a wrapper class which implements the <tt>Ordered</tt>
* interface. This allows user to add their own filter to the security chain. If the user's filter
* 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.
*
* @author Luke Taylor
@@ -39,16 +39,17 @@ public class OrderedFilterBeanDefinitionDecorator implements BeanDefinitionDecor
Element elt = (Element)node;
String order = getOrder(elt, parserContext);
BeanDefinition filter = holder.getBeanDefinition();
BeanDefinitionBuilder wrapper = BeanDefinitionBuilder.rootBeanDefinition("org.springframework.security.config.OrderedFilterBeanDefinitionDecorator$OrderedFilterDecorator");
wrapper.addConstructorArg(holder.getBeanName());
wrapper.addConstructorArg(filter);
wrapper.addConstructorArg(new RuntimeBeanReference(holder.getBeanName()));
if (StringUtils.hasText(order)) {
wrapper.addPropertyValue("order", order);
}
return new BeanDefinitionHolder(wrapper.getBeanDefinition(), holder.getBeanName());
ConfigUtils.addHttpFilter(parserContext, wrapper.getBeanDefinition());
return holder;
}
/**
@@ -119,5 +120,13 @@ public class OrderedFilterBeanDefinitionDecorator implements BeanDefinitionDecor
public String getBeanName() {
return beanName;
}
public String toString() {
return "OrderedFilterDecorator[ delegate=" + delegate + "; order=" + getOrder() + "]";
}
Filter getDelegate() {
return delegate;
}
}
}
@@ -35,6 +35,7 @@ public class PasswordEncoderParser {
static final String ATT_BASE_64 = "base64";
static final String OPT_HASH_PLAINTEXT = "plaintext";
static final String OPT_HASH_SHA = "sha";
static final String OPT_HASH_SHA256 = "sha-256";
static final String OPT_HASH_MD4 = "md4";
static final String OPT_HASH_MD5 = "md5";
static final String OPT_HASH_LDAP_SHA = "{sha}";
@@ -45,6 +46,7 @@ public class PasswordEncoderParser {
ENCODER_CLASSES = new HashMap();
ENCODER_CLASSES.put(OPT_HASH_PLAINTEXT, PlaintextPasswordEncoder.class);
ENCODER_CLASSES.put(OPT_HASH_SHA, ShaPasswordEncoder.class);
ENCODER_CLASSES.put(OPT_HASH_SHA256, ShaPasswordEncoder.class);
ENCODER_CLASSES.put(OPT_HASH_MD4, Md4PasswordEncoder.class);
ENCODER_CLASSES.put(OPT_HASH_MD5, Md5PasswordEncoder.class);
ENCODER_CLASSES.put(OPT_HASH_LDAP_SHA, LdapShaPasswordEncoder.class);
@@ -74,6 +76,11 @@ public class PasswordEncoderParser {
} else {
Class beanClass = (Class) ENCODER_CLASSES.get(hash);
RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
if (OPT_HASH_SHA256.equals(hash)) {
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, new Integer(256));
}
beanDefinition.setSource(parserContext.extractSource(element));
if (useBase64) {
if (BaseDigestPasswordEncoder.class.isAssignableFrom(beanClass)) {
@@ -23,12 +23,14 @@ import org.w3c.dom.Element;
*/
public class RememberMeBeanDefinitionParser implements BeanDefinitionParser {
static final String ATT_KEY = "key";
static final String DEF_KEY = "doesNotMatter";
static final String DEF_KEY = "SpringSecured";
static final String ATT_DATA_SOURCE = "data-source";
static final String ATT_DATA_SOURCE = "data-source-ref";
static final String ATT_SERVICES_REF = "services-ref";
static final String ATT_TOKEN_REPOSITORY = "token-repository-ref";
static final String ATT_USER_SERVICE_REF = "user-service-ref";
static final String ATT_TOKEN_VALIDITY = "token-validity-seconds";
protected final Log logger = LogFactory.getLog(getClass());
public BeanDefinition parse(Element element, ParserContext parserContext) {
@@ -37,73 +39,100 @@ public class RememberMeBeanDefinitionParser implements BeanDefinitionParser {
String key = null;
Object source = null;
String userServiceRef = null;
String rememberMeServicesRef = null;
String tokenValiditySeconds = null;
if (element != null) {
tokenRepository = element.getAttribute(ATT_TOKEN_REPOSITORY);
dataSource = element.getAttribute(ATT_DATA_SOURCE);
key = element.getAttribute(ATT_KEY);
userServiceRef = element.getAttribute(ATT_USER_SERVICE_REF);
userServiceRef = element.getAttribute(ATT_USER_SERVICE_REF);
rememberMeServicesRef = element.getAttribute(ATT_SERVICES_REF);
tokenValiditySeconds = element.getAttribute(ATT_TOKEN_VALIDITY);
source = parserContext.extractSource(element);
}
RootBeanDefinition filter = new RootBeanDefinition(RememberMeProcessingFilter.class);
RootBeanDefinition services = new RootBeanDefinition(PersistentTokenBasedRememberMeServices.class);
filter.getPropertyValues().addPropertyValue("authenticationManager",
new RuntimeBeanReference(BeanIds.AUTHENTICATION_MANAGER));
if (!StringUtils.hasText(key)) {
key = DEF_KEY;
}
RootBeanDefinition services = null;
boolean dataSourceSet = StringUtils.hasText(dataSource);
boolean tokenRepoSet = StringUtils.hasText(tokenRepository);
boolean servicesRefSet = StringUtils.hasText(rememberMeServicesRef);
boolean userServiceSet = StringUtils.hasText(userServiceRef);
boolean tokenValiditySet = StringUtils.hasText(tokenValiditySeconds);
if (servicesRefSet && (dataSourceSet || tokenRepoSet || userServiceSet || tokenValiditySet)) {
parserContext.getReaderContext().error(ATT_SERVICES_REF + " can't be used in combination with attributes "
+ ATT_TOKEN_REPOSITORY + "," + ATT_DATA_SOURCE + ", " + ATT_USER_SERVICE_REF + " or " + ATT_TOKEN_VALIDITY, source);
}
if (dataSourceSet && tokenRepoSet) {
parserContext.getReaderContext().error("Specify tokenRepository or dataSource but not both", element);
parserContext.getReaderContext().error("Specify " + ATT_TOKEN_REPOSITORY + " or " +
ATT_DATA_SOURCE +" but not both", source);
}
boolean isPersistent = dataSourceSet | tokenRepoSet;
if (isPersistent) {
Object tokenRepo;
services = new RootBeanDefinition(PersistentTokenBasedRememberMeServices.class);
if (tokenRepoSet) {
tokenRepo = new RuntimeBeanReference(tokenRepository);
} 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);
} else {
isPersistent = false;
} else if (!servicesRefSet) {
services = new RootBeanDefinition(TokenBasedRememberMeServices.class);
}
if (!StringUtils.hasText(key) && !isPersistent) {
key = DEF_KEY;
if (services != null) {
if (userServiceSet) {
services.getPropertyValues().addPropertyValue("userDetailsService", new RuntimeBeanReference(userServiceRef));
}
if (tokenValiditySet) {
services.getPropertyValues().addPropertyValue("tokenValiditySeconds", new Integer(tokenValiditySeconds));
}
services.setSource(source);
services.getPropertyValues().addPropertyValue(ATT_KEY, key);
parserContext.getRegistry().registerBeanDefinition(BeanIds.REMEMBER_ME_SERVICES, services);
} else {
parserContext.getRegistry().registerAlias(rememberMeServicesRef, BeanIds.REMEMBER_ME_SERVICES);
}
BeanDefinition authManager = ConfigUtils.registerProviderManagerIfNecessary(parserContext);
RootBeanDefinition provider = new RootBeanDefinition(RememberMeAuthenticationProvider.class);
filter.setSource(source);
services.setSource(source);
provider.setSource(source);
if (StringUtils.hasText(userServiceRef)) {
services.getPropertyValues().addPropertyValue("userDetailsService", new RuntimeBeanReference(userServiceRef));
}
provider.getPropertyValues().addPropertyValue(ATT_KEY, key);
services.getPropertyValues().addPropertyValue(ATT_KEY, key);
ManagedList providers = (ManagedList) authManager.getPropertyValues().getPropertyValue("providers").getValue();
providers.add(provider);
filter.getPropertyValues().addPropertyValue("rememberMeServices",
new RuntimeBeanReference(BeanIds.REMEMBER_ME_SERVICES));
parserContext.getRegistry().registerBeanDefinition(BeanIds.REMEMBER_ME_SERVICES, services);
parserContext.getRegistry().registerBeanDefinition(BeanIds.REMEMBER_ME_FILTER, filter);
registerProvider(parserContext, source, key);
registerFilter(parserContext, source);
return null;
}
private void registerProvider(ParserContext pc, Object source, String key) {
BeanDefinition authManager = ConfigUtils.registerProviderManagerIfNecessary(pc);
RootBeanDefinition provider = new RootBeanDefinition(RememberMeAuthenticationProvider.class);
provider.setSource(source);
provider.getPropertyValues().addPropertyValue(ATT_KEY, key);
ManagedList providers = (ManagedList) authManager.getPropertyValues().getPropertyValue("providers").getValue();
providers.add(provider);
}
private void registerFilter(ParserContext pc, Object source) {
RootBeanDefinition filter = new RootBeanDefinition(RememberMeProcessingFilter.class);
filter.setSource(source);
filter.getPropertyValues().addPropertyValue("authenticationManager",
new RuntimeBeanReference(BeanIds.AUTHENTICATION_MANAGER));
filter.getPropertyValues().addPropertyValue("rememberMeServices",
new RuntimeBeanReference(BeanIds.REMEMBER_ME_SERVICES));
pc.getRegistry().registerBeanDefinition(BeanIds.REMEMBER_ME_FILTER, filter);
ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.REMEMBER_ME_FILTER));
}
}
@@ -33,7 +33,7 @@ public class RememberMeServicesInjectionBeanPostProcessor implements BeanPostPro
logger.info("Setting RememberMeServices on bean " + beanName);
pf.setRememberMeServices(getRememberMeServices());
}
} else if (beanName.equals(BeanIds.BASIC_AUTHENTICATION_FILTER)) {
} else if (BeanIds.BASIC_AUTHENTICATION_FILTER.equals(beanName)) {
// NB: For remember-me to be sent back, a user must submit a "_spring_security_remember_me" with their login request.
// Most of the time a user won't present such a parameter with their BASIC authentication request.
// In the future we might support setting the AbstractRememberMeServices.alwaysRemember = true, but I am reluctant to
@@ -30,5 +30,6 @@ public class SecurityNamespaceHandler extends NamespaceHandlerSupport {
registerBeanDefinitionDecorator(Elements.FILTER_CHAIN_MAP, new FilterChainMapBeanDefinitionDecorator());
registerBeanDefinitionDecorator(Elements.CUSTOM_FILTER, new OrderedFilterBeanDefinitionDecorator());
registerBeanDefinitionDecorator(Elements.CUSTOM_AUTH_PROVIDER, new CustomAuthenticationProviderBeanDefinitionDecorator());
registerBeanDefinitionDecorator(Elements.CUSTOM_AFTER_INVOCATION_PROVIDER, new CustomAfterInvocationProviderBeanDefinitionDecorator());
}
}
@@ -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;
}
}
@@ -0,0 +1,139 @@
package org.springframework.security.config;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.security.providers.preauth.PreAuthenticatedAuthenticationProvider;
import org.springframework.security.ui.rememberme.AbstractRememberMeServices;
import org.springframework.security.userdetails.UserDetailsByNameServiceWrapper;
import org.springframework.security.userdetails.UserDetailsService;
import org.springframework.util.Assert;
/**
*
* @author Luke Taylor
* @since 2.0.2
*/
public class UserDetailsServiceInjectionBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
private final Log logger = LogFactory.getLog(getClass());
private ConfigurableListableBeanFactory beanFactory;
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (BeanIds.X509_AUTH_PROVIDER.equals(beanName)) {
injectUserDetailsServiceIntoX509Provider((PreAuthenticatedAuthenticationProvider) bean);
} else if (BeanIds.REMEMBER_ME_SERVICES.equals(beanName)) {
injectUserDetailsServiceIntoRememberMeServices((AbstractRememberMeServices)bean);
} else if (BeanIds.OPEN_ID_PROVIDER.equals(beanName)) {
injectUserDetailsServiceIntoOpenIDProvider(bean);
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
private void injectUserDetailsServiceIntoRememberMeServices(AbstractRememberMeServices services) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(BeanIds.REMEMBER_ME_SERVICES);
PropertyValue pv = beanDefinition.getPropertyValues().getPropertyValue("userDetailsService");
if (pv == null) {
services.setUserDetailsService(getUserDetailsService());
} else {
UserDetailsService cachingUserService = getCachingUserService(pv.getValue());
if (cachingUserService != null) {
services.setUserDetailsService(cachingUserService);
}
}
}
private void injectUserDetailsServiceIntoX509Provider(PreAuthenticatedAuthenticationProvider provider) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(BeanIds.X509_AUTH_PROVIDER);
PropertyValue pv = beanDefinition.getPropertyValues().getPropertyValue("preAuthenticatedUserDetailsService");
UserDetailsByNameServiceWrapper wrapper = new UserDetailsByNameServiceWrapper();
if (pv == null) {
wrapper.setUserDetailsService(getUserDetailsService());
provider.setPreAuthenticatedUserDetailsService(wrapper);
} else {
RootBeanDefinition preAuthUserService = (RootBeanDefinition) pv.getValue();
Object userService =
preAuthUserService.getPropertyValues().getPropertyValue("userDetailsService").getValue();
UserDetailsService cachingUserService = getCachingUserService(userService);
if (cachingUserService != null) {
wrapper.setUserDetailsService(cachingUserService);
provider.setPreAuthenticatedUserDetailsService(wrapper);
}
}
}
private void injectUserDetailsServiceIntoOpenIDProvider(Object bean) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(BeanIds.OPEN_ID_PROVIDER);
PropertyValue pv = beanDefinition.getPropertyValues().getPropertyValue("userDetailsService");
if (pv == null) {
BeanWrapperImpl beanWrapper = new BeanWrapperImpl(bean);
beanWrapper.setPropertyValue("userDetailsService", getUserDetailsService());
}
}
/**
* Obtains a user details service for use in RememberMeServices etc. Will return a caching version
* if available so should not be used for beans which need to separate the two.
*/
UserDetailsService getUserDetailsService() {
Map beans = beanFactory.getBeansOfType(CachingUserDetailsService.class);
if (beans.size() == 0) {
beans = beanFactory.getBeansOfType(UserDetailsService.class);
}
if (beans.size() == 0) {
throw new SecurityConfigurationException("No UserDetailsService registered.");
} else if (beans.size() > 1) {
throw new SecurityConfigurationException("More than one UserDetailsService registered. Please " +
"use a specific Id in your configuration");
}
return (UserDetailsService) beans.values().toArray()[0];
}
private UserDetailsService getCachingUserService(Object userServiceRef) {
Assert.isInstanceOf(RuntimeBeanReference.class, userServiceRef,
"userDetailsService property value must be a RuntimeBeanReference");
String id = ((RuntimeBeanReference)userServiceRef).getBeanName();
// Overwrite with the caching version if available
String cachingId = id + AbstractUserDetailsServiceBeanDefinitionParser.CACHING_SUFFIX;
if (beanFactory.containsBeanDefinition(cachingId)) {
return (UserDetailsService) beanFactory.getBean(cachingId);
}
return null;
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
}
}
@@ -6,7 +6,6 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.security.userdetails.memory.InMemoryDaoImpl;
import org.springframework.security.userdetails.memory.UserMap;
import org.springframework.security.userdetails.User;
import org.springframework.security.util.AuthorityUtils;
@@ -33,8 +32,8 @@ public class UserServiceBeanDefinitionParser extends AbstractUserDetailsServiceB
static final String ATT_DISABLED = "disabled";
static final String ATT_LOCKED = "locked";
protected Class getBeanClass(Element element) {
return InMemoryDaoImpl.class;
protected String getBeanClassName(Element element) {
return "org.springframework.security.userdetails.memory.InMemoryDaoImpl";
}
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
@@ -45,8 +45,8 @@ public class X509BeanDefinitionParser implements BeanDefinitionParser {
}
BeanDefinition provider = new RootBeanDefinition(PreAuthenticatedAuthenticationProvider.class);
ConfigUtils.getRegisteredProviders(parserContext).add(provider);
parserContext.getRegistry().registerBeanDefinition(BeanIds.X509_AUTH_PROVIDER, provider);
ConfigUtils.getRegisteredProviders(parserContext).add(new RuntimeBeanReference(BeanIds.X509_AUTH_PROVIDER));
String userServiceRef = element.getAttribute(ATT_USER_SERVICE_REF);
@@ -62,6 +62,7 @@ public class X509BeanDefinitionParser implements BeanDefinitionParser {
filterBuilder.addPropertyValue("authenticationManager", new RuntimeBeanReference(BeanIds.AUTHENTICATION_MANAGER));
parserContext.getRegistry().registerBeanDefinition(BeanIds.X509_FILTER, filterBuilder.getBeanDefinition());
ConfigUtils.addHttpFilter(parserContext, new RuntimeBeanReference(BeanIds.X509_FILTER));
return null;
}
@@ -28,6 +28,8 @@ import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
import org.springframework.security.AuthenticationTrustResolver;
import org.springframework.security.AuthenticationTrustResolverImpl;
import org.springframework.security.ui.SpringSecurityFilter;
import org.springframework.security.ui.FilterChainOrder;
@@ -150,6 +152,8 @@ public class HttpSessionContextIntegrationFilter extends SpringSecurityFilter im
* method.
*/
private boolean cloneFromHttpSession = false;
private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
public boolean isCloneFromHttpSession() {
return cloneFromHttpSession;
@@ -176,6 +180,8 @@ public class HttpSessionContextIntegrationFilter extends SpringSecurityFilter im
throw new IllegalArgumentException(
"If using forceEagerSessionCreation, you must set allowSessionCreation to also be true");
}
contextObject = generateNewContext();
}
public void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
@@ -320,7 +326,8 @@ public class HttpSessionContextIntegrationFilter extends SpringSecurityFilter im
/**
* Stores the supplied security context in the session (if available) and if it has changed since it was
* set at the start of the request.
* set at the start of the request. If the AuthenticationTrustResolver identifies the current user as
* anonymous, then the context will not be stored.
*
* @param securityContext the context object obtained from the SecurityContextHolder after the request has
* been processed by the filter chain. SecurityContextHolder.getContext() cannot be used to obtain
@@ -356,7 +363,7 @@ public class HttpSessionContextIntegrationFilter extends SpringSecurityFilter im
+ "(because the allowSessionCreation property is false) - SecurityContext thus not "
+ "stored for next request");
}
} else if (!contextObject.equals(securityContext)) {
} else if (!contextObject.equals(securityContext)) {
if (logger.isDebugEnabled()) {
logger.debug("HttpSession being created as SecurityContextHolder contents are non-default");
}
@@ -376,11 +383,18 @@ public class HttpSessionContextIntegrationFilter extends SpringSecurityFilter im
// If HttpSession exists, store current SecurityContextHolder contents but only if
// the SecurityContext has actually changed (see JIRA SEC-37)
if (httpSession != null && securityContext.hashCode() != contextHashBeforeChainExecution) {
httpSession.setAttribute(SPRING_SECURITY_CONTEXT_KEY, securityContext);
if (logger.isDebugEnabled()) {
logger.debug("SecurityContext stored to HttpSession: '" + securityContext + "'");
}
// See SEC-766
if (authenticationTrustResolver.isAnonymous(securityContext.getAuthentication())) {
if (logger.isDebugEnabled()) {
logger.debug("SecurityContext contents are anonymous - context will not be stored in HttpSession. ");
}
} else {
httpSession.setAttribute(SPRING_SECURITY_CONTEXT_KEY, securityContext);
if (logger.isDebugEnabled()) {
logger.debug("SecurityContext stored to HttpSession: '" + securityContext + "'");
}
}
}
}
@@ -61,7 +61,7 @@ import java.util.Collection;
* interceptor. It will also implement the proper handling of secure object invocations, namely:
* <ol>
* <li>Obtain the {@link Authentication} object from the {@link SecurityContextHolder}.</li>
* <li>Determine if the request relates to a secured or public invocation by ooking up the secure object request
* <li>Determine if the request relates to a secured or public invocation by looking up the secure object request
* against the {@link ObjectDefinitionSource}.</li>
* <li>For an invocation that is secured (there is a
* <code>ConfigAttributeDefinition</code> for the secure object invocation):
@@ -142,7 +142,7 @@ public final class ProtectPointcutPostProcessor implements BeanPostProcessor {
}
public void addPointcut(String pointcutExpression, ConfigAttributeDefinition definition) {
Assert.hasText(pointcutExpression, "An AspecTJ pointcut expression is required");
Assert.hasText(pointcutExpression, "An AspectJ pointcut expression is required");
Assert.notNull(definition, "ConfigAttributeDefinition required");
pointcutMap.put(pointcutExpression, definition);
@@ -23,13 +23,18 @@ import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.AbstractPointcutAdvisor;
import org.springframework.aop.support.StaticMethodMatcherPointcut;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.security.intercept.method.MethodDefinitionSource;
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 <code>MethodSecurityInterceptor</code> run and find out itself that it has no work to do.
* public (ie non-secure) methods.
* <p>
* 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
* {@link org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator}, which makes
@@ -42,22 +47,47 @@ import org.springframework.util.Assert;
* @author Ben Alex
* @version $Id$
*/
public class MethodDefinitionSourceAdvisor extends AbstractPointcutAdvisor {
public class MethodDefinitionSourceAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {
//~ Instance fields ================================================================================================
private MethodDefinitionSource attributeSource;
private MethodSecurityInterceptor interceptor;
private Pointcut pointcut;
private Pointcut pointcut = new MethodDefinitionSourcePointcut();
private BeanFactory beanFactory;
private String adviceBeanName;
private final Object adviceMonitor = new Object();
//~ Constructors ===================================================================================================
/**
* @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");
this.interceptor = advice;
Assert.notNull(advice.getObjectDefinitionSource(), "Cannot construct a MethodDefinitionSourceAdvisor using a MethodSecurityInterceptor that has no ObjectDefinitionSource configured");
this.attributeSource = advice.getObjectDefinitionSource();
this.pointcut = new MethodDefinitionSourcePointcut();
}
/**
* 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
* (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.
*
* @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;
}
//~ Methods ========================================================================================================
@@ -67,7 +97,20 @@ public class MethodDefinitionSourceAdvisor extends AbstractPointcutAdvisor {
}
public Advice getAdvice() {
return interceptor;
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;
}
//~ Inner Classes ==================================================================================================
@@ -78,7 +78,7 @@ public class DefaultFilterInvocationDefinitionSource implements FilterInvocation
DefaultFilterInvocationDefinitionSource(UrlMatcher urlMatcher) {
this.urlMatcher = urlMatcher;
}
/**
* Builds the internal request map from the supplied map. The key elements should be of type {@link RequestKey},
* which contains a URL path and an optional HTTP method (may be null). The path stored in the key will depend on
@@ -179,7 +179,7 @@ public class DefaultFilterInvocationDefinitionSource implements FilterInvocation
* @return the <code>ConfigAttributeDefinition</code> that applies to the specified <code>FilterInvocation</code>
* or null if no match is foud
*/
protected ConfigAttributeDefinition lookupAttributes(String url, String method) {
public ConfigAttributeDefinition lookupAttributes(String url, String method) {
if (stripQueryStringFromUrls) {
// Strip anything after a question mark symbol, as per SEC-161. See also SEC-321
int firstQuestionMarkIndex = url.indexOf("?");
@@ -252,7 +252,7 @@ public class DefaultFilterInvocationDefinitionSource implements FilterInvocation
return urlMatcher.requiresLowerCaseUrl();
}
protected void setStripQueryStringFromUrls(boolean stripQueryStringFromUrls) {
public void setStripQueryStringFromUrls(boolean stripQueryStringFromUrls) {
this.stripQueryStringFromUrls = stripQueryStringFromUrls;
}
}
@@ -3,6 +3,7 @@ package org.springframework.security.intercept.web;
/**
* @author Luke Taylor
* @version $Id$
* @since 2.0
*/
public class RequestKey {
private String url;
@@ -47,10 +48,10 @@ public class RequestKey {
return false;
}
if (method == null && key.method != null) {
return false;
if (method == null) {
return key.method == null;
}
return method.equals(key.method);
}
}
@@ -17,8 +17,6 @@ package org.springframework.security.ldap;
import org.springframework.security.GrantedAuthority;
import org.springframework.security.ldap.LdapDataAccessException;
import org.springframework.ldap.core.DirContextOperations;
@@ -42,7 +40,6 @@ public interface LdapAuthoritiesPopulator {
*
* @return the granted authorities for the given user.
*
* @throws LdapDataAccessException if there is a problem accessing the directory.
*/
GrantedAuthority[] getGrantedAuthorities(DirContextOperations userData, String username);
}
@@ -22,6 +22,7 @@ import org.springframework.ldap.core.ContextSource;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.ldap.core.DistinguishedName;
import org.springframework.ldap.core.LdapEncoder;
import org.springframework.util.Assert;
import org.apache.commons.logging.Log;
@@ -135,9 +136,16 @@ public class SpringSecurityLdapTemplate extends org.springframework.ldap.core.Ld
* @return the set of String values for the attribute as a union of the values found in all the matching entries.
*/
public Set searchForSingleAttributeValues(final String base, final String filter, final Object[] params,
final String attributeName) {
String formattedFilter = MessageFormat.format(filter, params);
final String attributeName) {
// Escape the params acording to RFC2254
Object[] encodedParams = new String[params.length];
for (int i=0; i < params.length; i++) {
encodedParams[i] = LdapEncoder.filterEncode(params[i].toString());
}
String formattedFilter = MessageFormat.format(filter, encodedParams);
logger.debug("Using filter: " + formattedFilter);
final HashSet set = new HashSet();
@@ -69,13 +69,13 @@ import java.util.Set;
* <pre>
* &lt;bean id="ldapAuthoritiesPopulator"
* class="org.springframework.security.providers.ldap.populator.DefaultLdapAuthoritiesPopulator">
* &lt;constructor-arg>&lt;ref local="contextSource"/>&lt;/constructor-arg>
* &lt;constructor-arg>&lt;value>ou=groups&lt;/value>&lt;/constructor-arg>
* &lt;property name="groupRoleAttribute">&lt;value>ou&lt;/value>&lt;/property>
* &lt;constructor-arg ref="contextSource"/>
* &lt;constructor-arg value="ou=groups"/>
* &lt;property name="groupRoleAttribute" value="ou"/>
* &lt;!-- the following properties are shown with their default values -->
* &lt;property name="searchSubTree">&lt;value>false&lt;/value>&lt;/property>
* &lt;property name="rolePrefix">&lt;value>ROLE_&lt;/value>&lt;/property>
* &lt;property name="convertToUpperCase">&lt;value>true&lt;/value>&lt;/property>
* &lt;property name="searchSubTree" value="false"/>
* &lt;property name="rolePrefix" value="ROLE_"/>
* &lt;property name="convertToUpperCase" value="true"/>
* &lt;/bean>
* </pre>
* A search for roles for user "uid=ben,ou=people,dc=springframework,dc=org" would return the single granted authority
@@ -284,6 +284,10 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
this.groupSearchFilter = groupSearchFilter;
}
/**
* Sets the prefix which will be prepended to the values loaded from the directory.
* Defaults to "ROLE_" for compatibility with <tt>RoleVoter/tt>.
*/
public void setRolePrefix(String rolePrefix) {
Assert.notNull(rolePrefix, "rolePrefix must not be null");
this.rolePrefix = rolePrefix;
@@ -158,7 +158,7 @@ public class JaasAuthenticationProvider implements AuthenticationProvider, Appli
Assert.hasLength(loginContextName, "loginContextName must be set on " + getClass());
configureJaas(loginConfig);
Assert.notNull(Configuration.getConfiguration(),
"As per http://java.sun.com/j2se/1.5.0/docs/api/javax/security/auth/login/Configuration.html "
+ "\"If a Configuration object was set via the Configuration.setConfiguration method, then that object is "
@@ -246,6 +246,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 +378,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));
}
}
/**
@@ -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;
}
}
@@ -0,0 +1,59 @@
package org.springframework.security.token;
import java.util.Date;
import org.springframework.util.Assert;
/**
* The default implementation of {@link Token}.
*
* @author Ben Alex
* @since 2.0.1
*/
public class DefaultToken implements Token {
private String key;
private long keyCreationTime;
private String extendedInformation;
public DefaultToken(String key, long keyCreationTime, String extendedInformation) {
Assert.hasText(key, "Key required");
Assert.notNull(extendedInformation, "Extended information cannot be null");
this.key = key;
this.keyCreationTime = keyCreationTime;
this.extendedInformation = extendedInformation;
}
public String getKey() {
return key;
}
public long getKeyCreationTime() {
return keyCreationTime;
}
public String getExtendedInformation() {
return extendedInformation;
}
public boolean equals(Object obj) {
if (obj != null && obj instanceof DefaultToken) {
DefaultToken rhs = (DefaultToken) obj;
return this.key.equals(rhs.key) && this.keyCreationTime == rhs.keyCreationTime && this.extendedInformation.equals(rhs.extendedInformation);
}
return false;
}
public int hashCode() {
int code = 979;
code = code * key.hashCode();
code = code * new Long(keyCreationTime).hashCode();
code = code * extendedInformation.hashCode();
return code;
}
public String toString() {
return "DefaultToken[key=" + new String(key) + "; creation=" + new Date(keyCreationTime) + "; extended=" + extendedInformation + "]";
}
}
@@ -0,0 +1,170 @@
package org.springframework.security.token;
import java.io.UnsupportedEncodingException;
import java.security.SecureRandom;
import java.util.Date;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.util.Sha512DigestUtils;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Basic implementation of {@link TokenService} that is compatible with clusters and across machine restarts,
* without requiring database persistence.
*
* <p>
* Keys are produced in the format:
* </p>
*
* <p>
* Base64(creationTime + ":" + hex(pseudoRandomNumber) + ":" + extendedInformation + ":" +
* Sha512Hex(creationTime + ":" + hex(pseudoRandomNumber) + ":" + extendedInformation + ":" + serverSecret) )
* </p>
*
* <p>
* In the above, <code>creationTime</code>, <code>tokenKey</code> and <code>extendedInformation</code>
* are equal to that stored in {@link Token}. The <code>Sha512Hex</code> includes the same payload,
* plus a <code>serverSecret</code>.
* </p>
*
* <p>
* The <code>serverSecret</code> varies every millisecond. It relies on two static server-side secrets. The first
* is a password, and the second is a server integer. Both of these must remain the same for any issued keys
* to subsequently be recognised. The applicable <code>serverSecret</code> in any millisecond is computed by
* <code>password</code> + ":" + (<code>creationTime</code> % <code>serverInteger</code>). This approach
* further obfuscates the actual server secret and renders attempts to compute the server secret more
* limited in usefulness (as any false tokens would be forced to have a <code>creationTime</code> equal
* to the computed hash). Recall that framework features depending on token services should reject tokens
* that are relatively old in any event.
* </p>
*
* <p>
* A further consideration of this class is the requirement for cryptographically strong pseudo-random numbers.
* To this end, the use of {@link SecureRandomFactoryBean} is recommended to inject the property.
* </p>
*
* <p>
* This implementation uses UTF-8 encoding internally for string manipulation.
* </p>
*
* @author Ben Alex
*
*/
public class KeyBasedPersistenceTokenService implements TokenService, InitializingBean {
private int pseudoRandomNumberBits = 256;
private String serverSecret;
private Integer serverInteger;
private SecureRandom secureRandom;
public Token allocateToken(String extendedInformation) {
Assert.notNull(extendedInformation, "Must provided non-null extendedInformation (but it can be empty)");
long creationTime = new Date().getTime();
String serverSecret = computeServerSecretApplicableAt(creationTime);
String pseudoRandomNumber = generatePseudoRandomNumber();
String content = new Long(creationTime).toString() + ":" + pseudoRandomNumber + ":" + extendedInformation;
// Compute key
String sha512Hex = Sha512DigestUtils.shaHex(content + ":" + serverSecret);
String keyPayload = content + ":" + sha512Hex;
String key = convertToString(Base64.encodeBase64(convertToBytes(keyPayload)));
return new DefaultToken(key, creationTime, extendedInformation);
}
public Token verifyToken(String key) {
if (key == null || "".equals(key)) {
return null;
}
String[] tokens = StringUtils.delimitedListToStringArray(convertToString(Base64.decodeBase64(convertToBytes(key))), ":");
Assert.isTrue(tokens.length >= 4, "Expected 4 or more tokens but found " + tokens.length);
long creationTime;
try {
creationTime = Long.decode(tokens[0]).longValue();
} catch (NumberFormatException nfe) {
throw new IllegalArgumentException("Expected number but found " + tokens[0]);
}
String serverSecret = computeServerSecretApplicableAt(creationTime);
String pseudoRandomNumber = tokens[1];
// Permit extendedInfo to itself contain ":" characters
StringBuffer extendedInfo = new StringBuffer();
for (int i = 2; i < tokens.length-1; i++) {
if (i > 2) {
extendedInfo.append(":");
}
extendedInfo.append(tokens[i]);
}
String sha1Hex = tokens[tokens.length-1];
// Verification
String content = new Long(creationTime).toString() + ":" + pseudoRandomNumber + ":" + extendedInfo.toString();
String expectedSha512Hex = Sha512DigestUtils.shaHex(content + ":" + serverSecret);
Assert.isTrue(expectedSha512Hex.equals(sha1Hex), "Key verification failure");
return new DefaultToken(key, creationTime, extendedInfo.toString());
}
private byte[] convertToBytes(String input) {
try {
return input.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
private String convertToString(byte[] bytes) {
try {
return new String(bytes, "UTF-8");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @return a pseduo random number (hex encoded)
*/
private String generatePseudoRandomNumber() {
byte[] randomizedBits = new byte[pseudoRandomNumberBits];
secureRandom.nextBytes(randomizedBits);
return new String(Hex.encodeHex(randomizedBits));
}
private String computeServerSecretApplicableAt(long time) {
return serverSecret + ":" + new Long(time % serverInteger.intValue()).intValue();
}
/**
* @param serverSecret the new secret, which can contain a ":" if desired (never being sent to the client)
*/
public void setServerSecret(String serverSecret) {
this.serverSecret = serverSecret;
}
public void setSecureRandom(SecureRandom secureRandom) {
this.secureRandom = secureRandom;
}
/**
* @param pseudoRandomNumberBits changes the number of bits issued (must be >= 0; defaults to 256)
*/
public void setPseudoRandomNumberBits(int pseudoRandomNumberBits) {
Assert.isTrue(pseudoRandomNumberBits >= 0, "Must have a positive pseudo random number bit size");
this.pseudoRandomNumberBits = pseudoRandomNumberBits;
}
public void setServerInteger(Integer serverInteger) {
this.serverInteger = serverInteger;
}
public void afterPropertiesSet() throws Exception {
Assert.hasText(serverSecret, "Server secret required");
Assert.notNull(serverInteger, "Server integer required");
Assert.notNull(secureRandom, "SecureRandom instance required");
}
}
@@ -0,0 +1,69 @@
package org.springframework.security.token;
import java.io.InputStream;
import java.security.SecureRandom;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
import org.springframework.util.FileCopyUtils;
/**
* Creates a {@link SecureRandom} instance.
*
* @author Ben Alex
* @since 2.0.1
*
*/
public class SecureRandomFactoryBean implements FactoryBean {
private String algorithm = "SHA1PRNG";
private Resource seed;
public Object getObject() throws Exception {
SecureRandom rnd = SecureRandom.getInstance(algorithm);
if (seed != null) {
// Seed specified, so use it
byte[] seedBytes = FileCopyUtils.copyToByteArray(seed.getInputStream());
rnd.setSeed(seedBytes);
} else {
// Request the next bytes, thus eagerly incurring the expense of default seeding
rnd.nextBytes(new byte[1]);
}
return rnd;
}
public Class getObjectType() {
return SecureRandom.class;
}
public boolean isSingleton() {
return false;
}
/**
* Allows the Pseudo Random Number Generator (PRNG) algorithm to be nominated. Defaults to
* SHA1PRNG.
*
* @param algorithm to use (mandatory)
*/
public void setAlgorithm(String algorithm) {
Assert.hasText(algorithm, "Algorithm required");
this.algorithm = algorithm;
}
/**
* Allows the user to specify a resource which will act as a seed for the {@link SecureRandom}
* instance. Specifically, the resource will be read into an {@link InputStream} and those
* bytes presented to the {@link SecureRandom#setSeed(byte[])} method. Note that this will
* simply supplement, rather than replace, the existing seed. As such, it is always safe to
* set a seed using this method (it never reduces randomness).
*
* @param seed to use, or <code>null</code> if no additional seeding is needed
*/
public void setSeed(Resource seed) {
this.seed = seed;
}
}
@@ -0,0 +1,45 @@
package org.springframework.security.token;
/**
* A token issued by {@link TokenService}.
*
* <p>
* It is important that the keys assigned to tokens are sufficiently randomised and secured that
* they can serve as identifying a unique user session. Implementations of {@link TokenService}
* are free to use encryption or encoding strategies of their choice. It is strongly recommended that
* keys are of sufficient length to balance safety against persistence cost. In relation to persistence
* cost, it is strongly recommended that returned keys are small enough for encoding in a cookie.
* </p>
*
* @author Ben Alex
* @since 2.0.1
*/
public interface Token {
/**
* Obtains the randomised, secure key assigned to this token. Presentation of this token to
* {@link TokenService} will always return a <code>Token</code> that is equal to the original
* <code>Token</code> issued for that key.
*
* @return a key with appropriate randomness and security.
*/
String getKey();
/**
* The time the token key was initially created is available from this method. Note that a given
* token must never have this creation time changed. If necessary, a new token can be
* requested from the {@link TokenService} to replace the original token.
*
* @return the time this token key was created, in the same format as specified by {@link Date#getTime()).
*/
long getKeyCreationTime();
/**
* Obtains the extended information associated within the token, which was presented when the token
* was first created.
*
* @return the user-specified extended information, if any
*/
String getExtendedInformation();
}
@@ -0,0 +1,46 @@
package org.springframework.security.token;
/**
* Provides a mechanism to allocate and rebuild secure, randomised tokens.
*
* <p>
* Implementations are solely concern with issuing a new {@link Token} on demand. The
* issued <code>Token</code> may contain user-specified extended information. The token also
* contains a cryptographically strong, byte array-based key. This permits the token to be
* used to identify a user session, if desired. The key can subsequently be re-presented
* to the <code>TokenService</code> for verification and reconstruction of a <code>Token</code>
* equal to the original <code>Token</code>.
* </p>
*
* <p>
* Given the tightly-focused behaviour provided by this interface, it can serve as a building block
* for more sophisticated token-based solutions. For example, authentication systems that depend on
* stateless session keys. These could, for instance, place the username inside the user-specified
* extended information associated with the key). It is important to recognise that we do not intend
* for this interface to be expanded to provide such capabilities directly.
* </p>
*
* @author Ben Alex
* @since 2.0.1
*
*/
public interface TokenService {
/**
* Forces the allocation of a new {@link Token}.
*
* @param the extended information desired in the token (cannot be <code>null</code>, but can be empty)
* @return a new token that has not been issued previously, and is guaranteed to be recognised
* by this implementation's {@link #verifyToken(String)} at any future time.
*/
Token allocateToken(String extendedInformation);
/**
* Permits verification the <{@link Token#getKey()} was issued by this <code>TokenService</code> and
* reconstructs the corresponding <code>Token</code>.
*
* @param key as obtained from {@link Token#getKey()} and created by this implementation
* @return the token, or <code>null</code> if the token was not issued by this <code>TokenService</code>
*/
Token verifyToken(String key);
}
@@ -21,7 +21,9 @@ import org.springframework.security.AuthenticationException;
import org.springframework.security.AuthenticationManager;
import org.springframework.security.util.RedirectUtils;
import org.springframework.security.util.SessionUtils;
import org.springframework.security.util.UrlUtils;
import org.springframework.security.concurrent.SessionRegistry;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.security.event.authentication.InteractiveAuthenticationSuccessEvent;
@@ -43,10 +45,6 @@ import org.springframework.util.Assert;
import java.io.IOException;
import java.util.Properties;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
@@ -210,13 +208,18 @@ public abstract class AbstractProcessingFilter extends SpringSecurityFilter impl
private boolean allowSessionCreation = true;
private boolean serverSideRedirect = false;
private SessionRegistry sessionRegistry;
//~ Methods ========================================================================================================
public void afterPropertiesSet() throws Exception {
Assert.hasLength(filterProcessesUrl, "filterProcessesUrl must be specified");
Assert.isTrue(UrlUtils.isValidRedirectUrl(filterProcessesUrl), filterProcessesUrl + " isn't a valid redirect URL");
Assert.hasLength(defaultTargetUrl, "defaultTargetUrl must be specified");
Assert.isTrue(UrlUtils.isValidRedirectUrl(defaultTargetUrl), defaultTargetUrl + " isn't a valid redirect URL");
// Assert.hasLength(authenticationFailureUrl, "authenticationFailureUrl must be specified");
Assert.isTrue(UrlUtils.isValidRedirectUrl(authenticationFailureUrl), authenticationFailureUrl + " isn't a valid redirect URL");
Assert.notNull(authenticationManager, "authenticationManager must be specified");
Assert.notNull(rememberMeServices, "rememberMeServices cannot be null");
Assert.notNull(targetUrlResolver, "targetUrlResolver cannot be null");
@@ -355,7 +358,7 @@ public abstract class AbstractProcessingFilter extends SpringSecurityFilter impl
}
if (invalidateSessionOnSuccessfulAuthentication) {
SessionUtils.startNewSessionIfRequired(request, migrateInvalidatedSessionAttributes, null);
SessionUtils.startNewSessionIfRequired(request, migrateInvalidatedSessionAttributes, sessionRegistry);
}
String targetUrl = determineTargetUrl(request);
@@ -567,5 +570,13 @@ public abstract class AbstractProcessingFilter extends SpringSecurityFilter impl
*/
public void setServerSideRedirect(boolean serverSideRedirect) {
this.serverSideRedirect = serverSideRedirect;
}
}
/**
* The session registry needs to be set if session fixation attack protection is in use (and concurrent
* session control is enabled).
*/
public void setSessionRegistry(SessionRegistry sessionRegistry) {
this.sessionRegistry = sessionRegistry;
}
}
@@ -54,7 +54,7 @@ public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
//~ Methods ========================================================================================================
public void handle(ServletRequest request, ServletResponse response, AccessDeniedException accessDeniedException)
throws IOException, ServletException {
throws IOException, ServletException {
if (errorPage != null) {
// Put exception into request scope (perhaps of use to a view)
((HttpServletRequest) request).setAttribute(SPRING_SECURITY_ACCESS_DENIED_EXCEPTION_KEY,
@@ -80,7 +80,7 @@ public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
*/
public void setErrorPage(String errorPage) {
if ((errorPage != null) && !errorPage.startsWith("/")) {
throw new IllegalArgumentException("ErrorPage must begin with '/'");
throw new IllegalArgumentException("errorPage must begin with '/'");
}
this.errorPage = errorPage;
@@ -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;
}
@@ -22,8 +22,7 @@ public abstract class FilterChainOrder {
public static final int CHANNEL_FILTER = FILTER_CHAIN_FIRST;
public static final int CONCURRENT_SESSION_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
public static final int HTTP_SESSION_CONTEXT_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
public static final int SESSION_FIXATION_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
public static final int HTTP_SESSION_CONTEXT_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
public static final int LOGOUT_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
public static final int X509_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
public static final int PRE_AUTH_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
@@ -37,6 +36,7 @@ public abstract class FilterChainOrder {
public static final int ANONYMOUS_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
public static final int EXCEPTION_TRANSLATION_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
public static final int NTLM_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
public static final int SESSION_FIXATION_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
public static final int FILTER_SECURITY_INTERCEPTOR = FILTER_CHAIN_FIRST + INTERVAL * i++;
public static final int SWITCH_USER_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
@@ -46,7 +46,6 @@ public abstract class FilterChainOrder {
filterNameToOrder.put("FIRST", new Integer(Integer.MIN_VALUE));
filterNameToOrder.put("CHANNEL_FILTER", new Integer(CHANNEL_FILTER));
filterNameToOrder.put("CONCURRENT_SESSION_FILTER", new Integer(CONCURRENT_SESSION_FILTER));
filterNameToOrder.put("SESSION_CONTEXT_INTEGRATION_FILTER", new Integer(HTTP_SESSION_CONTEXT_FILTER));
filterNameToOrder.put("LOGOUT_FILTER", new Integer(LOGOUT_FILTER));
filterNameToOrder.put("X509_FILTER", new Integer(X509_FILTER));
filterNameToOrder.put("PRE_AUTH_FILTER", new Integer(PRE_AUTH_FILTER));
@@ -59,6 +58,7 @@ public abstract class FilterChainOrder {
filterNameToOrder.put("ANONYMOUS_FILTER", new Integer(ANONYMOUS_FILTER));
filterNameToOrder.put("EXCEPTION_TRANSLATION_FILTER", new Integer(EXCEPTION_TRANSLATION_FILTER));
filterNameToOrder.put("NTLM_FILTER", new Integer(NTLM_FILTER));
filterNameToOrder.put("SESSION_CONTEXT_INTEGRATION_FILTER", new Integer(HTTP_SESSION_CONTEXT_FILTER));
filterNameToOrder.put("FILTER_SECURITY_INTERCEPTOR", new Integer(FILTER_SECURITY_INTERCEPTOR));
filterNameToOrder.put("SWITCH_USER_FILTER", new Integer(SWITCH_USER_FILTER));
filterNameToOrder.put("LAST", new Integer(Integer.MAX_VALUE));
@@ -6,24 +6,23 @@ import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import javax.servlet.http.HttpSession;
import org.springframework.security.Authentication;
import org.springframework.security.AuthenticationTrustResolver;
import org.springframework.security.AuthenticationTrustResolverImpl;
import org.springframework.security.concurrent.SessionRegistry;
import org.springframework.security.context.HttpSessionContextIntegrationFilter;
import org.springframework.security.context.SecurityContext;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.security.util.SessionUtils;
/**
* Detects that a user has been authenticated since the start of the request and starts a new session.
* <p>
* This is essentially a generalization of the functionality that was implemented for SEC-399. Additionally, it will
* update the configured SessionRegistry if one is in use, thus preventing problems when used with Spring Security's
* concurrent session control.
* <p>
* If the response has already been committed when the filter checks the authentication state, then it isn't possible
* to create a new session and the filter will print a warning to that effect.
* This is essentially a generalization of the functionality that was implemented for SEC-399.
* Additionally, it will update the configured SessionRegistry if one is in use, thus preventing problems when used
* with Spring Security's concurrent session control.
*
* @author Martin Algesten
* @author Luke Taylor
@@ -55,22 +54,17 @@ public class SessionFixationProtectionFilter extends SpringSecurityFilter {
}
request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
if (isAuthenticated()) {
// We don't have to worry about session fixation attack if already authenticated
chain.doFilter(request, response);
return;
}
HttpSession session = request.getSession();
SecurityContext sessionSecurityContext =
(SecurityContext) session.getAttribute(HttpSessionContextIntegrationFilter.SPRING_SECURITY_CONTEXT_KEY);
SessionFixationProtectionResponseWrapper wrapper =
new SessionFixationProtectionResponseWrapper(response, request);
try {
chain.doFilter(request, wrapper);
} finally {
if (!wrapper.isNewSessionStarted()) {
startNewSessionIfRequired(request, response);
}
if (sessionSecurityContext == null && isAuthenticated()) {
// The user has been authenticated during the current request, so do the session migration
startNewSessionIfRequired(request, response);
}
chain.doFilter(request, response);
}
private boolean isAuthenticated() {
@@ -92,83 +86,12 @@ public class SessionFixationProtectionFilter extends SpringSecurityFilter {
}
/**
* Called when the an initially unauthenticated request completes or a redirect or sendError occurs.
* Called when the a user wasn't authenticated at the start of the request but has been during it
* <p>
* If the user is now authenticated, a new session will be created, the session attributes copied to it (if
* <tt>migrateSessionAttributes</tt> is set and the sessionRegistry updated with the new session information.
* A new session will be created, the session attributes copied to it (if
* <tt>migrateSessionAttributes</tt> is set) and the sessionRegistry updated with the new session information.
*/
protected void startNewSessionIfRequired(HttpServletRequest request, HttpServletResponse response) {
if (isAuthenticated()) {
if (request.getSession(false) != null && response.isCommitted()) {
logger.warn("Response is already committed. Unable to create new session.");
}
SessionUtils.startNewSessionIfRequired(request, migrateSessionAttributes, sessionRegistry);
}
}
/**
* Response wrapper to handle the situation where we need to migrate the session after a redirect or sendError.
* Similar in function to Martin Algesten's OnRedirectUpdateSessionResponseWrapper used in
* HttpSessionContextIntegrationFilter.
* <p>
* Only used to wrap the response if the conditions are right at the start of the request to potentially
* require starting a new session, i.e. that the user isn't authenticated and a session existed to begin with.
*/
class SessionFixationProtectionResponseWrapper extends HttpServletResponseWrapper {
private HttpServletRequest request;
private boolean newSessionStarted;
SessionFixationProtectionResponseWrapper(HttpServletResponse response, HttpServletRequest request) {
super(response);
this.request = request;
}
/**
* Makes sure a new session is created before calling the
* superclass <code>sendError()</code>
*/
public void sendError(int sc) throws IOException {
startNewSession();
super.sendError(sc);
}
/**
* Makes sure a new session is created before calling the
* superclass <code>sendError()</code>
*/
public void sendError(int sc, String msg) throws IOException {
startNewSession();
super.sendError(sc, msg);
}
/**
* Makes sure a new session is created before calling the
* superclass <code>sendRedirect()</code>
*/
public void sendRedirect(String location) throws IOException {
startNewSession();
super.sendRedirect(location);
}
public void flushBuffer() throws IOException {
startNewSession();
super.flushBuffer();
}
/**
* Calls <code>startNewSessionIfRequired()</code>
*/
private void startNewSession() {
if (newSessionStarted) {
return;
}
startNewSessionIfRequired(request, this);
newSessionStarted = true;
}
boolean isNewSessionStarted() {
return newSessionStarted;
}
protected void startNewSessionIfRequired(HttpServletRequest request, HttpServletResponse response) {
SessionUtils.startNewSessionIfRequired(request, migrateSessionAttributes, sessionRegistry);
}
}
@@ -56,6 +56,6 @@ public abstract class SpringSecurityFilter implements Filter, Ordered {
protected abstract void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException;
public String toString() {
return getClass() + "[ order=" + getOrder() + "; ]";
return getClass().getName() + "[ order=" + getOrder() + "; ]";
}
}
@@ -25,6 +25,7 @@ import org.springframework.security.ui.savedrequest.SavedRequest;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Default implementation for {@link TargetUrlResolver}
* <p>
@@ -46,11 +47,10 @@ public class TargetUrlResolverImpl implements TargetUrlResolver {
/**
* If <code>true</code>, will only use <code>SavedRequest</code> to determine the target URL on successful
* authentication if the request that caused the authentication request was a GET.
* It will return null for a POST/PUT request.
* In most cases it's meaningless to redirect to a URL generated by a POST/PUT request.
* Defaults to true.
* It will then return null for a POST/PUT request.
* Defaults to false.
*/
private boolean justUseSavedRequestOnGet = true;
private boolean justUseSavedRequestOnGet = false;
/* (non-Javadoc)
* @see org.acegisecurity.ui.TargetUrlResolver#determineTargetUrl(org.acegisecurity.ui.savedrequest.SavedRequest, javax.servlet.http.HttpServletRequest, org.acegisecurity.Authentication)
@@ -93,6 +93,7 @@ public class BasicProcessingFilter extends SpringSecurityFilter implements Initi
private AuthenticationManager authenticationManager;
private RememberMeServices rememberMeServices;
private boolean ignoreFailure = false;
private String credentialsCharset = "UTF-8";
//~ Methods ========================================================================================================
@@ -114,8 +115,8 @@ public class BasicProcessingFilter extends SpringSecurityFilter implements Initi
}
if ((header != null) && header.startsWith("Basic ")) {
String base64Token = header.substring(6);
String token = new String(Base64.decodeBase64(base64Token.getBytes()));
byte[] base64Token = header.substring(6).getBytes("UTF-8");
String token = new String(Base64.decodeBase64(base64Token), getCredentialsCharset(httpRequest));
String username = "";
String password = "";
@@ -172,7 +173,7 @@ public class BasicProcessingFilter extends SpringSecurityFilter implements Initi
chain.doFilter(httpRequest, httpResponse);
}
private boolean authenticationIsRequired(String username) {
private boolean authenticationIsRequired(String username) {
// Only reauthenticate if username doesn't match SecurityContextHolder and user isn't authenticated
// (see SEC-53)
Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication();
@@ -235,7 +236,16 @@ public class BasicProcessingFilter extends SpringSecurityFilter implements Initi
this.rememberMeServices = rememberMeServices;
}
public int getOrder() {
public void setCredentialsCharset(String credentialsCharset) {
Assert.hasText(credentialsCharset, "credentialsCharset cannot be null or empty");
this.credentialsCharset = credentialsCharset;
}
protected String getCredentialsCharset(HttpServletRequest httpRequest) {
return credentialsCharset;
}
public int getOrder() {
return FilterChainOrder.BASIC_PROCESSING_FILTER;
}
}
@@ -26,6 +26,7 @@ import org.springframework.security.Authentication;
import org.springframework.security.ui.SpringSecurityFilter;
import org.springframework.security.ui.FilterChainOrder;
import org.springframework.security.util.RedirectUtils;
import org.springframework.security.util.UrlUtils;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -58,6 +59,7 @@ public class LogoutFilter extends SpringSecurityFilter {
public LogoutFilter(String logoutSuccessUrl, LogoutHandler[] handlers) {
Assert.notEmpty(handlers, "LogoutHandlers are required");
this.logoutSuccessUrl = logoutSuccessUrl;
Assert.isTrue(UrlUtils.isValidRedirectUrl(logoutSuccessUrl), logoutSuccessUrl + " isn't a valid redirect URL");
this.handlers = handlers;
}
@@ -130,7 +132,7 @@ public class LogoutFilter extends SpringSecurityFilter {
String targetUrl = request.getParameter("logoutSuccessUrl");
if(!StringUtils.hasLength(targetUrl)) {
targetUrl = logoutSuccessUrl;
targetUrl = getLogoutSuccessUrl();
}
if (!StringUtils.hasLength(targetUrl)) {
@@ -161,9 +163,14 @@ public class LogoutFilter extends SpringSecurityFilter {
public void setFilterProcessesUrl(String filterProcessesUrl) {
Assert.hasText(filterProcessesUrl, "FilterProcessesUrl required");
Assert.isTrue(UrlUtils.isValidRedirectUrl(filterProcessesUrl), filterProcessesUrl + " isn't a valid redirect URL");
this.filterProcessesUrl = filterProcessesUrl;
}
protected String getLogoutSuccessUrl() {
return logoutSuccessUrl;
}
protected String getFilterProcessesUrl() {
return filterProcessesUrl;
}
@@ -24,171 +24,182 @@ import org.apache.commons.logging.LogFactory;
* @since 2.0
*/
final class WASSecurityHelper {
private static final Log logger = LogFactory.getLog(WASSecurityHelper.class);
private static final Log logger = LogFactory.getLog(WASSecurityHelper.class);
private static final String USER_REGISTRY = "UserRegistry";
private static final String USER_REGISTRY = "UserRegistry";
private static Method getRunAsSubject = null;
private static Method getRunAsSubject = null;
private static Method getWSCredentialFromSubject = null;
private static Method getGroupsForUser = null;
private static Method getGroupsForUser = null;
private static Method getSecurityName = null;
private static Method getSecurityName = null;
// SEC-803
private static Class wsCredentialClass = null;
/**
* Get the security name for the given subject.
*
* @param subject
* The subject for which to retrieve the security name
* @return String the security name for the given subject
*/
private static final String getSecurityName(final Subject subject) {
if (logger.isDebugEnabled()) {
logger.debug("Determining Websphere security name for subject " + subject);
}
String userSecurityName = null;
if (subject != null) {
// SEC-803
Object credential = subject.getPublicCredentials(getWSCredentialClass()).iterator().next();
if (credential != null) {
userSecurityName = (String)invokeMethod(getSecurityNameMethod(),credential,null);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Websphere security name is " + userSecurityName + " for subject " + subject);
}
return userSecurityName;
}
/**
* Get the security name for the given subject.
*
* @param subject
* The subject for which to retrieve the security name
* @return String the security name for the given subject
*/
private static final String getSecurityName(final Subject subject) {
if (logger.isDebugEnabled()) {
logger.debug("Determining Websphere security name for subject " + subject);
}
String userSecurityName = null;
if (subject != null) {
Object credential = invokeMethod(getWSCredentialFromSubjectMethod(),null,new Object[]{subject});
if (credential != null) {
userSecurityName = (String)invokeMethod(getSecurityNameMethod(),credential,null);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Websphere security name is " + userSecurityName + " for subject " + subject);
}
return userSecurityName;
}
/**
* Get the current RunAs subject.
*
* @return Subject the current RunAs subject
*/
private static final Subject getRunAsSubject() {
logger.debug("Retrieving WebSphere RunAs subject");
// get Subject: WSSubject.getCallerSubject ();
return (Subject) invokeMethod(getRunAsSubjectMethod(), null, new Object[] {});
}
/**
* Get the current RunAs subject.
*
* @return Subject the current RunAs subject
*/
private static final Subject getRunAsSubject() {
logger.debug("Retrieving WebSphere RunAs subject");
// get Subject: WSSubject.getCallerSubject ();
return (Subject) invokeMethod(getRunAsSubjectMethod(), null, new Object[] {});
}
/**
* Get the WebSphere group names for the given subject.
*
* @param subject
* The subject for which to retrieve the WebSphere group names
* @return the WebSphere group names for the given subject
*/
private static final String[] getWebSphereGroups(final Subject subject) {
return getWebSphereGroups(getSecurityName(subject));
}
/**
* Get the WebSphere group names for the given subject.
*
* @param subject
* The subject for which to retrieve the WebSphere group names
* @return the WebSphere group names for the given subject
*/
private static final String[] getWebSphereGroups(final Subject subject) {
return getWebSphereGroups(getSecurityName(subject));
}
/**
* Get the WebSphere group names for the given security name.
*
* @param securityName
* The securityname for which to retrieve the WebSphere group names
* @return the WebSphere group names for the given security name
*/
private static final String[] getWebSphereGroups(final String securityName) {
Context ic = null;
try {
// TODO: Cache UserRegistry object
ic = new InitialContext();
Object objRef = ic.lookup(USER_REGISTRY);
Object userReg = PortableRemoteObject.narrow(objRef, Class.forName ("com.ibm.websphere.security.UserRegistry"));
if (logger.isDebugEnabled()) {
logger.debug("Determining WebSphere groups for user " + securityName + " using WebSphere UserRegistry " + userReg);
}
final Collection groups = (Collection) invokeMethod(getGroupsForUserMethod(), userReg, new Object[]{ securityName });
if (logger.isDebugEnabled()) {
logger.debug("Groups for user " + securityName + ": " + groups.toString());
}
String[] result = new String[groups.size()];
return (String[]) groups.toArray(result);
} catch (Exception e) {
logger.error("Exception occured while looking up groups for user", e);
throw new RuntimeException("Exception occured while looking up groups for user", e);
} finally {
try {
ic.close();
} catch (NamingException e) {
logger.debug("Exception occured while closing context", e);
}
}
}
/**
* Get the WebSphere group names for the given security name.
*
* @param securityName
* The securityname for which to retrieve the WebSphere group names
* @return the WebSphere group names for the given security name
*/
private static final String[] getWebSphereGroups(final String securityName) {
Context ic = null;
try {
// TODO: Cache UserRegistry object
ic = new InitialContext();
Object objRef = ic.lookup(USER_REGISTRY);
Object userReg = PortableRemoteObject.narrow(objRef, Class.forName ("com.ibm.websphere.security.UserRegistry"));
if (logger.isDebugEnabled()) {
logger.debug("Determining WebSphere groups for user " + securityName + " using WebSphere UserRegistry " + userReg);
}
final Collection groups = (Collection) invokeMethod(getGroupsForUserMethod(), userReg, new Object[]{ securityName });
if (logger.isDebugEnabled()) {
logger.debug("Groups for user " + securityName + ": " + groups.toString());
}
String[] result = new String[groups.size()];
return (String[]) groups.toArray(result);
} catch (Exception e) {
logger.error("Exception occured while looking up groups for user", e);
throw new RuntimeException("Exception occured while looking up groups for user", e);
} finally {
try {
ic.close();
} catch (NamingException e) {
logger.debug("Exception occured while closing context", e);
}
}
}
/**
* @return
*/
public static final String[] getGroupsForCurrentUser() {
return getWebSphereGroups(getRunAsSubject());
}
/**
* @return
*/
public static final String[] getGroupsForCurrentUser() {
return getWebSphereGroups(getRunAsSubject());
}
public static final String getCurrentUserName() {
return getSecurityName(getRunAsSubject());
}
private static final Object invokeMethod(Method method, Object instance, Object[] args)
{
try {
return method.invoke(instance,args);
} catch (IllegalArgumentException e) {
logger.error("Error while invoking method "+method.getClass().getName()+"."+method.getName()+"("+ Arrays.asList(args)+")",e);
throw new RuntimeException("Error while invoking method "+method.getClass().getName()+"."+method.getName()+"("+Arrays.asList(args)+")",e);
} catch (IllegalAccessException e) {
logger.error("Error while invoking method "+method.getClass().getName()+"."+method.getName()+"("+Arrays.asList(args)+")",e);
throw new RuntimeException("Error while invoking method "+method.getClass().getName()+"."+method.getName()+"("+Arrays.asList(args)+")",e);
} catch (InvocationTargetException e) {
logger.error("Error while invoking method "+method.getClass().getName()+"."+method.getName()+"("+Arrays.asList(args)+")",e);
throw new RuntimeException("Error while invoking method "+method.getClass().getName()+"."+method.getName()+"("+Arrays.asList(args)+")",e);
}
}
public static final String getCurrentUserName() {
return getSecurityName(getRunAsSubject());
}
private static final Object invokeMethod(Method method, Object instance, Object[] args)
{
try {
return method.invoke(instance,args);
} catch (IllegalArgumentException e) {
logger.error("Error while invoking method "+method.getClass().getName()+"."+method.getName()+"("+ Arrays.asList(args)+")",e);
throw new RuntimeException("Error while invoking method "+method.getClass().getName()+"."+method.getName()+"("+Arrays.asList(args)+")",e);
} catch (IllegalAccessException e) {
logger.error("Error while invoking method "+method.getClass().getName()+"."+method.getName()+"("+Arrays.asList(args)+")",e);
throw new RuntimeException("Error while invoking method "+method.getClass().getName()+"."+method.getName()+"("+Arrays.asList(args)+")",e);
} catch (InvocationTargetException e) {
logger.error("Error while invoking method "+method.getClass().getName()+"."+method.getName()+"("+Arrays.asList(args)+")",e);
throw new RuntimeException("Error while invoking method "+method.getClass().getName()+"."+method.getName()+"("+Arrays.asList(args)+")",e);
}
}
private static final Method getMethod(String className, String methodName, String[] parameterTypeNames) {
try {
Class c = Class.forName(className);
final int len = parameterTypeNames.length;
Class[] parameterTypes = new Class[len];
for (int i = 0; i < len; i++) {
parameterTypes[i] = Class.forName(parameterTypeNames[i]);
}
return c.getDeclaredMethod(methodName, parameterTypes);
} catch (ClassNotFoundException e) {
logger.error("Required class"+className+" not found");
throw new RuntimeException("Required class"+className+" not found",e);
} catch (NoSuchMethodException e) {
logger.error("Required method "+methodName+" with parameter types ("+ Arrays.asList(parameterTypeNames) +") not found on class "+className);
throw new RuntimeException("Required class"+className+" not found",e);
}
}
private static final Method getMethod(String className, String methodName, String[] parameterTypeNames) {
try {
Class c = Class.forName(className);
final int len = parameterTypeNames.length;
Class[] parameterTypes = new Class[len];
for (int i = 0; i < len; i++) {
parameterTypes[i] = Class.forName(parameterTypeNames[i]);
}
return c.getDeclaredMethod(methodName, parameterTypes);
} catch (ClassNotFoundException e) {
logger.error("Required class"+className+" not found");
throw new RuntimeException("Required class"+className+" not found",e);
} catch (NoSuchMethodException e) {
logger.error("Required method "+methodName+" with parameter types ("+ Arrays.asList(parameterTypeNames) +") not found on class "+className);
throw new RuntimeException("Required class"+className+" not found",e);
}
}
private static final Method getRunAsSubjectMethod() {
if (getRunAsSubject == null) {
getRunAsSubject = getMethod("com.ibm.websphere.security.auth.WSSubject", "getRunAsSubject", new String[] {});
}
return getRunAsSubject;
}
private static final Method getRunAsSubjectMethod() {
if (getRunAsSubject == null) {
getRunAsSubject = getMethod("com.ibm.websphere.security.auth.WSSubject", "getRunAsSubject", new String[] {});
}
return getRunAsSubject;
}
private static final Method getGroupsForUserMethod() {
if (getGroupsForUser == null) {
getGroupsForUser = getMethod("com.ibm.websphere.security.UserRegistry", "getGroupsForUser", new String[] { "java.lang.String" });
}
return getGroupsForUser;
}
private static final Method getWSCredentialFromSubjectMethod() {
if (getWSCredentialFromSubject == null) {
getWSCredentialFromSubject = getMethod("com.ibm.ws.security.auth.SubjectHelper", "getWSCredentialFromSubject",
new String[] { "javax.security.auth.Subject" });
}
return getWSCredentialFromSubject;
}
private static final Method getGroupsForUserMethod() {
if (getGroupsForUser == null) {
getGroupsForUser = getMethod("com.ibm.websphere.security.UserRegistry", "getGroupsForUser", new String[] { "java.lang.String" });
}
return getGroupsForUser;
}
private static final Method getSecurityNameMethod() {
if (getSecurityName == null) {
getSecurityName = getMethod("com.ibm.websphere.security.cred.WSCredential", "getSecurityName", new String[] {});
}
return getSecurityName;
}
private static final Method getSecurityNameMethod() {
if (getSecurityName == null) {
getSecurityName = getMethod("com.ibm.websphere.security.cred.WSCredential", "getSecurityName", new String[] {});
}
return getSecurityName;
}
// SEC-803
private static final Class getWSCredentialClass() {
if (wsCredentialClass == null) {
wsCredentialClass = getClass("com.ibm.websphere.security.cred.WSCredential");
}
return wsCredentialClass;
}
private static final Class getClass(String className) {
try {
return Class.forName(className);
} catch (ClassNotFoundException e) {
logger.error("Required class " + className + " not found");
throw new RuntimeException("Required class " + className + " not found",e);
}
}
}
@@ -1,6 +1,7 @@
package org.springframework.security.ui.preauth.websphere;
import org.apache.commons.lang.ArrayUtils;
import java.util.Arrays;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
@@ -68,8 +69,8 @@ public class WebSpherePreAuthenticatedAuthenticationDetailsSource extends Authen
String[] webSphereGroups = WASSecurityHelper.getGroupsForCurrentUser();
GrantedAuthority[] userGas = webSphereGroups2GrantedAuthoritiesMapper.getGrantedAuthorities(webSphereGroups);
if (LOG.isDebugEnabled()) {
LOG.debug("WebSphere groups [" + ArrayUtils.toString(webSphereGroups) + "] mapped to Granted Authorities: ["
+ ArrayUtils.toString(userGas) + "]");
LOG.debug("WebSphere groups: " + Arrays.asList(webSphereGroups) + " mapped to Granted Authorities: "
+ Arrays.asList(userGas));
}
return userGas;
}
@@ -313,7 +313,14 @@ public abstract class AbstractRememberMeServices implements RememberMeServices,
this.alwaysRemember = alwaysRemember;
}
/**
* Sets the name of the parameter which should be checked for to see if a remember-me has been requested
* during a login request. This should be the same name you assign to the checkbox in your login form.
*
* @param parameter the HTTP request parameter
*/
public void setParameter(String parameter) {
Assert.hasText(parameter, "Parameter name cannot be null");
this.parameter = parameter;
}
@@ -326,6 +333,7 @@ public abstract class AbstractRememberMeServices implements RememberMeServices,
}
public void setUserDetailsService(UserDetailsService userDetailsService) {
Assert.notNull(userDetailsService, "UserDetailsService canot be null");
this.userDetailsService = userDetailsService;
}
@@ -348,4 +356,9 @@ public abstract class AbstractRememberMeServices implements RememberMeServices,
protected AuthenticationDetailsSource getAuthenticationDetailsSource() {
return authenticationDetailsSource;
}
public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) {
Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource cannot be null");
this.authenticationDetailsSource = authenticationDetailsSource;
}
}
@@ -152,7 +152,7 @@ public class TokenBasedRememberMeServices extends AbstractRememberMeServices {
}
int tokenLifetime = calculateLoginLifetime(request, successfulAuthentication);
long expiryTime = System.currentTimeMillis() + 1000*tokenLifetime;
long expiryTime = System.currentTimeMillis() + 1000L*tokenLifetime;
String signatureValue = makeTokenSignature(expiryTime, username, password);
@@ -22,6 +22,7 @@ import org.springframework.security.providers.UsernamePasswordAuthenticationToke
import org.springframework.security.ui.AbstractProcessingFilter;
import org.springframework.security.ui.FilterChainOrder;
import org.springframework.security.util.TextUtils;
import org.springframework.util.Assert;
import javax.servlet.http.HttpServletRequest;
@@ -72,7 +73,7 @@ public class AuthenticationProcessingFilter extends AbstractProcessingFilter {
HttpSession session = request.getSession(false);
if (session != null || getAllowSessionCreation()) {
request.getSession().setAttribute(SPRING_SECURITY_LAST_USERNAME_KEY, username);
request.getSession().setAttribute(SPRING_SECURITY_LAST_USERNAME_KEY, TextUtils.escapeEntities(username));
}
// Allow subclasses to set the "details" property
@@ -23,6 +23,7 @@ import org.springframework.security.ui.rememberme.AbstractRememberMeServices;
*
* @author Luke Taylor
* @version $Id$
* @since 2.0
*/
public class DefaultLoginPageGeneratingFilter extends SpringSecurityFilter {
public static final String DEFAULT_LOGIN_PAGE_URL = "/spring_security_login";
@@ -71,11 +72,13 @@ public class DefaultLoginPageGeneratingFilter extends SpringSecurityFilter {
}
}
}
protected void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
if (isLoginUrlRequest(request)) {
response.getOutputStream().print(generateLoginPageHtml(request));
String loginPageHtml = generateLoginPageHtml(request);
response.setContentType("text/html;charset=UTF-8");
response.setContentLength(loginPageHtml.length());
response.getOutputStream().print(loginPageHtml);
return;
}
@@ -126,7 +126,7 @@ public class RoleHierarchyImpl implements RoleHierarchy {
* references a set of the reachable lower roles.
*/
private void buildRolesReachableInOneStepMap() {
String parsingRegex = "(\\s*(\\w+)\\s*\\>\\s*(\\w+))";
String parsingRegex = "(\\s*([^\\s>]+)\\s*\\>\\s*([^\\s>]+))";
Pattern pattern = Pattern.compile(parsingRegex);
Matcher roleHierarchyMatcher = pattern.matcher(roleHierarchyStringRepresentation);
@@ -15,19 +15,17 @@ import org.springframework.security.userdetails.UserDetails;
import org.springframework.security.userdetails.UserDetailsManager;
import org.springframework.security.userdetails.GroupManager;
import org.springframework.context.ApplicationContextException;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.object.MappingSqlQuery;
import org.springframework.jdbc.object.SqlQuery;
import org.springframework.jdbc.object.SqlUpdate;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.util.Assert;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.sql.DataSource;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.List;
/**
@@ -116,28 +114,6 @@ public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsMa
private String groupAuthoritiesSql = DEF_GROUP_AUTHORITIES_QUERY_SQL;
private String deleteGroupAuthoritySql = DEF_DELETE_GROUP_AUTHORITY_SQL;
private SqlUpdate insertUser;
private SqlUpdate deleteUser;
private SqlUpdate updateUser;
private SqlUpdate insertAuthority;
private SqlUpdate deleteUserAuthorities;
private SqlQuery userExistsQuery;
private SqlUpdate changePassword;
private SqlQuery findAllGroupsQuery;
private SqlQuery findUsersInGroupQuery;
private SqlUpdate insertGroup;
private SqlQuery findGroupIdQuery;
private SqlUpdate insertGroupAuthority;
private SqlUpdate deleteGroup;
private SqlUpdate deleteGroupMembers;
private SqlUpdate deleteGroupAuthorities;
private SqlUpdate renameGroup;
private SqlUpdate insertGroupMember;
private SqlUpdate deleteGroupMember;
private SqlQuery groupAuthoritiesQuery;
private SqlUpdate deleteGroupAuthority;
private AuthenticationManager authenticationManager;
private UserCache userCache = new NullUserCache();
@@ -150,43 +126,36 @@ public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsMa
"not be performed.");
}
insertUser = new InsertUser(getDataSource());
deleteUser = new DeleteUser(getDataSource());
updateUser = new UpdateUser(getDataSource());
insertAuthority = new InsertAuthority(getDataSource());
deleteUserAuthorities = new DeleteUserAuthorities(getDataSource());
userExistsQuery = new UserExistsQuery(getDataSource());
changePassword = new ChangePassword(getDataSource());
findAllGroupsQuery = new AllGroupsQuery(getDataSource());
findUsersInGroupQuery = new GroupMembersQuery(getDataSource());
insertGroup = new InsertGroup(getDataSource());
findGroupIdQuery = new FindGroupIdQuery(getDataSource());
insertGroupAuthority = new InsertGroupAuthority(getDataSource());
deleteGroup = new DeleteGroup(getDataSource());
deleteGroupAuthorities = new DeleteGroupAuthorities(getDataSource());
deleteGroupMembers = new DeleteGroupMembers(getDataSource());
renameGroup = new RenameGroup(getDataSource());
insertGroupMember = new InsertGroupMember(getDataSource());
deleteGroupMember = new DeleteGroupMember (getDataSource());
groupAuthoritiesQuery = new GroupAuthoritiesByGroupNameMapping(getDataSource());
deleteGroupAuthority = new DeleteGroupAuthority(getDataSource());
super.initDao();
}
//~ UserDetailsManager implementation ==============================================================================
public void createUser(UserDetails user) {
public void createUser(final UserDetails user) {
validateUserDetails(user);
insertUser.update(new Object[] {user.getUsername(), user.getPassword(), Boolean.valueOf(user.isEnabled())});
getJdbcTemplate().update(createUserSql, new PreparedStatementSetter() {
public void setValues(PreparedStatement ps) throws SQLException {
ps.setString(1, user.getUsername());
ps.setString(2, user.getPassword());
ps.setBoolean(3, user.isEnabled());
}
});
insertUserAuthorities(user);
}
public void updateUser(UserDetails user) {
public void updateUser(final UserDetails user) {
validateUserDetails(user);
updateUser.update(new Object[] {user.getPassword(), Boolean.valueOf(user.isEnabled()), user.getUsername()});
deleteUserAuthorities.update(user.getUsername());
getJdbcTemplate().update(updateUserSql, new PreparedStatementSetter() {
public void setValues(PreparedStatement ps) throws SQLException {
ps.setString(1, user.getPassword());
ps.setBoolean(2, user.isEnabled());
ps.setString(3, user.getUsername());
}
});
deleteUserAuthorities(user.getUsername());
insertUserAuthorities(user);
userCache.removeUserFromCache(user.getUsername());
@@ -194,15 +163,20 @@ public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsMa
private void insertUserAuthorities(UserDetails user) {
for (int i=0; i < user.getAuthorities().length; i++) {
insertAuthority.update(user.getUsername(), user.getAuthorities()[i].getAuthority());
getJdbcTemplate().update(createAuthoritySql,
new Object[] {user.getUsername(), user.getAuthorities()[i].getAuthority()});
}
}
public void deleteUser(String username) {
deleteUserAuthorities.update(username);
deleteUser.update(username);
deleteUserAuthorities(username);
getJdbcTemplate().update(deleteUserSql, new Object[] {username});
userCache.removeUserFromCache(username);
}
private void deleteUserAuthorities(String username) {
getJdbcTemplate().update(deleteUserAuthoritiesSql, new Object[] {username});
}
public void changePassword(String oldPassword, String newPassword) throws AuthenticationException {
Authentication currentUser = SecurityContextHolder.getContext().getAuthentication();
@@ -226,7 +200,7 @@ public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsMa
logger.debug("Changing password for user '"+ username + "'");
changePassword.update(new String[] {newPassword, username});
getJdbcTemplate().update(changePasswordSql, new String[] {newPassword, username});
SecurityContextHolder.getContext().setAuthentication(createNewAuthentication(currentUser, newPassword));
@@ -244,10 +218,10 @@ public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsMa
}
public boolean userExists(String username) {
List users = userExistsQuery.execute(username);
List users = getJdbcTemplate().queryForList(userExistsSql, new Object[] {username});
if (users.size() > 1) {
throw new IllegalStateException("More than one user found with name '" + username + "'");
throw new IncorrectResultSizeDataAccessException("More than one user found with name '" + username + "'", 1);
}
return users.size() == 1;
@@ -256,26 +230,33 @@ public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsMa
//~ GroupManager implementation ====================================================================================
public String[] findAllGroups() {
return (String[]) findAllGroupsQuery.execute().toArray(new String[0]);
return (String[]) getJdbcTemplate().queryForList(findAllGroupsSql, String.class).toArray(new String[0]);
}
public String[] findUsersInGroup(String groupName) {
Assert.hasText(groupName);
return (String[]) findUsersInGroupQuery.execute(groupName).toArray(new String[0]);
return (String[]) getJdbcTemplate().queryForList(findUsersInGroupSql, new String[] {groupName}, String.class).toArray(new String[0]);
}
public void createGroup(String groupName, GrantedAuthority[] authorities) {
public void createGroup(final String groupName, final GrantedAuthority[] authorities) {
Assert.hasText(groupName);
Assert.notNull(authorities);
logger.debug("Creating new group '" + groupName + "' with authorities " +
AuthorityUtils.authorityArrayToSet(authorities));
insertGroup.update(groupName);
Integer id = (Integer) findGroupIdQuery.findObject(groupName);
getJdbcTemplate().update(insertGroupSql, new String[] {groupName});
final int groupId = findGroupId(groupName);
for (int i=0; i < authorities.length; i++) {
insertGroupAuthority.update( new Object[] {id, authorities[i].getAuthority()});
final String authority = authorities[i].getAuthority();
getJdbcTemplate().update(insertGroupAuthoritySql, new PreparedStatementSetter() {
public void setValues(PreparedStatement ps) throws SQLException {
ps.setInt(1, groupId);
ps.setString(2, authority);
}
});
}
}
@@ -283,10 +264,15 @@ public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsMa
logger.debug("Deleting group '" + groupName + "'");
Assert.hasText(groupName);
int id = ((Integer) findGroupIdQuery.findObject(groupName)).intValue();
deleteGroupMembers.update(id);
deleteGroupAuthorities.update(id);
deleteGroup.update(id);
final int id = findGroupId(groupName);
PreparedStatementSetter groupIdPSS = new PreparedStatementSetter() {
public void setValues(PreparedStatement ps) throws SQLException {
ps.setInt(1, id);
}
};
getJdbcTemplate().update(deleteGroupMembersSql, groupIdPSS);
getJdbcTemplate().update(deleteGroupAuthoritiesSql, groupIdPSS);
getJdbcTemplate().update(deleteGroupSql, groupIdPSS);
}
public void renameGroup(String oldName, String newName) {
@@ -294,54 +280,90 @@ public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsMa
Assert.hasText(oldName);
Assert.hasText(newName);
renameGroup.update(newName, oldName);
getJdbcTemplate().update(renameGroupSql, new String[] {newName, oldName});
}
public void addUserToGroup(String username, String groupName) {
public void addUserToGroup(final String username, final String groupName) {
logger.debug("Adding user '" + username + "' to group '" + groupName + "'");
Assert.hasText(username);
Assert.hasText(groupName);
Integer id = (Integer) findGroupIdQuery.findObject(groupName);
final int id = findGroupId(groupName);
getJdbcTemplate().update(insertGroupMemberSql, new PreparedStatementSetter() {
public void setValues(PreparedStatement ps) throws SQLException {
ps.setInt(1, id);
ps.setString(2, username);
}
});
insertGroupMember.update(new Object[] {id, username});
userCache.removeUserFromCache(username);
}
public void removeUserFromGroup(String username, String groupName) {
public void removeUserFromGroup(final String username, final String groupName) {
logger.debug("Removing user '" + username + "' to group '" + groupName + "'");
Assert.hasText(username);
Assert.hasText(groupName);
Integer id = (Integer) findGroupIdQuery.findObject(groupName);
final int id = findGroupId(groupName);
getJdbcTemplate().update(deleteGroupMemberSql, new PreparedStatementSetter() {
public void setValues(PreparedStatement ps) throws SQLException {
ps.setInt(1, id);
ps.setString(2, username);
}
});
deleteGroupMember.update(new Object[] {id, username});
userCache.removeUserFromCache(username);
}
public GrantedAuthority[] findGroupAuthorities(String groupName) {
logger.debug("Loading authorities for group '" + groupName + "'");
Assert.hasText(groupName);
List authorities = getJdbcTemplate().query(groupAuthoritiesSql, new String[] {groupName}, new RowMapper() {
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
String roleName = getRolePrefix() + rs.getString(3);
GrantedAuthorityImpl authority = new GrantedAuthorityImpl(roleName);
return (GrantedAuthority[]) groupAuthoritiesQuery.execute(groupName).toArray(new GrantedAuthority[0]);
return authority;
}
});
return (GrantedAuthority[]) authorities.toArray(new GrantedAuthority[0]);
}
public void removeGroupAuthority(String groupName, GrantedAuthority authority) {
public void removeGroupAuthority(String groupName, final GrantedAuthority authority) {
logger.debug("Removing authority '" + authority + "' from group '" + groupName + "'");
Assert.hasText(groupName);
Assert.notNull(authority);
Integer id = (Integer) findGroupIdQuery.findObject(groupName);
deleteGroupAuthority.update(new Object[] {id, authority});
final int id = findGroupId(groupName);
getJdbcTemplate().update(deleteGroupAuthoritySql, new PreparedStatementSetter() {
public void setValues(PreparedStatement ps) throws SQLException {
ps.setInt(1, id);
ps.setString(2, authority.getAuthority());
}
});
}
public void addGroupAuthority(String groupName, GrantedAuthority authority) {
public void addGroupAuthority(final String groupName, final GrantedAuthority authority) {
logger.debug("Adding authority '" + authority + "' to group '" + groupName + "'");
Assert.hasText(groupName);
Assert.notNull(authority);
Integer id = (Integer) findGroupIdQuery.findObject(groupName);
insertGroupAuthority.update(new Object[] {id, authority.getAuthority()});
final int id = findGroupId(groupName);
getJdbcTemplate().update(insertGroupAuthoritySql, new PreparedStatementSetter() {
public void setValues(PreparedStatement ps) throws SQLException {
ps.setInt(1, id);
ps.setString(2, authority.getAuthority());
}
});
}
private int findGroupId(String group) {
return getJdbcTemplate().queryForInt(findGroupIdSql, new Object[] {group});
}
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
@@ -411,201 +433,4 @@ public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsMa
Assert.hasText(authorities[i].getAuthority(), "getAuthority() method must return a non-empty string");
}
}
//~ Inner Classes ==================================================================================================
private class InsertUser extends SqlUpdate {
public InsertUser(DataSource ds) {
super(ds, createUserSql);
declareParameter(new SqlParameter(Types.VARCHAR));
declareParameter(new SqlParameter(Types.VARCHAR));
declareParameter(new SqlParameter(Types.BOOLEAN));
compile();
}
}
private class DeleteUser extends SqlUpdate {
public DeleteUser(DataSource ds) {
super(ds, deleteUserSql);
declareParameter(new SqlParameter(Types.VARCHAR));
compile();
}
}
private class InsertAuthority extends SqlUpdate {
public InsertAuthority(DataSource ds) {
super(ds, createAuthoritySql);
declareParameter(new SqlParameter(Types.VARCHAR));
declareParameter(new SqlParameter(Types.VARCHAR));
compile();
}
}
private class DeleteUserAuthorities extends SqlUpdate {
public DeleteUserAuthorities(DataSource ds) {
super(ds, deleteUserAuthoritiesSql);
declareParameter(new SqlParameter(Types.VARCHAR));
compile();
}
}
private class UpdateUser extends SqlUpdate {
public UpdateUser(DataSource ds) {
super(ds, updateUserSql);
declareParameter(new SqlParameter(Types.VARCHAR));
declareParameter(new SqlParameter(Types.BOOLEAN));
declareParameter(new SqlParameter(Types.VARCHAR));
compile();
}
}
private class ChangePassword extends SqlUpdate {
public ChangePassword(DataSource ds) {
super(ds, changePasswordSql);
declareParameter(new SqlParameter(Types.VARCHAR));
declareParameter(new SqlParameter(Types.VARCHAR));
compile();
}
}
private class UserExistsQuery extends MappingSqlQuery {
public UserExistsQuery(DataSource ds) {
super(ds, userExistsSql);
declareParameter(new SqlParameter(Types.VARCHAR));
compile();
}
protected Object mapRow(ResultSet rs, int rowNum) throws SQLException {
return rs.getString(1);
}
}
private class AllGroupsQuery extends MappingSqlQuery {
public AllGroupsQuery(DataSource ds) {
super(ds, findAllGroupsSql);
compile();
}
protected Object mapRow(ResultSet rs, int rowNum) throws SQLException {
return rs.getString(1);
}
}
private class GroupMembersQuery extends MappingSqlQuery {
public GroupMembersQuery(DataSource ds) {
super(ds, findUsersInGroupSql);
declareParameter(new SqlParameter(Types.VARCHAR));
compile();
}
protected Object mapRow(ResultSet rs, int rowNum) throws SQLException {
return rs.getString(1);
}
}
private class InsertGroup extends SqlUpdate {
public InsertGroup(DataSource ds) {
super(ds, insertGroupSql);
declareParameter(new SqlParameter(Types.VARCHAR));
compile();
}
}
private class FindGroupIdQuery extends MappingSqlQuery {
public FindGroupIdQuery(DataSource ds) {
super(ds, findGroupIdSql);
declareParameter(new SqlParameter(Types.INTEGER));
compile();
}
protected Object mapRow(ResultSet rs, int rowNum) throws SQLException {
return new Integer(rs.getInt(1));
}
}
private class InsertGroupAuthority extends SqlUpdate {
public InsertGroupAuthority(DataSource ds) {
super(ds, insertGroupAuthoritySql);
declareParameter(new SqlParameter(Types.INTEGER));
declareParameter(new SqlParameter(Types.VARCHAR));
compile();
}
}
protected class DeleteGroup extends SqlUpdate {
public DeleteGroup(DataSource ds) {
super(ds, deleteGroupSql);
declareParameter(new SqlParameter(Types.INTEGER));
compile();
}
}
private class DeleteGroupMembers extends SqlUpdate {
public DeleteGroupMembers(DataSource ds) {
super(ds, deleteGroupMembersSql);
declareParameter(new SqlParameter(Types.INTEGER));
compile();
}
}
protected class DeleteGroupAuthorities extends SqlUpdate {
public DeleteGroupAuthorities(DataSource ds) {
super(ds, deleteGroupAuthoritiesSql);
declareParameter(new SqlParameter(Types.INTEGER));
compile();
}
}
private class RenameGroup extends SqlUpdate {
public RenameGroup(DataSource ds) {
super(ds, renameGroupSql);
declareParameter(new SqlParameter(Types.VARCHAR));
declareParameter(new SqlParameter(Types.VARCHAR));
compile();
}
}
protected class InsertGroupMember extends SqlUpdate {
public InsertGroupMember(DataSource ds) {
super(ds, insertGroupMemberSql);
declareParameter(new SqlParameter(Types.INTEGER));
declareParameter(new SqlParameter(Types.VARCHAR));
compile();
}
}
private class DeleteGroupMember extends SqlUpdate {
public DeleteGroupMember(DataSource ds) {
super(ds, deleteGroupMemberSql);
declareParameter(new SqlParameter(Types.INTEGER));
declareParameter(new SqlParameter(Types.VARCHAR));
compile();
}
}
private class GroupAuthoritiesByGroupNameMapping extends MappingSqlQuery {
protected GroupAuthoritiesByGroupNameMapping(DataSource ds) {
super(ds, groupAuthoritiesSql);
declareParameter(new SqlParameter(Types.VARCHAR));
compile();
}
protected Object mapRow(ResultSet rs, int rownum) throws SQLException {
String roleName = getRolePrefix() + rs.getString(3);
GrantedAuthorityImpl authority = new GrantedAuthorityImpl(roleName);
return authority;
}
}
private class DeleteGroupAuthority extends SqlUpdate {
public DeleteGroupAuthority(DataSource ds) {
super(ds, deleteGroupAuthoritySql);
declareParameter(new SqlParameter(Types.INTEGER));
declareParameter(new SqlParameter(Types.VARCHAR));
compile();
}
}
}

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