1
0
mirror of synced 2026-09-05 08:49:54 +00:00

Compare commits

...

170 Commits

Author SHA1 Message Date
Spring Buildmaster 48eb8788c4 Release version 3.2.8.RELEASE 2015-07-22 22:00:11 -07:00
Rob Winch 23de257508 SEC-3031: DelegatingSecurityContext(Runnable|Callable) only modify SecurityContext on new Thread
Modifying the SecurityContext on the same Thread can cause issues. For example, with a
RejectedExecutionHandler the SecurityContext may be cleared out on the original Thread.

This change modifies both the DelegatingSecurityContextRunnable and DelegatingSecurityContextCallable to,
by default, only modify the SecurityContext if they are invoked on a new Thread. The behavior can be changed
by setting the property enableOnOrigionalThread to true.
2015-07-22 16:48:04 -05:00
Rob Winch 12ed990aa2 SEC-3051: Add AbstractPreAuthenticatedProcessingFilter#principalChanged 2015-07-22 09:02:25 -05:00
Rob Winch d7d4ac9dc1 SEC-2993: OpenID Sample now uses me.yahoo.com 2015-07-21 11:12:53 -05:00
Rob Winch 7c62164392 SEC-3011: AbstractUrlAuthorizationConfigurer postProcess default AccessDecisionManager 2015-07-21 09:22:06 -05:00
Rob Winch eceb6a3587 SEC-2924: Add README.adoc in -dist.zip 2015-07-16 15:34:45 -05:00
Rob Winch c4a1f1b4a8 SEC-2965: Fix invalid formatted links in reference documentation 2015-07-16 15:26:43 -05:00
Rob Winch 7ecee8e733 SEC-3003: Document invalid intercept-url attributes for filter-security-metadata-source 2015-07-16 15:04:11 -05:00
Rob Winch 63a334317b SEC-3019: Java Config for Http Basic supports Rememberme 2015-07-16 11:13:12 -05:00
Rob Winch 704b114842 SEC-3002: Add JUnit Assume to GCM encryption tests
Not all JDKs have GCM installed on them.
2015-07-14 14:58:21 -05:00
Rob Winch fcc9a34356 SEC-2973: Add OnCommittedResponseWrapper
This ensures that Spring Session & Security's logic for performing
a save on the response being committed can easily be kept in synch.
Further this ensures that the SecurityContext is now persisted when
the response body meets the content length.
2015-07-14 14:49:12 -05:00
Rob Winch 00042ff70b SEC-2931: Fix CsrfFilter Javadoc 2015-07-14 13:41:44 -05:00
Rob Winch fc1450f72c SEC-2493: Fix javadoc for DefaultLdapAuthoritiesPopulator 2015-07-14 13:36:41 -05:00
Rob Winch a6cd1b6066 SEC-3034: AclPermissionEvaluator specifies Locale.ENGLISH 2015-07-13 23:57:14 -05:00
Rob Winch 567b0ed030 SEC-3013: Add messages_en.properties 2015-07-13 23:26:04 -05:00
Rob Winch 4e6b12f8b4 SEC-3002: Add new option for AES encryption with GCM
The Galois Counter Mode (GCM) is held to be superior than the current
default CBC. This change adds an extra parameter to the constructor
of AesBytesEncryptor and a new convenience method in Encryptors.
2015-07-10 00:01:13 -05:00
Rob Winch ae772294cb SEC-2851: Remove DataAccessException import from Persistent RememberMe 2015-04-21 15:04:51 -05:00
Spring Buildmaster c01f2d8501 Next development version 2015-03-25 20:56:37 -07:00
Rob Winch 28c6e9a8af SEC-2899: Update Spring OSGi version 2015-03-25 21:47:05 -05:00
Rob Winch 13cb51c15f SEC-2918: Update Spring Version 3.2.13 2015-03-25 21:43:11 -05:00
Rob Winch b0ad8173b0 SEC-2913: Post Process default session fixation AuthenticationStrategy
Before the default session fixation AuthenticationStrategy used a
NullEventPublisher when using the Java Configuration. This was due to the
fact that it is not exposed as a Bean and is not post processed.

We now post process the default session fixation AuthenticationStrategy
which initializes the EventPublisher properly.
2015-03-25 21:43:11 -05:00
Rob Winch ea27cb6593 Update springIoVersion to latest.integration 2015-03-25 21:43:11 -05:00
Rob Winch fe82c8ab4c SEC-2897: ActiveDirectoryLdapAuthenticationProvider uses bindPrincipal 2015-03-10 16:37:26 -05:00
Spring Buildmaster cf66f2f39e Next development version 2015-02-25 08:20:55 -08:00
Rob Winch 050407564c SEC-2871: Polish README.adoc 2015-02-25 09:47:05 -06:00
Rob Winch 1374898cd8 SEC-2879: Add Test 2015-02-24 23:19:27 -06:00
Michael Cramer d5ed97eba6 SEC-2879: JdbcTokenRepositoryImpl updateToken should use lastUsed arg 2015-02-24 23:19:22 -06:00
Marcin Mielnicki 8f29c2cc36 SEC-2878: Clean imports in UsernamePasswordAuthenticationFilter 2015-02-24 22:52:28 -06:00
Eugene Wolfson 99d503f0a9 SEC-2877: Fix doc typo in index.adoc
Replace "a`" with "a `"
2015-02-24 22:29:29 -06:00
Romain Fromi 6c185f649b SEC-2876: HttpSecurityBuilder addFilterAfter javadoc before->after 2015-02-24 22:20:45 -06:00
izeye 58be282f70 SEC-2875: Fix typo in hellomvc guide 2015-02-24 22:14:16 -06:00
Rob Winch 2df05ee2c3 SEC-1915: Polish
* Restore default search filter to remain passive
* Check the search filter in setSearchFilter
* Add additional tests
2015-02-24 21:39:39 -06:00
Mateusz Rasiński 72bc6bf539 SEC-1915: Custom ActiveDirectory search filter
Currently the search filter used when retrieving user details is hard coded.

New property in ActiveDirectoryLdapAuthenticationProvider:
- searchFilter - the LDAP search filter to use when searching for authorities,
default to search using 'userPrincipalName' (current) OR 'sAMAccountName'
2015-02-24 21:39:27 -06:00
Rob Winch 1b26d03479 SEC-2832: Fix config tests 2015-02-24 17:53:54 -06:00
Rob Winch dfaebfa63b SEC-2872: CsrfAuthenticationStrategy Delay Saving CsrfToken 2015-02-24 17:35:08 -06:00
Rob Winch f794272bac SEC-2832: Add Tests 2015-02-24 17:35:05 -06:00
Stillglade aa0a5b96ab SEC-2832: Update request attributes with new CsrfToken 2015-02-24 17:35:03 -06:00
Rob Winch 27c7cd150b SEC-2871: Polish README.adoc 2015-02-24 16:59:41 -06:00
shaehnel b3d108fa44 SEC-2871: readme.txt->README.adoc 2015-02-24 16:59:33 -06:00
Rob Winch 975e4ec019 SEC-2078: AbstractPreAuthenticatedProcessingFilter requriesAuthentication support for non-String Principals
Previously, if the Principal returned by getPreAuthenticatedPrincipal was not a String,
it prevented requiresAuthentication from detecting when the Principal was the same.
This caused the need to authenticate the user for every request even when the Principal
did not change.

Now requiresAuthentication will check to see if the result of
getPreAuthenticatedPrincipal is equal to the current Authentication.getPrincipal().
2015-02-24 16:44:21 -06:00
Rob Winch 74f8534b17 SEC-2791: AbstractRememberMeServices sets the version
If the maxAge < 1 then the version must be 1 otherwise browsers ignore
the value.
2015-02-04 15:58:49 -06:00
Rob Winch 478a9650aa SEC-2831: Regex/AntPath RequestMatcher handle invalid HTTP method 2015-02-04 12:05:25 -06:00
Rob Winch b79ba12502 SEC-2777: Fix <header> attributes in doc 2015-01-20 16:28:25 -06:00
Rob Winch 72de17d79a SEC-2822: Make EnableGlobalAuthenticationAutowiredConfigurer static Bean
This ensures that EnableGlobalAuthenticationAutowiredConfigurer is actually
used in newer versions of Spring. See SPR-12646
2015-01-20 14:30:04 -06:00
Rob Winch e27200a255 SEC-2815: Delay looking up AuthenticationConfiguration 2015-01-20 14:30:04 -06:00
Rob Winch c3f72f7b79 Merge pull request #160 from ractive/3.2.x
SEC-2812: Fix german translations in 3.2.x
2015-01-14 16:29:54 -06:00
james b42cb9e3e1 SEC-2812: Fix german translations in 3.2.x 2015-01-12 13:48:50 +01:00
Rob Winch b40088b73d Merge pull request #155 from wilkinsona/powermock-upgrade
Upgrade to PowerMock 1.6.1
2015-01-05 09:03:52 -06:00
Andy Wilkinson 4116596a6c Upgrade to PowerMock 1.6.1
The Platform would like to move to JUnit 4.12 but cannot do so at the
moment as Spring Security uses a version of PowerMock which is
incompatible with JUnit 4.12. This commit updates Spring Security to use
PowerMock 1.6.1 with is compatible with JUnit 4.12.
2015-01-05 09:52:26 +00:00
Christopher Pelloux 9de369c25f SEC-2800 Documentation typo in class name 2014-12-23 09:15:24 -06:00
Rob Winch bf2d2d4597 SEC-2773: Add Test for static delegatingApplicationListener 2014-12-01 12:07:07 -06:00
Oliver Gierke c05f27af6c SEC-2773: Prevent premature container initialization in WebSecurityConfiguration.
Changed the bean definition method for the DelegatingApplicationListener
to be static to avoid the need to instantiate the configuration class which
caused further premature initializations to satisfy the dependencies
expressed in setFilterChainProxySecurityConfigurer(…).
2014-12-01 12:07:05 -06:00
Rob Winch cdac4d990b SEC-2747: Remove spring-core dependency from spring-security-crypto 2014-11-20 16:28:06 -06:00
Rob Winch db66843e0b SEC-2749: CsrfConfigurer.requireCsrfProtectionMatcher correct null check 2014-11-20 14:42:53 -06:00
Rob Winch c36cc88ac4 SEC-2150: Support class level annotations on Spring Data Repositories 2014-11-20 12:17:47 -06:00
Rob Winch 7d82349b1e SEC-2150: Add tests to verify JSR-250 Spec behavior 2014-11-20 12:17:44 -06:00
Rob Winch b6ab9c85e9 SEC-2682: DelegatingSecurityContextRunnable/Callable delegate toString() 2014-11-20 11:51:26 -06:00
Rob Winch 29a8da4aa6 SEC-2574: Fix Bundlr 2014-11-20 11:10:58 -06:00
Rob Winch b71989ecde SEC-2574: JavaConfig default SessionRegistry processes SessionDestroyedEvents 2014-11-19 17:10:14 -06:00
Rob Winch eeef91498a SEC-2674: Documentation refers to httpStrictTransportSecurity() instead of hsts() 2014-11-19 13:33:27 -06:00
Spring Buildmaster 91bf099b01 Next development version 2014-08-15 11:20:59 -07:00
Rob Winch 137589325d SEC-2547: Update to cas-client-core-3.3.3 2014-08-15 12:42:07 -05:00
Rob Winch 0a184a8d79 SEC-2697: Fix logging of Spring Version Check 2014-08-15 12:41:26 -05:00
Rob Winch 2cb99f0791 SEC-2688: CAS Proxy Ticket Authentication uses Service for host & port 2014-08-11 15:20:58 -05:00
Rob Winch d85a0a20bc SEC-2595: @EnableGlobalMethodSecurity AspectJ tweaks for Spring 3.2.x 2014-07-29 09:39:55 -05:00
Rob Winch 0a45d3170c SEC-2595: @EnableGlobalMethodSecurity AspectJ fixes 2014-07-25 16:27:49 -05:00
Rob Winch 89c5c56849 SEC-2599: HttpSessionEventPublisher get required ApplicationContext
In order to get better error messages (avoid NullPointerException) the
HttpSessionEventPublisher now gets the required ApplicationContext which
throws an IllegalStateException with a good error message.
2014-07-22 09:20:38 -05:00
Rob Winch 47acf17323 SEC-2588: Javadoc fix channelSecurity->requiresChannel 2014-07-21 14:23:47 -05:00
Rob Winch 52c585aef1 SEC-2665: Fix samples/ldap-jc link in reference 2014-07-21 14:21:05 -05:00
Rob Winch 89d80ed5c9 SEC-2683: Correct spelling of assignamble in AuthenticationPrincipalResolver Exception 2014-07-18 13:57:40 -05:00
Mirko Zeibig 85a37bdc02 SEC-2656: Fix <frame-options> with whitelist strategy 2014-06-18 09:07:41 -05:00
Rob Winch fb1f2dc888 Next development version ldap/pom.xml 2014-06-18 09:04:48 -05:00
Rob Winch d5842f949b SEC-2657: Test for multi dynamic ports for LDAP Java Config 2014-06-17 17:25:40 -05:00
Rob Winch 3e3d819526 SEC-2660: Move config integration-test *.groovy to groovy source folder 2014-06-17 17:23:18 -05:00
Rob Winch 143c513f5c SEC-2659: ApacheDSContainer fails on import multiple ldif 2014-06-17 17:20:09 -05:00
Rob Winch 8eb89e3f12 SEC-2658: Java Config triggers usePasswordAttrCompare to be set 2014-06-17 17:11:20 -05:00
Rob Winch bdde468e7d SEC-2657: LdapAuthenticationProviderConfigurer find available port 2014-06-17 16:55:38 -05:00
Rob Winch f574f2a2ac SEC-2618: LdapAuthenticationProviderConfigurer passwordAttribute null check
If LdapAuthenticationProviderConfigurer passwordAttribute is null, do not
set on the PasswordComparisonAuthenticator
2014-06-17 16:52:04 -05:00
Rob Winch 439a15b108 SEC-2647: IntelliJ testSourceDirs 2014-06-17 16:40:43 -05:00
Rob Winch 44fbf678bb Fix jdbc-jc to work with tomcat gradle plugin
It is necessary to ensure that src/main/webapp exists to ensure the
application starts with the Tomcat Gradle Plugin.

This commit adds a Manifest file to src/main/webapp/META-INF to ensure
that git contains the otherwise empty directory.
2014-06-13 12:24:45 -05:00
Rob Winch 9c94ef358d SEC-2617: Add JSTL to sample poms 2014-06-12 15:07:50 -05:00
Rob Winch 655ad90813 SEC-2617: Fix JSTL Samples 2014-06-12 12:10:20 -05:00
Rob Winch a0ee80bc61 SEC-2650: Fix Jetty Warn NoInitialContextException on shutdown 2014-06-12 12:10:08 -05:00
Rob Winch 6b977d9b4b SEC-2649: Update to Tomcat 7.0.54 2014-06-12 12:09:58 -05:00
Rob Winch 7ea79bacbc SEC-2648: Update to gradle-tomcat-plugin:1.2.3 for samples 2014-06-12 12:09:49 -05:00
Rob Winch fa177a22b1 SEC-2647: Add integration test classpath in IntelliJ 2014-06-12 12:09:35 -05:00
Rob Winch b9fbc568fb SEC-2646: Update to Gradle 1.12 2014-06-12 12:09:21 -05:00
Spring Buildmaster 293e3e97ae Next development version 2014-05-21 14:27:51 -07:00
Rob Winch 08ec5797db SEC-2607: CAS Server logouts out synchronously 2014-05-21 15:43:05 -05:00
Rob Winch b20438562d SEC-2579: Ignore springio props in release checks 2014-05-21 14:04:18 -05:00
Rob Winch d4e26864d9 SEC-2606: ApacheDSServerIntegrationTests scan for available port 2014-05-21 06:49:00 -05:00
Rob Winch 519d85877c SEC-2603: Fix config groovy integration tests 2014-05-20 23:14:11 -05:00
Rob Winch f31a5c5411 SEC-2472: Support LDAP crypto PasswordEncoder 2014-05-20 23:13:07 -05:00
Rob Winch 77b9209640 SEC-2579: Remove duplicate snapshot repo and dependencies closure 2014-05-19 15:58:04 -05:00
Rob Winch ae39a4dcdd SEC-2579: Polish so that platformVersion has a default 2014-05-19 15:33:30 -05:00
Andy Wilkinson f37e414442 SEC-2579: Update Spring IO plugin configuration
- Upgrade to 0.0.3.RELEASE of the plugin
- Declare a dependency in the springIoVersions configuration on the
  Platform’s platform-versions artifact. An external property is used to
  provide the version of this artifact. This means that the build script
  does not need to refer to a snapshot artifact (the only version
  available at the time of writing) and also breaks a circular
  dependency between Spring Security (a project that’s part of the
  Platform) and the Platform itself.
- The plugin applied and the libs-snapshot repository is added only
  if the external property is specified. This removes the possibility
  of a release or milestone being shipped with a snapshot dependency
  (unless the release build is performed with -PplatformVersion=foo).
2014-05-19 15:25:57 -05:00
Andy Wilkinson be687d6a84 SEC-2600: Remove unused import 2014-05-19 12:28:05 -05:00
Rob Winch d6b81abcf2 SEC-2578: HttpSessionSecurityContextRepository traverses HttpServletResponseWrapper 2014-05-02 15:06:28 -05:00
Rob Winch bf918df7a3 SEC-2543: Logout with CSRF enabled requires POST by default 2014-05-02 11:17:57 -05:00
Rob Winch dc7a3b30ea SEC-2581: Update to propdeps-plugin:0.0.6 2014-04-30 16:41:51 -05:00
Rob Winch 3be2f375f6 SEC-2580: Include ApacheDS in samples/ldap-xml 2014-04-30 16:39:23 -05:00
Rob Winch 9146345673 Fix warnings in itest-web.gradle 2014-04-30 08:52:39 -05:00
Rob Winch fb2cf96fa4 SEC-2579: Fix acl tests to work w/ newer ehcache versions 2014-04-30 08:52:29 -05:00
Rob Winch 9a27f9f778 SEC-2579: Add springio-platform plugin 2014-04-29 16:59:32 -05:00
Alexander Grüneberg e0b4945fad SEC-2577: Add missing whitespace in reference 2014-04-28 16:25:38 -05:00
Rob Winch d6f540fdee SEC-2532: Add disclaimer about jdbcAuthentication() with persistent data stores 2014-04-28 14:45:51 -05:00
Rob Winch 3f24210a2a SEC-2537: Add nekohtml to openid module 2014-04-25 13:51:00 -05:00
Rob Winch 13c5750f5d SEC-2571: Improve wording of UsernamePasswordAuthenticationToken#setAuthenticated() error 2014-04-24 17:03:34 -05:00
Rob Winch 414289d466 SEC-2571: Failure in UserDetailsService->InternalAuthenticationServiceException 2014-04-24 16:35:58 -05:00
Rob Winch 5e88ebef2e SEC-2549: Remove LazyBean marker interface 2014-04-24 13:55:25 -05:00
Mattias Severson c074493f24 SEC-2573: RequestHeaderRequestMatcher constructor argument name has typo 2014-04-23 09:41:43 -05:00
Rob Winch 1337f99843 SEC-2560: Remove samples from .gitignore and add missing sample files 2014-04-16 21:16:34 -05:00
Rob Winch cf721afc29 SEC-2558: Fix failing ApacheDSContainerTests when port is taken 2014-04-15 13:39:03 -05:00
Rob Winch 9133491da9 Revert "SEC-2547: Consistent CAS client version"
This reverts commit d9e87d8222.
2014-04-15 10:16:51 -05:00
Rob Winch 3632564972 Revert "SEC-2547: Fix sample after updating CAS client version"
This reverts commit 7ea0d3d42f.
2014-04-15 10:16:49 -05:00
Rob Winch 7ea0d3d42f SEC-2547: Fix sample after updating CAS client version 2014-04-15 07:49:34 -05:00
Hans-Joachim Kliemeck d9e87d8222 SEC-2547: Consistent CAS client version 2014-04-14 22:45:18 -05:00
Grzegorz Rożniecki 3954e0b2c1 SEC-2556: Fix @Import example in manual 2014-04-14 22:38:41 -05:00
Rob Winch 79fa1c70eb SEC-2542: Polish dependency exclusions
This cleans up exclusions so the pom.xml are not as cluttered.
2014-04-02 08:49:25 -05:00
Rob Winch fd6f9da184 SEC-2542: Use exclusions to remove duplicate dependencies
A number of projects had duplicate dependencies on their classpaths
as a result of the same classes being available in more than one
artifact, each with different Maven coordinates. Typically this only
affected the tests, but meant that the actual classes that were
loaded was somewhat unpredictable and had the potential to vary
between an IDE and the command line depending on the order in which
the aritfacts appeared on the classpath. This commit adds a number of
exclusions to remove such duplicates.

In addition to the new exclusions, notable other changes are:

 - Spring Data JPA has been updated to 1.4.1. This brings its
   transitive dependency upon spring-data-commons into line with
   Spring LDAP's and prevents both spring-data-commons-core and
   spring-data-commons from being on the classpath
 - All Servlet API dependencies have been updated to use the official
   artifact with all transitive dependencies on unofficial servlet API
   artifacts being excluded.
 - In places, groovy has been replaced with groovy-all. This removes
   some duplicates caused by groovy's transitive dependencies.
 - JUnit has been updated to 4.11 which brings its transitive Hamcrest
   dependency into line with other components.

There appears to be a bug in Gradle which means that some exclusions
applied to an artifact do not work reliably. To work around this
problem it has been necessary to apply some exclusions at the
configuration level

Conflicts:
	samples/messages-jc/pom.xml
2014-04-02 08:48:55 -05:00
Rob Winch ea0466d666 Next developmenet version in pom.xml 2014-04-02 08:44:06 -05:00
Spring Buildmaster 2cc0d94481 Next development version 2014-03-25 12:05:00 -07:00
Rob Winch 32c767a30d SEC-2533: Global AuthenticationManagerBuilder disables clearing child credentials 2014-03-25 13:00:42 -05:00
Rob Winch a11746a8d1 SEC-2498: RequestCache allows POST when CSRF is disabled 2014-03-25 10:44:34 -05:00
Rob Winch a18265a163 SEC-2531: AuthenticationConfiguration#lazyBean should use BeanClassLoader 2014-03-24 14:54:02 -05:00
Rob Winch d7a2c0a98c SEC-2177: Polish 2014-03-18 15:49:20 -05:00
Maciej Zasada 9057fbe0ed SEC-2177: Striping off all leading schemes
Striping off all leading schemes in the DefaultRedirectStrategy, so it
will be less vulnerable to open redirect phishing attacks. More info can
be found at SEC-2177 JIRA issue.
2014-03-18 15:49:20 -05:00
Julien Dubois 537d8f974f SEC-2519: RememberMeAuthenticationException supports root cause
Added a constructor which keeps the root cause of the exception, and
added some documentation
2014-03-11 16:13:03 -05:00
Alexander Kjäll 783af4012b SEC-2518: UserDetailsService javadoc repeats "insensitive"
Typo in javadoc, "case insensitive" was repeated twice.
2014-03-11 15:38:28 -05:00
Spring Buildmaster 5f5b8052d8 Next development version 2014-03-10 15:24:45 -07:00
Rob Winch 7dbb8e777e SEC-2500: Prevent anonymous bind for ActiveDirectoryLdapAuthenticator 2014-03-10 14:21:32 -05:00
Rob Winch bb563967cc SEC-2507: WebExpressionVoter.supports support subclasses of FilterInvocation 2014-03-10 14:21:07 -05:00
Rob Winch 974105ed19 SEC-2515: Detect object cycle for AuthenticationManager configuration 2014-03-10 14:04:10 -05:00
Rob Winch 2cad2f401b SEC-2325: Polish CSRF Tag support
- Rename csrfField to csrfInput
- Make AbstractCsrfTag package scope
- rename FormFieldTag to CsrfInputTag
- rename MetaTagsTag to CsrfMetaTagsTag
- removed whitespace from tag output so output is
  minimized & improving browser performance
- Update @since
- changed test names to be more meaningful
2014-03-07 15:25:57 -06:00
beamerblvd 3048e2c6e7 SEC-2325 Added JSP tags for CSRF meta tags and form fields 2014-03-07 13:18:46 -06:00
beamerblvd 561f284718 SEC-2335 Added ACL schema files for MySQL, SQL Server, Oracle 2014-03-07 13:10:03 -06:00
John Tims afc6a6ee0d SEC-2514: Fix typo in hellomvc.asc
packags -> packages
2014-03-07 10:26:31 -06:00
John Tims fa05e9c590 SEC-2513: Add link to SpringSource CLA form 2014-03-07 10:22:36 -06:00
Manimaran Selvan 818be86d46 SEC-2512: Fix typo in reference`
udates -> updates
2014-03-06 22:21:22 -06:00
Rob Winch 60704eb50e SEC-2511: Remove double ALLOW-FROM in X-Frame-Options header 2014-03-06 22:00:09 -06:00
getvictor f02b77794f SEC-2511: Remove double ALLOW-FROM from X-Frame-Options header.
The interface documentation for getAllowFromValue states: Gets the value for ALLOW-FROM excluding the ALLOW-FROM.
2014-03-06 21:59:46 -06:00
Rob Winch 1172d44397 SEC-2499: Allow MethodSecurityExpressionHandler in parent context
Previously a NoSuchBeanDefintionException was thrown when the
MethodSecurityExpressionHandler was defined in the parent context. This
happened due to trying to work around ordering issues related to SEC-2136

This commit resolves this by not marking the
MethodSecurityExpressionHandler bean as lazy unless it exists.
2014-03-06 20:51:24 -06:00
Rob Winch 49738e4588 SEC-2508: Passivity test for SEC-2357 2014-03-06 15:13:34 -06:00
Rob Winch 3b13c1fdf4 SEC-2495: CSRF disables logout on GET 2014-02-20 09:33:30 -06:00
Spring Buildmaster 8f6450ede1 Next development version 2014-02-19 10:52:05 -06:00
Rob Winch f2cde4ffa3 SEC-2486: Update tests to Spring LDAP 2.0.1.RELEASE 2014-02-19 09:32:37 -06:00
Rob Winch 9810768186 SEC-2485: Update test to Spring 4.0.2.RELEASE 2014-02-19 09:31:46 -06:00
Rob Winch 7f99a2dfbb SEC-2487: Update to Spring 3.2.8.RELEASE 2014-02-19 09:30:40 -06:00
Rob Winch 85305050c0 SEC-2455: Fix XML default login generation 2014-02-18 13:52:05 -06:00
Rob Winch 8a3a7961cb SEC-2492: ExpressionUrlAuthorizationConfigurer private interceptUrl to void 2014-02-15 14:41:26 -06:00
Rob Winch fc8e4868ce SEC-2468: Fix tests 2014-02-15 14:25:46 -06:00
Rob Winch 65367e6547 SEC-2468: JdbcUserDetailsManager#createNewAuthentication uses null credentials 2014-02-14 16:53:26 -06:00
Rob Winch bf2df220ca SEC-2490: LdapAuthenticationProviderConfigurer allows custom LdapAuthoritiesPopulator 2014-02-13 16:37:33 -06:00
Rob Winch 152f41f61e SEC-2392: KeyBasedPersistenceTokenService uses bytes instead of bits
The method setPseudoRandomNumberBits actually sets the number of bytes. This
commit deprecates setPseudoRandomNumberBits and adds
setPseudoRandomNumberBytes. The default value is still 256 to remain passive
but will be updated in 4.x.
2014-02-13 15:36:47 -06:00
Rob Winch 7a3da28987 SEC-2479: Search parent context for AuthenticationManager 2014-02-12 08:11:26 -06:00
Rob Winch e17adad878 SEC-2469: Support Spring LDAP 2.0.1+ 2014-02-12 08:11:26 -06:00
Luke Taylor 058b9debef Minor slapd config changes 2014-02-11 14:23:54 +00:00
Rob Winch 6c35c33abe SEC-2447: Fix AuthenticationManagerBuilder ordering issues 2014-02-09 21:17:51 -06:00
Rob Winch c42e13c966 loginProcessing test 2014-02-07 17:01:11 -06:00
Rob Winch 6b42a2eae1 SEC-2461: Multi WebSecurityConfiguration does not create null springSecurityFilterChain 2014-02-07 17:01:11 -06:00
Rob Winch ec8b48150d SEC-2474: Update poms 2014-02-07 17:01:11 -06:00
Rob Winch 4eff50b48b SEC-2474: Update tests against Spring 4.0.1 2014-01-30 09:44:26 -06:00
Rob Winch 087b56da96 SEC-2473: Update to Spring 3.2.7 2014-01-30 09:44:26 -06:00
Rob Winch 8d8475deb1 SEC-2455: form-login@login-processing-url & logout@logout-url use matchers
Remove the deprecation warnings of using setFilterProcessingUrl by invoking
the matcher methods instead.
2014-01-29 15:35:18 -06:00
Rob Winch b5f5665ea6 SEC-2463: CSRF documentation includes EnableWebMvcSecurity 2014-01-29 09:28:51 -06:00
Rob Winch 3b05fd6fed SEC-2466: Add link to MultipartFilter in CSRF multipart section 2014-01-28 22:04:35 -06:00
Rob Winch 4c84805ac9 SEC-2466: CSRF MutipartFilter doc now uses <url-pattern> 2014-01-28 16:51:05 -06:00
james a99c6db327 SEC-2467: Fix Small errors in itest-web's jsps 2014-01-28 16:03:59 -06:00
Rob Winch 1f833b0d6b Add ExpressionUrlAuthorizationCOnfigurer tests
- Demo custom expression root
- Demo @Bean in expression example
2014-01-23 11:21:21 -06:00
Spring Buildmaster add3aae6ef Next development version 2013-12-16 11:27:25 -08:00
320 changed files with 10514 additions and 2616 deletions
+1 -2
View File
@@ -18,5 +18,4 @@ build/
*.ipr
*.iws
.gradle/
atlassian-ide-plugin.xml
/samples
atlassian-ide-plugin.xml
+1 -1
View File
@@ -16,7 +16,7 @@ Is there already an issue that addresses your concern? Do a bit of searching in
If you're considering anything more than correcting a typo or fixing a minor bug , please discuss it on the [Spring Security forums](http://forum.springsource.org/forumdisplay.php?33-Security) before submitting a pull request. We're happy to provide guidance but please spend an hour or two researching the subject on your own including searching the forums for prior discussions.
# Sign the Contributor License Agreement
If you have not previously done so, please fill out and submit the SpringSource CLA form. You'll receive a token when this process is complete. Keep track of this, you may be asked for it later!
If you have not previously done so, please fill out and submit the [SpringSource CLA form](https://support.springsource.com/spring_committer_signup). You'll receive a token when this process is complete. Keep track of this, you may be asked for it later!
* For **Project** select _Spring Security_
* For **Project Lead** enter _Rob Winch_
+58
View File
@@ -0,0 +1,58 @@
= Spring Security
Spring Security provides security services for the http://docs.spring.io[Spring IO Platform]. Spring Security 3.1 requires Spring 3.0.3 as
a minimum and also requires Java 5.
For a detailed list of features and access to the latest release, please visit http://spring.io/projects[Spring projects].
== Downloading Artifacts
See https://github.com/spring-projects/spring-framework/wiki/Downloading-Spring-artifacts[downloading Spring artifacts] for Maven repository information.
== Documentation
Be sure to read the http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/[Spring Security Reference].
Extensive JavaDoc for the Spring Security code is also available in the http://docs.spring.io/spring-security/site/docs/current/apidocs/[Spring Security API Documentation].
== Quick Start
We recommend you visit http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/[Spring Security Reference] and read the "Getting Started" page.
== Building from Source
Spring Security uses a http://gradle.org[Gradle]-based build system.
In the instructions below, http://vimeo.com/34436402[`./gradlew`] is invoked from the root of the source tree and serves as
a cross-platform, self-contained bootstrap mechanism for the build.
=== Prerequisites
http://help.github.com/set-up-git-redirect[Git] and the http://www.oracle.com/technetwork/java/javase/downloads[JDK7 build].
Be sure that your `JAVA_HOME` environment variable points to the `jdk1.7.0` folder extracted from the JDK download.
=== Check out sources
[indent=0]
----
git clone git@github.com:spring-projects/spring-security.git
----
=== Install all spring-\* jars into your local Maven cache
[indent=0]
----
./gradlew install
----
=== Compile and test; build all jars, distribution zips, and docs
[indent=0]
----
./gradlew build
----
Discover more commands with `./gradlew tasks`.
See also the https://github.com/spring-projects/spring-framework/wiki/Gradle-build-and-release-FAQ[Gradle build and release FAQ].
== Getting Support
Check out the http://stackoverflow.com/questions/tagged/spring-security[Spring Security tags on Stack Overflow].
http://spring.io/services[Commercial support] is available too.
== Contributing
http://help.github.com/send-pull-requests[Pull requests] are welcome; see the https://github.com/spring-projects/spring-security/blob/master/CONTRIBUTING.md[contributor guidelines] for details.
== License
Spring Security is Open Source software released under the
http://www.apache.org/licenses/LICENSE-2.0.html[Apache 2.0 license].
+2 -1
View File
@@ -2,13 +2,14 @@
dependencies {
compile project(':spring-security-core'),
springCoreDependency,
'aopalliance:aopalliance:1.0',
"org.springframework:spring-aop:$springVersion",
"org.springframework:spring-context:$springVersion",
"org.springframework:spring-tx:$springVersion",
"org.springframework:spring-jdbc:$springVersion"
optional "net.sf.ehcache:ehcache:$ehcacheVersion"
optional "net.sf.ehcache:ehcache-core:$ehcacheVersion"
testCompile "org.springframework:spring-beans:$springVersion",
"org.springframework:spring-context-support:$springVersion",
+14 -20
View File
@@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-acl</artifactId>
<version>3.2.0.CI-SNAPSHOT</version>
<version>3.2.8.RELEASE</version>
<name>spring-security-acl</name>
<description>spring-security-acl</description>
<url>http://spring.io/spring-security</url>
@@ -42,12 +42,6 @@
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-snasphot</id>
<url>https://repo.spring.io/snapshot</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>aopalliance</groupId>
@@ -58,25 +52,25 @@
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>3.2.0.CI-SNAPSHOT</version>
<version>3.2.8.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>3.2.6.RELEASE</version>
<version>3.2.13.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.2.6.RELEASE</version>
<version>3.2.13.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>3.2.6.RELEASE</version>
<version>3.2.13.RELEASE</version>
<scope>compile</scope>
<exclusions>
<exclusion>
@@ -88,13 +82,13 @@
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>3.2.6.RELEASE</version>
<version>3.2.13.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>3.2.6.RELEASE</version>
<version>3.2.13.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
@@ -106,8 +100,8 @@
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>1.6.2</version>
<artifactId>ehcache-core</artifactId>
<version>1.7.2</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
@@ -120,7 +114,7 @@
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -132,7 +126,7 @@
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>1.8.0.10</version>
<version>2.3.1</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -150,19 +144,19 @@
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>3.2.6.RELEASE</version>
<version>3.2.13.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>3.2.6.RELEASE</version>
<version>3.2.13.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>3.2.6.RELEASE</version>
<version>3.2.13.RELEASE</version>
<scope>test</scope>
</dependency>
</dependencies>
@@ -3,6 +3,7 @@ package org.springframework.security.acls;
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -121,8 +122,9 @@ public class AclPermissionEvaluator implements PermissionEvaluator {
try {
p = permissionFactory.buildFromName(permString);
} catch(IllegalArgumentException notfound) {
p = permissionFactory.buildFromName(permString.toUpperCase());
}
catch (IllegalArgumentException notfound) {
p = permissionFactory.buildFromName(permString.toUpperCase(Locale.ENGLISH));
}
if (p != null) {
+2 -2
View File
@@ -5,12 +5,12 @@
-- drop table acl_class;
-- drop table acl_sid;
create table acl_sid(
id bigint generated by default as identity(start with 100) not null primary key,
principal boolean not null,
sid varchar_ignorecase(100) not null,
constraint unique_uk_1 unique(sid,principal));
constraint unique_uk_1 unique(sid,principal)
);
create table acl_class(
id bigint generated by default as identity(start with 100) not null primary key,
@@ -0,0 +1,46 @@
-- ACL Schema SQL for MySQL 5.5+ / MariaDB equivalent
-- drop table acl_entry;
-- drop table acl_object_identity;
-- drop table acl_class;
-- drop table acl_sid;
CREATE TABLE acl_sid (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
principal BOOLEAN NOT NULL,
sid VARCHAR(100) NOT NULL,
UNIQUE KEY unique_acl_sid (sid, principal)
) ENGINE=InnoDB;
CREATE TABLE acl_class (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
class VARCHAR(100) NOT NULL,
UNIQUE KEY uk_acl_class (class)
) ENGINE=InnoDB;
CREATE TABLE acl_object_identity (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
object_id_class BIGINT UNSIGNED NOT NULL,
object_id_identity BIGINT NOT NULL,
parent_object BIGINT UNSIGNED,
owner_sid BIGINT UNSIGNED,
entries_inheriting BOOLEAN NOT NULL,
UNIQUE KEY uk_acl_object_identity (object_id_class, object_id_identity),
CONSTRAINT fk_acl_object_identity_parent FOREIGN KEY (parent_object) REFERENCES acl_object_identity (id),
CONSTRAINT fk_acl_object_identity_class FOREIGN KEY (object_id_class) REFERENCES acl_class (id),
CONSTRAINT fk_acl_object_identity_owner FOREIGN KEY (owner_sid) REFERENCES acl_sid (id)
) ENGINE=InnoDB;
CREATE TABLE acl_entry (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
acl_object_identity BIGINT UNSIGNED NOT NULL,
ace_order INTEGER NOT NULL,
sid BIGINT UNSIGNED NOT NULL,
mask INTEGER UNSIGNED NOT NULL,
granting BOOLEAN NOT NULL,
audit_success BOOLEAN NOT NULL,
audit_failure BOOLEAN NOT NULL,
UNIQUE KEY unique_acl_entry (acl_object_identity, ace_order),
CONSTRAINT fk_acl_entry_object FOREIGN KEY (acl_object_identity) REFERENCES acl_object_identity (id),
CONSTRAINT fk_acl_entry_acl FOREIGN KEY (sid) REFERENCES acl_sid (id)
) ENGINE=InnoDB;
@@ -0,0 +1,82 @@
-- ACL Schema SQL for Oracle Database 10g+
-- drop trigger acl_sid_id_trigger;
-- drop trigger acl_class_id_trigger;
-- drop trigger acl_object_identity_id_trigger;
-- drop trigger acl_entry_id_trigger;
-- drop sequence acl_sid_sequence;
-- drop sequence acl_class_sequence;
-- drop sequence acl_object_identity_sequence;
-- drop sequence acl_entry_sequence;
-- drop table acl_entry;
-- drop table acl_object_identity;
-- drop table acl_class;
-- drop table acl_sid;
CREATE TABLE acl_sid (
id NUMBER(38) NOT NULL PRIMARY KEY,
principal NUMBER(1) NOT NULL CHECK (principal in (0, 1)),
sid NVARCHAR2(100) NOT NULL,
CONSTRAINT unique_acl_sid UNIQUE (sid, principal)
);
CREATE SEQUENCE acl_sid_sequence START WITH 1 INCREMENT BY 1 NOMAXVALUE;
CREATE OR REPLACE TRIGGER acl_sid_id_trigger
BEFORE INSERT ON acl_sid
FOR EACH ROW
BEGIN
SELECT acl_sid_sequence.nextval INTO :new.id FROM dual;
END;
CREATE TABLE acl_class (
id NUMBER(38) NOT NULL PRIMARY KEY,
class NVARCHAR2(100) NOT NULL,
CONSTRAINT uk_acl_class UNIQUE (class)
);
CREATE SEQUENCE acl_class_sequence START WITH 1 INCREMENT BY 1 NOMAXVALUE;
CREATE OR REPLACE TRIGGER acl_class_id_trigger
BEFORE INSERT ON acl_class
FOR EACH ROW
BEGIN
SELECT acl_class_sequence.nextval INTO :new.id FROM dual;
END;
CREATE TABLE acl_object_identity (
id NUMBER(38) NOT NULL PRIMARY KEY,
object_id_class NUMBER(38) NOT NULL,
object_id_identity NUMBER(38) NOT NULL,
parent_object NUMBER(38),
owner_sid NUMBER(38),
entries_inheriting NUMBER(1) NOT NULL CHECK (entries_inheriting in (0, 1)),
CONSTRAINT uk_acl_object_identity UNIQUE (object_id_class, object_id_identity),
CONSTRAINT fk_acl_object_identity_parent FOREIGN KEY (parent_object) REFERENCES acl_object_identity (id),
CONSTRAINT fk_acl_object_identity_class FOREIGN KEY (object_id_class) REFERENCES acl_class (id),
CONSTRAINT fk_acl_object_identity_owner FOREIGN KEY (owner_sid) REFERENCES acl_sid (id)
);
CREATE SEQUENCE acl_object_identity_sequence START WITH 1 INCREMENT BY 1 NOMAXVALUE;
CREATE OR REPLACE TRIGGER acl_object_identity_id_trigger
BEFORE INSERT ON acl_object_identity
FOR EACH ROW
BEGIN
SELECT acl_object_identity_sequence.nextval INTO :new.id FROM dual;
END;
CREATE TABLE acl_entry (
id NUMBER(38) NOT NULL PRIMARY KEY,
acl_object_identity NUMBER(38) NOT NULL,
ace_order INTEGER NOT NULL,
sid NUMBER(38) NOT NULL,
mask INTEGER NOT NULL,
granting NUMBER(1) NOT NULL CHECK (granting in (0, 1)),
audit_success NUMBER(1) NOT NULL CHECK (audit_success in (0, 1)),
audit_failure NUMBER(1) NOT NULL CHECK (audit_failure in (0, 1)),
CONSTRAINT unique_acl_entry UNIQUE (acl_object_identity, ace_order),
CONSTRAINT fk_acl_entry_object FOREIGN KEY (acl_object_identity) REFERENCES acl_object_identity (id),
CONSTRAINT fk_acl_entry_acl FOREIGN KEY (sid) REFERENCES acl_sid (id)
);
CREATE SEQUENCE acl_entry_sequence START WITH 1 INCREMENT BY 1 NOMAXVALUE;
CREATE OR REPLACE TRIGGER acl_entry_id_trigger
BEFORE INSERT ON acl_entry
FOR EACH ROW
BEGIN
SELECT acl_entry_sequence.nextval INTO :new.id FROM dual;
END;
@@ -0,0 +1,46 @@
-- ACL Schema SQL for Microsoft SQL Server 2008+
-- drop table acl_entry;
-- drop table acl_object_identity;
-- drop table acl_class;
-- drop table acl_sid;
CREATE TABLE acl_sid (
id BIGINT NOT NULL IDENTITY PRIMARY KEY,
principal BIT NOT NULL,
sid VARCHAR(100) NOT NULL,
CONSTRAINT unique_acl_sid UNIQUE (sid, principal)
);
CREATE TABLE acl_class (
id BIGINT NOT NULL IDENTITY PRIMARY KEY,
class VARCHAR(100) NOT NULL,
CONSTRAINT uk_acl_class UNIQUE (class)
);
CREATE TABLE acl_object_identity (
id BIGINT NOT NULL IDENTITY PRIMARY KEY,
object_id_class BIGINT NOT NULL,
object_id_identity BIGINT NOT NULL,
parent_object BIGINT,
owner_sid BIGINT,
entries_inheriting BIT NOT NULL,
CONSTRAINT uk_acl_object_identity UNIQUE (object_id_class, object_id_identity),
CONSTRAINT fk_acl_object_identity_parent FOREIGN KEY (parent_object) REFERENCES acl_object_identity (id),
CONSTRAINT fk_acl_object_identity_class FOREIGN KEY (object_id_class) REFERENCES acl_class (id),
CONSTRAINT fk_acl_object_identity_owner FOREIGN KEY (owner_sid) REFERENCES acl_sid (id)
);
CREATE TABLE acl_entry (
id BIGINT NOT NULL IDENTITY PRIMARY KEY,
acl_object_identity BIGINT NOT NULL,
ace_order INTEGER NOT NULL,
sid BIGINT NOT NULL,
mask INTEGER NOT NULL,
granting BIT NOT NULL,
audit_success BIT NOT NULL,
audit_failure BIT NOT NULL,
CONSTRAINT unique_acl_entry UNIQUE (acl_object_identity, ace_order),
CONSTRAINT fk_acl_entry_object FOREIGN KEY (acl_object_identity) REFERENCES acl_object_identity (id),
CONSTRAINT fk_acl_entry_acl FOREIGN KEY (sid) REFERENCES acl_sid (id)
);
@@ -4,6 +4,8 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
import java.util.Locale;
import org.junit.Test;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.model.AclService;
@@ -36,4 +38,26 @@ public class AclPermissionEvaluatorTests {
assertTrue(pe.hasPermission(mock(Authentication.class), new Object(), "READ"));
}
@Test
public void resolvePermissionNonEnglishLocale() {
Locale systemLocale = Locale.getDefault();
Locale.setDefault(new Locale("tr"));
AclService service = mock(AclService.class);
AclPermissionEvaluator pe = new AclPermissionEvaluator(service);
ObjectIdentity oid = mock(ObjectIdentity.class);
ObjectIdentityRetrievalStrategy oidStrategy = mock(ObjectIdentityRetrievalStrategy.class);
when(oidStrategy.getObjectIdentity(anyObject())).thenReturn(oid);
pe.setObjectIdentityRetrievalStrategy(oidStrategy);
pe.setSidRetrievalStrategy(mock(SidRetrievalStrategy.class));
Acl acl = mock(Acl.class);
when(service.readAclById(any(ObjectIdentity.class), anyList())).thenReturn(acl);
when(acl.isGranted(anyList(), anyList(), eq(false))).thenReturn(true);
assertTrue(pe.hasPermission(mock(Authentication.class), new Object(), "write"));
Locale.setDefault(systemLocale);
}
}
@@ -1,11 +1,28 @@
package org.springframework.security.acls.jdbc;
import static org.mockito.Mockito.*;
import static org.junit.Assert.*;
import static org.fest.assertions.Assertions.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.List;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Ehcache;
import org.junit.*;
import net.sf.ehcache.Element;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.security.acls.domain.AclAuthorizationStrategy;
import org.springframework.security.acls.domain.AclAuthorizationStrategyImpl;
import org.springframework.security.acls.domain.AclImpl;
@@ -19,50 +36,43 @@ import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.util.FieldUtils;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.*;
import org.springframework.test.util.ReflectionTestUtils;
/**
* Tests {@link EhCacheBasedAclCache}
*
* @author Andrei Stefan
*/
@RunWith(MockitoJUnitRunner.class)
public class EhCacheBasedAclCacheTests {
private static final String TARGET_CLASS = "org.springframework.security.acls.TargetObject";
private static CacheManager cacheManager;
@Mock
private Ehcache cache;
@Captor
private ArgumentCaptor<Element> element;
@BeforeClass
public static void initCacheManaer() {
cacheManager = CacheManager.create();
// Use disk caching immediately (to test for serialization issue reported in SEC-527)
cacheManager.addCache(new Cache("ehcachebasedacltests", 0, true, false, 600, 300));
}
private EhCacheBasedAclCache myCache;
@AfterClass
public static void shutdownCacheManager() {
cacheManager.removalAll();
cacheManager.shutdown();
private MutableAcl acl;
@Before
public void setup() {
myCache = new EhCacheBasedAclCache(cache);
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(100));
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority("ROLE_AUDITING"),
new SimpleGrantedAuthority("ROLE_GENERAL"));
acl = new AclImpl(identity, Long.valueOf(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
}
@After
public void clearContext() {
public void cleanup() {
SecurityContextHolder.clearContext();
}
private Ehcache getCache() {
Ehcache cache = cacheManager.getCache("ehcachebasedacltests");
cache.removeAll();
return cache;
}
@Test(expected=IllegalArgumentException.class)
public void constructorRejectsNullParameters() throws Exception {
new EhCacheBasedAclCache(null);
@@ -70,9 +80,6 @@ public class EhCacheBasedAclCacheTests {
@Test
public void methodsRejectNullParameters() throws Exception {
Ehcache cache = new MockEhcache();
EhCacheBasedAclCache myCache = new EhCacheBasedAclCache(cache);
try {
Serializable id = null;
myCache.evictFromCache(id);
@@ -122,12 +129,6 @@ public class EhCacheBasedAclCacheTests {
// SEC-527
@Test
public void testDiskSerializationOfMutableAclObjectInstance() throws Exception {
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(100));
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority("ROLE_AUDITING"),
new SimpleGrantedAuthority("ROLE_GENERAL"));
MutableAcl acl = new AclImpl(identity, Long.valueOf(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
// Serialization test
File file = File.createTempFile("SEC_TEST", ".object");
FileOutputStream fos = new FileOutputStream(file);
@@ -150,111 +151,117 @@ public class EhCacheBasedAclCacheTests {
}
@Test
public void cacheOperationsAclWithoutParent() throws Exception {
Ehcache cache = getCache();
EhCacheBasedAclCache myCache = new EhCacheBasedAclCache(cache);
public void clearCache() throws Exception {
myCache.clearCache();
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(100));
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority("ROLE_AUDITING"),
new SimpleGrantedAuthority("ROLE_GENERAL"));
MutableAcl acl = new AclImpl(identity, Long.valueOf(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
assertEquals(0, cache.getDiskStoreSize());
myCache.putInCache(acl);
assertEquals(cache.getSize(), 2);
assertEquals(2, cache.getDiskStoreSize());
assertTrue(cache.isElementOnDisk(acl.getObjectIdentity()));
assertFalse(cache.isElementInMemory(acl.getObjectIdentity()));
// Check we can get from cache the same objects we put in
assertEquals(myCache.getFromCache(Long.valueOf(1)), acl);
assertEquals(myCache.getFromCache(identity), acl);
// Put another object in cache
ObjectIdentity identity2 = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(101));
MutableAcl acl2 = new AclImpl(identity2, Long.valueOf(2), aclAuthorizationStrategy, new ConsoleAuditLogger());
myCache.putInCache(acl2);
assertEquals(cache.getSize(), 4);
assertEquals(4, cache.getDiskStoreSize());
// Try to evict an entry that doesn't exist
myCache.evictFromCache(Long.valueOf(3));
myCache.evictFromCache(new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(102)));
assertEquals(cache.getSize(), 4);
assertEquals(4, cache.getDiskStoreSize());
myCache.evictFromCache(Long.valueOf(1));
assertEquals(cache.getSize(), 2);
assertEquals(2, cache.getDiskStoreSize());
// Check the second object inserted
assertEquals(myCache.getFromCache(Long.valueOf(2)), acl2);
assertEquals(myCache.getFromCache(identity2), acl2);
myCache.evictFromCache(identity2);
assertEquals(cache.getSize(), 0);
verify(cache).removeAll();
}
@SuppressWarnings("unchecked")
@Test
public void cacheOperationsAclWithParent() throws Exception {
Ehcache cache = getCache();
EhCacheBasedAclCache myCache = new EhCacheBasedAclCache(cache);
public void putInCache() throws Exception {
myCache.putInCache(acl);
verify(cache, times(2)).put(element.capture());
assertThat(element.getValue().getKey()).isEqualTo(acl.getId());
assertThat(element.getValue().getObjectValue()).isEqualTo(acl);
assertThat(element.getAllValues().get(0).getKey()).isEqualTo(acl.getObjectIdentity());
assertThat(element.getAllValues().get(0).getObjectValue()).isEqualTo(acl);
}
@Test
public void putInCacheAclWithParent() throws Exception {
Authentication auth = new TestingAuthenticationToken("user", "password", "ROLE_GENERAL");
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(1));
ObjectIdentity identityParent = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(2));
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority("ROLE_AUDITING"),
new SimpleGrantedAuthority("ROLE_GENERAL"));
MutableAcl acl = new AclImpl(identity, Long.valueOf(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
MutableAcl parentAcl = new AclImpl(identityParent, Long.valueOf(2), aclAuthorizationStrategy, new ConsoleAuditLogger());
acl.setParent(parentAcl);
assertEquals(0, cache.getDiskStoreSize());
myCache.putInCache(acl);
assertEquals(cache.getSize(), 4);
assertEquals(4, cache.getDiskStoreSize());
assertTrue(cache.isElementOnDisk(acl.getObjectIdentity()));
assertTrue(cache.isElementOnDisk(Long.valueOf(1)));
assertFalse(cache.isElementInMemory(acl.getObjectIdentity()));
assertFalse(cache.isElementInMemory(Long.valueOf(1)));
cache.flush();
// Wait for the spool to be written to disk (it's asynchronous)
Map spool = (Map) FieldUtils.getFieldValue(cache, "diskStore.spool");
while(spool.size() > 0) {
Thread.sleep(50);
}
verify(cache, times(4)).put(element.capture());
// Check we can get from cache the same objects we put in
AclImpl aclFromCache = (AclImpl) myCache.getFromCache(Long.valueOf(1));
// For the checks on transient fields, we need to be sure that the object is being loaded from the cache,
// not from the ehcache spool or elsewhere...
assertFalse(acl == aclFromCache);
assertEquals(acl, aclFromCache);
// SEC-951 check transient fields are set on parent
assertNotNull(FieldUtils.getFieldValue(aclFromCache.getParentAcl(), "aclAuthorizationStrategy"));
assertNotNull(FieldUtils.getFieldValue(aclFromCache.getParentAcl(), "permissionGrantingStrategy"));
assertEquals(acl, myCache.getFromCache(identity));
assertNotNull(FieldUtils.getFieldValue(aclFromCache, "aclAuthorizationStrategy"));
AclImpl parentAclFromCache = (AclImpl) myCache.getFromCache(Long.valueOf(2));
assertEquals(parentAcl, parentAclFromCache);
assertNotNull(FieldUtils.getFieldValue(parentAclFromCache, "aclAuthorizationStrategy"));
assertEquals(parentAcl, myCache.getFromCache(identityParent));
List<Element> allValues = element.getAllValues();
assertThat(allValues.get(0).getKey()).isEqualTo(parentAcl.getObjectIdentity());
assertThat(allValues.get(0).getObjectValue()).isEqualTo(parentAcl);
assertThat(allValues.get(1).getKey()).isEqualTo(parentAcl.getId());
assertThat(allValues.get(1).getObjectValue()).isEqualTo(parentAcl);
assertThat(allValues.get(2).getKey()).isEqualTo(acl.getObjectIdentity());
assertThat(allValues.get(2).getObjectValue()).isEqualTo(acl);
assertThat(allValues.get(3).getKey()).isEqualTo(acl.getId());
assertThat(allValues.get(3).getObjectValue()).isEqualTo(acl);
}
//~ Inner Classes ==================================================================================================
@Test
public void getFromCacheSerializable() throws Exception {
when(cache.get(acl.getId())).thenReturn(new Element(acl.getId(),acl));
private class MockEhcache extends Cache {
public MockEhcache() {
super("cache", 0, true, true, 0, 0);
}
assertThat(myCache.getFromCache(acl.getId())).isEqualTo(acl);
}
@Test
public void getFromCacheSerializablePopulatesTransient() throws Exception {
when(cache.get(acl.getId())).thenReturn(new Element(acl.getId(),acl));
myCache.putInCache(acl);
ReflectionTestUtils.setField(acl, "permissionGrantingStrategy", null);
ReflectionTestUtils.setField(acl, "aclAuthorizationStrategy", null);
MutableAcl fromCache = myCache.getFromCache(acl.getId());
assertThat(ReflectionTestUtils.getField(fromCache, "aclAuthorizationStrategy")).isNotNull();
assertThat(ReflectionTestUtils.getField(fromCache, "permissionGrantingStrategy")).isNotNull();
}
@Test
public void getFromCacheObjectIdentity() throws Exception {
when(cache.get(acl.getId())).thenReturn(new Element(acl.getId(),acl));
assertThat(myCache.getFromCache(acl.getId())).isEqualTo(acl);
}
@Test
public void getFromCacheObjectIdentityPopulatesTransient() throws Exception {
when(cache.get(acl.getObjectIdentity())).thenReturn(new Element(acl.getId(),acl));
myCache.putInCache(acl);
ReflectionTestUtils.setField(acl, "permissionGrantingStrategy", null);
ReflectionTestUtils.setField(acl, "aclAuthorizationStrategy", null);
MutableAcl fromCache = myCache.getFromCache(acl.getObjectIdentity());
assertThat(ReflectionTestUtils.getField(fromCache, "aclAuthorizationStrategy")).isNotNull();
assertThat(ReflectionTestUtils.getField(fromCache, "permissionGrantingStrategy")).isNotNull();
}
@Test
public void evictCacheSerializable() throws Exception {
when(cache.get(acl.getObjectIdentity())).thenReturn(new Element(acl.getId(),acl));
myCache.evictFromCache(acl.getObjectIdentity());
verify(cache).remove(acl.getId());
verify(cache).remove(acl.getObjectIdentity());
}
@Test
public void evictCacheObjectIdentity() throws Exception {
when(cache.get(acl.getId())).thenReturn(new Element(acl.getId(),acl));
myCache.evictFromCache(acl.getId());
verify(cache).remove(acl.getId());
verify(cache).remove(acl.getObjectIdentity());
}
}
+1
View File
@@ -1,6 +1,7 @@
dependencies {
compile project(':spring-security-core'),
springCoreDependency,
"org.springframework:spring-beans:$springVersion",
"org.springframework:spring-context:$springVersion"
+8 -14
View File
@@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-aspects</artifactId>
<version>3.2.0.CI-SNAPSHOT</version>
<version>3.2.8.RELEASE</version>
<name>spring-security-aspects</name>
<description>spring-security-aspects</description>
<url>http://spring.io/spring-security</url>
@@ -42,35 +42,29 @@
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-snasphot</id>
<url>https://repo.spring.io/snapshot</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>3.2.0.CI-SNAPSHOT</version>
<version>3.2.8.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>3.2.6.RELEASE</version>
<version>3.2.13.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.2.6.RELEASE</version>
<version>3.2.13.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>3.2.6.RELEASE</version>
<version>3.2.13.RELEASE</version>
<scope>compile</scope>
<exclusions>
<exclusion>
@@ -108,7 +102,7 @@
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -132,13 +126,13 @@
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>3.2.6.RELEASE</version>
<version>3.2.13.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>3.2.6.RELEASE</version>
<version>3.2.13.RELEASE</version>
<scope>test</scope>
</dependency>
</dependencies>
+17 -32
View File
@@ -2,12 +2,13 @@ import groovy.text.SimpleTemplateEngine
buildscript {
repositories {
maven { url "http://repo.springsource.org/plugins-release" }
maven { url "https://repo.spring.io/plugins-release" }
}
dependencies {
classpath("org.springframework.build.gradle:propdeps-plugin:0.0.5")
classpath("org.springframework.build.gradle:propdeps-plugin:0.0.6")
classpath("org.springframework.build.gradle:bundlor-plugin:0.1.2")
classpath("org.gradle.api.plugins:gradle-tomcat-plugin:0.9.8")
classpath("org.springframework.build.gradle:spring-io-plugin:0.0.3.RELEASE")
classpath("org.gradle.api.plugins:gradle-tomcat-plugin:1.2.3")
classpath('me.champeau.gradle:gradle-javadoc-hotfix-plugin:0.1')
classpath('org.asciidoctor:asciidoctor-gradle-plugin:0.7.0')
classpath('org.asciidoctor:asciidoctor-java-integration:0.1.4.preview.1')
@@ -25,14 +26,17 @@ allprojects {
ext.releaseBuild = version.endsWith('RELEASE')
ext.snapshotBuild = version.endsWith('SNAPSHOT')
ext.springVersion = '3.2.6.RELEASE'
ext.spring4Version = '4.0.0.RELEASE'
ext.springVersion = '3.2.13.RELEASE'
ext.spring4Version = '4.1.6.RELEASE'
ext.springLdapVersion = '1.3.2.RELEASE'
ext.springLdap2Version = '2.0.1.RELEASE'
group = 'org.springframework.security'
repositories {
mavenCentral()
maven { url "http://repo.springsource.org/plugins-release" }
maven { url "https://repo.spring.io/libs-snapshot" }
maven { url "https://repo.spring.io/plugins-release" }
maven { url "http://repo.terracotta.org/maven2/" }
}
@@ -58,7 +62,7 @@ ext.javaProjects = subprojects.findAll { project -> project.name != 'docs' && pr
ext.sampleProjects = subprojects.findAll { project -> project.name.startsWith('spring-security-samples') }
ext.itestProjects = subprojects.findAll { project -> project.name.startsWith('itest') }
ext.coreModuleProjects = javaProjects - sampleProjects - itestProjects
ext.aspectjProjects = [project(':spring-security-aspects'), project(':spring-security-samples-aspectj-xml')]
ext.aspectjProjects = [project(':spring-security-aspects'), project(':spring-security-samples-aspectj-xml'), project(':spring-security-samples-aspectj-jc')]
configure(allprojects - javaProjects) {
task afterEclipseImport {
@@ -97,6 +101,9 @@ configure(javaProjects) {
configure(coreModuleProjects) {
apply plugin: 'bundlor'
apply plugin: 'emma'
apply plugin: 'spring-io'
ext.springIoVersion = project.hasProperty('platformVersion') ? platformVersion : 'latest.integration'
bundlor.doFirst {
def templateText = file("template.mf").text
@@ -104,24 +111,10 @@ configure(coreModuleProjects) {
}
configurations {
jacoco //Configuration Group used by Sonar to provide Code Coverage using JaCoCo
spring4TestRuntime.extendsFrom testRuntime
}
configurations.spring4TestRuntime {
resolutionStrategy.eachDependency { DependencyResolveDetails details ->
if (details.requested.group == 'org.springframework') {
details.useVersion spring4Version
}
if (details.requested.name == 'ehcache') {
details.useVersion '2.6.5'
}
if (details.requested.name == 'ehcache-terracotta') {
details.useVersion '2.1.1'
}
}
}
dependencies {
jacoco "org.jacoco:org.jacoco.agent:0.6.2.201302030002:runtime"
springIoVersions "io.spring.platform:platform-versions:${springIoVersion}@properties"
}
test {
jvmArgs "-javaagent:${configurations.jacoco.asPath}=destfile=${buildDir}/jacoco.exec,includes=${project.group}.*"
@@ -129,14 +122,6 @@ configure(coreModuleProjects) {
integrationTest {
jvmArgs "-javaagent:${configurations.jacoco.asPath}=destfile=${buildDir}/jacoco.exec,includes=${project.group}.*"
}
task spring4Test(type: Test) {
jvmArgs = ['-ea', '-Xmx500m', '-XX:MaxPermSize=128M']
exclude "**/EhCacheBasedAclCacheTests.class", "**/Issue55Tests.class"
classpath = sourceSets.test.output + sourceSets.main.output + configurations.spring4TestRuntime
testResultsDir = file("$buildDir/spring4-test-results/")
testReportDir = file("$buildDir/reports/spring4-tests/")
}
check.dependsOn spring4Test
}
configure (aspectjProjects) {
@@ -160,7 +145,7 @@ task dist(type: Zip) {
def zipRootDir = "${project.name}-$version"
into(zipRootDir) {
from(rootDir) {
include '*.txt'
include '*.adoc'
}
into('docs') {
with(project(':docs').apiSpec)
@@ -188,5 +173,5 @@ artifacts {
}
task wrapper(type: Wrapper) {
gradleVersion = '1.10-rc-2'
gradleVersion = '1.12'
}
+4 -3
View File
@@ -2,12 +2,13 @@
dependencies {
compile project(':spring-security-core'),
project(':spring-security-web'),
springCoreDependency,
"org.springframework:spring-context:$springVersion",
"org.springframework:spring-beans:$springVersion",
"org.springframework:spring-web:$springVersion",
"org.jasig.cas.client:cas-client-core:3.2.1"
"org.jasig.cas.client:cas-client-core:3.3.3"
optional "net.sf.ehcache:ehcache:$ehcacheVersion"
optional "net.sf.ehcache:ehcache-core:$ehcacheVersion"
provided "org.apache.tomcat:tomcat-servlet-api:$servletApiVersion"
provided "javax.servlet:javax.servlet-api:$servletApiVersion"
}
+15 -21
View File
@@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-cas</artifactId>
<version>3.2.0.CI-SNAPSHOT</version>
<version>3.2.8.RELEASE</version>
<name>spring-security-cas</name>
<description>spring-security-cas</description>
<url>http://spring.io/spring-security</url>
@@ -42,47 +42,41 @@
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-snasphot</id>
<url>https://repo.spring.io/snapshot</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.jasig.cas.client</groupId>
<artifactId>cas-client-core</artifactId>
<version>3.2.1</version>
<version>3.3.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>3.2.0.CI-SNAPSHOT</version>
<version>3.2.8.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>3.2.0.CI-SNAPSHOT</version>
<version>3.2.8.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>3.2.6.RELEASE</version>
<version>3.2.13.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.2.6.RELEASE</version>
<version>3.2.13.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>3.2.6.RELEASE</version>
<version>3.2.13.RELEASE</version>
<scope>compile</scope>
<exclusions>
<exclusion>
@@ -94,7 +88,7 @@
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>3.2.6.RELEASE</version>
<version>3.2.13.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
@@ -106,15 +100,15 @@
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>1.6.2</version>
<artifactId>ehcache-core</artifactId>
<version>1.7.2</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-servlet-api</artifactId>
<version>7.0.33</version>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
@@ -126,7 +120,7 @@
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -150,7 +144,7 @@
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>3.2.6.RELEASE</version>
<version>3.2.13.RELEASE</version>
<scope>test</scope>
</dependency>
</dependencies>
@@ -49,9 +49,7 @@ public class ServiceProperties implements InitializingBean {
//~ Methods ========================================================================================================
public void afterPropertiesSet() throws Exception {
if(!authenticateAllArtifacts) {
Assert.hasLength(this.service, "service must be specified unless authenticateAllArtifacts is true.");
}
Assert.hasLength(this.service, "service cannot be empty.");
Assert.hasLength(this.artifactParameter, "artifactParameter cannot be empty.");
Assert.hasLength(this.serviceParameter, "serviceParameter cannot be empty.");
}
@@ -15,6 +15,8 @@
*/
package org.springframework.security.cas.web.authentication;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
@@ -50,11 +52,13 @@ final class DefaultServiceAuthenticationDetails extends WebAuthenticationDetails
* string from containing the artifact name and value. This can
* be created using {@link #createArtifactPattern(String)}.
*/
DefaultServiceAuthenticationDetails(HttpServletRequest request, Pattern artifactPattern) {
DefaultServiceAuthenticationDetails(String casService, HttpServletRequest request, Pattern artifactPattern) throws MalformedURLException {
super(request);
URL casServiceUrl = new URL(casService);
int port = getServicePort(casServiceUrl);
final String query = getQueryString(request,artifactPattern);
this.serviceUrl = UrlUtils.buildFullRequestUrl(request.getScheme(),
request.getServerName(), request.getServerPort(),
this.serviceUrl = UrlUtils.buildFullRequestUrl(casServiceUrl.getProtocol(),
casServiceUrl.getHost(), port,
request.getRequestURI(), query);
}
@@ -128,4 +132,17 @@ final class DefaultServiceAuthenticationDetails extends WebAuthenticationDetails
Assert.hasLength(artifactParameterName);
return Pattern.compile("&?"+Pattern.quote(artifactParameterName)+"=[^&]*");
}
/**
* Gets the port from the casServiceURL ensuring to return the proper value if the default port is being used.
* @param casServiceUrl the casServerUrl to be used (i.e. "https://example.com/context/j_spring_security_cas_check")
* @return the port that is configured for the casServerUrl
*/
private static int getServicePort(URL casServiceUrl) {
int port = casServiceUrl.getPort();
if(port == -1) {
port = casServiceUrl.getDefaultPort();
}
return port;
}
}
@@ -15,12 +15,18 @@
*/
package org.springframework.security.cas.web.authentication;
import java.net.MalformedURLException;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.cas.ServiceProperties;
import org.springframework.util.Assert;
/**
* The {@code AuthenticationDetailsSource} that is set on the
@@ -33,20 +39,33 @@ import org.springframework.security.cas.ServiceProperties;
* @author Rob Winch
*/
public class ServiceAuthenticationDetailsSource implements AuthenticationDetailsSource<HttpServletRequest,
ServiceAuthenticationDetails> {
ServiceAuthenticationDetails>, ApplicationContextAware {
//~ Instance fields ================================================================================================
private final Pattern artifactPattern;
private ServiceProperties serviceProperties;
//~ Constructors ===================================================================================================
/**
* Creates an implementation that uses the default CAS artifactParameterName.
* @deprecated Use ServiceAuthenticationDetailsSource(ServiceProperties)
*/
@Deprecated
public ServiceAuthenticationDetailsSource() {
this(ServiceProperties.DEFAULT_CAS_ARTIFACT_PARAMETER);
}
/**
* Creates an implementation that uses the specified ServiceProperites and the default CAS artifactParameterName.
*
* @param serviceProperties The ServiceProperties to use to construct the serviceUrl.
*/
public ServiceAuthenticationDetailsSource(ServiceProperties serviceProperties) {
this(serviceProperties,ServiceProperties.DEFAULT_CAS_ARTIFACT_PARAMETER);
}
/**
* Creates an implementation that uses the specified artifactParameterName
*
@@ -54,11 +73,27 @@ public class ServiceAuthenticationDetailsSource implements AuthenticationDetails
* the artifactParameterName that is removed from the current
* URL. The result becomes the service url. Cannot be null and
* cannot be an empty String.
* @deprecated Use ServiceAuthenticationDetailsSource(ServiceProperties,String)
*/
public ServiceAuthenticationDetailsSource(final String artifactParameterName) {
this.artifactPattern = DefaultServiceAuthenticationDetails.createArtifactPattern(artifactParameterName);
}
/**
* Creates an implementation that uses the specified artifactParameterName
*
* @param serviceProperties The ServiceProperties to use to construct the serviceUrl.
* @param artifactParameterName
* the artifactParameterName that is removed from the current
* URL. The result becomes the service url. Cannot be null and
* cannot be an empty String.
*/
public ServiceAuthenticationDetailsSource(ServiceProperties serviceProperties, String artifactParameterName) {
Assert.notNull(serviceProperties, "serviceProperties cannot be null");
this.serviceProperties = serviceProperties;
this.artifactPattern = DefaultServiceAuthenticationDetails.createArtifactPattern(artifactParameterName);
}
//~ Methods ========================================================================================================
/**
@@ -66,6 +101,17 @@ public class ServiceAuthenticationDetailsSource implements AuthenticationDetails
* @return the {@code ServiceAuthenticationDetails} containing information about the current request
*/
public ServiceAuthenticationDetails buildDetails(HttpServletRequest context) {
return new DefaultServiceAuthenticationDetails(context,artifactPattern);
try {
return new DefaultServiceAuthenticationDetails(serviceProperties.getService(),context,artifactPattern);
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if(serviceProperties == null) {
serviceProperties = applicationContext.getBean(ServiceProperties.class);
}
}
}
@@ -36,10 +36,13 @@ public class ServicePropertiesTests {
}
@Test
public void allowNullServiceWhenAuthenticateAllTokens() throws Exception {
public void nullServiceWhenAuthenticateAllTokens() throws Exception {
ServiceProperties sp = new ServiceProperties();
sp.setAuthenticateAllArtifacts(true);
sp.afterPropertiesSet();
try {
sp.afterPropertiesSet();
fail("Expected Exception");
}catch(IllegalArgumentException success) {}
sp.setAuthenticateAllArtifacts(false);
try {
sp.afterPropertiesSet();
@@ -18,11 +18,21 @@ import static org.junit.Assert.assertEquals;
import java.util.regex.Pattern;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.security.cas.ServiceProperties;
import org.springframework.security.web.util.UrlUtils;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
*
@@ -32,9 +42,13 @@ public class DefaultServiceAuthenticationDetailsTests {
private DefaultServiceAuthenticationDetails details;
private MockHttpServletRequest request;
private Pattern artifactPattern;
private String casServiceUrl;
private ConfigurableApplicationContext context;
@Before
public void setUp() {
casServiceUrl = "https://localhost:8443/j_spring_security_cas";
request = new MockHttpServletRequest();
request.setScheme("https");
request.setServerName("localhost");
@@ -44,45 +58,82 @@ public class DefaultServiceAuthenticationDetailsTests {
}
@Test
public void getServiceUrlNullQuery() throws Exception {
details = new DefaultServiceAuthenticationDetails(request,artifactPattern);
assertEquals(UrlUtils.buildFullRequestUrl(request),details.getServiceUrl());
@After
public void cleanup() {
if(context != null) {
context.close();
}
}
@Test
public void getServiceUrlTicketOnlyParam() {
public void getServiceUrlNullQuery() throws Exception {
details = new DefaultServiceAuthenticationDetails(casServiceUrl, request,artifactPattern);
assertEquals(UrlUtils.buildFullRequestUrl(request), details.getServiceUrl());
}
@Test
public void getServiceUrlTicketOnlyParam() throws Exception {
request.setQueryString("ticket=123");
details = new DefaultServiceAuthenticationDetails(request,artifactPattern);
details = new DefaultServiceAuthenticationDetails(casServiceUrl,request,artifactPattern);
String serviceUrl = details.getServiceUrl();
request.setQueryString(null);
assertEquals(UrlUtils.buildFullRequestUrl(request),serviceUrl);
}
@Test
public void getServiceUrlTicketFirstMultiParam() {
public void getServiceUrlTicketFirstMultiParam() throws Exception {
request.setQueryString("ticket=123&other=value");
details = new DefaultServiceAuthenticationDetails(request,artifactPattern);
details = new DefaultServiceAuthenticationDetails(casServiceUrl, request,artifactPattern);
String serviceUrl = details.getServiceUrl();
request.setQueryString("other=value");
assertEquals(UrlUtils.buildFullRequestUrl(request),serviceUrl);
}
@Test
public void getServiceUrlTicketLastMultiParam() {
public void getServiceUrlTicketLastMultiParam() throws Exception {
request.setQueryString("other=value&ticket=123");
details = new DefaultServiceAuthenticationDetails(request,artifactPattern);
details = new DefaultServiceAuthenticationDetails(casServiceUrl,request,artifactPattern);
String serviceUrl = details.getServiceUrl();
request.setQueryString("other=value");
assertEquals(UrlUtils.buildFullRequestUrl(request),serviceUrl);
}
@Test
public void getServiceUrlTicketMiddleMultiParam() {
public void getServiceUrlTicketMiddleMultiParam() throws Exception {
request.setQueryString("other=value&ticket=123&last=this");
details = new DefaultServiceAuthenticationDetails(request,artifactPattern);
details = new DefaultServiceAuthenticationDetails(casServiceUrl,request,artifactPattern);
String serviceUrl = details.getServiceUrl();
request.setQueryString("other=value&last=this");
assertEquals(UrlUtils.buildFullRequestUrl(request),serviceUrl);
}
@Test
public void getServiceUrlDoesNotUseHostHeader() throws Exception {
casServiceUrl = "https://example.com/j_spring_security_cas";
request.setServerName("evil.com");
details = new DefaultServiceAuthenticationDetails(casServiceUrl, request,artifactPattern);
assertEquals("https://example.com/cas-sample/secure/",details.getServiceUrl());
}
@Test
public void getServiceUrlDoesNotUseHostHeaderPassivity() {
casServiceUrl = "https://example.com/j_spring_security_cas";
request.setServerName("evil.com");
ServiceAuthenticationDetails details = loadServiceAuthenticationDetails("defaultserviceauthenticationdetails-passivity.xml");
assertEquals("https://example.com/cas-sample/secure/", details.getServiceUrl());
}
@Test
public void getServiceUrlDoesNotUseHostHeaderExplicit() {
casServiceUrl = "https://example.com/j_spring_security_cas";
request.setServerName("evil.com");
ServiceAuthenticationDetails details = loadServiceAuthenticationDetails("defaultserviceauthenticationdetails-explicit.xml");
assertEquals("https://example.com/cas-sample/secure/", details.getServiceUrl());
}
private ServiceAuthenticationDetails loadServiceAuthenticationDetails(String resourceName) {
context = new GenericXmlApplicationContext(getClass(), resourceName);
ServiceAuthenticationDetailsSource source = context.getBean(ServiceAuthenticationDetailsSource.class);
return source.buildDetails(request);
}
}
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<bean id="serviceProperties"
class="org.springframework.security.cas.ServiceProperties">
<property name="service"
value="https://example.com/j_spring_security_cas"/>
<property name="sendRenew" value="false"/>
</bean>
<bean id="serviceProperties2"
class="org.springframework.security.cas.ServiceProperties">
<property name="service"
value="https://example2.com/j_spring_security_cas"/>
<property name="sendRenew" value="false"/>
</bean>
<bean class="org.springframework.security.cas.web.authentication.ServiceAuthenticationDetailsSource">
<constructor-arg ref="serviceProperties"/>
</bean>
</beans>
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<bean id="serviceProperties"
class="org.springframework.security.cas.ServiceProperties">
<property name="service"
value="https://example.com/j_spring_security_cas"/>
<property name="sendRenew" value="false"/>
</bean>
<bean class="org.springframework.security.cas.web.authentication.ServiceAuthenticationDetailsSource"/>
</beans>
+1 -1
View File
@@ -14,7 +14,7 @@ Import-Template:
org.springframework.security.core.*;version="${secRange}",
org.springframework.security.authentication.*;version="${secRange}",
org.springframework.security.web.*;version="${secRange}",
org.springframework.beans.factory;version="${springRange}",
org.springframework.beans.*;version="${springRange}",
org.springframework.cache.*;version="${springRange}";resolution:=optional,
org.springframework.context.*;version="${springRange}",
org.springframework.dao;version="${springRange}",
+14 -7
View File
@@ -10,6 +10,7 @@ compileTestJava.dependsOn(':spring-security-core:compileTestJava')
dependencies {
// NB: Don't add other compile time dependencies to the config module as this breaks tooling
compile project(':spring-security-core'),
springCoreDependency,
'aopalliance:aopalliance:1.0',
"org.springframework:spring-aop:$springVersion",
"org.springframework:spring-context:$springVersion",
@@ -18,13 +19,14 @@ dependencies {
optional project(':spring-security-web'),
project(':spring-security-ldap'),
project(':spring-security-openid'),
project(':spring-security-aspects'),
"org.springframework:spring-web:$springVersion",
"org.springframework:spring-webmvc:$springVersion",
"org.aspectj:aspectjweaver:$aspectjVersion",
"org.springframework:spring-jdbc:$springVersion",
"org.springframework:spring-tx:$springVersion"
provided "org.apache.tomcat:tomcat-servlet-api:$servletApiVersion"
provided "javax.servlet:javax.servlet-api:$servletApiVersion"
testCompile project(':spring-security-cas'),
project(':spring-security-core').sourceSets.test.output,
@@ -34,9 +36,8 @@ dependencies {
"org.springframework:spring-jdbc:$springVersion",
"org.springframework:spring-orm:$springVersion",
"org.springframework:spring-tx:$springVersion",
"org.spockframework:spock-core:$spockVersion",
"org.slf4j:jcl-over-slf4j:$slf4jVersion",
"org.hibernate.javax.persistence:hibernate-jpa-2.0-api:1.0.1.Final",
"org.eclipse.persistence:javax.persistence:2.0.5",
"org.hibernate:hibernate-entitymanager:4.1.0.Final",
"org.codehaus.groovy:groovy-all:$groovyVersion",
"org.apache.directory.server:apacheds-core:$apacheDsVersion",
@@ -46,11 +47,17 @@ dependencies {
"org.apache.directory.server:apacheds-server-jndi:$apacheDsVersion",
'org.apache.directory.shared:shared-ldap:0.9.15',
'ldapsdk:ldapsdk:4.1',
powerMockDependencies
testCompile('org.openid4java:openid4java-nodeps:0.9.6') {
exclude group: 'com.google.code.guice', module: 'guice'
}
powerMockDependencies,
"org.hibernate:hibernate-entitymanager:3.6.10.Final",
"org.hsqldb:hsqldb:2.2.8",
spockDependencies
testCompile('org.openid4java:openid4java-nodeps:0.9.6') {
exclude group: 'com.google.code.guice', module: 'guice'
}
testCompile('org.springframework.data:spring-data-jpa:1.4.1.RELEASE') {
exclude group: 'org.aspectj', module: 'aspectjrt'
}
testRuntime "org.hsqldb:hsqldb:$hsqlVersion",
"cglib:cglib-nodep:2.2"
+91 -42
View File
@@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>3.2.0.CI-SNAPSHOT</version>
<version>3.2.8.RELEASE</version>
<name>spring-security-config</name>
<description>spring-security-config</description>
<url>http://spring.io/spring-security</url>
@@ -42,12 +42,6 @@
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-snasphot</id>
<url>https://repo.spring.io/snapshot</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>aopalliance</groupId>
@@ -58,31 +52,31 @@
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>3.2.0.CI-SNAPSHOT</version>
<version>3.2.8.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>3.2.6.RELEASE</version>
<version>3.2.13.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>3.2.6.RELEASE</version>
<version>3.2.13.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.2.6.RELEASE</version>
<version>3.2.13.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>3.2.6.RELEASE</version>
<version>3.2.13.RELEASE</version>
<scope>compile</scope>
<exclusions>
<exclusion>
@@ -105,59 +99,66 @@
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-aspects</artifactId>
<version>3.2.8.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-ldap</artifactId>
<version>3.2.0.CI-SNAPSHOT</version>
<version>3.2.8.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-openid</artifactId>
<version>3.2.0.CI-SNAPSHOT</version>
<version>3.2.8.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>3.2.0.CI-SNAPSHOT</version>
<version>3.2.8.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>3.2.6.RELEASE</version>
<version>3.2.13.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>3.2.6.RELEASE</version>
<version>3.2.13.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>3.2.6.RELEASE</version>
<version>3.2.13.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>3.2.6.RELEASE</version>
<version>3.2.13.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-servlet-api</artifactId>
<version>7.0.33</version>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
@@ -172,12 +173,6 @@
<version>0.9.29</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>1.8.0.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>jsr250-api</artifactId>
@@ -187,7 +182,7 @@
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -245,9 +240,9 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.0-api</artifactId>
<version>1.0.1.Final</version>
<groupId>org.eclipse.persistence</groupId>
<artifactId>javax.persistence</artifactId>
<version>2.0.5</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -256,6 +251,24 @@
<version>4.1.0.Final</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>3.6.10.Final</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.2.8</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.3.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
@@ -277,37 +290,43 @@
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>1.5.1</version>
<version>1.6.1</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>mockito-all</artifactId>
<groupId>org.mockito</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-support</artifactId>
<version>1.5.1</version>
<version>1.6.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-core</artifactId>
<version>1.5.1</version>
<version>1.6.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>1.5.1</version>
<version>1.6.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4-common</artifactId>
<version>1.5.1</version>
<version>1.6.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-reflect</artifactId>
<version>1.5.1</version>
<version>1.6.1</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -321,6 +340,36 @@
<artifactId>spock-core</artifactId>
<version>0.7-groovy-2.0</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>junit-dep</artifactId>
<groupId>junit</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-spring</artifactId>
<version>0.7-groovy-2.0</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>junit-dep</artifactId>
<groupId>junit</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.4.1.RELEASE</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>aspectjrt</artifactId>
<groupId>org.aspectj</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.ldap</groupId>
@@ -331,25 +380,25 @@
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-cas</artifactId>
<version>3.2.0.CI-SNAPSHOT</version>
<version>3.2.8.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>3.2.6.RELEASE</version>
<version>3.2.13.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>3.2.6.RELEASE</version>
<version>3.2.13.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>3.2.6.RELEASE</version>
<version>3.2.13.RELEASE</version>
<scope>test</scope>
</dependency>
</dependencies>
@@ -15,10 +15,12 @@
*/
package org.springframework.security.config.annotation.authentication.ldap
import org.springframework.beans.factory.config.AutowireCapableBeanFactory
import org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Import
import org.springframework.ldap.core.ContextSource;
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
import org.springframework.security.authentication.AuthenticationManager
@@ -27,7 +29,13 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio
import org.springframework.security.config.annotation.BaseSpringSpec
import org.springframework.security.config.annotation.SecurityBuilder;
import org.springframework.security.config.annotation.authentication.AuthenticationManagerBuilder
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
import org.springframework.security.config.annotation.authentication.configuration.EnableGlobalAuthentication
import org.springframework.security.config.annotation.configuration.AutowireBeanFactoryObjectPostProcessor
import org.springframework.security.config.annotation.configuration.AutowireBeanFactoryObjectPostProcessorTests
import org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
import org.springframework.security.ldap.server.ApacheDSContainer;
@@ -57,6 +65,7 @@ class LdapAuthenticationProviderBuilderSecurityBuilderTests extends BaseSpringSp
auth
.ldapAuthentication()
.contextSource(contextSource())
.userDnPatterns("uid={0},ou=people")
}
}
@@ -74,6 +83,7 @@ class LdapAuthenticationProviderBuilderSecurityBuilderTests extends BaseSpringSp
auth
.ldapAuthentication()
.contextSource(contextSource())
.userDnPatterns("uid={0},ou=people")
.groupRoleAttribute("group")
}
}
@@ -92,6 +102,7 @@ class LdapAuthenticationProviderBuilderSecurityBuilderTests extends BaseSpringSp
auth
.ldapAuthentication()
.contextSource(contextSource())
.userDnPatterns("uid={0},ou=people")
.groupSearchFilter("ou=groupName");
}
}
@@ -110,6 +121,7 @@ class LdapAuthenticationProviderBuilderSecurityBuilderTests extends BaseSpringSp
auth
.ldapAuthentication()
.contextSource(contextSource())
.userDnPatterns("uid={0},ou=people")
.rolePrefix("role_")
}
}
@@ -120,7 +132,7 @@ class LdapAuthenticationProviderBuilderSecurityBuilderTests extends BaseSpringSp
AuthenticationManager auth = context.getBean(AuthenticationManager)
then:
auth
auth.authenticate(new UsernamePasswordAuthenticationToken("admin","password")).authorities.collect { it.authority }.sort() == ["ROLE_ADMIN","ROLE_USER"]
auth.authenticate(new UsernamePasswordAuthenticationToken("bob","bobspassword")).authorities.collect { it.authority }.sort() == ["ROLE_DEVELOPERS"]
}
@Configuration
@@ -130,6 +142,29 @@ class LdapAuthenticationProviderBuilderSecurityBuilderTests extends BaseSpringSp
.ldapAuthentication()
.contextSource(contextSource())
.groupSearchBase("ou=groups")
.groupSearchFilter("(member={0})")
.userDnPatterns("uid={0},ou=people");
}
}
def "SEC-2472: Can use crypto PasswordEncoder"() {
setup:
loadConfig(PasswordEncoderConfig)
when:
AuthenticationManager auth = context.getBean(AuthenticationManager)
then:
auth.authenticate(new UsernamePasswordAuthenticationToken("bcrypt","password")).authorities.collect { it.authority }.sort() == ["ROLE_DEVELOPERS"]
}
@Configuration
static class PasswordEncoderConfig extends BaseLdapServerConfig {
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.contextSource(contextSource())
.passwordEncoder(new BCryptPasswordEncoder())
.groupSearchBase("ou=groups")
.groupSearchFilter("(member={0})")
.userDnPatterns("uid={0},ou=people");
}
}
@@ -142,23 +177,44 @@ class LdapAuthenticationProviderBuilderSecurityBuilderTests extends BaseSpringSp
static abstract class BaseLdapServerConfig extends BaseLdapProviderConfig {
@Bean
public ApacheDSContainer ldapServer() throws Exception {
ApacheDSContainer apacheDSContainer = new ApacheDSContainer("dc=springframework,dc=org", "classpath:/users.ldif");
apacheDSContainer.setPort(33389);
ApacheDSContainer apacheDSContainer = new ApacheDSContainer("dc=springframework,dc=org", "classpath:/test-server.ldif");
apacheDSContainer.setPort(getPort());
return apacheDSContainer;
}
}
@Configuration
@EnableGlobalAuthentication
@Import(ObjectPostProcessorConfiguration)
static abstract class BaseLdapProviderConfig {
@Bean
public BaseLdapPathContextSource contextSource() throws Exception {
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(
"ldap://127.0.0.1:33389/dc=springframework,dc=org")
"ldap://127.0.0.1:"+ getPort() + "/dc=springframework,dc=org")
contextSource.userDn = "uid=admin,ou=system"
contextSource.password = "secret"
contextSource.afterPropertiesSet();
contextSource.afterPropertiesSet()
return contextSource;
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationManagerBuilder auth) {
configure(auth)
auth.build()
}
abstract protected void configure(AuthenticationManagerBuilder auth)
}
static Integer port;
static int getPort() {
if(port == null) {
ServerSocket socket = new ServerSocket(0)
port = socket.localPort
socket.close()
}
port
}
}
@@ -0,0 +1,61 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.authentication.ldap
import org.springframework.context.annotation.Configuration
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.config.annotation.BaseSpringSpec
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
import org.springframework.security.config.annotation.authentication.ldap.NamespaceLdapAuthenticationProviderTestsConfigs.LdapAuthenticationProviderConfig
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider
import org.springframework.security.ldap.authentication.PasswordComparisonAuthenticator
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator
import org.springframework.security.ldap.userdetails.PersonContextMapper
import org.springframework.test.util.ReflectionTestUtils
import static org.springframework.security.config.annotation.authentication.ldap.NamespaceLdapAuthenticationProviderTestsConfigs.*
/**
*
* @author Rob Winch
*
*/
class LdapAuthenticationProviderConfigurerTests extends BaseSpringSpec {
def "authentication-manager support multiple default ldap contexts (ports dynamically allocated)"() {
when:
loadConfig(MultiLdapAuthenticationProvidersConfig)
then:
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("bob","bobspassword"))
}
@EnableWebSecurity
@Configuration
static class MultiLdapAuthenticationProvidersConfig extends WebSecurityConfigurerAdapter {
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.groupSearchBase("ou=groups")
.userDnPatterns("uid={0},ou=people")
.and()
.ldapAuthentication()
.groupSearchBase("ou=groups")
.userDnPatterns("uid={0},ou=people")
}
}
}
@@ -26,6 +26,7 @@ import org.springframework.security.config.annotation.authentication.builders.Au
import org.springframework.security.config.annotation.authentication.ldap.NamespaceLdapAuthenticationProviderTestsConfigs.LdapAuthenticationProviderConfig;
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
import org.springframework.security.ldap.authentication.PasswordComparisonAuthenticator;
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;
import org.springframework.security.ldap.userdetails.PersonContextMapper;
import org.springframework.test.util.ReflectionTestUtils;
@@ -39,7 +40,7 @@ class NamespaceLdapAuthenticationProviderTests extends BaseSpringSpec {
when:
loadConfig(LdapAuthenticationProviderConfig)
then:
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("user","password")).authorities*.authority.sort() == ['ROLE_USER']
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("bob","bobspassword"))
}
def "ldap-authentication-provider custom"() {
@@ -57,12 +58,23 @@ class NamespaceLdapAuthenticationProviderTests extends BaseSpringSpec {
provider.authenticator.userSearch.searchFilter == "(uid={0})"
}
def "SEC-2490: ldap-authentication-provider custom LdapAuthoritiesPopulator"() {
setup:
LdapAuthoritiesPopulator LAP = Mock()
CustomAuthoritiesPopulatorConfig.LAP = LAP
when:
loadConfig(CustomAuthoritiesPopulatorConfig)
LdapAuthenticationProvider provider = findAuthenticationProvider(LdapAuthenticationProvider)
then:
provider.authoritiesPopulator == LAP
}
def "ldap-authentication-provider password compare"() {
when:
loadConfig(PasswordCompareLdapConfig)
LdapAuthenticationProvider provider = findAuthenticationProvider(LdapAuthenticationProvider)
then:
provider.authenticator instanceof PasswordComparisonAuthenticator
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("user","password")).authorities*.authority.sort() == ['ROLE_USER']
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("bob","bobspassword"))
}
}
@@ -1,7 +1,13 @@
package org.springframework.security.config.ldap
import static org.mockito.Mockito.*
import java.text.MessageFormat
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.security.config.AbstractXmlConfigTests
import org.springframework.security.config.BeanIds
import org.springframework.security.util.FieldUtils
@@ -36,7 +42,7 @@ class LdapProviderBeanDefinitionParserTests extends AbstractXmlConfigTests {
def simpleProviderAuthenticatesCorrectly() {
xml.'ldap-server'()
xml.'ldap-server'(ldif:'test-server.ldif')
xml.'authentication-manager'{
'ldap-authentication-provider'('group-search-filter':'member={0}')
}
@@ -66,7 +72,7 @@ class LdapProviderBeanDefinitionParserTests extends AbstractXmlConfigTests {
}
def supportsPasswordComparisonAuthentication() {
xml.'ldap-server'()
xml.'ldap-server'(ldif:'test-server.ldif')
xml.'authentication-manager'{
'ldap-authentication-provider'('user-dn-pattern': 'uid={0},ou=people')
'password-compare'
@@ -83,7 +89,7 @@ class LdapProviderBeanDefinitionParserTests extends AbstractXmlConfigTests {
}
def supportsPasswordComparisonAuthenticationWithHashAttribute() {
xml.'ldap-server'()
xml.'ldap-server'(ldif:'test-server.ldif')
xml.'authentication-manager'{
'ldap-authentication-provider'('user-dn-pattern': 'uid={0},ou=people') {
'password-compare'('password-attribute': 'uid', hash: 'plaintext')
@@ -102,7 +108,7 @@ class LdapProviderBeanDefinitionParserTests extends AbstractXmlConfigTests {
}
def supportsPasswordComparisonAuthenticationWithPasswordEncoder() {
xml.'ldap-server'()
xml.'ldap-server'(ldif:'test-server.ldif')
xml.'authentication-manager'{
'ldap-authentication-provider'('user-dn-pattern': 'uid={0},ou=people') {
'password-compare'('password-attribute': 'uid') {
@@ -122,6 +128,28 @@ class LdapProviderBeanDefinitionParserTests extends AbstractXmlConfigTests {
notThrown(AuthenticationException)
}
def 'SEC-2472: Supports Crypto PasswordEncoder'() {
setup:
xml.'ldap-server'(ldif:'test-server.ldif')
xml.'authentication-manager'{
'ldap-authentication-provider'('user-dn-pattern': 'uid={0},ou=people') {
'password-compare'() {
'password-encoder'(ref: 'pe')
}
}
}
xml.'b:bean'(id:'pe','class':BCryptPasswordEncoder.class.name)
createAppContext('')
def am = appContext.getBean(BeanIds.AUTHENTICATION_MANAGER)
when:
def auth = am.authenticate(new UsernamePasswordAuthenticationToken("bcrypt", 'password'))
then:
auth != null
}
def inetOrgContextMapperIsSupported() {
xml.'ldap-server'(url: 'ldap://127.0.0.1:343/dc=springframework,dc=org')
xml.'authentication-manager'{
@@ -20,6 +20,7 @@ import org.springframework.security.authentication.encoding.PlaintextPasswordEnc
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;
import org.springframework.security.ldap.userdetails.PersonContextMapper;
/**
@@ -55,7 +56,7 @@ public class NamespaceLdapAuthenticationProviderTestsConfigs {
.userSearchFilter("(uid={0})") // ldap-authentication-provider@user-search-filter
// .contextSource(contextSource) // ldap-authentication-provider@server-ref
.contextSource()
.ldif("classpath:user.ldif") // ldap-server@ldif
.ldif("classpath:users.xldif") // ldap-server@ldif
.managerDn("uid=admin,ou=system") // ldap-server@manager-dn
.managerPassword("secret") // ldap-server@manager-password
.port(33399) // ldap-server@port
@@ -65,6 +66,18 @@ public class NamespaceLdapAuthenticationProviderTestsConfigs {
}
}
@Configuration
@EnableWebSecurity
static class CustomAuthoritiesPopulatorConfig extends WebSecurityConfigurerAdapter {
static LdapAuthoritiesPopulator LAP;
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.userSearchFilter("(uid={0})")
.ldapAuthoritiesPopulator(LAP);
}
}
@Configuration
@EnableWebSecurity
static class PasswordCompareLdapConfig extends WebSecurityConfigurerAdapter {
@@ -28,6 +28,16 @@ sn: Alex
uid: ben
userPassword: {SHA}nFCebWjxfaLbHHG1Qk5UU4trbvQ=
dn: uid=bcrypt,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: BCrypt user
sn: BCrypt
uid: bcrypt
userPassword: $2a$10$FBAKClV1zBIOOC9XMXf3AO8RoGXYVYsfvUdoLxGkd/BnXEn4tqT3u
dn: uid=bob,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
@@ -58,11 +68,22 @@ sn: Mouse
uid: jerry
userPassword: jerryspassword
dn: uid=bcrypt,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: BCrypt user
sn: BCrypt
uid: bcrypt
userPassword: $2a$10$lDa0YFNHAt63MjIzK/wUqeM0qjIhzPhp3RNI/MLUQEAUbzhB/SnnS
dn: cn=developers,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfNames
cn: developers
ou: developer
member: uid=bcrypt,ou=people,dc=springframework,dc=org
member: uid=ben,ou=people,dc=springframework,dc=org
member: uid=bob,ou=people,dc=springframework,dc=org
@@ -1,42 +0,0 @@
dn: ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: groups
dn: ou=people,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: people
dn: uid=admin,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Rod Johnson
sn: Johnson
uid: admin
userPassword: password
dn: uid=user,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Dianne Emu
sn: Emu
uid: user
userPassword: password
dn: cn=user,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfNames
cn: user
uniqueMember: uid=admin,ou=people,dc=springframework,dc=org
uniqueMember: uid=user,ou=people,dc=springframework,dc=org
dn: cn=admin,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfNames
cn: admin
uniqueMember: uid=admin,ou=people,dc=springframework,dc=org
@@ -0,0 +1,92 @@
dn: ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: groups
dn: ou=subgroups,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: subgroups
dn: ou=people,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: people
dn: ou=otherpeople,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: otherpeople
dn: uid=ben,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Ben Alex
sn: Alex
uid: ben
userPassword: {SHA}nFCebWjxfaLbHHG1Qk5UU4trbvQ=
dn: uid=bcrypt,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: BCrypt user
sn: BCrypt
uid: bcrypt
userPassword: $2a$10$lDa0YFNHAt63MjIzK/wUqeM0qjIhzPhp3RNI/MLUQEAUbzhB/SnnS
dn: uid=bob,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Bob Hamilton
sn: Hamilton
uid: bob
userPassword: bobspassword
dn: uid=joe,ou=otherpeople,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Joe Smeth
sn: Smeth
uid: joe
userPassword: joespassword
dn: cn=mouse\, jerry,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Mouse, Jerry
sn: Mouse
uid: jerry
userPassword: jerryspassword
dn: cn=developers,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfNames
cn: developers
ou: developer
member: uid=ben,ou=people,dc=springframework,dc=org
member: uid=bob,ou=people,dc=springframework,dc=org
dn: cn=managers,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfNames
cn: managers
ou: manager
member: uid=ben,ou=people,dc=springframework,dc=org
member: cn=mouse\, jerry,ou=people,dc=springframework,dc=org
dn: cn=submanagers,ou=subgroups,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfNames
cn: submanagers
ou: submanager
member: uid=ben,ou=people,dc=springframework,dc=org
@@ -29,8 +29,6 @@ import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.util.Assert;
import org.springframework.web.filter.DelegatingFilterProxy;
import com.google.inject.internal.ImmutableList.Builder;
/**
* <p>A base {@link SecurityBuilder} that allows {@link SecurityConfigurer} to be
* applied to it. This makes modifying the {@link SecurityBuilder} a strategy
@@ -27,6 +27,7 @@ import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.SecurityBuilder;
import org.springframework.security.config.annotation.SecurityConfigurer;
import org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;
import org.springframework.security.config.annotation.authentication.configurers.ldap.LdapAuthenticationProviderConfigurer;
import org.springframework.security.config.annotation.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer;
@@ -77,6 +78,9 @@ public class AuthenticationManagerBuilder extends AbstractConfiguredSecurityBuil
*/
public AuthenticationManagerBuilder parentAuthenticationManager(
AuthenticationManager authenticationManager) {
if(authenticationManager instanceof ProviderManager) {
eraseCredentials(((ProviderManager) authenticationManager).isEraseCredentialsAfterAuthentication());
}
this.parentAuthenticationManager = authenticationManager;
return this;
}
@@ -134,19 +138,31 @@ public class AuthenticationManagerBuilder extends AbstractConfiguredSecurityBuil
/**
* Add JDBC authentication to the {@link AuthenticationManagerBuilder} and
* return a {@link JdbcUserDetailsManagerConfigurer} to allow customization of the
* JDBC authentication.
* return a {@link JdbcUserDetailsManagerConfigurer} to allow customization
* of the JDBC authentication.
*
* <p>
* When using with a persistent data store, it is best to add users external
* of configuration using something like <a
* href="http://flywaydb.org/">Flyway</a> or <a
* href="http://www.liquibase.org/">Liquibase</a> to create the schema and
* adding users to ensure these steps are only done once and that the
* optimal SQL is used.
* </p>
*
* <p>
* This method also ensure that a {@link UserDetailsService} is available
* for the {@link #getDefaultUserDetailsService()} method. Note that
* additional {@link UserDetailsService}'s may override this
* {@link UserDetailsService} as the default.
* {@link UserDetailsService} as the default. See the <a href=
* "http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#user-schema"
* >User Schema</a> section of the reference for the default schema.
* </p>
*
* @return a {@link JdbcUserDetailsManagerConfigurer} to allow customization of the
* JDBC authentication
* @throws Exception if an error occurs when adding the JDBC authentication
* @return a {@link JdbcUserDetailsManagerConfigurer} to allow customization
* of the JDBC authentication
* @throws Exception
* if an error occurs when adding the JDBC authentication
*/
public JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder> jdbcAuthentication()
throws Exception {
@@ -221,7 +237,7 @@ public class AuthenticationManagerBuilder extends AbstractConfiguredSecurityBuil
@Override
protected ProviderManager performBuild() throws Exception {
if(authenticationProviders.isEmpty() && parentAuthenticationManager == null) {
if(!isConfigured()) {
logger.debug("No authenticationProviders and no parentAuthenticationManager defined. Returning null.");
return null;
}
@@ -236,6 +252,26 @@ public class AuthenticationManagerBuilder extends AbstractConfiguredSecurityBuil
return providerManager;
}
/**
* Determines if the {@link AuthenticationManagerBuilder} is configured to
* build a non null {@link AuthenticationManager}. This means that either a
* non-null parent is specified or at least one
* {@link AuthenticationProvider} has been specified.
*
* <p>
* When using {@link SecurityConfigurer} instances, the
* {@link AuthenticationManagerBuilder} will not be configured until the
* {@link SecurityConfigurer#configure(SecurityBuilder)} methods. This means
* a {@link SecurityConfigurer} that is last could check this method and
* provide a default configuration in the
* {@link SecurityConfigurer#configure(SecurityBuilder)} method.
*
* @return
*/
public boolean isConfigured() {
return !authenticationProviders.isEmpty() || parentAuthenticationManager != null;
}
/**
* Gets the default {@link UserDetailsService} for the
* {@link AuthenticationManagerBuilder}. The result may be null in some
@@ -15,10 +15,26 @@
*/
package org.springframework.security.config.annotation.authentication.configuration;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.aop.target.LazyInitTargetSource;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configurers.GlobalAuthenticationConfigurerAdapter;
import org.springframework.util.Assert;
/**
* Exports the authentication {@link Configuration}
@@ -29,9 +45,97 @@ import org.springframework.security.config.annotation.authentication.builders.Au
*/
@Configuration
public class AuthenticationConfiguration {
private ApplicationContext applicationContext;
private AuthenticationManager authenticationManager;
private boolean authenticationManagerInitialized;
private List<GlobalAuthenticationConfigurerAdapter> globalAuthConfigures = Collections.emptyList();
private ObjectPostProcessor<Object> objectPostProcessor;
@Bean
public AuthenticationManagerBuilder authenticationManagerBuilder(ObjectPostProcessor<Object> objectPostProcessor) {
return new AuthenticationManagerBuilder(objectPostProcessor);
}
}
@Bean
public static GlobalAuthenticationConfigurerAdapter enableGlobalAuthenticationAutowiredConfigurer(ApplicationContext context) {
return new EnableGlobalAuthenticationAutowiredConfigurer(context);
}
public AuthenticationManager getAuthenticationManager() throws Exception {
if(authenticationManagerInitialized) {
return authenticationManager;
}
AuthenticationManagerBuilder authBuilder = authenticationManagerBuilder(objectPostProcessor);
for(GlobalAuthenticationConfigurerAdapter config : globalAuthConfigures) {
authBuilder.apply(config);
}
authenticationManager = authBuilder.build();
if(authenticationManager == null) {
authenticationManager = getAuthenticationMangerBean();
}
this.authenticationManagerInitialized = true;
return authenticationManager;
}
@Autowired(required = false)
public void setGlobalAuthenticationConfigurers(List<GlobalAuthenticationConfigurerAdapter> configurers) throws Exception {
Collections.sort(configurers, AnnotationAwareOrderComparator.INSTANCE);
this.globalAuthConfigures = configurers;
}
@Autowired
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Autowired
public void setObjectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {
this.objectPostProcessor = objectPostProcessor;
}
@SuppressWarnings("unchecked")
private <T> T lazyBean(Class<T> interfaceName) {
LazyInitTargetSource lazyTargetSource = new LazyInitTargetSource();
String[] beanNamesForType = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, interfaceName);
if(beanNamesForType.length == 0) {
return null;
}
Assert.isTrue(beanNamesForType.length == 1 , "Expecting to only find a single bean for type " + interfaceName + ", but found " + Arrays.asList(beanNamesForType));
lazyTargetSource.setTargetBeanName(beanNamesForType[0]);
lazyTargetSource.setBeanFactory(applicationContext);
ProxyFactoryBean proxyFactory = new ProxyFactoryBean();
proxyFactory = objectPostProcessor.postProcess(proxyFactory);
proxyFactory.setTargetSource(lazyTargetSource);
return (T) proxyFactory.getObject();
}
private AuthenticationManager getAuthenticationMangerBean() {
return lazyBean(AuthenticationManager.class);
}
private static class EnableGlobalAuthenticationAutowiredConfigurer extends GlobalAuthenticationConfigurerAdapter {
private final ApplicationContext context;
private static final Log logger = LogFactory.getLog(EnableGlobalAuthenticationAutowiredConfigurer.class);
public EnableGlobalAuthenticationAutowiredConfigurer(ApplicationContext context) {
this.context = context;
}
@Override
public void init(AuthenticationManagerBuilder auth) {
Map<String, Object> beansWithAnnotation = context.getBeansWithAnnotation(EnableGlobalAuthentication.class);
if(logger.isDebugEnabled()) {
logger.debug("Eagerly initializing " + beansWithAnnotation);
}
}
}
}
@@ -0,0 +1,91 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.authentication.configuration;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
/**
* The {@link EnableGlobalAuthentication} annotation signals that the annotated
* class can be used to configure a global instance of
* {@link AuthenticationManagerBuilder}. For example:
*
* <pre class="code">
* &#064;Configuration
* &#064;EnableGlobalAuthentication
* public class MyGlobalAuthenticationConfiguration {
*
* &#064;Autowired
* public void configureGlobal(AuthenticationManagerBuilder auth) {
* auth
* .inMemoryAuthentication()
* .withUser(&quot;user&quot;).password(&quot;password&quot;).roles(&quot;USER&quot;).and()
* .withUser(&quot;admin&quot;).password(&quot;password&quot;).roles(&quot;USER&quot;, &quot;ADMIN&quot;);
* }
* }
* </pre>
*
* Annotations that are annotated with {@link EnableGlobalAuthentication} also
* signal that the annotated class can be used to configure a global instance of
* {@link AuthenticationManagerBuilder}. For example:
*
* <pre class="code">
* &#064;Configuration
* &#064;EnableWebSecurity
* public class MyWebSecurityConfiguration extends WebSecurityConfigurerAdapter {
*
* &#064;Autowired
* public void configureGlobal(AuthenticationManagerBuilder auth) {
* auth
* .inMemoryAuthentication()
* .withUser(&quot;user&quot;).password(&quot;password&quot;).roles(&quot;USER&quot;).and()
* .withUser(&quot;admin&quot;).password(&quot;password&quot;).roles(&quot;USER&quot;, &quot;ADMIN&quot;);
* }
*
* // Possibly overridden methods ...
* }
* </pre>
*
* The following annotations are annotated with {@link EnableGlobalAuthentication}
*
* <ul>
* <li> {@link EnableWebSecurity} </li>
* <li> {@link EnableWebMvcSecurity} </li>
* <li> {@link EnableGlobalMethodSecurity} </li>
* </ul>
*
* Configuring {@link AuthenticationManagerBuilder} in a class without the {@link EnableGlobalAuthentication} annotation has
* unpredictable results.
*
* @see EnableWebMvcSecurity
* @see EnableWebSecurity
* @see EnableGlobalMethodSecurity
*
* @author Rob Winch
*
*/
@Retention(value=java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value={java.lang.annotation.ElementType.TYPE})
@Documented
@Import(AuthenticationConfiguration.class)
public @interface EnableGlobalAuthentication {}
@@ -0,0 +1,39 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.authentication.configurers;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.SecurityConfigurer;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
/**
* A {@link SecurityConfigurer} that can be exposed as a bean to configure the
* global {@link AuthenticationManagerBuilder}. Beans of this type are
* automatically used by {@link AuthenticationConfiguration} to configure the
* global {@link AuthenticationManagerBuilder}.
*
* @since 3.2.1
* @author Rob Winch
*/
@Order(100)
public abstract class GlobalAuthenticationConfigurerAdapter implements SecurityConfigurer<AuthenticationManager, AuthenticationManagerBuilder> {
public void init(AuthenticationManagerBuilder auth) throws Exception {}
public void configure(AuthenticationManagerBuilder auth) throws Exception {}
}
@@ -36,9 +36,14 @@ import org.springframework.security.ldap.search.LdapUserSearch;
import org.springframework.security.ldap.server.ApacheDSContainer;
import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator;
import org.springframework.security.ldap.userdetails.InetOrgPersonContextMapper;
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;
import org.springframework.security.ldap.userdetails.LdapUserDetailsMapper;
import org.springframework.security.ldap.userdetails.PersonContextMapper;
import org.springframework.security.ldap.userdetails.UserDetailsContextMapper;
import org.springframework.util.Assert;
import java.io.IOException;
import java.net.ServerSocket;
/**
* Configures LDAP {@link AuthenticationProvider} in the {@link ProviderManagerBuilder}.
@@ -59,17 +64,15 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
private BaseLdapPathContextSource contextSource;
private ContextSourceBuilder contextSourceBuilder = new ContextSourceBuilder();
private UserDetailsContextMapper userDetailsContextMapper;
private PasswordEncoder passwordEncoder;
private Object passwordEncoder;
private String passwordAttribute;
private LdapAuthoritiesPopulator ldapAuthoritiesPopulator;
private LdapAuthenticationProvider build() throws Exception {
BaseLdapPathContextSource contextSource = getContextSource();
LdapAuthenticator ldapAuthenticator = createLdapAuthenticator(contextSource);
DefaultLdapAuthoritiesPopulator authoritiesPopulator = new DefaultLdapAuthoritiesPopulator(
contextSource, groupSearchBase);
authoritiesPopulator.setGroupRoleAttribute(groupRoleAttribute);
authoritiesPopulator.setGroupSearchFilter(groupSearchFilter);
LdapAuthoritiesPopulator authoritiesPopulator = getLdapAuthoritiesPopulator();
LdapAuthenticationProvider ldapAuthenticationProvider = new LdapAuthenticationProvider(
ldapAuthenticator, authoritiesPopulator);
@@ -83,6 +86,17 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
return ldapAuthenticationProvider;
}
/**
* Specifies the {@link LdapAuthoritiesPopulator}.
*
* @param ldapAuthoritiesPopulator the {@link LdapAuthoritiesPopulator} the default is {@link DefaultLdapAuthoritiesPopulator}
* @return the {@link LdapAuthenticationProviderConfigurer} for further customizations
*/
public LdapAuthenticationProviderConfigurer<B> ldapAuthoritiesPopulator(LdapAuthoritiesPopulator ldapAuthoritiesPopulator) {
this.ldapAuthoritiesPopulator = ldapAuthoritiesPopulator;
return this;
}
/**
* Adds an {@link ObjectPostProcessor} for this class.
*
@@ -94,6 +108,25 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
return this;
}
/**
* Gets the {@link LdapAuthoritiesPopulator} and defaults to {@link DefaultLdapAuthoritiesPopulator}
*
* @return the {@link LdapAuthoritiesPopulator}
*/
private LdapAuthoritiesPopulator getLdapAuthoritiesPopulator() {
if(ldapAuthoritiesPopulator != null) {
return ldapAuthoritiesPopulator;
}
DefaultLdapAuthoritiesPopulator defaultAuthoritiesPopulator = new DefaultLdapAuthoritiesPopulator(
contextSource, groupSearchBase);
defaultAuthoritiesPopulator.setGroupRoleAttribute(groupRoleAttribute);
defaultAuthoritiesPopulator.setGroupSearchFilter(groupSearchFilter);
this.ldapAuthoritiesPopulator = defaultAuthoritiesPopulator;
return defaultAuthoritiesPopulator;
}
/**
* Creates the {@link LdapAuthenticator} to use
*
@@ -120,7 +153,9 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
*/
private PasswordComparisonAuthenticator createPasswordCompareAuthenticator(BaseLdapPathContextSource contextSource) {
PasswordComparisonAuthenticator ldapAuthenticator = new PasswordComparisonAuthenticator(contextSource);
ldapAuthenticator.setPasswordAttributeName(passwordAttribute);
if(passwordAttribute != null) {
ldapAuthenticator.setPasswordAttributeName(passwordAttribute);
}
ldapAuthenticator.setPasswordEncoder(passwordEncoder);
return ldapAuthenticator;
}
@@ -175,12 +210,26 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
*
* @param passwordEncoder the {@link PasswordEncoder} to use
* @return the {@link LdapAuthenticationProviderConfigurer} for further customization
* @deprecated Use {@link #passwordEncoder(org.springframework.security.crypto.password.PasswordEncoder)} instead
*/
public LdapAuthenticationProviderConfigurer<B> passwordEncoder(PasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
return this;
}
/**
* Specifies the {@link org.springframework.security.crypto.password.PasswordEncoder} to be used when authenticating with
* password comparison.
*
* @param passwordEncoder the {@link org.springframework.security.crypto.password.PasswordEncoder} to use
* @return the {@link LdapAuthenticationProviderConfigurer} for further customization
*/
public LdapAuthenticationProviderConfigurer<B> passwordEncoder(final org.springframework.security.crypto.password.PasswordEncoder passwordEncoder) {
Assert.notNull(passwordEncoder, "passwordEncoder must not be null.");
this.passwordEncoder = passwordEncoder;
return this;
}
/**
* If your users are at a fixed location in the directory (i.e. you can work
* out the DN directly from the username without doing a directory search),
@@ -344,7 +393,8 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
private String ldif = "classpath*:*.ldif";
private String managerPassword;
private String managerDn;
private int port = 33389;
private Integer port;
private static final int DEFAULT_PORT = 33389;
private String root = "dc=springframework,dc=org";
private String url;
@@ -389,7 +439,7 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
}
/**
* The port to connect to LDAP to (the default is 33389).
* The port to connect to LDAP to (the default is 33389 or random available port if unavailable).
* @param port the port to connect to
* @return the {@link ContextSourceBuilder} for further customization
*/
@@ -449,14 +499,43 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
return contextSource;
}
ApacheDSContainer apacheDsContainer = new ApacheDSContainer(root, ldif);
apacheDsContainer.setPort(port);
apacheDsContainer.setPort(getPort());
postProcess(apacheDsContainer);
return contextSource;
}
private int getPort() {
if(port == null) {
port = getDefaultPort();
}
return port;
}
private int getDefaultPort() {
ServerSocket serverSocket = null;
try {
try {
serverSocket = new ServerSocket(DEFAULT_PORT);
} catch (IOException e) {
try {
serverSocket = new ServerSocket(0);
} catch(IOException e2) {
return DEFAULT_PORT;
}
}
return serverSocket.getLocalPort();
} finally {
if(serverSocket != null) {
try {
serverSocket.close();
} catch (IOException e) {}
}
}
}
private String getProviderUrl() {
if(url == null) {
return "ldap://127.0.0.1:" + port + "/" + root;
return "ldap://127.0.0.1:" + getPort() + "/" + root;
}
return url;
}
@@ -23,7 +23,7 @@ import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.Import;
import org.springframework.core.Ordered;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.authentication.configuration.EnableGlobalAuthentication;
import org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration;
/**
@@ -44,7 +44,8 @@ import org.springframework.security.config.annotation.configuration.ObjectPostPr
@Retention(value=java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value={java.lang.annotation.ElementType.TYPE})
@Documented
@Import({GlobalMethodSecuritySelector.class,ObjectPostProcessorConfiguration.class,AuthenticationConfiguration.class})
@Import({GlobalMethodSecuritySelector.class,ObjectPostProcessorConfiguration.class})
@EnableGlobalAuthentication
public @interface EnableGlobalMethodSecurity {
/**
@@ -1,67 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.method.configuration;
import java.util.Map;
import org.springframework.aop.config.AopConfigUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
/**
* Registers an
* {@link org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator
* AnnotationAwareAspectJAutoProxyCreator} against the current
* {@link BeanDefinitionRegistry} as appropriate based on a given @
* {@link EnableGlobalMethodSecurity} annotation.
*
* <p>
* Note: This class is necessary because AspectJAutoProxyRegistrar only supports
* EnableAspectJAutoProxy.
* </p>
*
* @author Rob Winch
* @since 3.2
*/
class GlobalMethodSecurityAspectJAutoProxyRegistrar implements
ImportBeanDefinitionRegistrar {
/**
* Register, escalate, and configure the AspectJ auto proxy creator based on
* the value of the @{@link EnableGlobalMethodSecurity#proxyTargetClass()}
* attribute on the importing {@code @Configuration} class.
*/
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
AopConfigUtils
.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
Map<String, Object> annotationAttributes = importingClassMetadata
.getAnnotationAttributes(EnableGlobalMethodSecurity.class
.getName());
AnnotationAttributes enableAJAutoProxy = AnnotationAttributes
.fromMap(annotationAttributes);
if (enableAJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
}
}
@@ -0,0 +1,42 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.method.configuration;
import org.aopalliance.intercept.MethodInterceptor;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Role;
import org.springframework.security.access.intercept.aspectj.AspectJMethodSecurityInterceptor;
import org.springframework.security.access.intercept.aspectj.aspect.AnnotationSecurityAspect;
/**
* Creates the AnnotationSecurityAspect for use with AspectJ based security.
*
* @author Rob Winch
* @since 3.2
*/
class GlobalMethodSecurityAspectJConfiguration {
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@Bean
public AnnotationSecurityAspect annotationSecurityAspect(@Qualifier("methodSecurityInterceptor") MethodInterceptor methodSecurityInterceptor) {
AnnotationSecurityAspect result = AnnotationSecurityAspect.aspectOf();
result.setSecurityInterceptor((AspectJMethodSecurityInterceptor )methodSecurityInterceptor);
return result;
}
}
@@ -16,23 +16,16 @@
package org.springframework.security.config.annotation.method.configuration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.aopalliance.intercept.MethodInterceptor;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.aop.target.LazyInitTargetSource;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportAware;
import org.springframework.context.annotation.Role;
import org.springframework.context.annotation.*;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.type.AnnotationMetadata;
@@ -53,6 +46,7 @@ import org.springframework.security.access.intercept.AfterInvocationProviderMana
import org.springframework.security.access.intercept.RunAsManager;
import org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor;
import org.springframework.security.access.intercept.aopalliance.MethodSecurityMetadataSourceAdvisor;
import org.springframework.security.access.intercept.aspectj.AspectJMethodSecurityInterceptor;
import org.springframework.security.access.method.DelegatingMethodSecurityMetadataSource;
import org.springframework.security.access.method.MethodSecurityMetadataSource;
import org.springframework.security.access.prepost.PostInvocationAdviceProvider;
@@ -67,6 +61,7 @@ import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.util.Assert;
/**
@@ -81,7 +76,6 @@ import org.springframework.util.Assert;
@Configuration
public class GlobalMethodSecurityConfiguration implements ImportAware {
private static final Log logger = LogFactory.getLog(GlobalMethodSecurityConfiguration.class);
private ApplicationContext context;
private ObjectPostProcessor<Object> objectPostProcessor = new ObjectPostProcessor<Object>() {
public <T> T postProcess(T object) {
throw new IllegalStateException(ObjectPostProcessor.class.getName()+ " is a required bean. Ensure you have used @"+EnableGlobalMethodSecurity.class.getName());
@@ -92,6 +86,7 @@ public class GlobalMethodSecurityConfiguration implements ImportAware {
private AuthenticationManagerBuilder auth;
private boolean disableAuthenticationRegistry;
private AnnotationAttributes enableMethodSecurity;
private ApplicationContext context;
private MethodSecurityExpressionHandler expressionHandler;
/**
@@ -115,7 +110,7 @@ public class GlobalMethodSecurityConfiguration implements ImportAware {
*/
@Bean
public MethodInterceptor methodSecurityInterceptor() throws Exception {
MethodSecurityInterceptor methodSecurityInterceptor = new MethodSecurityInterceptor();
MethodSecurityInterceptor methodSecurityInterceptor = isAspectJ() ? new AspectJMethodSecurityInterceptor() : new MethodSecurityInterceptor();
methodSecurityInterceptor
.setAccessDecisionManager(accessDecisionManager());
methodSecurityInterceptor
@@ -248,19 +243,11 @@ public class GlobalMethodSecurityConfiguration implements ImportAware {
auth = new AuthenticationManagerBuilder(objectPostProcessor);
auth.authenticationEventPublisher(eventPublisher);
configure(auth);
if(!disableAuthenticationRegistry) {
if(disableAuthenticationRegistry) {
authenticationManager = getAuthenticationConfiguration().getAuthenticationManager();
} else {
authenticationManager = auth.build();
}
if(authenticationManager == null) {
try {
authenticationManager = context.getBean(AuthenticationManagerBuilder.class).getOrBuild();
} catch(NoSuchBeanDefinitionException e) {
logger.debug("Could not obtain the AuthenticationManagerBuilder. This is ok for now, we will try using an AuthenticationManager directly",e);
}
}
if(authenticationManager == null) {
authenticationManager = lazyBean(AuthenticationManager.class);
}
}
return authenticationManager;
}
@@ -351,11 +338,6 @@ public class GlobalMethodSecurityConfiguration implements ImportAware {
this.defaultMethodExpressionHandler.setTrustResolver(trustResolver);
}
@Autowired
public void setApplicationContext(ApplicationContext context) {
this.context = context;
}
@Autowired(required=false)
public void setObjectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {
this.objectPostProcessor = objectPostProcessor;
@@ -370,17 +352,13 @@ public class GlobalMethodSecurityConfiguration implements ImportAware {
this.defaultMethodExpressionHandler.setPermissionEvaluator(permissionEvaluators.get(0));
}
@SuppressWarnings("unchecked")
private <T> T lazyBean(Class<T> interfaceName) {
LazyInitTargetSource lazyTargetSource = new LazyInitTargetSource();
String[] beanNamesForType = context.getBeanNamesForType(interfaceName);
Assert.isTrue(beanNamesForType.length == 1 , "Expecting to only find a single bean for type " + interfaceName + ", but found " + Arrays.asList(beanNamesForType));
lazyTargetSource.setTargetBeanName(beanNamesForType[0]);
lazyTargetSource.setBeanFactory(context);
ProxyFactoryBean proxyFactory = new ProxyFactoryBean();
proxyFactory.setTargetSource(lazyTargetSource);
proxyFactory.setInterfaces(new Class[] { interfaceName });
return (T) proxyFactory.getObject();
@Autowired
public void setApplicationContext(ApplicationContext context) {
this.context = context;
}
private AuthenticationConfiguration getAuthenticationConfiguration() {
return context.getBean(AuthenticationConfiguration.class);
}
private boolean prePostEnabled() {
@@ -399,6 +377,10 @@ public class GlobalMethodSecurityConfiguration implements ImportAware {
return (Integer) enableMethodSecurity().get("order");
}
private boolean isAspectJ() {
return enableMethodSecurity().getEnum("mode") == AdviceMode.ASPECTJ;
}
private AnnotationAttributes enableMethodSecurity() {
if (enableMethodSecurity == null) {
// if it is null look at this instance (i.e. a subclass was used)
@@ -414,5 +396,4 @@ public class GlobalMethodSecurityConfiguration implements ImportAware {
}
return this.enableMethodSecurity;
}
}
}
@@ -48,7 +48,7 @@ final class GlobalMethodSecuritySelector implements ImportSelector {
AdviceMode mode = attributes.getEnum("mode");
String autoProxyClassName = AdviceMode.PROXY == mode ? AutoProxyRegistrar.class.getName()
: GlobalMethodSecurityAspectJAutoProxyRegistrar.class.getName();
: GlobalMethodSecurityAspectJConfiguration.class.getName();
if(skipMethodSecurityConfiguration) {
return new String[] { autoProxyClassName };
}
@@ -113,7 +113,7 @@ public interface HttpSecurityBuilder<H extends HttpSecurityBuilder<H>> extends S
* been added using {@link #addFilterAfter(Filter, Class)} or
* {@link #addFilterBefore(Filter, Class)}.
*
* @param filter the {@link Filter} to register before the type {@code afterFilter}
* @param filter the {@link Filter} to register after the type {@code afterFilter}
* @param afterFilter the Class of the known {@link Filter}.
* @return the {@link HttpSecurity} for further customizations
*/
@@ -171,4 +171,4 @@ public interface HttpSecurityBuilder<H extends HttpSecurityBuilder<H>> extends S
* @return the {@link HttpSecurity} for further customizations
*/
H addFilter(Filter filter);
}
}
@@ -1000,7 +1000,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
* .and()
* .formLogin()
* .and()
* .channelSecurity()
* .requiresChannel()
* .anyRequest().requiresSecure();
* }
*
@@ -0,0 +1,58 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configuration;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.servlet.Filter;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.security.config.annotation.SecurityConfigurer;
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.util.Assert;
/**
* A class used to get all the {@link WebSecurityConfigurer} instances from the
* current {@link ApplicationContext} but ignoring the parent.
*
* @author Rob Winch
*
*/
final class AutowiredWebSecurityConfigurersIgnoreParents {
private final ConfigurableListableBeanFactory beanFactory;
public AutowiredWebSecurityConfigurersIgnoreParents(ConfigurableListableBeanFactory beanFactory) {
Assert.notNull(beanFactory,"beanFactory cannot be null");
this.beanFactory = beanFactory;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public List<SecurityConfigurer<Filter, WebSecurity>> getWebSecurityConfigurers() {
List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new ArrayList<SecurityConfigurer<Filter, WebSecurity>>();
Map<String, WebSecurityConfigurer> beansOfType = beanFactory.getBeansOfType(WebSecurityConfigurer.class);
for(Entry<String,WebSecurityConfigurer> entry : beansOfType.entrySet()) {
webSecurityConfigurers.add(entry.getValue());
}
return webSecurityConfigurers;
}
}
@@ -20,7 +20,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.authentication.configuration.EnableGlobalAuthentication;
import org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration;
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
@@ -77,7 +77,8 @@ import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
@Retention(value=java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value={java.lang.annotation.ElementType.TYPE})
@Documented
@Import({WebSecurityConfiguration.class,ObjectPostProcessorConfiguration.class,AuthenticationConfiguration.class})
@Import({WebSecurityConfiguration.class,ObjectPostProcessorConfiguration.class})
@EnableGlobalAuthentication
public @interface EnableWebSecurity {
/**
@@ -23,6 +23,8 @@ import javax.servlet.Filter;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
@@ -34,11 +36,11 @@ import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.security.access.expression.SecurityExpressionHandler;
import org.springframework.security.config.annotation.AlreadyBuiltException;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.SecurityConfigurer;
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.context.DelegatingApplicationListener;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
@@ -71,6 +73,11 @@ public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAwa
private ClassLoader beanClassLoader;
@Bean
public static DelegatingApplicationListener delegatingApplicationListener() {
return new DelegatingApplicationListener();
}
@Bean
@DependsOn(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public SecurityExpressionHandler<FilterInvocation> webSecurityExpressionHandler() {
@@ -88,11 +95,7 @@ public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAwa
if(!hasConfigurers) {
throw new IllegalStateException("At least one non-null instance of "+ WebSecurityConfigurer.class.getSimpleName()+" must be exposed as a @Bean when using @EnableWebSecurity. Hint try extending "+ WebSecurityConfigurerAdapter.class.getSimpleName());
}
try {
return webSecurity.build();
} catch (AlreadyBuiltException e) {
return webSecurity.getObject();
}
return webSecurity.build();
}
/**
@@ -115,7 +118,7 @@ public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAwa
*/
@Autowired(required = false)
public void setFilterChainProxySecurityConfigurer(ObjectPostProcessor<Object> objectPostProcessor,
List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers) throws Exception {
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers) throws Exception {
webSecurity = objectPostProcessor.postProcess(new WebSecurity(objectPostProcessor));
if(debugEnabled != null) {
webSecurity.debug(debugEnabled);
@@ -137,6 +140,10 @@ public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAwa
this.webSecurityConfigurers = webSecurityConfigurers;
}
@Bean
public AutowiredWebSecurityConfigurersIgnoreParents autowiredWebSecurityConfigurersIgnoreParents(ConfigurableListableBeanFactory beanFactory) {
return new AutowiredWebSecurityConfigurersIgnoreParents(beanFactory);
}
/**
* A custom verision of the Spring provided AnnotationAwareOrderComparator
@@ -16,12 +16,20 @@
package org.springframework.security.config.annotation.web.configuration;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.aop.TargetSource;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.target.LazyInitTargetSource;
import org.springframework.beans.FatalBeanException;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.Order;
@@ -31,6 +39,7 @@ import org.springframework.security.authentication.AuthenticationTrustResolverIm
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
@@ -43,6 +52,8 @@ import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.accept.ContentNegotiationStrategy;
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
@@ -68,9 +79,10 @@ public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigu
}
};
private AuthenticationConfiguration authenticationConfiguration;
private AuthenticationManagerBuilder authenticationBuilder;
private AuthenticationManagerBuilder parentAuthenticationBuilder;
private boolean disableAuthenticationRegistration;
private AuthenticationManagerBuilder localConfigureAuthenticationBldr;
private boolean disableLocalConfigureAuthenticationBldr;
private boolean authenticationManagerInitialized;
private AuthenticationManager authenticationManager;
private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
@@ -146,7 +158,7 @@ public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigu
* @throws Exception
*/
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
this.disableAuthenticationRegistration = true;
this.disableLocalConfigureAuthenticationBldr = true;
}
/**
@@ -161,11 +173,11 @@ public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigu
}
DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor.postProcess(new DefaultAuthenticationEventPublisher());
parentAuthenticationBuilder.authenticationEventPublisher(eventPublisher);
localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
AuthenticationManager authenticationManager = authenticationManager();
authenticationBuilder.parentAuthenticationManager(authenticationManager);
http = new HttpSecurity(objectPostProcessor,authenticationBuilder, parentAuthenticationBuilder.getSharedObjects());
http = new HttpSecurity(objectPostProcessor,authenticationBuilder, localConfigureAuthenticationBldr.getSharedObjects());
http.setSharedObject(UserDetailsService.class, userDetailsService());
http.setSharedObject(ApplicationContext.class, context);
http.setSharedObject(ContentNegotiationStrategy.class, contentNegotiationStrategy);
@@ -205,7 +217,7 @@ public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigu
* @throws Exception
*/
public AuthenticationManager authenticationManagerBean() throws Exception {
return new AuthenticationManagerDelegator(authenticationBuilder);
return new AuthenticationManagerDelegator(authenticationBuilder, context);
}
/**
@@ -219,22 +231,11 @@ public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigu
*/
protected AuthenticationManager authenticationManager() throws Exception {
if(!authenticationManagerInitialized) {
configure(parentAuthenticationBuilder);
if(disableAuthenticationRegistration) {
try {
authenticationManager = context.getBean(AuthenticationManagerBuilder.class).getOrBuild();
} catch(NoSuchBeanDefinitionException e) {
logger.debug("Could not obtain the AuthenticationManagerBuilder. This is ok for now, we will try using an AuthenticationManager directly",e);
}
if(authenticationManager == null) {
try {
authenticationManager = context.getBean(AuthenticationManager.class);
} catch(NoSuchBeanDefinitionException e) {
logger.debug("The AuthenticationManager was not found. This is ok for now as it may not be required.",e);
}
}
configure(localConfigureAuthenticationBldr);
if(disableLocalConfigureAuthenticationBldr) {
authenticationManager = authenticationConfiguration.getAuthenticationManager();
} else {
authenticationManager = parentAuthenticationBuilder.build();
authenticationManager = localConfigureAuthenticationBldr.build();
}
authenticationManagerInitialized = true;
}
@@ -262,7 +263,7 @@ public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigu
*/
public UserDetailsService userDetailsServiceBean() throws Exception {
AuthenticationManagerBuilder globalAuthBuilder = context.getBean(AuthenticationManagerBuilder.class);
return new UserDetailsServiceDelegator(Arrays.asList(parentAuthenticationBuilder, globalAuthBuilder));
return new UserDetailsServiceDelegator(Arrays.asList(localConfigureAuthenticationBldr, globalAuthBuilder));
}
/**
@@ -275,7 +276,7 @@ public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigu
*/
protected UserDetailsService userDetailsService() {
AuthenticationManagerBuilder globalAuthBuilder = context.getBean(AuthenticationManagerBuilder.class);
return new UserDetailsServiceDelegator(Arrays.asList(parentAuthenticationBuilder, globalAuthBuilder));
return new UserDetailsServiceDelegator(Arrays.asList(localConfigureAuthenticationBldr, globalAuthBuilder));
}
public void init(final WebSecurity web) throws Exception {
@@ -341,12 +342,12 @@ public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigu
this.contentNegotiationStrategy = contentNegotiationStrategy;
}
@Autowired(required=false)
@Autowired
public void setObjectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {
this.objectPostProcessor = objectPostProcessor;
authenticationBuilder = new AuthenticationManagerBuilder(objectPostProcessor);
parentAuthenticationBuilder = new AuthenticationManagerBuilder(objectPostProcessor) {
localConfigureAuthenticationBldr = new AuthenticationManagerBuilder(objectPostProcessor) {
@Override
public AuthenticationManagerBuilder eraseCredentials(boolean eraseCredentials) {
authenticationBuilder.eraseCredentials(eraseCredentials);
@@ -356,6 +357,10 @@ public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigu
};
}
@Autowired
public void setAuthenticationConfiguration(AuthenticationConfiguration authenticationConfiguration) {
this.authenticationConfiguration = authenticationConfiguration;
}
/**
* Delays the use of the {@link UserDetailsService} from the
@@ -415,9 +420,15 @@ public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigu
private AuthenticationManagerBuilder delegateBuilder;
private AuthenticationManager delegate;
private final Object delegateMonitor = new Object();
private Set<String> beanNames;
AuthenticationManagerDelegator(AuthenticationManagerBuilder authentication) {
this.delegateBuilder = authentication;
AuthenticationManagerDelegator(AuthenticationManagerBuilder delegateBuilder, ApplicationContext context) {
Assert.notNull(delegateBuilder,"delegateBuilder cannot be null");
Field parentAuthMgrField = ReflectionUtils.findField(AuthenticationManagerBuilder.class, "parentAuthenticationManager");
ReflectionUtils.makeAccessible(parentAuthMgrField);
beanNames = getAuthenticationManagerBeanNames(context);
validateBeanCycle(ReflectionUtils.getField(parentAuthMgrField, delegateBuilder), beanNames);
this.delegateBuilder = delegateBuilder;
}
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
@@ -434,5 +445,26 @@ public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigu
return delegate.authenticate(authentication);
}
private static Set<String> getAuthenticationManagerBeanNames(ApplicationContext applicationContext) {
String[] beanNamesForType = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, AuthenticationManager.class);
return new HashSet<String>(Arrays.asList(beanNamesForType));
}
private static void validateBeanCycle(Object auth, Set<String> beanNames) {
if(auth != null && !beanNames.isEmpty()) {
if(auth instanceof Advised){
Advised advised = (Advised) auth;
TargetSource targetSource = advised.getTargetSource();
if(targetSource instanceof LazyInitTargetSource) {
LazyInitTargetSource lits = (LazyInitTargetSource) targetSource;
if(beanNames.contains(lits.getTargetBeanName())) {
throw new FatalBeanException("A dependency cycle was detected when trying to resolve the AuthenticationManager. Please ensure you have configured authentication.");
}
}
}
beanNames = Collections.emptySet();
}
}
}
}
@@ -151,7 +151,8 @@ abstract class AbstractInterceptUrlConfigurer<C extends AbstractInterceptUrlConf
* @return the default {@code AccessDecisionManager}
*/
private AccessDecisionManager createDefaultAccessDecisionManager(H http) {
return new AffirmativeBased(getDecisionVoters(http));
AffirmativeBased result = new AffirmativeBased(getDecisionVoters(http));
return postProcess(result);
}
/**
@@ -97,7 +97,7 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>> extends Abst
* @return the {@link CsrfConfigurer} for further customizations
*/
public CsrfConfigurer<H> requireCsrfProtectionMatcher(RequestMatcher requireCsrfProtectionMatcher) {
Assert.notNull(csrfTokenRepository, "requireCsrfProtectionMatcher cannot be null");
Assert.notNull(requireCsrfProtectionMatcher, "requireCsrfProtectionMatcher cannot be null");
this.requireCsrfProtectionMatcher = requireCsrfProtectionMatcher;
return this;
}
@@ -132,13 +132,11 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
*
* @param requestMatchers the {@link RequestMatcher} instances to register to the {@link ConfigAttribute} instances
* @param configAttributes the {@link ConfigAttribute} to be mapped by the {@link RequestMatcher} instances
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further customization.
*/
private ExpressionUrlAuthorizationConfigurer<H> interceptUrl(Iterable<? extends RequestMatcher> requestMatchers, Collection<ConfigAttribute> configAttributes) {
private void interceptUrl(Iterable<? extends RequestMatcher> requestMatchers, Collection<ConfigAttribute> configAttributes) {
for(RequestMatcher requestMatcher : requestMatchers) {
REGISTRY.addMapping(new AbstractConfigAttributeRequestMatcherRegistry.UrlMapping(requestMatcher, configAttributes));
}
return null;
}
@Override
@@ -32,12 +32,13 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint;
import org.springframework.security.web.authentication.RememberMeServices;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
import org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.web.accept.ContentNegotiationStrategy;
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
@@ -167,6 +168,10 @@ public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>> extends
if(authenticationDetailsSource != null) {
basicAuthenticationFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
}
RememberMeServices rememberMeServices = http.getSharedObject(RememberMeServices.class);
if(rememberMeServices != null) {
basicAuthenticationFilter.setRememberMeServices(rememberMeServices);
}
basicAuthenticationFilter = postProcess(basicAuthenticationFilter);
http.addFilter(basicAuthenticationFilter);
}
@@ -294,7 +294,9 @@ public final class LogoutConfigurer<H extends HttpSecurityBuilder<H>> extends Ab
}
if(http.getConfigurer(CsrfConfigurer.class) != null) {
this.logoutRequestMatcher = new AntPathRequestMatcher(this.logoutUrl, "POST");
} else {
this.logoutRequestMatcher = new AntPathRequestMatcher(this.logoutUrl);
}
return new AntPathRequestMatcher(this.logoutUrl);
return this.logoutRequestMatcher;
}
}
@@ -15,7 +15,9 @@
*/
package org.springframework.security.config.annotation.web.configurers;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.springframework.http.MediaType;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
@@ -114,12 +116,13 @@ public final class RequestCacheConfigurer<H extends HttpSecurityBuilder<H>> exte
return defaultCache;
}
@SuppressWarnings("unchecked")
private RequestMatcher createDefaultSavedRequestMatcher(H http) {
ContentNegotiationStrategy contentNegotiationStrategy = http.getSharedObject(ContentNegotiationStrategy.class);
if(contentNegotiationStrategy == null) {
contentNegotiationStrategy = new HeaderContentNegotiationStrategy();
}
RequestMatcher getRequests = new AntPathRequestMatcher("/**", "GET");
RequestMatcher notFavIcon = new NegatedRequestMatcher(new AntPathRequestMatcher("/**/favicon.ico"));
MediaTypeRequestMatcher jsonRequest = new MediaTypeRequestMatcher(contentNegotiationStrategy, MediaType.APPLICATION_JSON);
@@ -127,6 +130,18 @@ public final class RequestCacheConfigurer<H extends HttpSecurityBuilder<H>> exte
RequestMatcher notJson = new NegatedRequestMatcher(jsonRequest);
RequestMatcher notXRequestedWith = new NegatedRequestMatcher(new RequestHeaderRequestMatcher("X-Requested-With","XMLHttpRequest"));
return new AndRequestMatcher(getRequests, notFavIcon, notJson, notXRequestedWith);
boolean isCsrfEnabled = http.getConfigurer(CsrfConfigurer.class) != null;
List<RequestMatcher> matchers = new ArrayList<RequestMatcher>();
if(isCsrfEnabled) {
RequestMatcher getRequests = new AntPathRequestMatcher("/**", "GET");
matchers.add(0, getRequests);
}
matchers.add(notFavIcon);
matchers.add(notJson);
matchers.add(notXRequestedWith);
return new AndRequestMatcher(matchers);
}
}
@@ -22,10 +22,15 @@ import java.util.List;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.GenericApplicationListenerAdapter;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.context.DelegatingApplicationListener;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
@@ -83,11 +88,12 @@ import org.springframework.util.Assert;
* @see ConcurrentSessionFilter
*/
public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<SessionManagementConfigurer<H>,H> {
private SessionAuthenticationStrategy sessionFixationAuthenticationStrategy = createDefaultSessionFixationProtectionStrategy();
private final SessionAuthenticationStrategy DEFAULT_SESSION_FIXATION_STRATEGY = createDefaultSessionFixationProtectionStrategy();
private SessionAuthenticationStrategy sessionFixationAuthenticationStrategy = DEFAULT_SESSION_FIXATION_STRATEGY;
private SessionAuthenticationStrategy sessionAuthenticationStrategy;
private InvalidSessionStrategy invalidSessionStrategy;
private List<SessionAuthenticationStrategy> sessionAuthenticationStrategies = new ArrayList<SessionAuthenticationStrategy>();
private SessionRegistry sessionRegistry = new SessionRegistryImpl();
private SessionRegistry sessionRegistry;
private Integer maximumSessions;
private String expiredUrl;
private boolean maxSessionsPreventsLogin;
@@ -367,14 +373,14 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
http.setSharedObject(RequestCache.class, new NullRequestCache());
}
}
http.setSharedObject(SessionAuthenticationStrategy.class, getSessionAuthenticationStrategy());
http.setSharedObject(SessionAuthenticationStrategy.class, getSessionAuthenticationStrategy(http));
http.setSharedObject(InvalidSessionStrategy.class, getInvalidSessionStrategy());
}
@Override
public void configure(H http) throws Exception {
SecurityContextRepository securityContextRepository = http.getSharedObject(SecurityContextRepository.class);
SessionManagementFilter sessionManagementFilter = new SessionManagementFilter(securityContextRepository, getSessionAuthenticationStrategy());
SessionManagementFilter sessionManagementFilter = new SessionManagementFilter(securityContextRepository, getSessionAuthenticationStrategy(http));
if(sessionAuthenticationErrorUrl != null) {
sessionManagementFilter.setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler(sessionAuthenticationErrorUrl));
}
@@ -389,7 +395,7 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
http.addFilter(sessionManagementFilter);
if(isConcurrentSessionControlEnabled()) {
ConcurrentSessionFilter concurrentSessionFilter = new ConcurrentSessionFilter(sessionRegistry, expiredUrl);
ConcurrentSessionFilter concurrentSessionFilter = new ConcurrentSessionFilter(getSessionRegistry(http), expiredUrl);
concurrentSessionFilter = postProcess(concurrentSessionFilter);
http.addFilter(concurrentSessionFilter);
}
@@ -444,12 +450,16 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
*
* @return the {@link SessionAuthenticationStrategy} to use
*/
private SessionAuthenticationStrategy getSessionAuthenticationStrategy() {
private SessionAuthenticationStrategy getSessionAuthenticationStrategy(H http) {
if(sessionAuthenticationStrategy != null) {
return sessionAuthenticationStrategy;
}
List<SessionAuthenticationStrategy> delegateStrategies = sessionAuthenticationStrategies;
if(DEFAULT_SESSION_FIXATION_STRATEGY == sessionFixationAuthenticationStrategy) {
sessionFixationAuthenticationStrategy = postProcess(sessionFixationAuthenticationStrategy);
}
if(isConcurrentSessionControlEnabled()) {
SessionRegistry sessionRegistry = getSessionRegistry(http);
ConcurrentSessionControlAuthenticationStrategy concurrentSessionControlStrategy = new ConcurrentSessionControlAuthenticationStrategy(sessionRegistry);
concurrentSessionControlStrategy.setMaximumSessions(maximumSessions);
concurrentSessionControlStrategy.setExceptionIfMaximumExceeded(maxSessionsPreventsLogin);
@@ -466,6 +476,28 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
return sessionAuthenticationStrategy;
}
private SessionRegistry getSessionRegistry(H http) {
if(sessionRegistry == null) {
SessionRegistryImpl sessionRegistry = new SessionRegistryImpl();
registerDelegateApplicationListener(http, sessionRegistry);
this.sessionRegistry = sessionRegistry;
}
return sessionRegistry;
}
private void registerDelegateApplicationListener(H http, ApplicationListener<?> delegate) {
ApplicationContext context = http.getSharedObject(ApplicationContext.class);
if(context == null) {
return;
}
if(context.getBeansOfType(DelegatingApplicationListener.class).isEmpty()) {
return;
}
DelegatingApplicationListener delegating = context.getBean(DelegatingApplicationListener.class);
SmartApplicationListener smartListener = new GenericApplicationListenerAdapter(delegate);
delegating.addListener(smartListener);
}
/**
* Returns true if the number of concurrent sessions per user should be restricted.
* @return
@@ -20,6 +20,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;
import org.springframework.security.config.annotation.authentication.configuration.EnableGlobalAuthentication;
/**
@@ -33,5 +34,6 @@ import org.springframework.context.annotation.Import;
@Target(value={java.lang.annotation.ElementType.TYPE})
@Documented
@Import(WebMvcSecurityConfiguration.class)
@EnableGlobalAuthentication
public @interface EnableWebMvcSecurity {
}
@@ -52,4 +52,4 @@ public class WebMvcSecurityConfiguration extends WebMvcConfigurerAdapter {
public RequestDataValueProcessor requestDataValueProcessor() {
return new CsrfRequestDataValueProcessor();
}
}
}
@@ -128,6 +128,9 @@ final class AuthenticationConfigBuilder {
private final BeanReference portResolver;
private final BeanMetadataElement csrfLogoutHandler;
private String loginProcessingUrl;
private String openidLoginProcessingUrl;
public AuthenticationConfigBuilder(Element element, ParserContext pc, SessionCreationPolicy sessionPolicy,
BeanReference requestCache, BeanReference authenticationManager, BeanReference sessionStrategy, BeanReference portMapper, BeanReference portResolver, BeanMetadataElement csrfLogoutHandler) {
this.httpElt = element;
@@ -197,6 +200,7 @@ final class AuthenticationConfigBuilder {
parser.parse(formLoginElt, pc);
formFilter = parser.getFilterBean();
formEntryPoint = parser.getEntryPointBean();
loginProcessingUrl = parser.getLoginProcessingUrl();
}
if (formFilter != null) {
@@ -221,6 +225,7 @@ final class AuthenticationConfigBuilder {
parser.parse(openIDLoginElt, pc);
openIDFilter = parser.getFilterBean();
openIDEntryPoint = parser.getEntryPointBean();
openidLoginProcessingUrl = parser.getLoginProcessingUrl();
List<Element> attrExElts = DomUtils.getChildElementsByTagName(openIDLoginElt, Elements.OPENID_ATTRIBUTE_EXCHANGE);
@@ -473,10 +478,12 @@ final class AuthenticationConfigBuilder {
if (formFilterId != null) {
loginPageFilter.addConstructorArgReference(formFilterId);
loginPageFilter.addPropertyValue("authenticationUrl", loginProcessingUrl);
}
if (openIDFilterId != null) {
loginPageFilter.addConstructorArgReference(openIDFilterId);
loginPageFilter.addPropertyValue("openIDauthenticationUrl", openidLoginProcessingUrl);
}
loginPageGenerationFilter = loginPageFilter.getBeanDefinition();
@@ -66,6 +66,7 @@ public class FormLoginBeanDefinitionParser {
private RootBeanDefinition filterBean;
private RootBeanDefinition entryPointBean;
private String loginPage;
private String loginProcessingUrl;
FormLoginBeanDefinitionParser(String defaultLoginProcessingUrl, String filterClassName,
BeanReference requestCache, BeanReference sessionStrategy, boolean allowSessionCreation, BeanReference portMapper, BeanReference portResolver) {
@@ -148,7 +149,12 @@ public class FormLoginBeanDefinitionParser {
loginUrl = defaultLoginProcessingUrl;
}
filterBuilder.addPropertyValue("filterProcessesUrl", loginUrl);
this.loginProcessingUrl = loginUrl;
BeanDefinitionBuilder matcherBuilder = BeanDefinitionBuilder.rootBeanDefinition("org.springframework.security.web.authentication.logout.LogoutFilter$FilterProcessUrlRequestMatcher");
matcherBuilder.addConstructorArgValue(loginUrl);
filterBuilder.addPropertyValue("requiresAuthenticationRequestMatcher", matcherBuilder.getBeanDefinition());
if (StringUtils.hasText(successHandlerRef)) {
filterBuilder.addPropertyReference("authenticationSuccessHandler", successHandlerRef);
@@ -201,4 +207,8 @@ public class FormLoginBeanDefinitionParser {
String getLoginPage() {
return loginPage;
}
String getLoginProcessingUrl() {
return loginProcessingUrl;
}
}
@@ -208,7 +208,7 @@ public class HeadersBeanDefinitionParser implements BeanDefinitionParser {
}
String fromParameter = getAttribute(frameElt, ATT_FROM_PARAMETER, "from");
allowFromStrategy.addPropertyValue("allowFromParameterName", fromParameter);
builder.addConstructorArgValue(allowFromStrategy);
builder.addConstructorArgValue(allowFromStrategy.getBeanDefinition());
}
} else {
parserContext.getReaderContext().error("One of 'strategy' and 'strategy-ref' must be set.",
@@ -46,10 +46,12 @@ class LogoutBeanDefinitionParser implements BeanDefinitionParser {
final String rememberMeServices;
private ManagedList<BeanMetadataElement> logoutHandlers = new ManagedList<BeanMetadataElement>();
private boolean csrfEnabled;
public LogoutBeanDefinitionParser(String rememberMeServices, BeanMetadataElement csrfLogoutHandler) {
this.rememberMeServices = rememberMeServices;
if(csrfLogoutHandler != null) {
this.csrfEnabled = csrfLogoutHandler != null;
if(this.csrfEnabled) {
logoutHandlers.add(csrfLogoutHandler);
}
}
@@ -78,7 +80,9 @@ class LogoutBeanDefinitionParser implements BeanDefinitionParser {
if (!StringUtils.hasText(logoutUrl)) {
logoutUrl = DEF_LOGOUT_URL;
}
builder.addPropertyValue("filterProcessesUrl", logoutUrl);
builder.addPropertyValue("logoutRequestMatcher", getLogoutRequestMatcher(logoutUrl));
if (StringUtils.hasText(successHandlerRef)) {
if (StringUtils.hasText(logoutSuccessUrl)) {
@@ -114,6 +118,19 @@ class LogoutBeanDefinitionParser implements BeanDefinitionParser {
return builder.getBeanDefinition();
}
private BeanDefinition getLogoutRequestMatcher(String logoutUrl) {
if(this.csrfEnabled) {
BeanDefinitionBuilder matcherBuilder = BeanDefinitionBuilder.rootBeanDefinition("org.springframework.security.web.util.matcher.AntPathRequestMatcher");
matcherBuilder.addConstructorArgValue(logoutUrl);
matcherBuilder.addConstructorArgValue("POST");
return matcherBuilder.getBeanDefinition();
} else {
BeanDefinitionBuilder matcherBuilder = BeanDefinitionBuilder.rootBeanDefinition("org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter$FilterProcessUrlRequestMatcher");
matcherBuilder.addConstructorArgValue(logoutUrl);
return matcherBuilder.getBeanDefinition();
}
}
ManagedList<BeanMetadataElement> getLogoutHandlers() {
return logoutHandlers;
}
@@ -452,6 +452,9 @@ public class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionP
}
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
if(!registry.containsBeanDefinition(beanName)) {
return;
}
BeanDefinition beanDefinition = registry.getBeanDefinition(beanName);
beanDefinition.setLazyInit(true);
}
@@ -123,7 +123,8 @@ abstract class BaseSpringSpec extends Specification {
AuthenticationManager getAuthenticationManager() {
try {
authenticationManager().delegateBuilder.getObject()
} catch(NoSuchBeanDefinitionException e) {}
} catch(NoSuchBeanDefinitionException e) {
} catch(MissingPropertyException e) {}
findFilter(FilterSecurityInterceptor).authenticationManager
}
@@ -87,4 +87,37 @@ class AuthenticationManagerBuilderTests extends BaseSpringSpec {
.withUser("admin").password("password").roles("USER","ADMIN")
}
}
def "isConfigured with AuthenticationProvider"() {
setup:
ObjectPostProcessor opp = Mock()
AuthenticationProvider provider = Mock()
AuthenticationManagerBuilder auth = new AuthenticationManagerBuilder(opp)
when:
auth
.authenticationProvider(provider)
then:
auth.isConfigured()
}
def "isConfigured with parent"() {
setup:
ObjectPostProcessor opp = Mock()
AuthenticationManager parent = Mock()
AuthenticationManagerBuilder auth = new AuthenticationManagerBuilder(opp)
when:
auth
.parentAuthenticationManager(parent)
then:
auth.isConfigured()
}
def "isConfigured not configured"() {
setup:
ObjectPostProcessor opp = Mock()
when:
AuthenticationManagerBuilder auth = new AuthenticationManagerBuilder(opp)
then:
auth.isConfigured() == false
}
}
@@ -15,6 +15,7 @@
*/
package org.springframework.security.config.annotation.authentication
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.authentication.AuthenticationManager
@@ -89,4 +90,25 @@ class NamespaceAuthenticationManagerTests extends BaseSpringSpec {
return super.authenticationManagerBean();
}
}
def "SEC-2533: global authentication-manager@erase-credentials=false"() {
when:
loadConfig(GlobalEraseCredentialsFalseConfig)
Authentication auth = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("user","password"))
then:
auth.credentials == "password"
auth.principal.password == "password"
}
@EnableWebSecurity
@Configuration
static class GlobalEraseCredentialsFalseConfig extends WebSecurityConfigurerAdapter {
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.eraseCredentials(false)
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER")
}
}
}
@@ -0,0 +1,373 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.authentication.configuration;
import org.springframework.aop.framework.ProxyFactoryBean
import org.springframework.beans.BeansException
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.config.BeanPostProcessor
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory
import org.springframework.beans.factory.support.BeanDefinitionRegistry
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor
import org.springframework.context.ApplicationContext
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Import
import org.springframework.core.Ordered
import org.springframework.core.annotation.Order
import org.springframework.security.access.annotation.Secured
import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.authentication.TestingAuthenticationToken
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.authentication.dao.DaoAuthenticationProvider
import org.springframework.security.config.annotation.BaseSpringSpec
import org.springframework.security.config.annotation.ObjectPostProcessor
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
import org.springframework.security.config.annotation.authentication.configurers.GlobalAuthenticationConfigurerAdapter
import org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity
import org.springframework.security.core.AuthenticationException
import org.springframework.security.core.authority.AuthorityUtils
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.security.core.userdetails.User
import org.springframework.security.provisioning.InMemoryUserDetailsManager
class AuthenticationConfigurationTests extends BaseSpringSpec {
def "Ordering Autowired on EnableGlobalMethodSecurity"() {
setup:
SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken("user", "password","ROLE_USER"))
when:
loadConfig(GlobalMethodSecurityAutowiredConfigAndServicesConfig)
then:
context.getBean(Service).run()
}
@Configuration
@Import([GlobalMethodSecurityAutowiredConfig,ServicesConfig])
static class GlobalMethodSecurityAutowiredConfigAndServicesConfig {}
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true)
static class GlobalMethodSecurityAutowiredConfig {
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
auth.inMemoryAuthentication().withUser("user").password("password").roles("USER")
}
}
def "Ordering Autowired on EnableWebSecurity"() {
setup:
SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken("user", "password","ROLE_USER"))
when:
loadConfig(GlobalMethodSecurityConfigAndServicesConfig)
then:
context.getBean(Service).run()
}
@Configuration
@Import([GlobalMethodSecurityConfig,WebSecurityConfig,ServicesConfig])
static class GlobalMethodSecurityConfigAndServicesConfig {}
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true)
static class GlobalMethodSecurityConfig {}
@Configuration
@EnableWebSecurity
static class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
auth.inMemoryAuthentication().withUser("user").password("password").roles("USER")
}
}
//
def "Ordering Autowired on EnableWebMvcSecurity"() {
setup:
SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken("user", "password","ROLE_USER"))
when:
loadConfig(GlobalMethodSecurityMvcSecurityAndServicesConfig)
then:
context.getBean(Service).run()
}
@Configuration
@Import([GlobalMethodSecurityConfig,WebMvcSecurityConfig,ServicesConfig])
static class GlobalMethodSecurityMvcSecurityAndServicesConfig {}
@Configuration
@EnableWebMvcSecurity
static class WebMvcSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
auth.inMemoryAuthentication().withUser("user").password("password").roles("USER")
}
}
//
def "no authentication getAuthenticationManager falls back to null"() {
when:
loadConfig(AuthenticationConfiguration,ObjectPostProcessorConfiguration)
then:
context.getBean(AuthenticationConfiguration).authenticationManager == null
}
def "QuiesentGlobalAuthenticationConfiguererAdapter falls back to null"() {
when:
loadConfig(AuthenticationConfiguration,ObjectPostProcessorConfiguration,QuiesentGlobalAuthenticationConfiguererAdapter)
then:
context.getBean(AuthenticationConfiguration).authenticationManager == null
}
@Configuration
static class QuiesentGlobalAuthenticationConfiguererAdapter extends GlobalAuthenticationConfigurerAdapter {}
//
def "GlobalAuthenticationConfiguererAdapterImpl configures authentication successfully"() {
setup:
def token = new UsernamePasswordAuthenticationToken("user", "password")
when:
loadConfig(AuthenticationConfiguration,ObjectPostProcessorConfiguration,GlobalAuthenticationConfiguererAdapterImpl)
then:
context.getBean(AuthenticationConfiguration).authenticationManager.authenticate(token)?.name == "user"
}
@Configuration
static class GlobalAuthenticationConfiguererAdapterImpl extends GlobalAuthenticationConfigurerAdapter {
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user").password("password").roles("USER")
}
}
//
def "AuthenticationManagerBean configures authentication successfully"() {
setup:
def token = new UsernamePasswordAuthenticationToken("user", "password")
def auth = new UsernamePasswordAuthenticationToken("user", "password", AuthorityUtils.createAuthorityList("ROLE_USER"))
AuthenticationManagerBeanConfig.AM = Mock(AuthenticationManager)
1 * AuthenticationManagerBeanConfig.AM.authenticate(token) >> auth
when:
loadConfig(AuthenticationConfiguration,ObjectPostProcessorConfiguration,AuthenticationManagerBeanConfig)
then:
context.getBean(AuthenticationConfiguration).authenticationManager.authenticate(token).name == auth.name
}
@Configuration
static class AuthenticationManagerBeanConfig {
static AuthenticationManager AM
@Bean
public AuthenticationManager authenticationManager() {
AM
}
}
//
@Configuration
static class ServicesConfig {
@Bean
public Service service() {
return new ServiceImpl()
}
}
static interface Service {
public void run();
}
static class ServiceImpl implements Service {
@Secured("ROLE_USER")
public void run() {}
}
//
def "GlobalAuthenticationConfigurerAdapter are ordered"() {
setup:
loadConfig(AuthenticationConfiguration,ObjectPostProcessorConfiguration)
AuthenticationConfiguration config = context.getBean(AuthenticationConfiguration)
config.setGlobalAuthenticationConfigurers([new LowestOrderGlobalAuthenticationConfigurerAdapter(), new HighestOrderGlobalAuthenticationConfigurerAdapter(), new DefaultOrderGlobalAuthenticationConfigurerAdapter()])
when:
config.getAuthenticationManager()
then:
DefaultOrderGlobalAuthenticationConfigurerAdapter.inits == [HighestOrderGlobalAuthenticationConfigurerAdapter,DefaultOrderGlobalAuthenticationConfigurerAdapter,LowestOrderGlobalAuthenticationConfigurerAdapter]
DefaultOrderGlobalAuthenticationConfigurerAdapter.configs == [HighestOrderGlobalAuthenticationConfigurerAdapter,DefaultOrderGlobalAuthenticationConfigurerAdapter,LowestOrderGlobalAuthenticationConfigurerAdapter]
}
static class DefaultOrderGlobalAuthenticationConfigurerAdapter extends GlobalAuthenticationConfigurerAdapter {
static List inits = []
static List configs = []
public void init(AuthenticationManagerBuilder auth) throws Exception {
inits.add(getClass())
}
public void configure(AuthenticationManagerBuilder auth) throws Exception {
configs.add(getClass())
}
}
@Order(Ordered.LOWEST_PRECEDENCE)
static class LowestOrderGlobalAuthenticationConfigurerAdapter extends DefaultOrderGlobalAuthenticationConfigurerAdapter {}
@Order(Ordered.HIGHEST_PRECEDENCE)
static class HighestOrderGlobalAuthenticationConfigurerAdapter extends DefaultOrderGlobalAuthenticationConfigurerAdapter {}
//
def "Spring Boot not triggered when already configured"() {
setup:
loadConfig(AuthenticationConfiguration,ObjectPostProcessorConfiguration)
AuthenticationConfiguration config = context.getBean(AuthenticationConfiguration)
config.setGlobalAuthenticationConfigurers([new ConfiguresInMemoryConfigurerAdapter(), new BootGlobalAuthenticationConfigurerAdapter()])
AuthenticationManager authenticationManager = config.authenticationManager
when:
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("user","password"))
then:
noExceptionThrown()
when:
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("boot","password"))
then:
thrown(AuthenticationException)
}
def "Spring Boot is triggered when not already configured"() {
setup:
loadConfig(AuthenticationConfiguration,ObjectPostProcessorConfiguration)
AuthenticationConfiguration config = context.getBean(AuthenticationConfiguration)
config.setGlobalAuthenticationConfigurers([new BootGlobalAuthenticationConfigurerAdapter()])
AuthenticationManager authenticationManager = config.authenticationManager
when:
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("boot","password"))
then:
noExceptionThrown()
}
static class ConfiguresInMemoryConfigurerAdapter extends GlobalAuthenticationConfigurerAdapter {
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER")
}
}
@Order(Ordered.LOWEST_PRECEDENCE)
static class BootGlobalAuthenticationConfigurerAdapter extends DefaultOrderGlobalAuthenticationConfigurerAdapter {
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.apply(new DefaultBootGlobalAuthenticationConfigurerAdapter())
}
}
static class DefaultBootGlobalAuthenticationConfigurerAdapter extends DefaultOrderGlobalAuthenticationConfigurerAdapter {
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
if(auth.isConfigured()) {
return;
}
User user = new User("boot","password", AuthorityUtils.createAuthorityList("ROLE_USER"))
List<User> users = Arrays.asList(user);
InMemoryUserDetailsManager inMemory = new InMemoryUserDetailsManager(users);
DaoAuthenticationProvider provider = new DaoAuthenticationProvider()
provider.userDetailsService = inMemory
auth.authenticationProvider(provider)
}
}
def "SEC-2531: AuthenticationConfiguration#lazyBean should use BeanClassLoader on ProxyFactoryBean"() {
setup:
ObjectPostProcessor opp = Mock()
Sec2531Config. opp = opp
loadConfig(Sec2531Config)
when:
AuthenticationConfiguration config = context.getBean(AuthenticationConfiguration)
config.getAuthenticationManager()
then:
1 * opp.postProcess(_ as ProxyFactoryBean) >> { args ->
args[0]
}
}
@Configuration
@Import(AuthenticationConfiguration)
static class Sec2531Config {
static ObjectPostProcessor opp
@Bean
public ObjectPostProcessor objectPostProcessor() {
opp
}
@Bean
public AuthenticationManager manager() {
null
}
}
def "SEC-2822: Cannot Force Authentication already built"() {
setup:
loadConfig(Sec2822WebSecurity,Sec2822UseAuth,Sec2822Config)
when:
AuthenticationConfiguration config = context.getBean(AuthenticationConfiguration)
config.getAuthenticationManager()
then:
noExceptionThrown()
}
@Configuration
@Import(AuthenticationConfiguration)
static class Sec2822Config {}
@Configuration
@EnableWebSecurity
static class Sec2822WebSecurity extends WebSecurityConfigurerAdapter {
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
auth.inMemoryAuthentication()
}
}
@Configuration
static class Sec2822UseAuth {
@Autowired
public void useAuthenticationManager(AuthenticationConfiguration auth) {
auth.authenticationManager
}
// Ensures that Sec2822UseAuth is initialized before Sec2822WebSecurity
// must have additional GlobalAuthenticationConfigurerAdapter to trigger SEC-2822
@Bean
public static GlobalAuthenticationConfigurerAdapter bootGlobalAuthenticationConfigurerAdapter() {
new BootGlobalAuthenticationConfigurerAdapter()
}
static class BootGlobalAuthenticationConfigurerAdapter extends GlobalAuthenticationConfigurerAdapter { }
}
}
@@ -0,0 +1,68 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.issue50;
import javax.sql.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.security.config.annotation.issue50.domain.User;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* @author Rob Winch
*
*/
@Configuration
@EnableJpaRepositories("org.springframework.security.config.annotation.issue50.repo")
@EnableTransactionManagement
public class ApplicationConfig {
@Bean
public DataSource dataSource() {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
return builder.setType(EmbeddedDatabaseType.HSQL).build();
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setDatabase(Database.HSQL);
vendorAdapter.setGenerateDdl(true);
vendorAdapter.setShowSql(true);
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan(User.class.getPackage().getName());
factory.setDataSource(dataSource());
return factory;
}
@Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory().getObject());
return txManager;
}
}
@@ -0,0 +1,100 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.issue50;
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.security.access.AccessDeniedException
import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.authentication.BadCredentialsException
import org.springframework.security.authentication.TestingAuthenticationToken
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.config.annotation.issue50.domain.User
import org.springframework.security.config.annotation.issue50.repo.UserRepository
import org.springframework.security.core.Authentication
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.security.core.userdetails.UsernameNotFoundException
import org.springframework.security.web.FilterChainProxy
import org.springframework.test.context.ContextConfiguration
import org.springframework.transaction.annotation.Transactional
import spock.lang.Specification
/**
* @author Rob Winch
*
*/
@ContextConfiguration(classes=[ApplicationConfig,SecurityConfig])
@Transactional
class Issue50Tests extends Specification {
@Autowired
private FilterChainProxy springSecurityFilterChain
@Autowired
private AuthenticationManager authenticationManager
@Autowired
private UserRepository userRepo
def setup() {
SecurityContextHolder.context.authentication = new TestingAuthenticationToken("test",null,"ROLE_ADMIN")
}
def cleanup() {
SecurityContextHolder.clearContext()
}
// https://github.com/SpringSource/spring-security-javaconfig/issues/50
def "#50 - GlobalMethodSecurityConfiguration should load AuthenticationManager lazily"() {
when:
"Configuration Loads"
then: "GlobalMethodSecurityConfiguration loads AuthenticationManager lazily"
noExceptionThrown()
}
def "AuthenticationManager will not authenticate missing user"() {
when:
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("test", "password"))
then:
thrown(UsernameNotFoundException)
}
def "AuthenticationManager will not authenticate with invalid password"() {
when:
User user = new User(username:"test",password:"password")
userRepo.save(user)
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.username , "invalid"))
then:
thrown(BadCredentialsException)
}
def "AuthenticationManager can be used to authenticate a user"() {
when:
User user = new User(username:"test",password:"password")
userRepo.save(user)
Authentication result = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.username , user.password))
then:
result.principal == user.username
}
def "Global Method Security is enabled and works"() {
setup:
SecurityContextHolder.context.authentication = new TestingAuthenticationToken("test",null,"ROLE_USER")
when:
User user = new User(username:"denied",password:"password")
userRepo.save(user)
Authentication result = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.username , user.password))
then:
thrown(AccessDeniedException)
}
}
@@ -0,0 +1,90 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.issue50;
import org.spockframework.util.Assert;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.issue50.domain.User;
import org.springframework.security.config.annotation.issue50.repo.UserRepository;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
/**
* @author Rob Winch
*
*/
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserRepository myUserRepository;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.authenticationProvider(authenticationProvider());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/*").permitAll();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean()
throws Exception {
return super.authenticationManagerBean();
}
@Bean
public AuthenticationProvider authenticationProvider() {
Assert.notNull(myUserRepository);
return new AuthenticationProvider() {
public boolean supports(Class<?> authentication) {
return true;
}
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
Object principal = authentication.getPrincipal();
String username = String.valueOf(principal);
User user = myUserRepository.findByUsername(username);
if(user == null) {
throw new UsernameNotFoundException("No user for principal "+principal);
}
if(!authentication.getCredentials().equals(user.getPassword())) {
throw new BadCredentialsException("Invalid password");
}
return new TestingAuthenticationToken(principal, null, "ROLE_USER");
}
};
}
}
@@ -0,0 +1,62 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.issue50.domain;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
/**
* @author Rob Winch
*
*/
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String username;
private String password;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
@@ -0,0 +1,30 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.issue50.repo;
import org.springframework.data.repository.CrudRepository;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.config.annotation.issue50.domain.User;
/**
* @author Rob Winch
*
*/
public interface UserRepository extends CrudRepository<User, String> {
@PreAuthorize("hasRole('ROLE_ADMIN')")
User findByUsername(String username);
}
@@ -15,6 +15,13 @@
*/
package org.springframework.security.config.annotation.method.configuration
import org.springframework.beans.BeansException
import org.springframework.beans.factory.config.BeanPostProcessor
import org.springframework.security.config.annotation.authentication.configurers.GlobalAuthenticationConfigurerAdapter
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
import javax.sql.DataSource
import static org.fest.assertions.Assertions.assertThat
import static org.junit.Assert.fail
@@ -22,6 +29,7 @@ import org.aopalliance.intercept.MethodInterceptor
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.ApplicationContext
import org.springframework.context.ApplicationListener
import org.springframework.context.annotation.AnnotationConfigApplicationContext
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.access.AccessDeniedException
@@ -301,4 +309,100 @@ public class GlobalMethodSecurityConfigurationTests extends BaseSpringSpec {
new MethodSecurityServiceImpl()
}
}
def "SEC-2479: Support AuthenticationManager in parent"() {
setup:
SecurityContextHolder.getContext().setAuthentication(
new TestingAuthenticationToken("user", "password","ROLE_USER"))
loadConfig(Sec2479ParentConfig)
def child = new AnnotationConfigApplicationContext()
child.register(Sec2479ChildConfig)
child.parent = context
child.refresh()
MethodSecurityService service = child.getBean(MethodSecurityService)
when:
service.preAuthorize()
then:
thrown(AccessDeniedException)
cleanup:
child?.close()
}
@Configuration
static class Sec2479ParentConfig {
static AuthenticationManager AM
@Bean
public AuthenticationManager am() {
AM
}
}
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
static class Sec2479ChildConfig {
@Bean
public MethodSecurityService service() {
new MethodSecurityServiceImpl()
}
}
def "SEC-2815: @EnableGlobalMethodSecurity does not trigger eager initialization of Beans in GlobalAuthenticationConfigurer"() {
setup:
Sec2815Config.dataSource = Mock(DataSource)
when: 'load a Configuration that uses a Bean (DataSource) in a GlobalAuthenticationConfigurerAdapter'
loadConfig(Sec2815Config)
then: 'The Bean (DataSource) is still properly post processed with all BeanPostProcessor'
context.getBean(MockBeanPostProcessor).beforeInit['dataSource']
context.getBean(MockBeanPostProcessor).afterInit['dataSource']
}
@EnableGlobalMethodSecurity(prePostEnabled = true)
static class Sec2815Config {
static DataSource dataSource;
@Bean
public MethodSecurityService service() {
new MethodSecurityServiceImpl()
}
@Bean
public MockBeanPostProcessor mockBeanPostProcessor() {
new MockBeanPostProcessor()
}
@Bean
public DataSource dataSource() {
dataSource
}
@Configuration
static class AuthConfig extends GlobalAuthenticationConfigurerAdapter {
@Autowired
DataSource dataSource
@Override
void init(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
}
}
}
static class MockBeanPostProcessor implements BeanPostProcessor {
Map<String,Object> beforeInit = new HashMap<String,Object>()
Map<String,Object> afterInit = new HashMap<String,Object>()
@Override
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
beforeInit[beanName] = bean
bean
}
@Override
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
afterInit[beanName] = bean
bean
}
}
}
@@ -15,11 +15,14 @@
*/
package org.springframework.security.config.annotation.method.configuration
import org.springframework.security.access.intercept.aspectj.AspectJMethodSecurityInterceptor
import static org.fest.assertions.Assertions.assertThat
import static org.junit.Assert.fail
import java.lang.reflect.Method
import org.springframework.security.access.intercept.aspectj.aspect.AnnotationSecurityAspect
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator
import org.springframework.beans.factory.BeanCreationException
import org.springframework.context.ConfigurableApplicationContext
@@ -188,8 +191,8 @@ public class NamespaceGlobalMethodSecurityTests extends BaseSpringSpec {
when:
context = new AnnotationConfigApplicationContext(AspectJModeConfig)
then:
AnnotationAwareAspectJAutoProxyCreator autoProxyCreator = context.getBean(AnnotationAwareAspectJAutoProxyCreator)
autoProxyCreator.proxyTargetClass == true
context.getBean(AnnotationSecurityAspect)
context.getBean(AspectJMethodSecurityInterceptor)
}
@Configuration
@@ -201,8 +204,8 @@ public class NamespaceGlobalMethodSecurityTests extends BaseSpringSpec {
when:
context = new AnnotationConfigApplicationContext(BaseMethodConfig,AspectJModeExtendsGMSCConfig)
then:
AnnotationAwareAspectJAutoProxyCreator autoProxyCreator = context.getBean(AnnotationAwareAspectJAutoProxyCreator)
autoProxyCreator.proxyTargetClass == false
context.getBean(AnnotationSecurityAspect)
context.getBean(AspectJMethodSecurityInterceptor)
}
@Configuration
@@ -0,0 +1,117 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configuration;
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.FatalBeanException;
import org.springframework.context.annotation.AnnotationConfigApplicationContext
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.authentication.TestingAuthenticationToken
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.config.annotation.BaseSpringSpec
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
public class Sec2515Tests extends BaseSpringSpec {
def "SEC-2515: Prevent StackOverflow with bean graph cycle"() {
when:
loadConfig(StackOverflowSecurityConfig)
then:
thrown(FatalBeanException)
}
@EnableWebSecurity
@Configuration
static class StackOverflowSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
@Bean
public AuthenticationManager authenticationManagerBean()
throws Exception {
return super.authenticationManagerBean();
}
}
def "Custom Name Prevent StackOverflow with bean graph cycle"() {
when:
loadConfig(StackOverflowSecurityConfig)
then:
thrown(FatalBeanException)
}
@EnableWebSecurity
@Configuration
static class CustomBeanNameStackOverflowSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
@Bean(name="custom")
public AuthenticationManager authenticationManagerBean()
throws Exception {
return super.authenticationManagerBean();
}
}
def "SEC-2549: Can load with child classloader"() {
setup:
CanLoadWithChildConfig.AM = Mock(AuthenticationManager)
context = new AnnotationConfigApplicationContext()
context.classLoader = new URLClassLoader(new URL[0], context.classLoader)
context.register(CanLoadWithChildConfig)
context.refresh()
when:
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("user", "password"))
then:
noExceptionThrown()
1 * CanLoadWithChildConfig.AM.authenticate(_) >> new TestingAuthenticationToken("user","password","ROLE_USER")
}
@EnableWebSecurity
@Configuration
static class CanLoadWithChildConfig extends WebSecurityConfigurerAdapter {
static AuthenticationManager AM
@Bean
public AuthenticationManager am() {
AM
}
}
def "SEC-2515: @Bean still works when configure(AuthenticationManagerBuilder) used"() {
when:
loadConfig(SecurityConfig)
then:
noExceptionThrown();
}
@EnableWebSecurity
@Configuration
static class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
@Bean
public AuthenticationManager authenticationManagerBean()
throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.inMemoryAuthentication()
}
}
}
@@ -13,35 +13,34 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configuration;
package org.springframework.security.config.annotation.web.configuration
import java.lang.reflect.Modifier
import static org.junit.Assert.*
import java.util.List;
import org.springframework.beans.factory.BeanCreationException
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.AnnotationConfigApplicationContext
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.annotation.Order
import org.springframework.expression.ExpressionParser;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.security.access.expression.SecurityExpressionHandler;
import org.springframework.expression.ExpressionParser
import org.springframework.mock.web.MockHttpServletRequest
import org.springframework.security.access.expression.SecurityExpressionHandler
import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
import org.springframework.security.config.annotation.BaseSpringSpec
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.builders.WebSecurity
import org.springframework.security.web.FilterChainProxy
import org.springframework.security.web.SecurityFilterChain
import org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator;
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
import org.springframework.security.web.access.expression.WebSecurityExpressionHandler;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler
import org.springframework.security.web.access.expression.WebSecurityExpressionHandler
import org.springframework.security.web.util.matcher.AnyRequestMatcher
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.test.util.ReflectionTestUtils
/**
* @author Rob Winch
@@ -313,4 +312,41 @@ class WebSecurityConfigurationTests extends BaseSpringSpec {
}
}
}
def "SEC-2461: Multiple WebSecurityConfiguration instances cause null springSecurityFilterChain"() {
setup:
def parent = loadConfig(ParentConfig)
def child = new AnnotationConfigApplicationContext()
child.register(ChildConfig)
child.parent = parent
when:
child.refresh()
then: "springSecurityFilterChain can be found in parent and child"
parent.getBean("springSecurityFilterChain")
child.getBean("springSecurityFilterChain")
and: "springSecurityFilterChain is defined in both parent and child (don't search parent)"
parent.containsBeanDefinition("springSecurityFilterChain")
child.containsBeanDefinition("springSecurityFilterChain")
cleanup:
child?.close()
// parent.close() is in superclass
}
@EnableWebSecurity
@Configuration
static class ParentConfig extends WebSecurityConfigurerAdapter {
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
auth.inMemoryAuthentication()
}
}
@EnableWebSecurity
@Configuration
static class ChildConfig extends WebSecurityConfigurerAdapter { }
def "SEC-2773: delegatingApplicationListener is static method"() {
expect: 'delegatingApplicationListener to prevent premature instantiation of WebSecurityConfiguration'
Modifier.isStatic(WebSecurityConfiguration.metaClass.methods.find { it.name == 'delegatingApplicationListener'}.modifiers)
}
}
@@ -17,9 +17,10 @@ package org.springframework.security.config.annotation.web.configuration.sec2377
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@EnableWebSecurity
@Configuration
public class Sec2377BConfig {
public class Sec2377BConfig extends WebSecurityConfigurerAdapter {
}
}
@@ -15,6 +15,8 @@
*/
package org.springframework.security.config.annotation.web.configurers
import org.springframework.security.web.util.matcher.AntPathRequestMatcher
import javax.servlet.http.HttpServletResponse
import org.springframework.context.annotation.Configuration
@@ -102,6 +104,49 @@ class CsrfConfigurerTests extends BaseSpringSpec {
}
}
def "SEC-2498: Disable CSRF enables RequestCache for any method"() {
setup:
loadConfig(DisableCsrfEnablesRequestCacheConfig)
request.requestURI = '/tosave'
request.method = "POST"
clearCsrfToken()
when:
springSecurityFilterChain.doFilter(request,response,chain)
then:
response.redirectedUrl
when:
super.setupWeb(request.session)
request.method = "POST"
request.servletPath = '/login'
request.parameters['username'] = ['user'] as String[]
request.parameters['password'] = ['password'] as String[]
springSecurityFilterChain.doFilter(request,response,chain)
then:
response.redirectedUrl == 'http://localhost/tosave'
}
@Configuration
@EnableWebSecurity
static class DisableCsrfEnablesRequestCacheConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().and()
.csrf().disable()
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER")
}
}
def "SEC-2422: csrf expire CSRF token and session-management invalid-session-url"() {
setup:
loadConfig(InvalidSessionUrlConfig)
@@ -192,6 +237,7 @@ class CsrfConfigurerTests extends BaseSpringSpec {
setup:
CsrfTokenRepositoryConfig.repo = Mock(CsrfTokenRepository)
(1.._) * CsrfTokenRepositoryConfig.repo.loadToken(_) >> csrfToken
(1.._) * CsrfTokenRepositoryConfig.repo.generateToken(_) >> csrfToken
loadConfig(CsrfTokenRepositoryConfig)
request.method = "POST"
request.getSession()
@@ -293,6 +339,18 @@ class CsrfConfigurerTests extends BaseSpringSpec {
currentAuthentication != null
}
def "SEC-2543: CSRF means logout requires POST"() {
setup:
loadConfig(LogoutConfig)
login()
request.servletPath = "/logout"
request.method = "GET"
when:
springSecurityFilterChain.doFilter(request,response,chain)
then: "logout with GET is not performed"
currentAuthentication != null
}
@Configuration
@EnableWebSecurity
static class LogoutConfig extends WebSecurityConfigurerAdapter {
@@ -305,9 +363,36 @@ class CsrfConfigurerTests extends BaseSpringSpec {
}
}
def "CSRF can explicitly enable GET for logout"() {
setup:
loadConfig(LogoutAllowsGetConfig)
login()
request.servletPath = "/logout"
request.method = "GET"
when:
springSecurityFilterChain.doFilter(request,response,chain)
then: "logout with GET is not performed"
currentAuthentication == null
}
@Configuration
@EnableWebSecurity
static class LogoutAllowsGetConfig extends WebSecurityConfigurerAdapter {
static AccessDeniedHandler deniedHandler
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin().and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
}
}
def "csrf disables POST requests from RequestCache"() {
setup:
CsrfDisablesPostRequestFromRequestCacheConfig.repo = Mock(CsrfTokenRepository)
(1.._) * CsrfDisablesPostRequestFromRequestCacheConfig.repo.generateToken(_) >> csrfToken
loadConfig(CsrfDisablesPostRequestFromRequestCacheConfig)
request.servletPath = "/some-url"
request.requestURI = "/some-url"
@@ -334,6 +419,7 @@ class CsrfConfigurerTests extends BaseSpringSpec {
def "csrf enables GET requests with RequestCache"() {
setup:
CsrfDisablesPostRequestFromRequestCacheConfig.repo = Mock(CsrfTokenRepository)
(1.._) * CsrfDisablesPostRequestFromRequestCacheConfig.repo.generateToken(_) >> csrfToken
loadConfig(CsrfDisablesPostRequestFromRequestCacheConfig)
request.servletPath = "/some-url"
request.requestURI = "/some-url"
@@ -381,6 +467,13 @@ class CsrfConfigurerTests extends BaseSpringSpec {
}
}
def 'SEC-2749: requireCsrfProtectionMatcher null'() {
when:
new CsrfConfigurer<>().requireCsrfProtectionMatcher(null)
then:
thrown(IllegalArgumentException)
}
def clearCsrfToken() {
request.removeAllParameters()
}
@@ -18,16 +18,23 @@ package org.springframework.security.config.annotation.web.configurers;
import java.util.Arrays;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.expression.SecurityExpressionHandler;
import org.springframework.security.access.expression.SecurityExpressionOperations;
import org.springframework.security.access.vote.AffirmativeBased;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
import org.springframework.security.web.access.expression.WebExpressionVoter;
import org.springframework.security.web.access.expression.WebSecurityExpressionRoot;
/**
*
@@ -60,4 +67,90 @@ public class ExpressionUrlAuthorizationConfigurerConfigs {
.formLogin();
}
}
@EnableWebSecurity
@Configuration
static class UseBeansInExpressions extends WebSecurityConfigurerAdapter {
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/user/**").hasRole("USER")
.antMatchers("/allow/**").access("@permission.check(authentication,'user')")
.anyRequest().access("@permission.check(authentication,'admin')");
}
@Bean
public Checker permission() {
return new Checker();
}
static class Checker {
public boolean check(Authentication authentication, String customArg) {
return authentication.getName().contains(customArg);
}
}
}
@EnableWebSecurity
@Configuration
static class CustomExpressionRootConfig extends WebSecurityConfigurerAdapter {
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.expressionHandler(expressionHandler())
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/user/**").hasRole("USER")
.antMatchers("/allow/**").access("check('user')")
.anyRequest().access("check('admin')");
}
@Bean
public CustomExpressionHandler expressionHandler() {
return new CustomExpressionHandler();
}
static class CustomExpressionHandler extends DefaultWebSecurityExpressionHandler {
@Override
protected SecurityExpressionOperations createSecurityExpressionRoot(
Authentication authentication, FilterInvocation fi) {
WebSecurityExpressionRoot root = new CustomExpressionRoot(authentication, fi);
root.setPermissionEvaluator(getPermissionEvaluator());
root.setTrustResolver(new AuthenticationTrustResolverImpl());
root.setRoleHierarchy(getRoleHierarchy());
return root;
}
}
static class CustomExpressionRoot extends WebSecurityExpressionRoot {
public CustomExpressionRoot(Authentication a, FilterInvocation fi) {
super(a, fi);
}
public boolean check(String customArg) {
Authentication auth = this.getAuthentication();
return auth.getName().contains(customArg);
}
}
}
}
@@ -19,8 +19,12 @@ import static org.springframework.security.config.annotation.web.configurers.Exp
import javax.servlet.http.HttpServletResponse
import org.springframework.beans.BeansException
import org.springframework.beans.factory.BeanCreationException
import org.springframework.beans.factory.config.BeanPostProcessor
import org.springframework.context.ApplicationListener
import org.springframework.context.annotation.Bean
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.context.annotation.Configuration
import org.springframework.security.access.event.AuthorizedEvent
import org.springframework.security.access.vote.AffirmativeBased
@@ -31,6 +35,7 @@ import org.springframework.security.config.annotation.authentication.builders.Au
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurerConfigs.CustomExpressionRootConfig
import org.springframework.security.core.authority.AuthorityUtils
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor
@@ -475,4 +480,112 @@ public class ExpressionUrlAuthorizationConfigurerTests extends BaseSpringSpec {
then:
1 * al.onApplicationEvent(_ as AuthorizedEvent)
}
def "Use @permission.check in access"() {
setup:
loadConfig(UseBeansInExpressions)
when: "invoke standard expression that denies access"
login()
request.servletPath = "/admin/1"
springSecurityFilterChain.doFilter(request, response, chain)
then: "standard expression works - get forbidden"
response.status == HttpServletResponse.SC_FORBIDDEN
when: "invoke standard expression that allows access"
super.setup()
login()
request.servletPath = "/user/1"
springSecurityFilterChain.doFilter(request, response, chain)
then: "standard expression works - get ok"
response.status == HttpServletResponse.SC_OK
when: "invoke custom bean as expression that allows access"
super.setup()
login()
request.servletPath = "/allow/1"
springSecurityFilterChain.doFilter(request, response, chain)
then: "custom bean expression allows access"
response.status == HttpServletResponse.SC_OK
when: "invoke custom bean as expression that denies access"
super.setup()
login()
request.servletPath = "/deny/1"
springSecurityFilterChain.doFilter(request, response, chain)
then: "custom bean expression denies access"
response.status == HttpServletResponse.SC_FORBIDDEN
}
def "Use custom expressionroot in access"() {
setup:
loadConfig(CustomExpressionRootConfig)
when: "invoke standard expression that denies access"
login()
request.servletPath = "/admin/1"
springSecurityFilterChain.doFilter(request, response, chain)
then: "standard expression works - get forbidden"
response.status == HttpServletResponse.SC_FORBIDDEN
when: "invoke standard expression that allows access"
super.setup()
login()
request.servletPath = "/user/1"
springSecurityFilterChain.doFilter(request, response, chain)
then: "standard expression works - get ok"
response.status == HttpServletResponse.SC_OK
when: "invoke custom bean as expression that allows access"
super.setup()
login()
request.servletPath = "/allow/1"
springSecurityFilterChain.doFilter(request, response, chain)
then: "custom bean expression allows access"
response.status == HttpServletResponse.SC_OK
when: "invoke custom bean as expression that denies access"
super.setup()
login()
request.servletPath = "/deny/1"
springSecurityFilterChain.doFilter(request, response, chain)
then: "custom bean expression denies access"
response.status == HttpServletResponse.SC_FORBIDDEN
}
def "SEC-3011: Default AccessDecisionManager postProcessed"() {
when:
loadConfig(Sec3011Config)
then:
context.getBean(MockBeanPostProcessor).beans.find { it instanceof AccessDecisionManager }
}
@EnableWebSecurity
static class Sec3011Config extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication();
}
@Bean
static MockBeanPostProcessor mbpp() {
return new MockBeanPostProcessor();
}
}
static class MockBeanPostProcessor implements BeanPostProcessor {
List<Object> beans = new ArrayList<Object>();
public Object postProcessBeforeInitialization(Object bean,
String beanName) throws BeansException {
beans.add(bean);
return bean;
}
public Object postProcessAfterInitialization(Object bean,
String beanName) throws BeansException {
return bean;
}
}
}
@@ -186,6 +186,45 @@ class FormLoginConfigurerTests extends BaseSpringSpec {
}
}
def "FormLogin loginProcessingUrl"() {
setup:
loadConfig(FormLoginLoginProcessingUrlConfig)
request.servletPath = "/loginCheck"
request.method = "POST"
request.parameters.username = ["user"] as String[]
request.parameters.password = ["password"] as String[]
when:
springSecurityFilterChain.doFilter(request, response, new MockFilterChain())
then:
response.redirectedUrl == "/"
}
@Configuration
@EnableWebSecurity
static class FormLoginLoginProcessingUrlConfig extends BaseWebConfig {
@Override
protected void configure(HttpSecurity http) {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginProcessingUrl("/loginCheck")
.loginPage("/login")
//.failureUrl("/loginFailure")
.defaultSuccessUrl("/", true)
.passwordParameter("password")
.usernameParameter("username")
.permitAll()
.and()
.logout()
.logoutSuccessUrl("/login")
.logoutUrl("/logout")
.deleteCookies("JSESSIONID")
}
}
def "FormLogin uses PortMapper"() {
when: "load formLogin() with permitAll"
FormLoginUsesPortMapperConfig.PORT_MAPPER = Mock(PortMapper)
@@ -24,6 +24,7 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
import org.springframework.security.web.AuthenticationEntryPoint
import org.springframework.security.web.access.ExceptionTranslationFilter
import org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter;
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter
@@ -133,4 +134,29 @@ class HttpBasicConfigurerTests extends BaseSpringSpec {
.inMemoryAuthentication()
}
}
def "SEC-3019: Basic Authentication uses RememberMe Config"() {
when:
loadConfig(BasicUsesRememberMeConfig)
then:
findFilter(BasicAuthenticationFilter).rememberMeServices == findFilter(RememberMeAuthenticationFilter).rememberMeServices
}
@EnableWebSecurity
@Configuration
static class BasicUsesRememberMeConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic().and()
.rememberMe()
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
}
}
}
@@ -19,6 +19,7 @@ import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.config.annotation.BaseSpringSpec
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
@@ -36,11 +37,13 @@ import org.springframework.stereotype.Component
class Issue55Tests extends BaseSpringSpec {
def "WebSecurityConfigurerAdapter defaults to @Autowired"() {
setup:
TestingAuthenticationToken token = new TestingAuthenticationToken("test", "this")
when:
loadConfig(WebSecurityConfigurerAdapterDefaultsAuthManagerConfig)
loadConfig(WebSecurityConfigurerAdapterDefaultsAuthManagerConfig)
then:
context.getBean(FilterChainProxy)
findFilter(FilterSecurityInterceptor).authenticationManager.parent.class == CustomAuthenticationManager
context.getBean(FilterChainProxy)
findFilter(FilterSecurityInterceptor).authenticationManager.authenticate(token) == CustomAuthenticationManager.RESULT
}
@Configuration
@@ -66,12 +69,14 @@ class Issue55Tests extends BaseSpringSpec {
}
def "multi http WebSecurityConfigurerAdapter defaults to @Autowired"() {
setup:
TestingAuthenticationToken token = new TestingAuthenticationToken("test", "this")
when:
loadConfig(MultiWebSecurityConfigurerAdapterDefaultsAuthManagerConfig)
loadConfig(MultiWebSecurityConfigurerAdapterDefaultsAuthManagerConfig)
then:
context.getBean(FilterChainProxy)
findFilter(FilterSecurityInterceptor).authenticationManager.parent.class == CustomAuthenticationManager
findFilter(FilterSecurityInterceptor,1).authenticationManager.parent.class == CustomAuthenticationManager
context.getBean(FilterChainProxy)
findFilter(FilterSecurityInterceptor).authenticationManager.authenticate(token) == CustomAuthenticationManager.RESULT
findFilter(FilterSecurityInterceptor,1).authenticationManager.authenticate(token) == CustomAuthenticationManager.RESULT
}
@Configuration
@@ -107,8 +112,9 @@ class Issue55Tests extends BaseSpringSpec {
}
static class CustomAuthenticationManager implements AuthenticationManager {
static Authentication RESULT = new TestingAuthenticationToken("test", "this","ROLE_USER")
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
return null;
return RESULT;
}
}
}
@@ -15,19 +15,20 @@
*/
package org.springframework.security.config.annotation.web.configurers
import org.springframework.context.ApplicationListener
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.mock.web.MockHttpSession
import org.springframework.security.authentication.TestingAuthenticationToken
import org.springframework.security.config.annotation.BaseSpringSpec
import org.springframework.security.config.annotation.ObjectPostProcessor
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
import org.springframework.security.core.session.SessionRegistry
import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy
import org.springframework.security.web.authentication.session.SessionFixationProtectionEvent
import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy
import org.springframework.security.web.context.SecurityContextPersistenceFilter
import org.springframework.security.web.context.SecurityContextRepository
import org.springframework.security.web.session.ConcurrentSessionFilter
import org.springframework.security.web.session.SessionManagementFilter
@@ -141,6 +142,29 @@ class NamespaceSessionManagementTests extends BaseSpringSpec {
}
}
def "SEC-2913: Default JavaConfig session fixation AuthenticationStrategy has NullEventPublisher"() {
setup:
loadConfig(SFPPostProcessedConfig)
when:
findSessionAuthenticationStrategy(SessionFixationProtectionStrategy).onSessionChange("id", new MockHttpSession(), new TestingAuthenticationToken("u","p","ROLE_USER"))
then:
context.getBean(MockEventListener).events
}
@EnableWebSecurity
static class SFPPostProcessedConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement()
}
@Bean
public MockEventListener eventListener() {
new MockEventListener()
}
}
def "http/session-management@session-fixation-protection=newSession"() {
when:
loadConfig(SFPNewSessionSessionManagementConfig)
@@ -163,4 +187,13 @@ class NamespaceSessionManagementTests extends BaseSpringSpec {
.newSession()
}
}
static class MockEventListener implements ApplicationListener<SessionFixationProtectionEvent> {
List<SessionFixationProtectionEvent> events = []
public void onApplicationEvent(SessionFixationProtectionEvent event) {
events.add(event)
}
}
}
@@ -21,7 +21,7 @@ import org.springframework.context.annotation.Configuration
import org.springframework.mock.web.MockFilterChain
import org.springframework.mock.web.MockHttpServletRequest
import org.springframework.mock.web.MockHttpServletResponse
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolver
import org.springframework.security.config.annotation.AnyObjectPostProcessor
import org.springframework.security.config.annotation.BaseSpringSpec
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
@@ -33,11 +33,13 @@ import org.springframework.security.web.access.ExceptionTranslationFilter
import org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy
import org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy
import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy
import org.springframework.security.web.context.NullSecurityContextRepository
import org.springframework.security.web.context.SecurityContextPersistenceFilter
import org.springframework.security.web.context.SecurityContextRepository
import org.springframework.security.web.savedrequest.RequestCache
import org.springframework.security.web.session.ConcurrentSessionFilter
import org.springframework.security.web.session.HttpSessionDestroyedEvent
import org.springframework.security.web.session.SessionManagementFilter
/**
@@ -154,12 +156,14 @@ class SessionManagementConfigurerTests extends BaseSpringSpec {
def 'session fixation and enable concurrency control'() {
setup: "context where session fixation is disabled and concurrency control is enabled"
loadConfig(ConcurrencyControlConfig)
def authenticatedSession
when: "authenticate successfully"
request.servletPath = "/login"
request.method = "POST"
request.setParameter("username", "user");
request.setParameter("password","password")
springSecurityFilterChain.doFilter(request, response, chain)
authenticatedSession = request.session
then: "authentication is sucessful"
response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
response.redirectedUrl == "/"
@@ -173,6 +177,17 @@ class SessionManagementConfigurerTests extends BaseSpringSpec {
then:
response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
response.redirectedUrl == '/login?error'
when: 'SEC-2574: When Session Expires and authentication attempted'
context.publishEvent(new HttpSessionDestroyedEvent(authenticatedSession))
super.setup()
request.servletPath = "/login"
request.method = "POST"
request.setParameter("username", "user");
request.setParameter("password","password")
springSecurityFilterChain.doFilter(request, response, chain)
then: "authentication is successful"
response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
response.redirectedUrl == "/"
}
@EnableWebSecurity
@@ -217,6 +232,8 @@ class SessionManagementConfigurerTests extends BaseSpringSpec {
1 * opp.postProcess(_ as CompositeSessionAuthenticationStrategy) >> {CompositeSessionAuthenticationStrategy o -> o}
and: "RegisterSessionAuthenticationStrategy is registered with ObjectPostProcessor"
1 * opp.postProcess(_ as RegisterSessionAuthenticationStrategy) >> {RegisterSessionAuthenticationStrategy o -> o}
and: "SessionFixationProtectionStrategy is registered with ObjectPostProcessor"
1 * opp.postProcess(_ as SessionFixationProtectionStrategy) >> {SessionFixationProtectionStrategy o -> o}
}
def "use sharedObject trustResolver"() {
@@ -12,27 +12,29 @@
*/
package org.springframework.security.config.http
import static org.mockito.Mockito.*
import static org.mockito.Matchers.*
import static org.mockito.Mockito.*
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import org.spockframework.compiler.model.WhenBlock;
import org.springframework.mock.web.MockFilterChain
import org.springframework.mock.web.MockHttpServletRequest
import org.springframework.mock.web.MockHttpServletResponse
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurerTests.CsrfTokenRepositoryConfig;
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurerTests.RequireCsrfProtectionMatcherConfig
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.access.AccessDeniedException
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.core.Authentication
import org.springframework.security.core.authority.AuthorityUtils
import org.springframework.security.core.context.SecurityContextImpl
import org.springframework.security.web.access.AccessDeniedHandler
import org.springframework.security.web.context.HttpRequestResponseHolder
import org.springframework.security.web.context.HttpSessionSecurityContextRepository
import org.springframework.security.web.csrf.CsrfFilter
import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.security.web.csrf.CsrfTokenRepository;
import org.springframework.security.web.csrf.DefaultCsrfToken;
import org.springframework.security.web.servlet.support.csrf.CsrfRequestDataValueProcessor
import org.springframework.security.web.csrf.CsrfToken
import org.springframework.security.web.csrf.CsrfTokenRepository
import org.springframework.security.web.csrf.DefaultCsrfToken
import org.springframework.security.web.util.matcher.RequestMatcher
import org.springframework.web.servlet.support.RequestDataValueProcessor;
import org.springframework.web.servlet.support.RequestDataValueProcessor
import spock.lang.Unroll
@@ -117,6 +119,7 @@ class CsrfConfigTests extends AbstractHttpConfigTests {
CsrfTokenRepository repo = appContext.getBean("repo",CsrfTokenRepository)
CsrfToken token = new DefaultCsrfToken("X-CSRF-TOKEN","_csrf", "abc")
when(repo.loadToken(any(HttpServletRequest))).thenReturn(token)
when(repo.generateToken(any(HttpServletRequest))).thenReturn(token)
request.setParameter(token.parameterName,token.token)
request.servletPath = "/some-url"
request.requestURI = "/some-url"
@@ -151,6 +154,7 @@ class CsrfConfigTests extends AbstractHttpConfigTests {
CsrfTokenRepository repo = appContext.getBean("repo",CsrfTokenRepository)
CsrfToken token = new DefaultCsrfToken("X-CSRF-TOKEN","_csrf", "abc")
when(repo.loadToken(any(HttpServletRequest))).thenReturn(token)
when(repo.generateToken(any(HttpServletRequest))).thenReturn(token)
request.setParameter(token.parameterName,token.token)
request.servletPath = "/some-url"
request.requestURI = "/some-url"
@@ -203,6 +207,7 @@ class CsrfConfigTests extends AbstractHttpConfigTests {
}
mockBean(RequestMatcher,'matcher')
createAppContext()
request.method = 'POST'
RequestMatcher matcher = appContext.getBean("matcher",RequestMatcher)
when:
when(matcher.matches(any(HttpServletRequest))).thenReturn(false)
@@ -249,6 +254,7 @@ class CsrfConfigTests extends AbstractHttpConfigTests {
CsrfTokenRepository repo = appContext.getBean("repo",CsrfTokenRepository)
CsrfToken token = new DefaultCsrfToken("X-CSRF-TOKEN","_csrf", "abc")
when(repo.loadToken(any(HttpServletRequest))).thenReturn(token)
when(repo.generateToken(any(HttpServletRequest))).thenReturn(token)
request.setParameter(token.parameterName,token.token)
request.method = "POST"
request.setParameter("j_username","user")
@@ -272,10 +278,43 @@ class CsrfConfigTests extends AbstractHttpConfigTests {
when(repo.loadToken(any(HttpServletRequest))).thenReturn(token)
request.setParameter(token.parameterName,token.token)
request.method = "POST"
request.requestURI = "/j_spring_security_logout"
request.servletPath = "/j_spring_security_logout"
when:
springSecurityFilterChain.doFilter(request,response,chain)
then:
verify(repo).saveToken(eq(null),any(HttpServletRequest), any(HttpServletResponse))
}
def "SEC-2495: csrf disables logout on GET"() {
setup:
httpAutoConfig {
'csrf'()
}
createAppContext()
login()
request.method = "GET"
request.requestURI = "/j_spring_security_logout"
when:
springSecurityFilterChain.doFilter(request,response,chain)
then:
getAuthentication(request) != null
}
def login(String username="user", String role="ROLE_USER") {
login(new UsernamePasswordAuthenticationToken(username, null, AuthorityUtils.createAuthorityList(role)))
}
def login(Authentication auth) {
HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository()
HttpRequestResponseHolder requestResponseHolder = new HttpRequestResponseHolder(request, response)
repo.loadContext(requestResponseHolder)
repo.saveContext(new SecurityContextImpl(authentication:auth), requestResponseHolder.request, requestResponseHolder.response)
}
def getAuthentication(HttpServletRequest request) {
HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository()
HttpRequestResponseHolder requestResponseHolder = new HttpRequestResponseHolder(request, response)
repo.loadContext(requestResponseHolder)?.authentication
}
}
@@ -0,0 +1,109 @@
package org.springframework.security.config.http
import org.springframework.mock.web.MockFilterChain
import org.springframework.mock.web.MockHttpServletRequest
import org.springframework.mock.web.MockHttpServletResponse
/**
*
* @author Luke Taylor
*/
class FormLoginBeanDefinitionParserTests extends AbstractHttpConfigTests {
def 'form-login default login page'() {
setup:
MockHttpServletRequest request = new MockHttpServletRequest(method:'GET',requestURI:'/spring_security_login')
MockHttpServletResponse response = new MockHttpServletResponse()
MockFilterChain chain = new MockFilterChain()
httpAutoConfig {
}
createAppContext()
when:
springSecurityFilterChain.doFilter(request,response,chain)
then:
response.getContentAsString() == """<html><head><title>Login Page</title></head><body onload='document.f.j_username.focus();'>
<h3>Login with Username and Password</h3><form name='f' action='/j_spring_security_check' method='POST'>
<table>
<tr><td>User:</td><td><input type='text' name='j_username' value=''></td></tr>
<tr><td>Password:</td><td><input type='password' name='j_password'/></td></tr>
<tr><td colspan='2'><input name="submit" type="submit" value="Login"/></td></tr>
</table>
</form></body></html>"""
}
def 'form-login default login page custom attributes'() {
setup:
MockHttpServletRequest request = new MockHttpServletRequest(method:'GET',requestURI:'/spring_security_login')
MockHttpServletResponse response = new MockHttpServletResponse()
MockFilterChain chain = new MockFilterChain()
httpAutoConfig {
'form-login'('login-processing-url':'/login_custom','username-parameter':'custom_user','password-parameter':'custom_password')
}
createAppContext()
when:
springSecurityFilterChain.doFilter(request,response,chain)
then:
response.getContentAsString() == """<html><head><title>Login Page</title></head><body onload='document.f.custom_user.focus();'>
<h3>Login with Username and Password</h3><form name='f' action='/login_custom' method='POST'>
<table>
<tr><td>User:</td><td><input type='text' name='custom_user' value=''></td></tr>
<tr><td>Password:</td><td><input type='password' name='custom_password'/></td></tr>
<tr><td colspan='2'><input name="submit" type="submit" value="Login"/></td></tr>
</table>
</form></body></html>"""
}
def 'openid-login default login page'() {
setup:
MockHttpServletRequest request = new MockHttpServletRequest(method:'GET',requestURI:'/spring_security_login')
MockHttpServletResponse response = new MockHttpServletResponse()
MockFilterChain chain = new MockFilterChain()
httpAutoConfig {
'openid-login'()
}
createAppContext()
when:
springSecurityFilterChain.doFilter(request,response,chain)
then:
response.getContentAsString() == """<html><head><title>Login Page</title></head><body onload='document.f.j_username.focus();'>
<h3>Login with Username and Password</h3><form name='f' action='/j_spring_security_check' method='POST'>
<table>
<tr><td>User:</td><td><input type='text' name='j_username' value=''></td></tr>
<tr><td>Password:</td><td><input type='password' name='j_password'/></td></tr>
<tr><td colspan='2'><input name="submit" type="submit" value="Login"/></td></tr>
</table>
</form><h3>Login with OpenID Identity</h3><form name='oidf' action='/j_spring_openid_security_check' method='POST'>
<table>
<tr><td>Identity:</td><td><input type='text' size='30' name='openid_identifier'/></td></tr>
<tr><td colspan='2'><input name="submit" type="submit" value="Login"/></td></tr>
</table>
</form></body></html>"""
}
def 'openid-login default login page custom attributes'() {
setup:
MockHttpServletRequest request = new MockHttpServletRequest(method:'GET',requestURI:'/spring_security_login')
MockHttpServletResponse response = new MockHttpServletResponse()
MockFilterChain chain = new MockFilterChain()
httpAutoConfig {
'openid-login'('login-processing-url':'/login_custom')
}
createAppContext()
when:
springSecurityFilterChain.doFilter(request,response,chain)
then:
response.getContentAsString() == """<html><head><title>Login Page</title></head><body onload='document.f.j_username.focus();'>
<h3>Login with Username and Password</h3><form name='f' action='/j_spring_security_check' method='POST'>
<table>
<tr><td>User:</td><td><input type='text' name='j_username' value=''></td></tr>
<tr><td>Password:</td><td><input type='password' name='j_password'/></td></tr>
<tr><td colspan='2'><input name="submit" type="submit" value="Login"/></td></tr>
</table>
</form><h3>Login with OpenID Identity</h3><form name='oidf' action='/login_custom' method='POST'>
<table>
<tr><td>Identity:</td><td><input type='text' size='30' name='openid_identifier'/></td></tr>
<tr><td colspan='2'><input name="submit" type="submit" value="Login"/></td></tr>
</table>
</form></body></html>"""
}
}
@@ -6,6 +6,8 @@ import org.springframework.security.web.access.ExceptionTranslationFilter
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.util.ReflectionUtils;
/**
*
@@ -102,4 +104,18 @@ class FormLoginConfigTests extends AbstractHttpConfigTests {
apf.usernameParameter == 'xname';
apf.passwordParameter == 'xpass'
}
def 'SEC-2455: http@login-processing-url'() {
when:
xml.http {
'form-login'('login-processing-url':'/authenticate')
}
createAppContext()
def apf = getFilter(UsernamePasswordAuthenticationFilter);
then:
apf.filterProcessesUrl == null // SEC-2455 setFilterProcessesUrl was not invoked
FieldUtils.getFieldValue(apf,'requiresAuthenticationRequestMatcher.filterProcessesUrl') == '/authenticate'
}
}
@@ -172,6 +172,26 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
assertHeaders(response, ['X-Frame-Options':'ALLOW-FROM https://example.com'])
}
def 'http headers frame-options ALLOW-FROM with whitelist strategy'() {
when:
httpAutoConfig {
'headers'() {
'frame-options'(policy : 'ALLOW-FROM', strategy: 'whitelist', value : 'https://example.com')
}
}
createAppContext()
def hf = getFilter(HeaderWriterFilter)
MockHttpServletResponse response = new MockHttpServletResponse()
def request = new MockHttpServletRequest()
request.setParameter("from", "https://example.com");
hf.doFilter(request, response, new MockFilterChain())
then:
assertHeaders(response, ['X-Frame-Options':'ALLOW-FROM https://example.com'])
}
def 'http headers header a=b'() {
when:
httpAutoConfig {
@@ -0,0 +1,25 @@
package org.springframework.security.config.http
import org.springframework.security.util.FieldUtils
import org.springframework.security.web.authentication.logout.LogoutFilter
/**
*
* @author Rob Winch
*/
class LogoutConfigTests extends AbstractHttpConfigTests {
def 'SEC-2455: logout@logout-url'() {
when:
httpAutoConfig {
'logout'('logout-url':'/logout')
}
createAppContext()
def lf = getFilter(LogoutFilter);
then:
lf.filterProcessesUrl == null // SEC-2455 setFilterProcessesUrl was not invoked
FieldUtils.getFieldValue(lf,'logoutRequestMatcher.filterProcessesUrl') == '/logout'
}
}
@@ -0,0 +1,34 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:org/springframework/security/config/sec-2508.xml" )
public class Sec2508Tests {
@Autowired
private AuthenticationEntryPoint ep;
@Test
public void loads() {}
}
@@ -0,0 +1,50 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.method.sec2499;
import org.junit.After;
import org.junit.Test;
import org.springframework.context.support.GenericXmlApplicationContext;
/**
*
* @author Rob Winch
*
*/
public class Sec2499Tests {
private GenericXmlApplicationContext parent;
private GenericXmlApplicationContext child;
@After
public void cleanup() {
if(parent != null) {
parent.close();
}
if(child != null) {
child.close();
}
}
@Test
public void methodExpressionHandlerInParentContextLoads() {
parent = new GenericXmlApplicationContext("org/springframework/security/config/method/sec2499/parent.xml");
child = new GenericXmlApplicationContext();
child.load("org/springframework/security/config/method/sec2499/child.xml");
child.setParent(parent);
child.refresh();
}
}
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<global-method-security pre-post-annotations="enabled">
<expression-handler ref="expressionHandler"/>
</global-method-security>
</b:beans>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="expressionHandler"
class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler"/>
</beans>
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint">
<constructor-arg>
<map>
<entry>
<key>
<bean class="org.springframework.security.web.util.AntPathRequestMatcher">
<constructor-arg value="/**"/>
</bean>
</key>
<bean class="org.springframework.security.web.authentication.Http403ForbiddenEntryPoint"/>
</entry>
</map>
</constructor-arg>
<property name="defaultEntryPoint">
<bean class="org.springframework.security.web.authentication.Http403ForbiddenEntryPoint"/>
</property>
</bean>
</beans>
+1
View File
@@ -16,6 +16,7 @@ Import-Template:
org.openid4java.*;version="${openid4javaRange}";resolution:=optional,
org.springframework.security.access.*;version="${secRange}",
org.springframework.security.authentication.*;version="${secRange}",
org.springframework.security.context.*;version="${secRange}";resolution:=optional,
org.springframework.security.core.*;version="${secRange}",
org.springframework.security.crypto.bcrypt.*;version="${secRange}",
org.springframework.security.crypto.password.*;version="${secRange}",
+7 -5
View File
@@ -8,16 +8,19 @@ configurations {
included
compile.extendsFrom included
testCompile.exclude group: 'org.mockito', module: 'mockito-all'
}
dependencies {
compile 'aopalliance:aopalliance:1.0',
compile springCoreDependency,
'aopalliance:aopalliance:1.0',
"org.springframework:spring-aop:$springVersion",
"org.springframework:spring-beans:$springVersion",
"org.springframework:spring-context:$springVersion",
"org.springframework:spring-expression:$springVersion"
optional "net.sf.ehcache:ehcache:$ehcacheVersion",
optional "net.sf.ehcache:ehcache-core:$ehcacheVersion",
'javax.annotation:jsr250-api:1.0',
"org.aspectj:aspectjrt:$aspectjVersion",
"org.springframework:spring-jdbc:$springVersion",
@@ -30,8 +33,7 @@ dependencies {
"org.slf4j:jcl-over-slf4j:$slf4jVersion",
powerMockDependencies
testRuntime "org.hsqldb:hsqldb:$hsqlVersion",
"cglib:cglib-nodep:$cglibVersion"
testRuntime "org.hsqldb:hsqldb:$hsqlVersion"
}
classes.doLast {
@@ -43,7 +45,7 @@ classes.doLast {
sourceJar.from cryptoProject.sourceSets.main.java
configure([test,spring4Test]) {
configure(project.tasks.withType(Test)) {
systemProperties['springSecurityVersion'] = version
systemProperties['springVersion'] = springVersion
}

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