1
0
mirror of synced 2026-07-11 13:50:35 +00:00

Compare commits

..

204 Commits

Author SHA1 Message Date
Luke Taylor d2263c855d [maven-release-plugin] copy for tag release_1_0_6 2007-12-24 17:13:05 +00:00
Luke Taylor 0a7d6225f7 Corrected tagBase url 2007-12-24 17:08:52 +00:00
Luke Taylor 75360d1358 Corrected tagBase url 2007-12-24 17:06:50 +00:00
Luke Taylor a84b405744 [maven-release-plugin] rollback the release of release_1_0_6 2007-12-24 16:54:36 +00:00
Luke Taylor 4c8f61ec90 [maven-release-plugin] prepare release release_1_0_6 2007-12-24 16:41:24 +00:00
Luke Taylor c2a13b0aa8 [maven-release-plugin] rollback the release of release_1_0_6 2007-12-24 16:32:46 +00:00
Luke Taylor ad45967cef [maven-release-plugin] prepare release release_1_0_6 2007-12-24 16:26:44 +00:00
Luke Taylor bdd78ced7f Corrected scm element 2007-12-24 16:15:11 +00:00
Luke Taylor 9174fb7746 Removed scm element from core and core-tiger 2007-12-24 16:10:34 +00:00
Luke Taylor b5ac6e42e3 Reinstated version as 1.0.6-SNAPSHOT after mvn release plugin failure 2007-12-24 16:07:24 +00:00
Luke Taylor e3247231d7 [maven-release-plugin] prepare release release_1_0_6 2007-12-24 16:00:00 +00:00
Luke Taylor ab022a5ed3 testing svn commit 2007-12-24 15:56:53 +00:00
Luke Taylor 18d8084744 Fix jetty plugin version at 6.1.6. 2007-12-24 14:51:50 +00:00
Luke Taylor 1216673deb Fix jetty plugin version at 6.1.6. 2007-12-24 14:48:45 +00:00
Luke Taylor 8e6e373a3b Removed unused import. 2007-12-07 16:20:31 +00:00
Luke Taylor 2b0ee23396 SEC-618: Move copyDetails method into ProviderManager and call it before checking with ConcurrentSessionController. 2007-12-07 16:17:59 +00:00
Luke Taylor 89cde2507d SEC-594: Set password on UserDetails object returned by BindAuthenticator. 2007-12-07 16:04:43 +00:00
Luke Taylor ac0e308354 Updated spring and acegi version numbers in petclinic tutorial file. 2007-12-03 23:15:29 +00:00
Ben Alex 724b9e23ba Remove OrderedUtils (was used for old namespace testing). 2007-12-03 05:05:30 +00:00
Ben Alex 99c5479f9a SEC-584: Require user to explicitly set SessionRegistry, so the implementation can benefit from application context services. 2007-12-02 03:00:26 +00:00
Ben Alex c3dc3a3a4f SEC-610: Reauthenticate even if AnonymousAuthenticationToken is present. 2007-12-02 02:15:18 +00:00
Ben Alex 859ce607cd Now using mvn eclipse:eclipse instead; do not check these back in. 2007-12-02 00:47:38 +00:00
Ben Alex d6fd9980bd Remove configuration from sandbox; this support exists in 2.0 trunk. 2007-12-01 23:52:52 +00:00
Luke Taylor 831981ac69 Changed forum description. 2007-11-20 16:50:12 +00:00
Scott Battaglia 7b981d3e84 SEC-592
implemented NullStatelessTicketCache and test cases and made it the default for CasAuthenticationProvider.
2007-11-07 21:55:59 +00:00
Luke Taylor 38a0bf7ca4 SEC-564: Documented checkout of 1.0.x branch and trunk separately. 2007-11-07 00:24:02 +00:00
Luke Taylor e243855822 SEC-557: Reinstated default AccessDeniedHandler (AccessDeniedHandlerImpl) which had been removed by mistake. 2007-09-19 16:41:06 +00:00
Luke Taylor e6e461d9a0 SEC-549: Merged fix from trunk (trim space from username). 2007-09-14 14:32:19 +00:00
Luke Taylor 0ff8662f12 SEC-556: Removed namespace meta-inf file from 1.0 branch. 2007-09-12 21:37:07 +00:00
Luke Taylor 0878d7d563 Changed release notes to include deployment in tomcat *and* Jetty (after jstl issues with tutorial). 2007-09-12 21:19:02 +00:00
Luke Taylor ae4b5907b9 Merged website style changes from trunk. 2007-09-12 21:12:29 +00:00
Luke Taylor 5da9a0c0c0 Removed unnecessary repository declarations. 2007-09-12 21:10:29 +00:00
Luke Taylor 95d5c61115 SEC-554: Added jstl jars to tutorial pom. 2007-09-12 21:04:17 +00:00
Ben Alex 98a91f5ac1 Corrections to use M2_REPO. 2007-09-06 05:53:33 +00:00
Luke Taylor 8d4b97f685 Updated poms post-release 1.0.5 2007-09-06 02:52:09 +00:00
Luke Taylor ac30552c42 Release 1.0.5 - additional poms 2007-09-06 01:59:22 +00:00
Luke Taylor c8b6111418 Release 1.0.5. 2007-09-06 01:52:53 +00:00
Luke Taylor 21c0c8e5f5 Removed ant build file. 2007-09-06 00:18:20 +00:00
Luke Taylor 293545eb4e Correct css file patch for different depths in site directory. 2007-09-06 00:13:11 +00:00
Luke Taylor e23d6fb29b Corrected link to sslhowto.txt 2007-09-06 00:03:46 +00:00
Luke Taylor b011acbfe6 Changed position of (min/max)depth options to find command to prevent warning on linux. 2007-09-05 23:55:26 +00:00
Luke Taylor 72cca2c483 Removed explicit spring-2.0.4 dependency and hard-coded acegi-security version. 2007-09-05 23:45:52 +00:00
Luke Taylor e8e3f93843 Switched core-tiger module to use standard project spring version. 2007-09-05 23:39:19 +00:00
Luke Taylor d752611acd Updated release process description to include sf ftp info. 2007-09-05 23:12:57 +00:00
Luke Taylor 6469c2f563 Updated release process description. 2007-09-05 23:03:17 +00:00
Luke Taylor de21cde132 Deleted unused build.xml and cvsignore files from contacts sample. 2007-09-05 22:20:55 +00:00
Luke Taylor ac9c516434 Configured project info plugin to provide selective reports (e.g. omitted continuous integration report). 2007-09-05 22:19:58 +00:00
Luke Taylor 343a2d786c Increase depth for html patch to reach files in sample modules. 2007-09-05 15:56:49 +00:00
Luke Taylor 590b2e859f Corrected site directory usage. 2007-09-05 15:55:34 +00:00
Luke Taylor 74d7d8ebad Added creation of archives to release script. 2007-09-05 15:54:19 +00:00
Luke Taylor 09b8575c30 Disable agilejava repo for snapshots. 2007-09-05 15:53:44 +00:00
Luke Taylor 545a3263f4 Changed artifact Id for consistency with tutorial. 2007-09-05 15:52:30 +00:00
Luke Taylor 2532518ffd Minor text changes to readme. 2007-09-05 15:46:25 +00:00
Luke Taylor 5d8076056e Added readme explaining that contacts is now a single application. 2007-09-05 15:44:15 +00:00
Luke Taylor 66b47f5f67 Added note that JDK 1.5 is required to run Jetty plugin. 2007-09-05 14:59:37 +00:00
Luke Taylor c2c2fb24be Corrected link to ssl howto in site.xml 2007-09-05 12:09:40 +00:00
Luke Taylor 58bf2ffbc8 Corrected link to ssl howto in site.xml 2007-09-05 12:08:49 +00:00
Luke Taylor 8cd7a03fac SEC-540: removed doc project module. 2007-09-05 11:40:31 +00:00
Luke Taylor 31360ecce6 SEC-540: Moved announcements and upgrade directories from doc module to src/site. 2007-09-05 11:32:20 +00:00
Luke Taylor 699f5389f4 removed description of other versions of contacts app since it now produces a single war file. 2007-09-05 11:27:04 +00:00
Luke Taylor e691f3630c Added padding to sourceforge image. 2007-09-05 11:17:09 +00:00
Luke Taylor 248e9c85fd Cropped logo image. 2007-09-05 11:10:07 +00:00
Luke Taylor bf433bb5a7 Moved contacts ssl howto.txt so that it is picked up by maven 2 site generation. 2007-09-05 01:21:42 +00:00
Luke Taylor e971c15390 Added interface21 link. 2007-09-05 00:57:43 +00:00
Luke Taylor 289540ae5c More improvements to release script. 2007-09-05 00:35:41 +00:00
Luke Taylor 10ddd576f6 SEC-540,SEC-541. Forgot to add stylesheets. 2007-09-05 00:00:27 +00:00
Luke Taylor 6988be2ef2 SEC-540,SEC-541. Reorganized to get docbook to generate prior to the site with images correct for both pdf and html. Borrowed nuxeo project stylesheets. 2007-09-04 23:59:25 +00:00
Luke Taylor 0ee0084969 Replaced <code> tags in index.apt file. 2007-09-04 16:20:31 +00:00
Luke Taylor 7cab6197a2 Changed xargs use to more portable syntax 2007-09-04 15:14:54 +00:00
Luke Taylor d905160b5b Extra patching of generated maven site files. 2007-09-04 01:12:31 +00:00
Luke Taylor 3de8745494 Commented out (another) failing captcha test whose behaviour varies with speed of the build server (makes assumptions about the interval within which certain lines of code are executed). 2007-09-04 01:06:58 +00:00
Luke Taylor 6289503643 Commented out failing captcha test whose behaviour varies with speed of the build server (makes assumptions about the interval within which certain lines of code are executed). 2007-09-03 23:33:13 +00:00
Luke Taylor 3612674644 Updated name elements in samples and tutorial contents. 2007-09-03 22:28:54 +00:00
Luke Taylor d7b7d36314 Updated jstl dependency information to use correct group name. 2007-09-03 22:27:52 +00:00
Luke Taylor db13131a07 Deleted original properties files from contacts app resources directory. 2007-09-03 22:26:30 +00:00
Luke Taylor 01610bdd94 Moved properties files into WEB-INF/classes directory in contacts app. Jetty plugin attemps to load the log4j file from there when using mvn jetty:run. 2007-09-03 22:24:59 +00:00
Luke Taylor 944fcf1484 Changed reports link to point directly to reports page in core module. 2007-09-03 22:16:18 +00:00
Luke Taylor f76e762641 Change L+F of navcolumn on maven site. 2007-09-03 15:59:12 +00:00
Luke Taylor 22ec394ea4 Sandbox pom changes to allow build with spring 1.2.9. Commented out 2.0 config module 2007-09-03 15:54:30 +00:00
Luke Taylor 34527c3305 Changed spring version to 1.2.9 and modified dependencies to get build to work with this version. Corrected some javadoc links. 2007-09-03 15:47:39 +00:00
Luke Taylor 4cce316f89 Added support forum to menu. 2007-09-03 15:45:03 +00:00
Luke Taylor a98405eb37 Removed project resources section. 2007-09-03 15:42:36 +00:00
Luke Taylor d14fb1e87d Added code to modify the module html files to make them point to the root css file. 2007-09-03 09:55:08 +00:00
Luke Taylor e09b145803 Correct version name of site plugin. 2007-09-02 21:36:11 +00:00
Luke Taylor 7207dd6f19 Created a release script.
Use a local directory for deploying the site, since it seems impossible to get an accurate copy of the site otherwise using maven 2.

Revert to cobertura 2.0 plugin as 2.1 is broken (gives 100% coverage for everything).
2007-09-02 18:14:02 +00:00
Luke Taylor 2ff9a5507e Minor changes to headings to reduce standard maven look. 2007-09-02 18:12:00 +00:00
Luke Taylor e4f5bd0a09 Removed incorect text. 2007-09-02 18:11:17 +00:00
Luke Taylor 15ee5b2364 SEC-540,SEC-541: Changes for maven 2 site generation and use of docbkx. 2007-09-02 13:22:24 +00:00
Luke Taylor 10b3ab6f8a Corrected tag name in site.xml 2007-09-02 10:46:17 +00:00
Luke Taylor f9e16d6ee3 SEC-540,SEC-541: Changes for maven 2 site generation and use of docbkx. 2007-09-02 10:00:44 +00:00
Luke Taylor 4e452046ec Comment out System.out.println 2007-09-01 14:59:41 +00:00
Ray Krueger edd7bbeceb Removed repeated downcasting of ServletRequest and ServletResponse 2007-09-01 14:43:09 +00:00
Luke Taylor b2799985f2 SEC-398: Added patch which uses response wrapper to set context in session on redirect or error. 2007-08-31 20:39:33 +00:00
Luke Taylor 219b865c01 SEC-544: Added German localization messages from Andreas Senft. 2007-08-31 12:15:13 +00:00
Luke Taylor 976fdb0371 Rolled back changes for SEC-441. 2007-08-30 22:28:04 +00:00
Luke Taylor 0d8be5012d Corrected comment. 2007-08-30 22:27:23 +00:00
Luke Taylor c021bf4682 SEC-542: Made SessionInformation serializable. Also remove unused default constructor. 2007-08-30 21:38:07 +00:00
Luke Taylor 0adf0d6f1c SEC-529: Added French translation of messages from Laurent Pireyn 2007-08-30 21:27:49 +00:00
Luke Taylor bc411c7c3b SEC-457: Added Czech translation of messages from Jan Novotný 2007-08-30 21:20:19 +00:00
Luke Taylor ea61964f56 SEC-483: Fix. Make getGroupSearchBase protected. 2007-08-30 21:15:14 +00:00
Luke Taylor 0c4916ee98 SEC-427: Fix. Added NullAuthoritiesPopulator and extra constructor. 2007-08-30 21:12:16 +00:00
Luke Taylor 301626fd6e SEC-346: Fix. Added suggested change. Also some minor tidying up of comments etc. 2007-08-30 20:55:49 +00:00
Luke Taylor 8cb836c6cf SEC-441: Fix. Added suggested changes. 2007-08-30 19:42:35 +00:00
Luke Taylor 2e8d16c538 SEC-484: Multithreaded tests for SessionRegistryImpl. 2007-08-30 19:26:24 +00:00
Luke Taylor ad43d433b4 SEC-484: Fix for NPE concurreny issue. Also reinstated synchronized on registerNewSession (had removed it for testing). 2007-08-30 19:04:18 +00:00
Luke Taylor aa4ee54f86 Added logging to SessionRegistryImpl. 2007-08-30 18:22:40 +00:00
Luke Taylor 7fcdd4a6ff More tidying... 2007-08-30 11:31:36 +00:00
Luke Taylor 510cd5050f Tidied up SessionRegistryImpl and rolled back reformatting of its test class to incorrect width. 2007-08-30 11:21:28 +00:00
Luke Taylor 6c169d9acf SEC-508: Added CDATA sections to multi-line text values in tutorial and contacts sample context files to prevent eclipse reforatting bug from messing them up. 2007-08-29 12:12:45 +00:00
Luke Taylor e87956358f Added missing "'" to login page in tutorial app. 2007-08-29 12:06:25 +00:00
Luke Taylor 5f993e5627 SEC-534: Refactored JaasAuthenticationProvider to use ApplicationPublisherAware rather than ApplicationContextAware. 2007-08-29 11:51:02 +00:00
Luke Taylor 1467527c0a SEC-538: Deleted maven 1 files. 2007-08-29 11:00:28 +00:00
Luke Taylor 5b7ed79b6a SEC-539: Reformatted "divider" comments (//~ Methods=== etc). Simplified boolean expression in afterPropertiesSet. 2007-08-28 23:19:06 +00:00
Luke Taylor d7cef1ba31 SEC-539: Moved SecurityContextHolder.setContext() call into the try {} block to emphasize that it is only set for the duration of chain.doFilter() and immediately cleared afterwards. Changed the debug messages about setting the context, since it has not strictly taken place when they are logged. 2007-08-28 23:11:58 +00:00
Luke Taylor 47c5a6d43f SEC-539: Renamed extractSecurityContextFromSession to readSecurityContextFromSession to emphasize that it doesn't actually modify anything (the context is still stored in the session). 2007-08-28 22:43:13 +00:00
Luke Taylor f7a6129657 SEC-539: Removed unnecessary check for a null request object. Removed unnecessary catch/rethrow of IOException and ServletException from try/finally around chain.doFilter. 2007-08-28 22:40:56 +00:00
Luke Taylor d1be9f9980 SEC-539: Refactored so that SecurityContextHolder.setContext() is called in exactly one place. Moved setting of httpSession = null to point immediately after its last use. 2007-08-28 22:38:55 +00:00
Luke Taylor 3dd0716611 SEC-539: Altered storeSecurityContextInSession to take the SecurityContext as a parameter rather than calling SecurityContextHolder.getContext(). This allows SecurityContextHolder.clearContext() to be called immediately after reading the context in the finally block of doFilter(). 2007-08-28 21:58:30 +00:00
Luke Taylor fa63d8ecfb SEC-539: Refactored if (httpSession == null) block in storeSecurityContextInSession() 2007-08-28 21:25:17 +00:00
Luke Taylor ce3eb599ed SEC-539: Renamed populateSecurityContextFromSession to extractSecurityContextFromSession and removed the side-effect of setting SecurityContextHolder. It now returns the context found in the session (or null) and SecurityContextHolder.setContext() is called in a single place in doFilter(). 2007-08-28 21:11:48 +00:00
Luke Taylor ba88214d1d SEC-539: Refactored populateSecurityContextFromSession() to reduce nested blocks and clarify logic. 2007-08-28 20:16:19 +00:00
Luke Taylor 27ef2caf45 SEC-539: Removed filterApplied boolean. 2007-08-28 19:56:33 +00:00
Luke Taylor e8d11f28f2 SEC-539: Extracted storeSecurityContextInSession() method. 2007-08-28 19:54:24 +00:00
Luke Taylor bcf69cbe3d SEC-539: Extracted populateSecurityContextFromSession() method. 2007-08-28 19:16:37 +00:00
Luke Taylor 6651a240de Replaced massive if/else with guard clause to reduce nesting. Moved declaration of filterApplied boolean to where it is actually set. It is only used when removing the attribute from the request at the end of the invocation, so should probably not be needed at all. request.removeAttribute() can be called regardless of whether the attribute is set or not. 2007-08-28 18:26:04 +00:00
Luke Taylor 6fe00b3433 SEC-501: Fix. Convert secure url paths to lower case if convertUrlToLowercaseBeforeComparison is true.
Also removed unnecessary assertions from PathBasedFilterDefinitionMapTests.
2007-08-28 16:53:05 +00:00
Luke Taylor 036ea034ac SEC-521: Updated svn URLs to match recent repository restructuring. 2007-08-28 15:31:36 +00:00
Luke Taylor 4ba77fa736 SEC-450: Added group subtree to LDAP test server and extra tests for DefaultLdapAuthoritiesPopulator to make sure searchSubtree parameter works as expected. 2007-08-28 15:26:59 +00:00
Luke Taylor e189bc685f SEC-408: Fix. Provide getter for filterProcessesUrl. 2007-08-28 11:37:05 +00:00
Luke Taylor c8077c5e87 SEC-506: Fix as suggested by reporter. Split the disgest header string ignoring separating commas which occur between quotes. 2007-08-28 00:31:30 +00:00
Luke Taylor 3f123e1478 SEC-518: Fix. "Cache" in EhCache is a class, so change the APIs to use the interface it implements (Ehcache). 2007-08-27 23:41:59 +00:00
Luke Taylor 87d6b8dedd SEC-412: Fix. Added extra constructor to UsernameNotFoundException allow use of extraInformation property of parent class. 2007-08-27 23:22:48 +00:00
Luke Taylor f47ccd81a6 SEC-487: Added documentation on use of #NONE# in FilterChainProxy. Also changed doc version to 1.0.5. 2007-08-27 23:05:16 +00:00
Luke Taylor dda88e3931 SEC-502: Fix. Use a Map instead of HashMap in the API. Also some minor tidying of test class. 2007-08-27 17:21:16 +00:00
Luke Taylor 57f3d268a1 SEC-519: Fix. Changed notNull() assertion for "key" parameter to hasText() to prevent the use of empty keys. 2007-08-27 17:17:25 +00:00
Luke Taylor 1c72b7989e Fix for SEC-522. Strip query parameters from logout URL before doing comparison with filterProcessesUrl. 2007-08-27 17:14:23 +00:00
Luke Taylor 82599a72ba Reformatted LogoutFilter. 2007-08-27 16:56:33 +00:00
Luke Taylor f8689b18b2 SEC-526: Fixed. Support for different case prefixes ({SHA}, {sha} etc). 2007-08-27 16:23:14 +00:00
Luke Taylor 0425d3b638 Rolled back unnecessary changes (whitespace, imports etc) for SEC-398 to make actual change from revision 1858 clearer. 2007-08-27 13:29:39 +00:00
Luke Taylor ed944fa537 SEC-514: Re-enable contact sample in maven build. 2007-08-27 12:35:23 +00:00
Luke Taylor 6a36ae7a0d SEC-509: removed clirr plugin declaration from maven build.
Also removed regexp javadoc link as it doesn't seem to be a project dependency any more.
2007-08-27 11:49:11 +00:00
Luke Taylor c682a79e46 SEC-505: Fixed. Minor corrections to docbook source. 2007-08-27 11:44:11 +00:00
Luke Taylor 709dba101c SEC-498: Correct name of AfterInvocationProviderManager 2007-08-27 00:39:14 +00:00
Luke Taylor 70875a3c70 SEC-523: Made sentence about where GrantedAuthority objects come from a bit clearer. 2007-08-27 00:28:17 +00:00
Luke Taylor cbc74de7c6 Removed old LDAP code from sandbox and adjusted dependencies accordingly. 2007-08-26 23:07:34 +00:00
Mark St. Godard 5474b3a78c SEC-279 - Deleting Contacts Tiger sample project 2007-08-25 23:16:22 +00:00
Ben Alex 93b303e343 Support Spring LDAP. 2007-08-25 00:16:12 +00:00
Ben Alex db3024f9a4 SEC-271: Revert Ordered and ApplicationContextAware usage at this time, due to release of 1.0.. 2007-08-25 00:15:30 +00:00
Ben Alex 7a5c1ee328 Rename to spring-security. This is only a temporary commit, as in the future Maven 2 will be used and this file will be removed from Subversion. 2007-08-24 14:36:59 +00:00
Ben Alex 8b6c592180 Finalization of repository restructure. 2007-08-24 14:28:00 +00:00
Vishal Puri 2b4d8a6378 Removed print statement 2007-08-22 04:48:04 +00:00
Luke Taylor 3fbc7beb88 SEC-251: Document use of {1} parameter in javadoc for DefaultLdapAuthoritiesPopulator. 2007-08-17 15:45:57 +00:00
Luke Taylor fd0d4cd8b0 SEC-521: Fixed sourceforge svn URLs. 2007-08-10 18:34:38 +00:00
John Lewis 8396f04ae6 implemented unit tests for portlet support 2007-08-04 17:38:51 +00:00
John Lewis 0efdd5d2bf added javadoc package.html files for portlet classes 2007-07-27 04:33:56 +00:00
John Lewis f70cba5d0e added PortletProcessingFilterEntryPoint for accessing servlet resources via portlet authentication 2007-07-27 00:54:54 +00:00
Vishal Puri bc30b903f8 SEC-398: Lazy update of 'filterApplied' to true 2007-07-25 05:34:40 +00:00
Luke Taylor 99cc55b94a Minor code style corrections. 2007-07-24 18:23:35 +00:00
Luke Taylor 156965b370 SEC-181: Remove acegifier application. 2007-07-24 18:20:22 +00:00
Luke Taylor ea42164af2 Added jetty plugin to tutorial app pom.xml. 2007-07-24 18:12:09 +00:00
Luke Taylor 5d64b86875 Removed user cache from tutorial app context, as it's session -based. 2007-07-24 18:11:32 +00:00
Luke Taylor fe4bbe0fbf SEC-514: Refactoring contacts sample into single webapp. 2007-07-24 17:46:43 +00:00
Luke Taylor a499e74102 SEC-449: Add spring-ldap dependency to pom.xml. 2007-07-24 17:23:47 +00:00
Luke Taylor b646a06443 Fix for SEC-512. Removed unnecessary context creation. 2007-07-24 17:01:36 +00:00
Luke Taylor aea1148ffb Fix broken test caused by null application context in AbtractAccessDecisionManager when auto-detection of voters is called. 2007-07-24 16:48:49 +00:00
Vishal Puri c5cc42e16c made two instance variables protected for RBA solution 2007-07-23 07:59:28 +00:00
Vishal Puri 5ea8232f84 SEC-484: fixed concurrency issue 2007-07-23 07:58:31 +00:00
John Lewis e70f01c260 minor tweaks to portlet support 2007-07-21 00:58:21 +00:00
Vishal Puri 918f7ca008 SEC-271: added method authoriztion BeanDefinition parser 2007-07-06 13:37:18 +00:00
Vishal Puri 0e46e5307c SEC-271: added Ordered interface to AcessDecisionVoters 2007-07-06 13:34:43 +00:00
Vishal Puri 02cf570be7 SEC-271: added AuthorizationManagerBeanDefinitionParser 2007-07-05 02:16:13 +00:00
Vishal Puri 35c6aea8e8 SEC-271: added AuthorizationManagerBeanDefinitionParser 2007-07-05 02:15:31 +00:00
Vishal Puri 97a568c078 SEC-271: Impemented FilterSecurityInovation parser for 'authorization-http-url' tag 2007-07-04 05:04:26 +00:00
Vishal Puri b1a39fe1d1 SEC-271: Impemented FilterSecurityInovation parser for 'authorization-http-url' tag 2007-07-04 05:03:46 +00:00
Vishal Puri c658efbc69 SEC-271: added ldap authentication parser 2007-07-02 07:54:59 +00:00
Ray Krueger 1dfc2baea2 OpenId4Java support
OpenIdAuthenticationProcessingFilter to replace OpenIDLoginInititationServlet
2007-07-02 03:44:07 +00:00
Vishal Puri ef38844a6d Improved comments and made TokenBasedRememberMeServices modular to support subclasses 2007-06-27 08:33:37 +00:00
Ray Krueger 61d44954ee Changed parent reference to 1.0.5-SNAPSHOT 2007-06-19 13:13:58 +00:00
Ray Krueger 683fc597d2 Changed parent reference to 1.0.5-SNAPSHOT 2007-06-19 13:10:42 +00:00
Ray Krueger 0159b617cf Refactored the failureUrl lookup into a protected method to allow customization 2007-06-19 13:09:57 +00:00
Vishal Puri e0956920c7 SEC-271: copied Bank* unit test and relevant classes to test @Secured annotation as a part of autoconfig tag work 2007-06-19 04:15:25 +00:00
Vishal Puri 3cdce8662e SEC-271: added jdk 1.5 support 2007-06-19 04:12:05 +00:00
Vishal Puri b2c30277f4 SEC-271: work on security:autoconfig 2007-06-19 04:08:19 +00:00
Vishal Puri 6e82a9bdfc SEC-271: Removed unnecessary comments 2007-06-14 11:19:52 +00:00
Vishal Puri 4b4807c138 SEC-271: Removed unnecessary AuthenticationProcessingFilterDependenciesConfigurer 2007-06-14 11:18:50 +00:00
Vishal Puri 207e8b932e SEC-271: implemented auto creation of AuthenticationProcessingFilter and it's dependencies 2007-06-14 11:17:05 +00:00
Vishal Puri 23024b20ad SEC-271: Removed AuthenticationRepositoryDependenciesConfigurer as the introspection of applicationcontext for collaborators is implemented in the bean itself 2007-06-14 06:27:32 +00:00
Vishal Puri 2987b62893 SEC-271: autoconfig work 2007-06-14 04:51:16 +00:00
Vishal Puri 1ee2a26e8f SEC-271: removed RemeberMeServicesDependenciesConfigurer as autodetection of dependencies is handled in TokenBasedRememberMeServices 2007-06-08 01:57:54 +00:00
Vishal Puri 5249befa25 SEC-271: tests for RememberMeServicesBeanDefinitionParser 2007-06-08 01:51:59 +00:00
Vishal Puri 6c22fea917 SEC-271: removed registration of BFPP(RemeberMeServicesDependenciesConfigurer) as autodetection of dependencies is now handled in TokenBasedRememberMeServices 2007-06-08 01:50:35 +00:00
Vishal Puri 10c2294aad SEC-271: removed import of spring-util.xsd as it is not required 2007-06-08 01:48:11 +00:00
Vishal Puri 66d7f754c1 SEC-271: changed pom versions, added log4j.properties in test classpath, added another junit test in ExceptionTranslationParserTests 2007-06-07 02:21:52 +00:00
Vishal Puri 1665e21873 SEC-271: Added version tag 2007-06-07 02:13:50 +00:00
Vishal Puri a0cb72cc3c SEC-271: Removed the registeration of BFPP(AccessDeniedHandlerLocator) and improved comments 2007-06-07 02:12:29 +00:00
Vishal Puri 4d783d7375 SEC-271: Deleted AccessDeniedHandlerBeanDefinitionLocator as the autodetection functionality is implemented in ExceptionTranslationfilter 2007-06-07 02:11:12 +00:00
Vishal Puri 917030f0a3 SEC-488: Added maven.src.dir property to point to src/main because of changed m2 directory structure 2007-06-06 03:00:13 +00:00
Vishal Puri e9e89b835d Added maven.src.dir property to point to src/main because of changed m2 directory structure 2007-06-06 02:49:44 +00:00
John Lewis 82b22563e5 initial checking of JSR-168 Portlet support in to sandbox 2007-06-04 08:01:11 +00:00
Carlos Sanchez 165d2c0122 [maven-release-plugin] prepare for next development iteration 2007-06-02 21:28:53 +00:00
Carlos Sanchez 942b5d7345 [maven-release-plugin] prepare release acegi-security-1.0.4-maven2 2007-06-02 21:21:51 +00:00
Carlos Sanchez e047ce6eba Fix typo 2007-06-02 21:15:18 +00:00
Carlos Sanchez e8e4f9c297 comment out snapshot plugin for release 2007-06-02 21:13:50 +00:00
Ben Alex 3f9988531a Add a few more entries. 2007-05-29 01:33:23 +00:00
491 changed files with 9333 additions and 23926 deletions
-86
View File
@@ -1,86 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="core/src/main/java"/>
<classpathentry kind="src" path="core/src/main/resources"/>
<classpathentry kind="src" path="core/src/test/java"/>
<classpathentry kind="src" path="core/src/test/resources"/>
<classpathentry kind="src" path="samples/dms/src/test/java"/>
<classpathentry kind="src" path="samples/dms/src/main/resources"/>
<classpathentry kind="src" path="samples/dms/src/main/java"/>
<classpathentry kind="src" path="samples/dms/src/test/resources"/>
<classpathentry kind="src" path="samples/contacts/src/main/java"/>
<classpathentry kind="src" path="samples/contacts/src/main/resources"/>
<classpathentry kind="src" path="samples/contacts/src/test/java"/>
<classpathentry kind="src" path="samples/contacts/src/test/resources"/>
<classpathentry kind="src" path="samples/acegifier/src/main/java"/>
<classpathentry kind="src" path="samples/acegifier/src/main/resources"/>
<classpathentry kind="src" path="samples/acegifier/src/test/java"/>
<classpathentry kind="src" path="samples/acegifier/src/test/resources"/>
<classpathentry kind="src" path="samples/attributes/src/main/java"/>
<classpathentry kind="src" path="samples/attributes/src/main/resources"/>
<classpathentry kind="src" path="samples/attributes/src/test/java"/>
<classpathentry kind="src" path="adapters/cas/src/test/resources"/>
<classpathentry kind="src" path="adapters/cas/src/main/resources"/>
<classpathentry kind="src" path="adapters/cas/src/main/java"/>
<classpathentry kind="src" path="adapters/cas/src/test/java"/>
<classpathentry kind="src" path="adapters/catalina/src/main/java"/>
<classpathentry kind="src" path="adapters/catalina/src/main/resources"/>
<classpathentry kind="src" path="adapters/catalina/src/test/java"/>
<classpathentry kind="src" path="adapters/jboss/src/main/java"/>
<classpathentry kind="src" path="adapters/jboss/src/test/java"/>
<classpathentry kind="src" path="adapters/jetty/src/main/java"/>
<classpathentry kind="src" path="adapters/jetty/src/test/java"/>
<classpathentry kind="src" path="adapters/resin/src/main/java"/>
<classpathentry kind="src" path="adapters/resin/src/test/java"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="var" path="MAVEN_REPO/com.caucho/jars/resin-3.0.9.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/org.springframework/jars/spring-1.2.9.jar" sourcepath="/MAVEN_REPO/org.springframework/src/spring-1.2.9-sources.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/org.springframework/jars/spring-mock-1.2.9.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/aopalliance/jars/aopalliance-1.0.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/aspectj/jars/aspectjrt-1.2.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/cas/jars/casclient-2.0.11.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/cas/jars/cas-2.0.12.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/commons-codec/jars/commons-codec-1.3.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/commons-collections/jars/commons-collections-3.1.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/commons-logging/jars/commons-logging-1.0.4.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/hsqldb/jars/hsqldb-1.8.0.4.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/oro/jars/oro-2.0.8.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/javax.servlet/jars/servlet-api-2.4.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/tomcat/jars/catalina-4.1.9.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/jetty/jars/org.mortbay.jetty-4.2.22.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/jboss/jars/jboss-common-3.2.3.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/jboss/jars/jbosssx-3.2.3.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/junit/jars/junit-3.8.1.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/net.sf.ehcache/jars/ehcache-1.2.4.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/javax.servlet/jars/jsp-api-2.0.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/hibernate/jars/hibernate-3.0.3.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/commons-beanutils/jars/commons-beanutils-1.6.1.jar" sourcepath="DIST_BASE/commons-beanutils-1.6.1-src/src/java"/>
<classpathentry kind="src" path="samples/contacts-tiger/src/main/java"/>
<classpathentry kind="src" path="core-tiger/src/main/java"/>
<classpathentry kind="src" path="core-tiger/src/main/resources"/>
<classpathentry kind="src" path="core-tiger/src/test/java"/>
<classpathentry kind="src" path="core-tiger/src/test/resources"/>
<classpathentry kind="src" path="samples/annotations/src/main/java"/>
<classpathentry kind="src" path="samples/annotations/src/main/resources"/>
<classpathentry kind="src" path="samples/annotations/src/test/java"/>
<classpathentry kind="var" path="MAVEN_REPO/org.samba.jcifs/jars/jcifs-1.2.6.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/dom4j/jars/dom4j-1.6.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/xerces/jars/xercesImpl-2.6.2.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/jmock/jars/jmock-1.0.1.jar" sourcepath="MAVEN_REPO/jmock/distributions/jmock-1.0.1-src.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/jdbm/jars/jdbm-1.0.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/regexp/jars/regexp-1.2.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/org.slf4j/jars/slf4j-log4j12-1.0-rc5.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/org.apache.directory.server/jars/apacheds-core-1.0.0.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/org.apache.directory.server/jars/apacheds-core-shared-1.0.0.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/org.apache.directory.shared/jars/shared-asn1-0.9.5.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/org.apache.directory.shared/jars/shared-ldap-0.9.5.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/antlr/jars/antlr-2.7.2.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/ldapsdk/jars/ldapsdk-4.1.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/cas/jars/cas-server-3.0.4.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/net.sourceforge.retroweaver/jars/retroweaver-1.0fcs.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/taglibs/jars/standard-1.0.6.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/commons-attributes/jars/commons-attributes-api-2.1.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/log4j/jars/log4j-1.2.9.jar"/>
<classpathentry kind="var" path="M2_REPO/postgresql/postgresql/8.1-407.jdbc3/postgresql-8.1-407.jdbc3.jar"/>
<classpathentry kind="output" path="target/eclipseclasses"/>
</classpath>
-17
View File
@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>acegisecurity</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
-39
View File
@@ -1,39 +0,0 @@
<!--
* ========================================================================
*
* Copyright 2004 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ========================================================================
-->
<project
xmlns:j="jelly:core"
xmlns:ant="jelly:ant"
xmlns:util="jelly:util"
xmlns:maven="jelly:maven"
>
<postGoal name="jar:jar">
<j:if test="${context.getVariable('signature.alias') != null}">
<echo>signature.alias defined; signing JAR(s)...</echo>
<ant:signjar lazy="true" alias="${signature.alias}" storepass="${signature.storepass}" keystore="${signature.keystore}">
<fileset dir="${maven.build.dir}">
<include name="*.jar"/>
</fileset>
</ant:signjar>
</j:if>
</postGoal>
</project>
+6 -2
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.acegisecurity</groupId>
<artifactId>acegi-security-adapters</artifactId>
<version>1.0.4-SNAPSHOT</version>
<version>1.0.6</version>
</parent>
<artifactId>acegi-security-cas</artifactId>
<name>Acegi Security System for Spring - CAS adapter</name>
@@ -18,5 +18,9 @@
<artifactId>cas</artifactId>
<version>2.0.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
</dependencies>
</project>
</project>
-43
View File
@@ -1,43 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project>
<extend>${basedir}/../project.xml</extend>
<pomVersion>3</pomVersion>
<artifactId>acegi-security-cas</artifactId>
<name>Acegi Security System for Spring - CAS adapter</name>
<siteDirectory>/home/groups/a/ac/acegisecurity/htdocs/multiproject/acegi-security-cas</siteDirectory>
<repository>
<connection>scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity</connection>
<developerConnection>scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity</developerConnection>
<url>http://acegisecurity.svn.sourceforge.net/viewcvs.cgi/acegisecurity/trunk/acegisecurity/adapters/cas/</url>
</repository>
<dependencies>
<dependency>
<groupId>cas</groupId>
<artifactId>cas</artifactId>
<version>2.0.12</version>
<type>jar</type>
<url>http://www.yale.edu/tp/cas</url>
</dependency>
<dependency>
<groupId>cas</groupId>
<artifactId>cas-server</artifactId>
<version>3.0.4</version>
<type>jar</type>
<url>http://www.ja-sig.org/products/cas/</url>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>${basedir}/../../</directory>
<targetPath>META-INF</targetPath>
<includes>
<include>notice.txt</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>
+11
View File
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<project name="Acegi Security CAS Adapter">
<body>
<menu ref="parent"/>
<menu ref="reports"/>
</body>
</project>
-39
View File
@@ -1,39 +0,0 @@
<!--
* ========================================================================
*
* Copyright 2004 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ========================================================================
-->
<project
xmlns:j="jelly:core"
xmlns:ant="jelly:ant"
xmlns:util="jelly:util"
xmlns:maven="jelly:maven"
>
<postGoal name="jar:jar">
<j:if test="${context.getVariable('signature.alias') != null}">
<echo>signature.alias defined; signing JAR(s)...</echo>
<ant:signjar lazy="true" alias="${signature.alias}" storepass="${signature.storepass}" keystore="${signature.keystore}">
<fileset dir="${maven.build.dir}">
<include name="*.jar"/>
</fileset>
</ant:signjar>
</j:if>
</postGoal>
</project>
+2 -2
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.acegisecurity</groupId>
<artifactId>acegi-security-adapters</artifactId>
<version>1.0.4-SNAPSHOT</version>
<version>1.0.6</version>
</parent>
<artifactId>acegi-security-catalina</artifactId>
<name>Acegi Security System for Spring - Catalina adapter</name>
@@ -14,4 +14,4 @@
<version>4.1.9</version>
</dependency>
</dependencies>
</project>
</project>
-62
View File
@@ -1,62 +0,0 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<project>
<extend>${basedir}/../project.xml</extend>
<pomVersion>3</pomVersion>
<artifactId>acegi-security-catalina</artifactId>
<name>Acegi Security System for Spring - Catalina adapter</name>
<siteDirectory>/home/groups/a/ac/acegisecurity/htdocs/multiproject/acegi-security-catalina</siteDirectory>
<repository>
<connection>scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity</connection>
<developerConnection>scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity</developerConnection>
<url>http://acegisecurity.svn.sourceforge.net/viewcvs.cgi/acegisecurity/trunk/acegisecurity/adapters/catalina/</url>
</repository>
<dependencies>
<dependency>
<groupId>tomcat</groupId>
<artifactId>catalina</artifactId>
<version>4.1.9</version>
<type>jar</type>
</dependency>
</dependencies>
<build>
<unitTest>
<includes>
<include>**/*Tests.java</include>
</includes>
<resources>
<!-- <resource>
<directory>${basedir}/../../core/src/test/resources</directory>
<includes>
<include>**/**</include>
</includes>
<filtering>false</filtering>
</resource>-->
<resource>
<directory>${basedir}/src/main/resources</directory>
<includes>
<include>**/**</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</unitTest>
<resources>
<resource>
<directory>${basedir}/../../</directory>
<targetPath>META-INF</targetPath>
<includes>
<include>notice.txt</include>
</includes>
<filtering>false</filtering>
</resource>
<!-- <resource>
<directory>${basedir}/../../src/main/resources</directory>
<includes>
<include>**/**</include>
</includes>
<filtering>false</filtering>
</resource>-->
</resources>
</build>
</project>
-39
View File
@@ -1,39 +0,0 @@
<!--
* ========================================================================
*
* Copyright 2004 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ========================================================================
-->
<project
xmlns:j="jelly:core"
xmlns:ant="jelly:ant"
xmlns:util="jelly:util"
xmlns:maven="jelly:maven"
>
<postGoal name="jar:jar">
<j:if test="${context.getVariable('signature.alias') != null}">
<echo>signature.alias defined; signing JAR(s)...</echo>
<ant:signjar lazy="true" alias="${signature.alias}" storepass="${signature.storepass}" keystore="${signature.keystore}">
<fileset dir="${maven.build.dir}">
<include name="*.jar"/>
</fileset>
</ant:signjar>
</j:if>
</postGoal>
</project>
+2 -2
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.acegisecurity</groupId>
<artifactId>acegi-security-adapters</artifactId>
<version>1.0.4-SNAPSHOT</version>
<version>1.0.6</version>
</parent>
<artifactId>acegi-security-jboss</artifactId>
<name>Acegi Security System for Spring - JBoss adapter</name>
@@ -20,4 +20,4 @@
<scope>provided</scope>
</dependency>
</dependencies>
</project>
</project>
-61
View File
@@ -1,61 +0,0 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<project>
<extend>${basedir}/../project.xml</extend>
<pomVersion>3</pomVersion>
<artifactId>acegi-security-jboss</artifactId>
<name>Acegi Security System for Spring - JBoss adapter</name>
<siteDirectory>/home/groups/a/ac/acegisecurity/htdocs/multiproject/acegi-security-jboss</siteDirectory>
<repository>
<connection>scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity</connection>
<developerConnection>scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity</developerConnection>
<url>http://acegisecurity.svn.sourceforge.net/viewcvs.cgi/acegisecurity/trunk/acegisecurity/adapters/jboss/</url>
</repository>
<dependencies>
<dependency>
<groupId>jboss</groupId>
<artifactId>jboss-common</artifactId>
<version>3.2.3</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>jboss</groupId>
<artifactId>jbosssx</artifactId>
<version>3.2.3</version>
<type>jar</type>
</dependency>
</dependencies>
<build>
<unitTest>
<includes>
<include>**/*Tests.java</include>
</includes>
<resources>
<resource>
<directory>${basedir}/../../</directory>
<targetPath>META-INF</targetPath>
<includes>
<include>notice.txt</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>${basedir}/../../core/src/main/resources</directory>
<includes>
<include>**/**</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</unitTest>
<resources>
<resource>
<directory>${basedir}/../../</directory>
<targetPath>META-INF</targetPath>
<includes>
<include>notice.txt</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>
@@ -18,6 +18,8 @@ package org.acegisecurity.adapters.jboss;
import org.acegisecurity.Authentication;
import org.acegisecurity.context.SecurityContextHolder;
import org.acegisecurity.context.HttpSessionContextIntegrationFilter;
import org.acegisecurity.context.SecurityContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -43,10 +45,10 @@ import javax.servlet.ServletResponse;
/**
* Populates a {@link org.acegisecurity.context.security.SecureContext} from JBoss'
* <code>java:comp/env/security/subject</code>.<p>This filter <b>never</b> preserves the
* <code>Authentication</code> on the <code>ContextHolder</code> - it is replaced every request.</p>
* <p>See {@link HttpSessionContextIntegrationFilter} for further information.</p>
* Populates a {@link SecurityContext} from JBoss' <code>java:comp/env/security/subject</code>.
* <p>This filter <b>never</b> preserves the <code>Authentication</code> on the <code>ContextHolder</code> -
* it is replaced every request.</p>
* <p>See {@link HttpSessionContextIntegrationFilter} for further information.</p>
*
* @author Ben Alex
* @version $Id$
@@ -20,6 +20,7 @@ import junit.framework.TestCase;
import org.acegisecurity.adapters.PrincipalAcegiUserToken;
import org.jboss.security.SimplePrincipal;
import org.jboss.security.SimpleGroup;
import java.io.IOException;
@@ -27,6 +28,7 @@ import java.security.Principal;
import java.security.acl.Group;
import java.util.Properties;
import java.util.Enumeration;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
@@ -318,7 +320,8 @@ public class JbossAcegiLoginModuleTests extends TestCase {
assertTrue(adapter.login());
Group[] result = adapter.getRoleSets();
assertEquals(1, result.length); // SimpleGroup called "Roles"
// Expect Roles group.
assertEquals(1, result.length);
Group roles = result[0];
assertTrue(roles.isMember(new SimplePrincipal("ROLE_TELLER")));
@@ -336,10 +339,6 @@ public class JbossAcegiLoginModuleTests extends TestCase {
this.password = password;
}
private MockCallbackHandler() {
super();
}
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
if (callbacks[i] instanceof NameCallback) {
-39
View File
@@ -1,39 +0,0 @@
<!--
* ========================================================================
*
* Copyright 2004 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ========================================================================
-->
<project
xmlns:j="jelly:core"
xmlns:ant="jelly:ant"
xmlns:util="jelly:util"
xmlns:maven="jelly:maven"
>
<postGoal name="jar:jar">
<j:if test="${context.getVariable('signature.alias') != null}">
<echo>signature.alias defined; signing JAR(s)...</echo>
<ant:signjar lazy="true" alias="${signature.alias}" storepass="${signature.storepass}" keystore="${signature.keystore}">
<fileset dir="${maven.build.dir}">
<include name="*.jar"/>
</fileset>
</ant:signjar>
</j:if>
</postGoal>
</project>
+2 -2
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.acegisecurity</groupId>
<artifactId>acegi-security-adapters</artifactId>
<version>1.0.4-SNAPSHOT</version>
<version>1.0.6</version>
</parent>
<artifactId>acegi-security-jetty</artifactId>
<name>Acegi Security System for Spring - Jetty adapter</name>
@@ -14,4 +14,4 @@
<version>4.2.22</version>
</dependency>
</dependencies>
</project>
</project>
-48
View File
@@ -1,48 +0,0 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<project>
<extend>${basedir}/../project.xml</extend>
<pomVersion>3</pomVersion>
<artifactId>acegi-security-jetty</artifactId>
<name>Acegi Security System for Spring - Jetty adapter</name>
<siteDirectory>/home/groups/a/ac/acegisecurity/htdocs/multiproject/acegi-security-jetty</siteDirectory>
<repository>
<connection>scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity</connection>
<developerConnection>scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity</developerConnection>
<url>http://acegisecurity.svn.sourceforge.net/viewcvs.cgi/acegisecurity/trunk/acegisecurity/adapters/jetty/</url>
</repository>
<dependencies>
<dependency>
<groupId>jetty</groupId>
<artifactId>org.mortbay.jetty</artifactId>
<version>4.2.22</version>
<type>jar</type>
</dependency>
</dependencies>
<build>
<unitTest>
<includes>
<include>**/*Tests.java</include>
</includes>
<resources>
<resource>
<directory>${basedir}/../../core/src/main/resources</directory>
<includes>
<include>**/**</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</unitTest>
<resources>
<resource>
<directory>${basedir}/../../</directory>
<targetPath>META-INF</targetPath>
<includes>
<include>notice.txt</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>
+2 -2
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.acegisecurity</groupId>
<artifactId>acegi-security-parent</artifactId>
<version>1.0.4-SNAPSHOT</version>
<version>1.0.6</version>
</parent>
<artifactId>acegi-security-adapters</artifactId>
<name>Acegi Security System for Spring - Adapters</name>
@@ -27,4 +27,4 @@
<module>jetty</module>
<module>resin</module>
</modules>
</project>
</project>
-25
View File
@@ -1,25 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project>
<extend>${basedir}/../project.xml</extend>
<pomVersion>3</pomVersion>
<artifactId>acegi-security-adapters</artifactId>
<name>Acegi Security System for Spring - Adapters</name>
<repository>
<connection>scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity</connection>
<developerConnection>scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity</developerConnection>
<url>http://acegisecurity.svn.sourceforge.net/viewcvs.cgi/acegisecurity/trunk/acegisecurity/adapters/</url>
</repository>
<dependencies>
<dependency>
<groupId>org.acegisecurity</groupId>
<artifactId>acegi-security</artifactId>
<version>1.0.4</version>
<type>jar</type>
<url>http://acegisecurity.org</url>
<properties>
<war.bundle>true</war.bundle>
</properties>
</dependency>
</dependencies>
</project>
-39
View File
@@ -1,39 +0,0 @@
<!--
* ========================================================================
*
* Copyright 2004 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ========================================================================
-->
<project
xmlns:j="jelly:core"
xmlns:ant="jelly:ant"
xmlns:util="jelly:util"
xmlns:maven="jelly:maven"
>
<postGoal name="jar:jar">
<j:if test="${context.getVariable('signature.alias') != null}">
<echo>signature.alias defined; signing JAR(s)...</echo>
<ant:signjar lazy="true" alias="${signature.alias}" storepass="${signature.storepass}" keystore="${signature.keystore}">
<fileset dir="${maven.build.dir}">
<include name="*.jar"/>
</fileset>
</ant:signjar>
</j:if>
</postGoal>
</project>
+2 -2
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.acegisecurity</groupId>
<artifactId>acegi-security-adapters</artifactId>
<version>1.0.4-SNAPSHOT</version>
<version>1.0.6</version>
</parent>
<artifactId>acegi-security-resin</artifactId>
<name>Acegi Security System for Spring - Resin adapter</name>
@@ -20,4 +20,4 @@
<scope>provided</scope>
</dependency>
</dependencies>
</project>
</project>
-66
View File
@@ -1,66 +0,0 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<project>
<extend>${basedir}/../project.xml</extend>
<pomVersion>3</pomVersion>
<artifactId>acegi-security-resin</artifactId>
<name>Acegi Security System for Spring - Resin adapter</name>
<siteDirectory>/home/groups/a/ac/acegisecurity/htdocs/multiproject/acegi-security-resin</siteDirectory>
<repository>
<connection>scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity</connection>
<developerConnection>scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity</developerConnection>
<url>http://acegisecurity.svn.sourceforge.net/viewcvs.cgi/acegisecurity/trunk/acegisecurity/adapters/resin/</url>
</repository>
<dependencies>
<dependency>
<groupId>com.caucho</groupId>
<artifactId>resin</artifactId>
<version>3.0.9</version>
</dependency>
<dependency>
<groupId>net.sourceforge.retroweaver</groupId>
<artifactId>retroweaver</artifactId>
<version>1.0fcs</version>
</dependency>
</dependencies>
<build>
<unitTest>
<includes>
<include>**/*Tests.java</include>
</includes>
<resources>
<resource>
<directory>${basedir}/../../core/src/test/resources</directory>
<includes>
<include>**/**</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>${basedir}/../../core/src/main/resources</directory>
<includes>
<include>**/**</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</unitTest>
<resources>
<resource>
<directory>${basedir}/../../</directory>
<targetPath>META-INF</targetPath>
<includes>
<include>notice.txt</include>
</includes>
<filtering>false</filtering>
</resource>
<!-- <resource>
<directory>${basedir}/../../src/main/resources</directory>
<includes>
<include>**/**</include>
</includes>
<filtering>false</filtering>
</resource>-->
</resources>
</build>
</project>
-39
View File
@@ -1,39 +0,0 @@
<!--
* ========================================================================
*
* Copyright 2004, 2005 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ========================================================================
-->
<project
xmlns:j="jelly:core"
xmlns:ant="jelly:ant"
xmlns:util="jelly:util"
xmlns:maven="jelly:maven"
>
<postGoal name="jar:jar">
<j:if test="${context.getVariable('signature.alias') != null}">
<echo>signature.alias defined; signing JAR(s)...</echo>
<ant:signjar lazy="true" alias="${signature.alias}" storepass="${signature.storepass}" keystore="${signature.keystore}">
<fileset dir="${maven.build.dir}">
<include name="*.jar"/>
</fileset>
</ant:signjar>
</j:if>
</postGoal>
</project>
+2 -8
View File
@@ -3,17 +3,11 @@
<parent>
<groupId>org.acegisecurity</groupId>
<artifactId>acegi-security-parent</artifactId>
<version>1.0.4-SNAPSHOT</version>
<version>1.0.6</version>
</parent>
<artifactId>acegi-security-tiger</artifactId>
<name>Acegi Security System for Spring - Java 5 (Tiger)</name>
<scm>
<connection>scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity/core-tiger</connection>
<developerConnection>scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity/core-tiger</developerConnection>
<url>http://acegisecurity.svn.sourceforge.net/viewcvs.cgi/acegisecurity/trunk/acegisecurity/core-tiger/</url>
</scm>
<dependencies>
<dependency>
<groupId>org.acegisecurity</groupId>
@@ -23,7 +17,7 @@
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>2.0.4</version>
<version>${spring.version}</version>
</dependency>
</dependencies>
-12
View File
@@ -1,12 +0,0 @@
# $Id$
# Values in this file will be overriden by any values with the same name
# in the user-created build.properties file.
# Compile settings
#
# Java 1.5 is required due to the use of annotations for metadata.
# (main Acegi Security project / parent) is Java 1.3 compatible
#
maven.compile.target=1.5
maven.compile.source=1.5
-42
View File
@@ -1,42 +0,0 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<project>
<extend>${basedir}/../project.xml</extend>
<pomVersion>3</pomVersion>
<artifactId>acegi-security-tiger</artifactId>
<name>Acegi Security System for Spring - Java 5 (Tiger)</name>
<siteDirectory>/home/groups/a/ac/acegisecurity/htdocs/multiproject/acegi-security-tiger</siteDirectory>
<repository>
<connection>scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity</connection>
<developerConnection>scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity</developerConnection>
<url>http://acegisecurity.svn.sourceforge.net/viewcvs.cgi/acegisecurity/trunk/acegisecurity/core-tiger/</url>
</repository>
<dependencies>
<dependency>
<groupId>org.acegisecurity</groupId>
<artifactId>acegi-security</artifactId>
<version>1.0.4</version>
<type>jar</type>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>${basedir}/src/main/resources/</directory>
<targetPath>/</targetPath>
<includes>
<include>*.xsl</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>${basedir}/../</directory>
<targetPath>META-INF</targetPath>
<includes>
<include>notice.txt</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>
-40
View File
@@ -1,40 +0,0 @@
<!--
* ========================================================================
*
* Copyright 2004 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ========================================================================
-->
<project
xmlns:j="jelly:core"
xmlns:ant="jelly:ant"
xmlns:util="jelly:util"
xmlns:maven="jelly:maven"
>
<postGoal name="jar:jar">
<j:if test="${context.getVariable('signature.alias') != null}">
<echo>signature.alias defined; signing JAR(s)...</echo>
<!-- To create your own free signing certificate, see http://www.dallaway.com/acad/webstart/ -->
<ant:signjar lazy="true" alias="${signature.alias}" storepass="${signature.storepass}" keystore="${signature.keystore}">
<fileset dir="${maven.build.dir}">
<include name="*.jar"/>
</fileset>
</ant:signjar>
</j:if>
</postGoal>
</project>
+20 -18
View File
@@ -1,30 +1,20 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.acegisecurity</groupId>
<artifactId>acegi-security-parent</artifactId>
<version>1.0.4-SNAPSHOT</version>
<version>1.0.6</version>
</parent>
<packaging>jar</packaging>
<artifactId>acegi-security</artifactId>
<name>Acegi Security System for Spring</name>
<scm>
<connection>
scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity/core
</connection>
<developerConnection>
scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity/core
</developerConnection>
<url>
http://acegisecurity.svn.sourceforge.net/viewcvs.cgi/acegisecurity/trunk/acegisecurity/core/
</url>
</scm>
<name>Acegi Security Core</name>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-remoting</artifactId>
</dependency>
@@ -38,11 +28,23 @@
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-mock</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<dependency>
<!-- TODO: Upgrade to 1.2 before 2.0 release -->
<groupId>org.springframework</groupId>
<artifactId>spring-ldap</artifactId>
<version>1.1.2</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>1.2.4</version>
-44
View File
@@ -1,44 +0,0 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<project>
<extend>${basedir}/../project.xml</extend>
<pomVersion>3</pomVersion>
<groupId>org.acegisecurity</groupId>
<artifactId>acegi-security</artifactId>
<name>Acegi Security System for Spring</name>
<siteDirectory>/home/groups/a/ac/acegisecurity/htdocs/multiproject/acegi-security</siteDirectory>
<repository>
<connection>scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity</connection>
<developerConnection>scm:svn:https://acegisecurity.svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity</developerConnection>
<url>http://acegisecurity.svn.sourceforge.net/viewcvs.cgi/acegisecurity/trunk/acegisecurity/core/</url>
</repository>
<build>
<resources>
<resource>
<directory>${basedir}/../</directory>
<targetPath>META-INF</targetPath>
<includes>
<include>notice.txt</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>${basedir}/src/main/resources/org/acegisecurity/taglibs</directory>
<targetPath>META-INF</targetPath>
<includes>
<include>*.tld</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>${basedir}/src/main/resources/</directory>
<targetPath>/</targetPath>
<includes>
<include>*.xsl</include>
<include>**/*.properties</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>
@@ -15,8 +15,6 @@
package org.acegisecurity;
import org.acegisecurity.providers.AbstractAuthenticationToken;
/**
* An abstract implementation of the {@link AuthenticationManager}.
@@ -42,35 +40,17 @@ public abstract class AbstractAuthenticationManager implements AuthenticationMan
public final Authentication authenticate(Authentication authRequest)
throws AuthenticationException {
try {
Authentication authResult = doAuthentication(authRequest);
copyDetails(authRequest, authResult);
return authResult;
return doAuthentication(authRequest);
} catch (AuthenticationException e) {
e.setAuthentication(authRequest);
throw e;
}
}
/**
* Copies the authentication details from a source Authentication object to a destination one, provided the
* latter does not already have one set.
*
* @param source source authentication
* @param dest the destination authentication object
*/
private void copyDetails(Authentication source, Authentication dest) {
if ((dest instanceof AbstractAuthenticationToken) && (dest.getDetails() == null)) {
AbstractAuthenticationToken token = (AbstractAuthenticationToken) dest;
token.setDetails(source.getDetails());
}
}
/**
* <p>Concrete implementations of this class override this method to provide the authentication service.</p>
* <p>The contract for this method is documented in the {@link
* AuthenticationManager#authenticate(org.acegisecurity.Authentication)}.</p>
* AuthenticationManager#authenticate(Authentication)}.</p>
*
* @param authentication the authentication request object
*
@@ -15,9 +15,9 @@
package org.acegisecurity.acl.basic.cache;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.Element;
import net.sf.ehcache.Ehcache;
import org.acegisecurity.acl.basic.AclObjectIdentity;
import org.acegisecurity.acl.basic.BasicAclEntry;
@@ -47,7 +47,7 @@ public class EhCacheBasedAclEntryCache implements BasicAclEntryCache, Initializi
//~ Instance fields ================================================================================================
private Cache cache;
private Ehcache cache;
//~ Methods ========================================================================================================
@@ -55,10 +55,6 @@ public class EhCacheBasedAclEntryCache implements BasicAclEntryCache, Initializi
Assert.notNull(cache, "cache mandatory");
}
public Cache getCache() {
return cache;
}
public BasicAclEntry[] getEntriesFromCache(AclObjectIdentity aclObjectIdentity) {
Element element = null;
@@ -101,7 +97,11 @@ public class EhCacheBasedAclEntryCache implements BasicAclEntryCache, Initializi
cache.remove(aclObjectIdentity);
}
public void setCache(Cache cache) {
public Ehcache getCache() {
return cache;
}
public void setCache(Ehcache cache) {
this.cache = cache;
}
}
@@ -14,9 +14,9 @@
*/
package org.acegisecurity.acls.jdbc;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.Element;
import net.sf.ehcache.Ehcache;
import org.acegisecurity.acls.MutableAcl;
import org.acegisecurity.acls.objectidentity.ObjectIdentity;
@@ -35,11 +35,11 @@ import java.io.Serializable;
public class EhCacheBasedAclCache implements AclCache {
//~ Instance fields ================================================================================================
private Cache cache;
private Ehcache cache;
//~ Constructors ===================================================================================================
public EhCacheBasedAclCache(Cache cache) {
public EhCacheBasedAclCache(Ehcache cache) {
Assert.notNull(cache, "Cache required");
this.cache = cache;
}
@@ -27,7 +27,6 @@ import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.core.Ordered;
import org.springframework.util.Assert;
@@ -39,23 +38,14 @@ import org.springframework.util.Assert;
* <code>AuthByAdapterProvider</code>-configured key.</p>
* <P>If the key does not match, a <code>BadCredentialsException</code> is thrown.</p>
*/
public class AuthByAdapterProvider implements InitializingBean, AuthenticationProvider, MessageSourceAware, Ordered {
public class AuthByAdapterProvider implements InitializingBean, AuthenticationProvider, MessageSourceAware {
//~ Instance fields ================================================================================================
protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor();
private String key;
private int order = -1; // default: same as non-Ordered
//~ Methods ========================================================================================================
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(key, "A Key is required and should match that configured for the adapters");
Assert.notNull(messages, "A message source must be set");
@@ -40,7 +40,7 @@ public class ConcurrentSessionControllerImpl implements ConcurrentSessionControl
//~ Instance fields ================================================================================================
protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor();
private SessionRegistry sessionRegistry = new SessionRegistryImpl();
private SessionRegistry sessionRegistry;
private boolean exceptionIfMaximumExceeded = false;
private int maximumSessions = 1;
@@ -18,6 +18,7 @@ package org.acegisecurity.concurrent;
import org.springframework.util.Assert;
import java.util.Date;
import java.io.Serializable;
/**
@@ -32,7 +33,7 @@ import java.util.Date;
* @author Ben Alex
* @version $Id$
*/
public class SessionInformation {
public class SessionInformation implements Serializable {
//~ Instance fields ================================================================================================
private Date lastRequest;
@@ -51,8 +52,6 @@ public class SessionInformation {
this.lastRequest = lastRequest;
}
private SessionInformation() {}
//~ Methods ========================================================================================================
public void expireNow() {
@@ -21,6 +21,8 @@ import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.util.Assert;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.util.ArrayList;
import java.util.Collections;
@@ -34,126 +36,140 @@ import java.util.Set;
import javax.servlet.http.HttpSession;
/**
* Base implementation of {@link org.acegisecurity.concurrent.SessionRegistry}
* which also listens for {@link
* org.acegisecurity.ui.session.HttpSessionDestroyedEvent}s published in the
* Spring application context.
*
* which also listens for {@link org.acegisecurity.ui.session.HttpSessionDestroyedEvent}s
* published in the Spring application context.
*
* <p>
* NB: It is important that you register the {@link
* org.acegisecurity.ui.session.HttpSessionEventPublisher} in
* <code>web.xml</code> so that this class is notified of sessions that
* expire.
* NB: It is important that you register the {@link org.acegisecurity.ui.session.HttpSessionEventPublisher} in
* <code>web.xml</code> so that this class is notified of sessions that expire.
* </p>
*
* @author Ben Alex
* @version $Id${date}
* @version $Id$
*/
public class SessionRegistryImpl implements SessionRegistry,
ApplicationListener {
//~ Instance fields ========================================================
public class SessionRegistryImpl implements SessionRegistry, ApplicationListener {
//~ Static fields/initializers =====================================================================================
private Map principals = Collections.synchronizedMap(new HashMap()); // <principal:Object,SessionIdSet>
private Map sessionIds = Collections.synchronizedMap(new HashMap()); // <sessionId:Object,SessionInformation>
protected static final Log logger = LogFactory.getLog(SessionRegistryImpl.class);
//~ Methods ================================================================
// ~ Instance fields ===============================================================================================
public Object[] getAllPrincipals() {
return principals.keySet().toArray();
}
private Map principals = Collections.synchronizedMap(new HashMap()); // <principal:Object,SessionIdSet>
private Map sessionIds = Collections.synchronizedMap(new HashMap()); // <sessionId:Object,SessionInformation>
public SessionInformation[] getAllSessions(Object principal,
boolean includeExpiredSessions) {
Set sessionsUsedByPrincipal = (Set) principals.get(principal);
// ~ Methods =======================================================================================================
public Object[] getAllPrincipals() {
return principals.keySet().toArray();
}
public SessionInformation[] getAllSessions(Object principal, boolean includeExpiredSessions) {
Set sessionsUsedByPrincipal = (Set) principals.get(principal);
if (sessionsUsedByPrincipal == null) {
return null;
}
return null;
}
List list = new ArrayList();
Iterator iter = sessionsUsedByPrincipal.iterator();
List list = new ArrayList();
while (iter.hasNext()) {
synchronized (sessionsUsedByPrincipal) {
String sessionId = (String) iter.next();
SessionInformation sessionInformation = getSessionInformation(sessionId);
synchronized (sessionsUsedByPrincipal) {
for (Iterator iter = sessionsUsedByPrincipal.iterator(); iter.hasNext();) {
String sessionId = (String) iter.next();
SessionInformation sessionInformation = getSessionInformation(sessionId);
if (sessionInformation == null) {
continue;
}
if (includeExpiredSessions || !sessionInformation.isExpired()) {
list.add(sessionInformation);
}
}
list.add(sessionInformation);
}
}
}
return (SessionInformation[]) list.toArray(new SessionInformation[] {});
}
public SessionInformation getSessionInformation(String sessionId) {
Assert.hasText(sessionId, "SessionId required as per interface contract");
return (SessionInformation) sessionIds.get(sessionId);
}
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof HttpSessionDestroyedEvent) {
String sessionId = ((HttpSession) event.getSource()).getId();
removeSessionInformation(sessionId);
}
}
public void refreshLastRequest(String sessionId) {
Assert.hasText(sessionId, "SessionId required as per interface contract");
SessionInformation info = getSessionInformation(sessionId);
if (info != null) {
info.refreshLastRequest();
}
}
public synchronized void registerNewSession(String sessionId, Object principal) {
Assert.hasText(sessionId, "SessionId required as per interface contract");
Assert.notNull(principal, "Principal required as per interface contract");
if (logger.isDebugEnabled()) {
logger.debug("Registering session " + sessionId +", for principal " + principal);
}
return (SessionInformation[]) list.toArray(new SessionInformation[] {});
}
public SessionInformation getSessionInformation(String sessionId) {
Assert.hasText(sessionId, "SessionId required as per interface contract");
return (SessionInformation) sessionIds.get(sessionId);
}
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof HttpSessionDestroyedEvent) {
String sessionId = ((HttpSession) event.getSource()).getId();
removeSessionInformation(sessionId);
}
}
public void refreshLastRequest(String sessionId) {
Assert.hasText(sessionId, "SessionId required as per interface contract");
SessionInformation info = getSessionInformation(sessionId);
if (info != null) {
info.refreshLastRequest();
}
}
public synchronized void registerNewSession(String sessionId, Object principal) {
Assert.hasText(sessionId, "SessionId required as per interface contract");
Assert.notNull(principal, "Principal required as per interface contract");
if (getSessionInformation(sessionId) != null) {
removeSessionInformation(sessionId);
}
removeSessionInformation(sessionId);
}
sessionIds.put(sessionId,
new SessionInformation(principal, sessionId, new Date()));
sessionIds.put(sessionId, new SessionInformation(principal, sessionId, new Date()));
Set sessionsUsedByPrincipal = (Set) principals.get(principal);
if (sessionsUsedByPrincipal == null) {
sessionsUsedByPrincipal = Collections.synchronizedSet(new HashSet());
}
if (sessionsUsedByPrincipal == null) {
sessionsUsedByPrincipal = Collections.synchronizedSet(new HashSet());
}
sessionsUsedByPrincipal.add(sessionId);
sessionsUsedByPrincipal.add(sessionId);
principals.put(principal, sessionsUsedByPrincipal);
}
principals.put(principal, sessionsUsedByPrincipal);
}
public void removeSessionInformation(String sessionId) {
Assert.hasText(sessionId, "SessionId required as per interface contract");
public void removeSessionInformation(String sessionId) {
Assert.hasText(sessionId, "SessionId required as per interface contract");
SessionInformation info = getSessionInformation(sessionId);
SessionInformation info = getSessionInformation(sessionId);
if (info != null) {
if (info != null) {
if (logger.isDebugEnabled()) {
logger.debug("Removing session " + sessionId + " from set of registered sessions");
}
sessionIds.remove(sessionId);
Set sessionsUsedByPrincipal = (Set) principals.get(info.getPrincipal());
if (sessionsUsedByPrincipal != null) {
synchronized (sessionsUsedByPrincipal) {
sessionsUsedByPrincipal.remove(sessionId);
if (sessionsUsedByPrincipal.size() == 0) {
// No need to keep object in principals Map anymore
principals.remove(info.getPrincipal());
synchronized (sessionsUsedByPrincipal) {
if (logger.isDebugEnabled()) {
logger.debug("Removing session " + sessionId + " from principal's set of registered sessions");
}
}
}
}
}
sessionsUsedByPrincipal.remove(sessionId);
if (sessionsUsedByPrincipal.size() == 0) {
// No need to keep object in principals Map anymore
if (logger.isDebugEnabled()) {
logger.debug("Removing principal " + info.getPrincipal() + " from registry");
}
principals.remove(info.getPrincipal());
}
}
}
}
}
}
@@ -25,6 +25,8 @@ import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import javax.servlet.http.HttpSession;
import org.apache.commons.logging.Log;
@@ -36,8 +38,8 @@ import org.springframework.util.ReflectionUtils;
/**
* Populates the {@link SecurityContextHolder} with information obtained from
* the <code>HttpSession</code>.
*
* <p>
* <p/>
* <p/>
* The <code>HttpSession</code> will be queried to retrieve the
* <code>SecurityContext</code> that should be stored against the
* <code>SecurityContextHolder</code> for the duration of the web request. At
@@ -45,15 +47,14 @@ import org.springframework.util.ReflectionUtils;
* <code>SecurityContextHolder</code> will be persisted back to the
* <code>HttpSession</code> by this filter.
* </p>
* <p>
* <p/>
* If a valid <code>SecurityContext</code> cannot be obtained from the
* <code>HttpSession</code> for whatever reason, a fresh
* <code>SecurityContext</code> will be created and used instead. The created
* object will be of the instance defined by the {@link #setContext(Class)}
* method (which defaults to {@link
* org.acegisecurity.context.SecurityContextImpl}.
* method (which defaults to {@link org.acegisecurity.context.SecurityContextImpl}.
* </p>
* <p>
* <p/>
* No <code>HttpSession</code> will be created by this filter if one does not
* already exist. If at the end of the web request the <code>HttpSession</code>
* does not exist, a <code>HttpSession</code> will <b>only</b> be created if
@@ -67,11 +68,11 @@ import org.springframework.util.ReflectionUtils;
* irrespective of normal session-minimisation logic (the default is
* <code>false</code>, as this is resource intensive and not recommended).
* </p>
* <p>
* <p/>
* This filter will only execute once per request, to resolve servlet container
* (specifically Weblogic) incompatibilities.
* </p>
* <p>
* <p/>
* If for whatever reason no <code>HttpSession</code> should <b>ever</b> be
* created (eg this filter is only being used with Basic authentication or
* similar clients that will never present the same <code>jsessionid</code>
@@ -84,332 +85,467 @@ import org.springframework.util.ReflectionUtils;
* <code>true</code> (setting it to <code>false</code> will cause a startup
* time error).
* </p>
* <p>
* <p/>
* This filter MUST be executed BEFORE any authentication processing mechanisms.
* Authentication processing mechanisms (eg BASIC, CAS processing filters etc)
* expect the <code>SecurityContextHolder</code> to contain a valid
* <code>SecurityContext</code> by the time they execute.
* </p>
*
*
* @author Ben Alex
* @author Patrick Burleson
* @version $Id: HttpSessionContextIntegrationFilter.java 1784 2007-02-24
* 21:00:24Z luke_t $
* @author Luke Taylor
* @author Martin Algesten
*
* @version $Id$
*/
public class HttpSessionContextIntegrationFilter implements InitializingBean, Filter {
// ~ Static fields/initializers
// =====================================================================================
//~ Static fields/initializers =====================================================================================
protected static final Log logger = LogFactory.getLog(HttpSessionContextIntegrationFilter.class);
protected static final Log logger = LogFactory.getLog(HttpSessionContextIntegrationFilter.class);
static final String FILTER_APPLIED = "__acegi_session_integration_filter_applied";
static final String FILTER_APPLIED = "__acegi_session_integration_filter_applied";
public static final String ACEGI_SECURITY_CONTEXT_KEY = "ACEGI_SECURITY_CONTEXT";
public static final String ACEGI_SECURITY_CONTEXT_KEY = "ACEGI_SECURITY_CONTEXT";
// ~ Instance fields
// ================================================================================================
//~ Instance fields ================================================================================================
private Class context = SecurityContextImpl.class;
private Class context = SecurityContextImpl.class;
private Object contextObject;
private Object contextObject;
/**
* Indicates if this filter can create a <code>HttpSession</code> if
* needed (sessions are always created sparingly, but setting this value to
* <code>false</code> will prohibit sessions from ever being created).
* Defaults to <code>true</code>. Do not set to <code>false</code> if
* you are have set {@link #forceEagerSessionCreation} to <code>true</code>,
* as the properties would be in conflict.
*/
private boolean allowSessionCreation = true;
/**
* Indicates if this filter can create a <code>HttpSession</code> if
* needed (sessions are always created sparingly, but setting this value to
* <code>false</code> will prohibit sessions from ever being created).
* Defaults to <code>true</code>. Do not set to <code>false</code> if
* you are have set {@link #forceEagerSessionCreation} to <code>true</code>,
* as the properties would be in conflict.
*/
private boolean allowSessionCreation = true;
/**
* Indicates if this filter is required to create a <code>HttpSession</code>
* for every request before proceeding through the filter chain, even if the
* <code>HttpSession</code> would not ordinarily have been created. By
* default this is <code>false</code>, which is entirely appropriate for
* most circumstances as you do not want a <code>HttpSession</code>
* created unless the filter actually needs one. It is envisaged the main
* situation in which this property would be set to <code>true</code> is
* if using other filters that depend on a <code>HttpSession</code>
* already existing, such as those which need to obtain a session ID. This
* is only required in specialised cases, so leave it set to
* <code>false</code> unless you have an actual requirement and are
* conscious of the session creation overhead.
*/
private boolean forceEagerSessionCreation = false;
/**
* Indicates if this filter is required to create a <code>HttpSession</code>
* for every request before proceeding through the filter chain, even if the
* <code>HttpSession</code> would not ordinarily have been created. By
* default this is <code>false</code>, which is entirely appropriate for
* most circumstances as you do not want a <code>HttpSession</code>
* created unless the filter actually needs one. It is envisaged the main
* situation in which this property would be set to <code>true</code> is
* if using other filters that depend on a <code>HttpSession</code>
* already existing, such as those which need to obtain a session ID. This
* is only required in specialised cases, so leave it set to
* <code>false</code> unless you have an actual requirement and are
* conscious of the session creation overhead.
*/
private boolean forceEagerSessionCreation = false;
/**
* Indicates whether the <code>SecurityContext</code> will be cloned from
* the <code>HttpSession</code>. The default is to simply reference (ie
* the default is <code>false</code>). The default may cause issues if
* concurrent threads need to have a different security identity from other
* threads being concurrently processed that share the same
* <code>HttpSession</code>. In most normal environments this does not
* represent an issue, as changes to the security identity in one thread is
* allowed to affect the security identitiy in other threads associated with
* the same <code>HttpSession</code>. For unusual cases where this is not
* permitted, change this value to <code>true</code> and ensure the
* {@link #context} is set to a <code>SecurityContext</code> that
* implements {@link Cloneable} and overrides the <code>clone()</code>
* method.
*/
private boolean cloneFromHttpSession = false;
/**
* Indicates whether the <code>SecurityContext</code> will be cloned from
* the <code>HttpSession</code>. The default is to simply reference (ie
* the default is <code>false</code>). The default may cause issues if
* concurrent threads need to have a different security identity from other
* threads being concurrently processed that share the same
* <code>HttpSession</code>. In most normal environments this does not
* represent an issue, as changes to the security identity in one thread is
* allowed to affect the security identitiy in other threads associated with
* the same <code>HttpSession</code>. For unusual cases where this is not
* permitted, change this value to <code>true</code> and ensure the
* {@link #context} is set to a <code>SecurityContext</code> that
* implements {@link Cloneable} and overrides the <code>clone()</code>
* method.
*/
private boolean cloneFromHttpSession = false;
public boolean isCloneFromHttpSession() {
return cloneFromHttpSession;
}
public boolean isCloneFromHttpSession() {
return cloneFromHttpSession;
}
public void setCloneFromHttpSession(boolean cloneFromHttpSession) {
this.cloneFromHttpSession = cloneFromHttpSession;
}
public void setCloneFromHttpSession(boolean cloneFromHttpSession) {
this.cloneFromHttpSession = cloneFromHttpSession;
}
public HttpSessionContextIntegrationFilter() throws ServletException {
this.contextObject = generateNewContext();
}
public HttpSessionContextIntegrationFilter() throws ServletException {
this.contextObject = generateNewContext();
}
// ~ Methods
// ========================================================================================================
//~ Methods ========================================================================================================
public void afterPropertiesSet() throws Exception {
if ((this.context == null) || (!SecurityContext.class.isAssignableFrom(this.context))) {
throw new IllegalArgumentException("context must be defined and implement SecurityContext "
+ "(typically use org.acegisecurity.context.SecurityContextImpl; existing class is " + this.context
+ ")");
}
public void afterPropertiesSet() throws Exception {
if ((this.context == null) || (!SecurityContext.class.isAssignableFrom(this.context))) {
throw new IllegalArgumentException("context must be defined and implement SecurityContext "
+ "(typically use org.acegisecurity.context.SecurityContextImpl; existing class is " + this.context
+ ")");
}
if ((forceEagerSessionCreation == true) && (allowSessionCreation == false)) {
throw new IllegalArgumentException(
"If using forceEagerSessionCreation, you must set allowSessionCreation to also be true");
}
}
if (forceEagerSessionCreation && !allowSessionCreation) {
throw new IllegalArgumentException(
"If using forceEagerSessionCreation, you must set allowSessionCreation to also be true");
}
}
/**
* Does nothing. We use IoC container lifecycle services instead.
*/
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException,
ServletException {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
ServletException {
boolean filterApplied = false;
if ((request != null) && (request.getAttribute(FILTER_APPLIED) != null)) {
// ensure that filter is only applied once per request
chain.doFilter(request, response);
}
else {
if (request != null) {
filterApplied = true;
request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
}
Assert.isInstanceOf(HttpServletRequest.class, req, "ServletRequest must be an instance of HttpServletRequest");
Assert.isInstanceOf(HttpServletResponse.class, res, "ServletResponse must be an instance of HttpServletResponse");
HttpSession httpSession = null;
boolean httpSessionExistedAtStartOfRequest = false;
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
try {
httpSession = ((HttpServletRequest) request).getSession(forceEagerSessionCreation);
}
catch (IllegalStateException ignored) {
}
if (request.getAttribute(FILTER_APPLIED) != null) {
// ensure that filter is only applied once per request
chain.doFilter(request, response);
if (httpSession != null) {
httpSessionExistedAtStartOfRequest = true;
return;
}
Object contextFromSessionObject = httpSession.getAttribute(ACEGI_SECURITY_CONTEXT_KEY);
HttpSession httpSession = null;
if (contextFromSessionObject != null) {
// Clone if required (see SEC-356)
if (cloneFromHttpSession) {
Assert.isInstanceOf(Cloneable.class, contextFromSessionObject,
"Context must implement Clonable and provide a Object.clone() method");
try {
Method m = contextFromSessionObject.getClass().getMethod("clone", new Class[] {});
if (!m.isAccessible()) {
m.setAccessible(true);
}
contextFromSessionObject = m.invoke(contextFromSessionObject, new Object[] {});
}
catch (Exception ex) {
ReflectionUtils.handleReflectionException(ex);
}
}
try {
httpSession = request.getSession(forceEagerSessionCreation);
}
catch (IllegalStateException ignored) {
}
if (contextFromSessionObject instanceof SecurityContext) {
if (logger.isDebugEnabled()) {
logger.debug("Obtained from ACEGI_SECURITY_CONTEXT a valid SecurityContext and "
+ "set to SecurityContextHolder: '" + contextFromSessionObject + "'");
}
boolean httpSessionExistedAtStartOfRequest = httpSession != null;
SecurityContextHolder.setContext((SecurityContext) contextFromSessionObject);
}
else {
if (logger.isWarnEnabled()) {
logger
.warn("ACEGI_SECURITY_CONTEXT did not contain a SecurityContext but contained: '"
+ contextFromSessionObject
+ "'; are you improperly modifying the HttpSession directly "
+ "(you should always use SecurityContextHolder) or using the HttpSession attribute "
+ "reserved for this class? - new SecurityContext instance associated with "
+ "SecurityContextHolder");
}
SecurityContext contextBeforeChainExecution = readSecurityContextFromSession(httpSession);
SecurityContextHolder.setContext(generateNewContext());
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("HttpSession returned null object for ACEGI_SECURITY_CONTEXT - new "
+ "SecurityContext instance associated with SecurityContextHolder");
}
// Make the HttpSession null, as we don't want to keep a reference to it lying
// around in case chain.doFilter() invalidates it.
httpSession = null;
SecurityContextHolder.setContext(generateNewContext());
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("No HttpSession currently exists - new SecurityContext instance "
+ "associated with SecurityContextHolder");
}
if (contextBeforeChainExecution == null) {
contextBeforeChainExecution = generateNewContext();
SecurityContextHolder.setContext(generateNewContext());
}
if (logger.isDebugEnabled()) {
logger.debug("New SecurityContext instance will be associated with SecurityContextHolder");
}
} else {
if (logger.isDebugEnabled()) {
logger.debug("Obtained a valid SecurityContext from ACEGI_SECURITY_CONTEXT to "
+ "associate with SecurityContextHolder: '" + contextBeforeChainExecution + "'");
}
}
// Make the HttpSession null, as we want to ensure we don't keep
// a reference to the HttpSession laying around in case the
// chain.doFilter() invalidates it.
httpSession = null;
int contextHashBeforeChainExecution = contextBeforeChainExecution.hashCode();
request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
// Proceed with chain
int contextWhenChainProceeded = SecurityContextHolder.getContext().hashCode();
// Create a wrapper that will eagerly update the session with the security context
// if anything in the chain does a sendError() or sendRedirect().
// See SEC-398
try {
chain.doFilter(request, response);
}
catch (IOException ioe) {
throw ioe;
}
catch (ServletException se) {
throw se;
}
finally {
// do clean up, even if there was an exception
// Store context back to HttpSession
try {
httpSession = ((HttpServletRequest) request).getSession(false);
}
catch (IllegalStateException ignored) {
}
OnRedirectUpdateSessionResponseWrapper responseWrapper =
new OnRedirectUpdateSessionResponseWrapper( response, request,
httpSessionExistedAtStartOfRequest, contextHashBeforeChainExecution );
if ((httpSession == null) && httpSessionExistedAtStartOfRequest) {
if (logger.isDebugEnabled()) {
logger.debug("HttpSession is now null, but was not null at start of request; "
+ "session was invalidated, so do not create a new session");
}
}
// Proceed with chain
// Generate a HttpSession only if we need to
if ((httpSession == null) && !httpSessionExistedAtStartOfRequest) {
if (!allowSessionCreation) {
if (logger.isDebugEnabled()) {
logger
.debug("The HttpSession is currently null, and the "
+ "HttpSessionContextIntegrationFilter is prohibited from creating an HttpSession "
+ "(because the allowSessionCreation property is false) - SecurityContext thus not "
+ "stored for next request");
}
}
else if (!contextObject.equals(SecurityContextHolder.getContext())) {
if (logger.isDebugEnabled()) {
logger.debug("HttpSession being created as SecurityContextHolder contents are non-default");
}
try {
// This is the only place in this class where SecurityContextHolder.setContext() is called
SecurityContextHolder.setContext(contextBeforeChainExecution);
try {
httpSession = ((HttpServletRequest) request).getSession(true);
}
catch (IllegalStateException ignored) {
}
}
else {
if (logger.isDebugEnabled()) {
logger
.debug("HttpSession is null, but SecurityContextHolder has not changed from default: ' "
+ SecurityContextHolder.getContext()
+ "'; not creating HttpSession or storing SecurityContextHolder contents");
}
}
}
chain.doFilter(request, responseWrapper);
}
finally {
// This is the only place in this class where SecurityContextHolder.getContext() is called
SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();
// If HttpSession exists, store current
// SecurityContextHolder contents but only if
// SecurityContext has
// actually changed (see JIRA SEC-37)
if ((httpSession != null)
&& (SecurityContextHolder.getContext().hashCode() != contextWhenChainProceeded)) {
httpSession.setAttribute(ACEGI_SECURITY_CONTEXT_KEY, SecurityContextHolder.getContext());
// Crucial removal of SecurityContextHolder contents - do this before anything else.
SecurityContextHolder.clearContext();
if (logger.isDebugEnabled()) {
logger.debug("SecurityContext stored to HttpSession: '" + SecurityContextHolder.getContext()
+ "'");
}
}
request.removeAttribute(FILTER_APPLIED);
if (filterApplied) {
request.removeAttribute(FILTER_APPLIED);
}
// Remove SecurityContextHolder contents
SecurityContextHolder.clearContext();
// storeSecurityContextInSession() might already be called by the response wrapper
// if something in the chain called sendError() or sendRedirect(). This ensures we only call it
// once per request.
if ( !responseWrapper.isSessionUpdateDone() ) {
storeSecurityContextInSession(contextAfterChainExecution, request,
httpSessionExistedAtStartOfRequest, contextHashBeforeChainExecution);
}
if (logger.isDebugEnabled()) {
logger.debug("SecurityContextHolder set to new context, as request processing completed");
}
}
}
}
if (logger.isDebugEnabled()) {
logger.debug("SecurityContextHolder now cleared, as request processing completed");
}
}
}
public SecurityContext generateNewContext() throws ServletException {
try {
return (SecurityContext) this.context.newInstance();
}
catch (InstantiationException ie) {
throw new ServletException(ie);
}
catch (IllegalAccessException iae) {
throw new ServletException(iae);
}
}
/**
* Gets the security context from the session (if available) and returns it.
* <p/>
* If the session is null, the context object is null or the context object stored in the session
* is not an instance of SecurityContext it will return null.
* <p/>
* If <tt>cloneFromHttpSession</tt> is set to true, it will attempt to clone the context object
* and return the cloned instance.
*
* @param httpSession the session obtained from the request.
*/
private SecurityContext readSecurityContextFromSession(HttpSession httpSession) {
if (httpSession == null) {
if (logger.isDebugEnabled()) {
logger.debug("No HttpSession currently exists");
}
public Class getContext() {
return context;
}
return null;
}
/**
* Does nothing. We use IoC container lifecycle services instead.
*
* @param filterConfig ignored
*
* @throws ServletException ignored
*/
public void init(FilterConfig filterConfig) throws ServletException {
}
// Session exists, so try to obtain a context from it.
public boolean isAllowSessionCreation() {
return allowSessionCreation;
}
Object contextFromSessionObject = httpSession.getAttribute(ACEGI_SECURITY_CONTEXT_KEY);
public boolean isForceEagerSessionCreation() {
return forceEagerSessionCreation;
}
if (contextFromSessionObject == null) {
if (logger.isDebugEnabled()) {
logger.debug("HttpSession returned null object for ACEGI_SECURITY_CONTEXT");
}
public void setAllowSessionCreation(boolean allowSessionCreation) {
this.allowSessionCreation = allowSessionCreation;
}
return null;
}
public void setContext(Class secureContext) {
this.context = secureContext;
}
// We now have the security context object from the session.
// Clone if required (see SEC-356)
if (cloneFromHttpSession) {
Assert.isInstanceOf(Cloneable.class, contextFromSessionObject,
"Context must implement Clonable and provide a Object.clone() method");
try {
Method m = contextFromSessionObject.getClass().getMethod("clone", new Class[]{});
if (!m.isAccessible()) {
m.setAccessible(true);
}
contextFromSessionObject = m.invoke(contextFromSessionObject, new Object[]{});
}
catch (Exception ex) {
ReflectionUtils.handleReflectionException(ex);
}
}
if (!(contextFromSessionObject instanceof SecurityContext)) {
if (logger.isWarnEnabled()) {
logger.warn("ACEGI_SECURITY_CONTEXT did not contain a SecurityContext but contained: '"
+ contextFromSessionObject
+ "'; are you improperly modifying the HttpSession directly "
+ "(you should always use SecurityContextHolder) or using the HttpSession attribute "
+ "reserved for this class?");
}
return null;
}
// Everything OK. The only non-null return from this method.
return (SecurityContext) contextFromSessionObject;
}
/**
* Stores the supplied security context in the session (if available) and if it has changed since it was
* set at the start of the request.
*
* @param securityContext the context object obtained from the SecurityContextHolder after the request has
* been processed by the filter chain. SecurityContextHolder.getContext() cannot be used to obtain
* the context as it has already been cleared by the time this method is called.
* @param request the request object (used to obtain the session, if one exists).
* @param httpSessionExistedAtStartOfRequest indicates whether there was a session in place before the
* filter chain executed. If this is true, and the session is found to be null, this indicates that it was
* invalidated during the request and a new session will now be created.
* @param contextHashBeforeChainExecution the hashcode of the context before the filter chain executed.
* The context will only be stored if it has a different hashcode, indicating that the context changed
* during the request.
*
*/
private void storeSecurityContextInSession(SecurityContext securityContext,
HttpServletRequest request,
boolean httpSessionExistedAtStartOfRequest,
int contextHashBeforeChainExecution) {
HttpSession httpSession = null;
try {
httpSession = request.getSession(false);
}
catch (IllegalStateException ignored) {
}
if (httpSession == null) {
if (httpSessionExistedAtStartOfRequest) {
if (logger.isDebugEnabled()) {
logger.debug("HttpSession is now null, but was not null at start of request; "
+ "session was invalidated, so do not create a new session");
}
} else {
// Generate a HttpSession only if we need to
if (!allowSessionCreation) {
if (logger.isDebugEnabled()) {
logger.debug("The HttpSession is currently null, and the "
+ "HttpSessionContextIntegrationFilter is prohibited from creating an HttpSession "
+ "(because the allowSessionCreation property is false) - SecurityContext thus not "
+ "stored for next request");
}
} else if (!contextObject.equals(securityContext)) {
if (logger.isDebugEnabled()) {
logger.debug("HttpSession being created as SecurityContextHolder contents are non-default");
}
try {
httpSession = request.getSession(true);
}
catch (IllegalStateException ignored) {
}
} else {
if (logger.isDebugEnabled()) {
logger.debug("HttpSession is null, but SecurityContextHolder has not changed from default: ' "
+ securityContext
+ "'; not creating HttpSession or storing SecurityContextHolder contents");
}
}
}
}
// If HttpSession exists, store current SecurityContextHolder contents but only if
// the SecurityContext has actually changed (see JIRA SEC-37)
if (httpSession != null && securityContext.hashCode() != contextHashBeforeChainExecution) {
httpSession.setAttribute(ACEGI_SECURITY_CONTEXT_KEY, securityContext);
if (logger.isDebugEnabled()) {
logger.debug("SecurityContext stored to HttpSession: '" + securityContext + "'");
}
}
}
public SecurityContext generateNewContext() throws ServletException {
try {
return (SecurityContext) this.context.newInstance();
}
catch (InstantiationException ie) {
throw new ServletException(ie);
}
catch (IllegalAccessException iae) {
throw new ServletException(iae);
}
}
/**
* Does nothing. We use IoC container lifecycle services instead.
*
* @param filterConfig ignored
* @throws ServletException ignored
*/
public void init(FilterConfig filterConfig) throws ServletException {
}
/**
* Does nothing. We use IoC container lifecycle services instead.
*/
public void destroy() {
}
public boolean isAllowSessionCreation() {
return allowSessionCreation;
}
public void setAllowSessionCreation(boolean allowSessionCreation) {
this.allowSessionCreation = allowSessionCreation;
}
public Class getContext() {
return context;
}
public void setContext(Class secureContext) {
this.context = secureContext;
}
public boolean isForceEagerSessionCreation() {
return forceEagerSessionCreation;
}
public void setForceEagerSessionCreation(boolean forceEagerSessionCreation) {
this.forceEagerSessionCreation = forceEagerSessionCreation;
}
//~ Inner Classes ==================================================================================================
/**
* Wrapper that is applied to every request to update the <code>HttpSession<code> with
* the <code>SecurityContext</code> when a <code>sendError()</code> or <code>sendRedirect</code>
* happens. See SEC-398. The class contains the fields needed to call
* <code>storeSecurityContextInSession()</code>
*/
private class OnRedirectUpdateSessionResponseWrapper extends HttpServletResponseWrapper {
HttpServletRequest request;
boolean httpSessionExistedAtStartOfRequest;
int contextHashBeforeChainExecution;
// Used to ensure storeSecurityContextInSession() is only
// called once.
boolean sessionUpdateDone = false;
/**
* Takes the parameters required to call <code>storeSecurityContextInSession()</code> in
* addition to the response object we are wrapping.
* @see HttpSessionContextIntegrationFilter#storeSecurityContextInSession(SecurityContext, ServletRequest, boolean, int)
*/
public OnRedirectUpdateSessionResponseWrapper(HttpServletResponse response,
HttpServletRequest request,
boolean httpSessionExistedAtStartOfRequest,
int contextHashBeforeChainExecution) {
super(response);
this.request = request;
this.httpSessionExistedAtStartOfRequest = httpSessionExistedAtStartOfRequest;
this.contextHashBeforeChainExecution = contextHashBeforeChainExecution;
}
/**
* Makes sure the session is updated before calling the
* superclass <code>sendError()</code>
*/
public void sendError(int sc) throws IOException {
doSessionUpdate();
super.sendError(sc);
}
/**
* Makes sure the session is updated before calling the
* superclass <code>sendError()</code>
*/
public void sendError(int sc, String msg) throws IOException {
doSessionUpdate();
super.sendError(sc, msg);
}
/**
* Makes sure the session is updated before calling the
* superclass <code>sendRedirect()</code>
*/
public void sendRedirect(String location) throws IOException {
doSessionUpdate();
super.sendRedirect(location);
}
/**
* Calls <code>storeSecurityContextInSession()</code>
*/
private void doSessionUpdate() {
if (sessionUpdateDone) {
return;
}
SecurityContext securityContext = SecurityContextHolder.getContext();
storeSecurityContextInSession(securityContext, request,
httpSessionExistedAtStartOfRequest, contextHashBeforeChainExecution);
sessionUpdateDone = true;
}
/**
* Tells if the response wrapper has called
* <code>storeSecurityContextInSession()</code>.
*/
public boolean isSessionUpdateDone() {
return sessionUpdateDone;
}
}
public void setForceEagerSessionCreation(boolean forceEagerSessionCreation) {
this.forceEagerSessionCreation = forceEagerSessionCreation;
}
}
@@ -54,397 +54,442 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/**
* Abstract class that implements security interception for secure objects.<p>The
* <code>AbstractSecurityInterceptor</code> will ensure the proper startup configuration of the security interceptor.
* It will also implement the proper handling of secure object invocations, being:
* <ol>
* <li>Obtain the {@link Authentication} object from the {@link SecurityContextHolder}.</li>
* <li>Determine if the request relates to a secured or public invocation by looking up the secure object
* request against the {@link ObjectDefinitionSource}.</li>
* <li>For an invocation that is secured (there is a <code>ConfigAttributeDefinition</code> for the secure
* object invocation):
* <ol type="a">
* <li>If either the {@link org.acegisecurity.Authentication#isAuthenticated()} returns
* <code>false</code>, or the {@link #alwaysReauthenticate} is <code>true</code>, authenticate the request
* against the configured {@link AuthenticationManager}. When authenticated, replace the
* <code>Authentication</code> object on the <code>SecurityContextHolder</code> with the returned value.</li>
* <li>Authorize the request against the configured {@link AccessDecisionManager}.</li>
* <li>Perform any run-as replacement via the configured {@link RunAsManager}.</li>
* <li>Pass control back to the concrete subclass, which will actually proceed with executing the
* object. A {@link InterceptorStatusToken} is returned so that after the subclass has finished proceeding
* with execution of the object, its finally clause can ensure the <code>AbstractSecurityInterceptor</code>
* is re-called and tidies up correctly.</li>
* <li>The concrete subclass will re-call the <code>AbstractSecurityInterceptor</code> via the
* {@link #afterInvocation(InterceptorStatusToken, Object)} method.</li>
* <li>If the <code>RunAsManager</code> replaced the <code>Authentication</code> object, return
* the <code>SecurityContextHolder</code> to the object that existed after the call to
* <code>AuthenticationManager</code>.</li>
* <li>If an <code>AfterInvocationManager</code> is defined, invoke the invocation manager and
* allow it to replace the object due to be returned to the caller.</li>
* </ol>
* </li>
* <li>For an invocation that is public (there is no <code>ConfigAttributeDefinition</code> for the secure
* object invocation):
* <ol type="a">
* <li>As described above, the concrete subclass will be returned an
* <code>InterceptorStatusToken</code> which is subsequently re-presented to the
* <code>AbstractSecurityInterceptor</code> after the secure object has been executed. The
* <code>AbstractSecurityInterceptor</code> will take no further action when its {@link
* #afterInvocation(InterceptorStatusToken, Object)} is called.</li>
* </ol>
* </li>
* <li>Control again returns to the concrete subclass, along with the <code>Object</code> that should be
* returned to the caller. The subclass will then return that result or exception to the original caller.</li>
* </ol>
* </p>
*
* Abstract class that implements security interception for secure objects.
* <p>
* The <code>AbstractSecurityInterceptor</code> will ensure the proper startup
* configuration of the security interceptor. It will also implement the proper
* handling of secure object invocations, being:
* <ol>
* <li>Obtain the {@link Authentication} object from the
* {@link SecurityContextHolder}.</li>
* <li>Determine if the request relates to a secured or public invocation by
* looking up the secure object request against the
* {@link ObjectDefinitionSource}.</li>
* <li>For an invocation that is secured (there is a
* <code>ConfigAttributeDefinition</code> for the secure object invocation):
* <ol type="a">
* <li>If either the {@link org.acegisecurity.Authentication#isAuthenticated()}
* returns <code>false</code>, or the {@link #alwaysReauthenticate} is
* <code>true</code>, authenticate the request against the configured
* {@link AuthenticationManager}. When authenticated, replace the
* <code>Authentication</code> object on the
* <code>SecurityContextHolder</code> with the returned value.</li>
* <li>Authorize the request against the configured
* {@link AccessDecisionManager}.</li>
* <li>Perform any run-as replacement via the configured {@link RunAsManager}.</li>
* <li>Pass control back to the concrete subclass, which will actually proceed
* with executing the object. A {@link InterceptorStatusToken} is returned so
* that after the subclass has finished proceeding with execution of the object,
* its finally clause can ensure the <code>AbstractSecurityInterceptor</code>
* is re-called and tidies up correctly.</li>
* <li>The concrete subclass will re-call the
* <code>AbstractSecurityInterceptor</code> via the
* {@link #afterInvocation(InterceptorStatusToken, Object)} method.</li>
* <li>If the <code>RunAsManager</code> replaced the
* <code>Authentication</code> object, return the
* <code>SecurityContextHolder</code> to the object that existed after the
* call to <code>AuthenticationManager</code>.</li>
* <li>If an <code>AfterInvocationManager</code> is defined, invoke the
* invocation manager and allow it to replace the object due to be returned to
* the caller.</li>
* </ol>
* </li>
* <li>For an invocation that is public (there is no
* <code>ConfigAttributeDefinition</code> for the secure object invocation):
* <ol type="a">
* <li>As described above, the concrete subclass will be returned an
* <code>InterceptorStatusToken</code> which is subsequently re-presented to
* the <code>AbstractSecurityInterceptor</code> after the secure object has
* been executed. The <code>AbstractSecurityInterceptor</code> will take no
* further action when its {@link #afterInvocation(InterceptorStatusToken,
* Object)} is called.</li>
* </ol>
* </li>
* <li>Control again returns to the concrete subclass, along with the
* <code>Object</code> that should be returned to the caller. The subclass
* will then return that result or exception to the original caller.</li>
* </ol>
* </p>
*
* @author Ben Alex
* @version $Id$
* @version $Id: AbstractSecurityInterceptor.java 1790 2007-03-30 18:27:19Z
* luke_t $
*/
public abstract class AbstractSecurityInterceptor implements InitializingBean, ApplicationEventPublisherAware,
MessageSourceAware {
//~ Static fields/initializers =====================================================================================
protected static final Log logger = LogFactory.getLog(AbstractSecurityInterceptor.class);
//~ Instance fields ================================================================================================
private AccessDecisionManager accessDecisionManager;
private AfterInvocationManager afterInvocationManager;
private ApplicationEventPublisher eventPublisher;
private AuthenticationManager authenticationManager;
protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor();
private RunAsManager runAsManager = new NullRunAsManager();
private boolean alwaysReauthenticate = false;
private boolean rejectPublicInvocations = false;
private boolean validateConfigAttributes = true;
//~ Methods ========================================================================================================
/**
* Completes the work of the <code>AbstractSecurityInterceptor</code> after the secure object invocation
* has been complete
*
* @param token as returned by the {@link #beforeInvocation(Object)}} method
* @param returnedObject any object returned from the secure object invocation (may be<code>null</code>)
*
* @return the object the secure object invocation should ultimately return to its caller (may be
* <code>null</code>)
*/
protected Object afterInvocation(InterceptorStatusToken token, Object returnedObject) {
if (token == null) {
// public object
return returnedObject;
}
if (token.isContextHolderRefreshRequired()) {
if (logger.isDebugEnabled()) {
logger.debug("Reverting to original Authentication: " + token.getAuthentication().toString());
}
SecurityContextHolder.getContext().setAuthentication(token.getAuthentication());
}
if (afterInvocationManager != null) {
// Attempt after invocation handling
try {
returnedObject = afterInvocationManager.decide(token.getAuthentication(), token.getSecureObject(),
token.getAttr(), returnedObject);
} catch (AccessDeniedException accessDeniedException) {
AuthorizationFailureEvent event = new AuthorizationFailureEvent(token.getSecureObject(),
token.getAttr(), token.getAuthentication(), accessDeniedException);
publishEvent(event);
throw accessDeniedException;
}
}
return returnedObject;
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(getSecureObjectClass(), "Subclass must provide a non-null response to getSecureObjectClass()");
Assert.notNull(this.messages, "A message source must be set");
Assert.notNull(this.authenticationManager, "An AuthenticationManager is required");
Assert.notNull(this.accessDecisionManager, "An AccessDecisionManager is required");
Assert.notNull(this.runAsManager, "A RunAsManager is required");
Assert.notNull(this.obtainObjectDefinitionSource(), "An ObjectDefinitionSource is required");
Assert.isTrue(this.obtainObjectDefinitionSource().supports(getSecureObjectClass()),
"ObjectDefinitionSource does not support secure object class: " + getSecureObjectClass());
Assert.isTrue(this.runAsManager.supports(getSecureObjectClass()),
"RunAsManager does not support secure object class: " + getSecureObjectClass());
Assert.isTrue(this.accessDecisionManager.supports(getSecureObjectClass()),
"AccessDecisionManager does not support secure object class: " + getSecureObjectClass());
if (this.afterInvocationManager != null) {
Assert.isTrue(this.afterInvocationManager.supports(getSecureObjectClass()),
"AfterInvocationManager does not support secure object class: " + getSecureObjectClass());
}
if (this.validateConfigAttributes) {
Iterator iter = this.obtainObjectDefinitionSource().getConfigAttributeDefinitions();
if (iter == null) {
logger.warn("Could not validate configuration attributes as the MethodDefinitionSource did not return "
+ "a ConfigAttributeDefinition Iterator");
return;
}
Set unsupportedAttrs = new HashSet();
while (iter.hasNext()) {
ConfigAttributeDefinition def = (ConfigAttributeDefinition) iter.next();
Iterator attributes = def.getConfigAttributes();
while (attributes.hasNext()) {
ConfigAttribute attr = (ConfigAttribute) attributes.next();
if (!this.runAsManager.supports(attr) && !this.accessDecisionManager.supports(attr)
&& ((this.afterInvocationManager == null) || !this.afterInvocationManager.supports(attr))) {
unsupportedAttrs.add(attr);
}
}
}
if (unsupportedAttrs.size() != 0) {
throw new IllegalArgumentException("Unsupported configuration attributes: " + unsupportedAttrs);
}
logger.info("Validated configuration attributes");
}
}
protected InterceptorStatusToken beforeInvocation(Object object) {
Assert.notNull(object, "Object was null");
if (!getSecureObjectClass().isAssignableFrom(object.getClass())) {
throw new IllegalArgumentException("Security invocation attempted for object "
+ object.getClass().getName()
+ " but AbstractSecurityInterceptor only configured to support secure objects of type: "
+ getSecureObjectClass());
}
ConfigAttributeDefinition attr = this.obtainObjectDefinitionSource().getAttributes(object);
if (attr == null) {
if(rejectPublicInvocations) {
throw new IllegalArgumentException(
"No public invocations are allowed via this AbstractSecurityInterceptor. "
+ "This indicates a configuration error because the "
+ "AbstractSecurityInterceptor.rejectPublicInvocations property is set to 'true'");
}
if (logger.isDebugEnabled()) {
logger.debug("Public object - authentication not attempted");
}
publishEvent(new PublicInvocationEvent(object));
return null; // no further work post-invocation
}
if (logger.isDebugEnabled()) {
logger.debug("Secure object: " + object.toString() + "; ConfigAttributes: " + attr.toString());
}
MessageSourceAware {
// ~ Static fields/initializers
// =====================================================================================
if (SecurityContextHolder.getContext().getAuthentication() == null) {
credentialsNotFound(messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound",
"An Authentication object was not found in the SecurityContext"), object, attr);
}
// Attempt authentication if not already authenticated, or user always wants reauthentication
Authentication authenticated;
if (!SecurityContextHolder.getContext().getAuthentication().isAuthenticated() || alwaysReauthenticate) {
try {
authenticated = this.authenticationManager.authenticate(SecurityContextHolder.getContext()
.getAuthentication());
} catch (AuthenticationException authenticationException) {
throw authenticationException;
}
// We don't authenticated.setAuthentication(true), because each provider should do that
if (logger.isDebugEnabled()) {
logger.debug("Successfully Authenticated: " + authenticated.toString());
}
SecurityContextHolder.getContext().setAuthentication(authenticated);
} else {
authenticated = SecurityContextHolder.getContext().getAuthentication();
if (logger.isDebugEnabled()) {
logger.debug("Previously Authenticated: " + authenticated.toString());
}
}
// Attempt authorization
try {
this.accessDecisionManager.decide(authenticated, object, attr);
} catch (AccessDeniedException accessDeniedException) {
AuthorizationFailureEvent event = new AuthorizationFailureEvent(object, attr, authenticated,
accessDeniedException);
publishEvent(event);
throw accessDeniedException;
}
if (logger.isDebugEnabled()) {
logger.debug("Authorization successful");
}
AuthorizedEvent event = new AuthorizedEvent(object, attr, authenticated);
publishEvent(event);
// Attempt to run as a different user
Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attr);
if (runAs == null) {
if (logger.isDebugEnabled()) {
logger.debug("RunAsManager did not change Authentication object");
}
// no further work post-invocation
return new InterceptorStatusToken(authenticated, false, attr, object);
} else {
if (logger.isDebugEnabled()) {
logger.debug("Switching to RunAs Authentication: " + runAs.toString());
}
SecurityContextHolder.getContext().setAuthentication(runAs);
// revert to token.Authenticated post-invocation
return new InterceptorStatusToken(authenticated, true, attr, object);
}
}
/**
* Helper method which generates an exception containing the passed reason, and publishes an event to the
* application context.<p>Always throws an exception.</p>
*
* @param reason to be provided in the exception detail
* @param secureObject that was being called
* @param configAttribs that were defined for the secureObject
*/
private void credentialsNotFound(String reason, Object secureObject, ConfigAttributeDefinition configAttribs) {
AuthenticationCredentialsNotFoundException exception = new AuthenticationCredentialsNotFoundException(reason);
AuthenticationCredentialsNotFoundEvent event = new AuthenticationCredentialsNotFoundEvent(secureObject,
configAttribs, exception);
publishEvent(event);
throw exception;
}
public AccessDecisionManager getAccessDecisionManager() {
return accessDecisionManager;
}
public AfterInvocationManager getAfterInvocationManager() {
return afterInvocationManager;
}
public AuthenticationManager getAuthenticationManager() {
return this.authenticationManager;
}
public RunAsManager getRunAsManager() {
return runAsManager;
}
/**
* Indicates the type of secure objects the subclass will be presenting to the abstract parent for
* processing. This is used to ensure collaborators wired to the <code>AbstractSecurityInterceptor</code> all
* support the indicated secure object class.
*
* @return the type of secure object the subclass provides services for
*/
public abstract Class getSecureObjectClass();
public boolean isAlwaysReauthenticate() {
return alwaysReauthenticate;
}
public boolean isRejectPublicInvocations() {
return rejectPublicInvocations;
}
public boolean isValidateConfigAttributes() {
return validateConfigAttributes;
}
public abstract ObjectDefinitionSource obtainObjectDefinitionSource();
public void setAccessDecisionManager(AccessDecisionManager accessDecisionManager) {
this.accessDecisionManager = accessDecisionManager;
}
public void setAfterInvocationManager(AfterInvocationManager afterInvocationManager) {
this.afterInvocationManager = afterInvocationManager;
}
/**
* Indicates whether the <code>AbstractSecurityInterceptor</code> should ignore the {@link
* Authentication#isAuthenticated()} property. Defaults to <code>false</code>, meaning by default the
* <code>Authentication.isAuthenticated()</code> property is trusted and re-authentication will not occur if the
* principal has already been authenticated.
*
* @param alwaysReauthenticate <code>true</code> to force <code>AbstractSecurityInterceptor</code> to disregard the
* value of <code>Authentication.isAuthenticated()</code> and always re-authenticate the request (defaults
* to <code>false</code>).
*/
public void setAlwaysReauthenticate(boolean alwaysReauthenticate) {
this.alwaysReauthenticate = alwaysReauthenticate;
}
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.eventPublisher = applicationEventPublisher;
}
public void setAuthenticationManager(AuthenticationManager newManager) {
this.authenticationManager = newManager;
}
public void setMessageSource(MessageSource messageSource) {
this.messages = new MessageSourceAccessor(messageSource);
}
/**
* By rejecting public invocations (and setting this property to <code>true</code>), essentially you are
* ensuring that every secure object invocation advised by <code>AbstractSecurityInterceptor</code> has a
* configuration attribute defined. This is useful to ensure a "fail safe" mode where undeclared secure objects
* will be rejected and configuration omissions detected early. An <code>IllegalArgumentException</code> will be
* thrown by the <code>AbstractSecurityInterceptor</code> if you set this property to <code>true</code> and an
* attempt is made to invoke a secure object that has no configuration attributes.
*
* @param rejectPublicInvocations set to <code>true</code> to reject invocations of secure objects that have no
* configuration attributes (by default it is <code>false</code> which treats undeclared secure objects as
* "public" or unauthorized)
*/
public void setRejectPublicInvocations(boolean rejectPublicInvocations) {
this.rejectPublicInvocations = rejectPublicInvocations;
}
public void setRunAsManager(RunAsManager runAsManager) {
this.runAsManager = runAsManager;
}
public void setValidateConfigAttributes(boolean validateConfigAttributes) {
this.validateConfigAttributes = validateConfigAttributes;
}
private void publishEvent(ApplicationEvent event) {
if (this.eventPublisher != null) {
this.eventPublisher.publishEvent(event);
}
}
protected static final Log logger = LogFactory.getLog(AbstractSecurityInterceptor.class);
// ~ Instance fields
// ================================================================================================
private AccessDecisionManager accessDecisionManager;
private AfterInvocationManager afterInvocationManager;
private ApplicationEventPublisher eventPublisher;
private AuthenticationManager authenticationManager;
protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor();
private RunAsManager runAsManager = new NullRunAsManager();
private boolean alwaysReauthenticate = false;
private boolean rejectPublicInvocations = false;
private boolean validateConfigAttributes = true;
// ~ Methods
// ========================================================================================================
/**
* Completes the work of the <code>AbstractSecurityInterceptor</code>
* after the secure object invocation has been complete
*
* @param token as returned by the {@link #beforeInvocation(Object)}}
* method
* @param returnedObject any object returned from the secure object
* invocation (may be<code>null</code>)
*
* @return the object the secure object invocation should ultimately return
* to its caller (may be <code>null</code>)
*/
protected Object afterInvocation(InterceptorStatusToken token, Object returnedObject) {
if (token == null) {
// public object
return returnedObject;
}
if (token.isContextHolderRefreshRequired()) {
if (logger.isDebugEnabled()) {
logger.debug("Reverting to original Authentication: " + token.getAuthentication().toString());
}
SecurityContextHolder.getContext().setAuthentication(token.getAuthentication());
}
if (afterInvocationManager != null) {
// Attempt after invocation handling
try {
returnedObject = afterInvocationManager.decide(token.getAuthentication(), token.getSecureObject(),
token.getAttr(), returnedObject);
}
catch (AccessDeniedException accessDeniedException) {
AuthorizationFailureEvent event = new AuthorizationFailureEvent(token.getSecureObject(), token
.getAttr(), token.getAuthentication(), accessDeniedException);
publishEvent(event);
throw accessDeniedException;
}
}
return returnedObject;
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(getSecureObjectClass(), "Subclass must provide a non-null response to getSecureObjectClass()");
Assert.notNull(this.messages, "A message source must be set");
Assert.notNull(this.authenticationManager, "An AuthenticationManager is required");
Assert.notNull(this.accessDecisionManager, "An AccessDecisionManager is required");
Assert.notNull(this.runAsManager, "A RunAsManager is required");
Assert.notNull(this.obtainObjectDefinitionSource(), "An ObjectDefinitionSource is required");
Assert.isTrue(this.obtainObjectDefinitionSource().supports(getSecureObjectClass()),
"ObjectDefinitionSource does not support secure object class: " + getSecureObjectClass());
Assert.isTrue(this.runAsManager.supports(getSecureObjectClass()),
"RunAsManager does not support secure object class: " + getSecureObjectClass());
Assert.isTrue(this.accessDecisionManager.supports(getSecureObjectClass()),
"AccessDecisionManager does not support secure object class: " + getSecureObjectClass());
if (this.afterInvocationManager != null) {
Assert.isTrue(this.afterInvocationManager.supports(getSecureObjectClass()),
"AfterInvocationManager does not support secure object class: " + getSecureObjectClass());
}
if (this.validateConfigAttributes) {
Iterator iter = this.obtainObjectDefinitionSource().getConfigAttributeDefinitions();
if (iter == null) {
logger.warn("Could not validate configuration attributes as the MethodDefinitionSource did not return "
+ "a ConfigAttributeDefinition Iterator");
return;
}
Set unsupportedAttrs = new HashSet();
while (iter.hasNext()) {
ConfigAttributeDefinition def = (ConfigAttributeDefinition) iter.next();
Iterator attributes = def.getConfigAttributes();
while (attributes.hasNext()) {
ConfigAttribute attr = (ConfigAttribute) attributes.next();
if (!this.runAsManager.supports(attr) && !this.accessDecisionManager.supports(attr)
&& ((this.afterInvocationManager == null) || !this.afterInvocationManager.supports(attr))) {
unsupportedAttrs.add(attr);
}
}
}
if (unsupportedAttrs.size() != 0) {
throw new IllegalArgumentException("Unsupported configuration attributes: " + unsupportedAttrs);
}
logger.info("Validated configuration attributes");
}
}
protected InterceptorStatusToken beforeInvocation(Object object) {
Assert.notNull(object, "Object was null");
if (!getSecureObjectClass().isAssignableFrom(object.getClass())) {
throw new IllegalArgumentException("Security invocation attempted for object "
+ object.getClass().getName()
+ " but AbstractSecurityInterceptor only configured to support secure objects of type: "
+ getSecureObjectClass());
}
ConfigAttributeDefinition attr = this.obtainObjectDefinitionSource().getAttributes(object);
if (attr == null) {
if (rejectPublicInvocations) {
throw new IllegalArgumentException(
"No public invocations are allowed via this AbstractSecurityInterceptor. "
+ "This indicates a configuration error because the "
+ "AbstractSecurityInterceptor.rejectPublicInvocations property is set to 'true'");
}
if (logger.isDebugEnabled()) {
logger.debug("Public object - authentication not attempted");
}
publishEvent(new PublicInvocationEvent(object));
return null; // no further work post-invocation
}
if (logger.isDebugEnabled()) {
logger.debug("Secure object: " + object.toString() + "; ConfigAttributes: " + attr.toString());
}
if (SecurityContextHolder.getContext().getAuthentication() == null) {
credentialsNotFound(messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound",
"An Authentication object was not found in the SecurityContext"), object, attr);
}
// Attempt authentication if not already authenticated, or user always
// wants reauthentication
Authentication authenticated;
if (!SecurityContextHolder.getContext().getAuthentication().isAuthenticated() || alwaysReauthenticate) {
try {
authenticated = this.authenticationManager.authenticate(SecurityContextHolder.getContext()
.getAuthentication());
}
catch (AuthenticationException authenticationException) {
throw authenticationException;
}
// We don't authenticated.setAuthentication(true), because each
// provider should do that
if (logger.isDebugEnabled()) {
logger.debug("Successfully Authenticated: " + authenticated.toString());
}
SecurityContextHolder.getContext().setAuthentication(authenticated);
}
else {
authenticated = SecurityContextHolder.getContext().getAuthentication();
if (logger.isDebugEnabled()) {
logger.debug("Previously Authenticated: " + authenticated.toString());
}
}
// Attempt authorization
try {
this.accessDecisionManager.decide(authenticated, object, attr);
}
catch (AccessDeniedException accessDeniedException) {
AuthorizationFailureEvent event = new AuthorizationFailureEvent(object, attr, authenticated,
accessDeniedException);
publishEvent(event);
throw accessDeniedException;
}
if (logger.isDebugEnabled()) {
logger.debug("Authorization successful");
}
AuthorizedEvent event = new AuthorizedEvent(object, attr, authenticated);
publishEvent(event);
// Attempt to run as a different user
Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attr);
if (runAs == null) {
if (logger.isDebugEnabled()) {
logger.debug("RunAsManager did not change Authentication object");
}
// no further work post-invocation
return new InterceptorStatusToken(authenticated, false, attr, object);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Switching to RunAs Authentication: " + runAs.toString());
}
SecurityContextHolder.getContext().setAuthentication(runAs);
// revert to token.Authenticated post-invocation
return new InterceptorStatusToken(authenticated, true, attr, object);
}
}
/**
* Helper method which generates an exception containing the passed reason,
* and publishes an event to the application context.
* <p>
* Always throws an exception.
* </p>
*
* @param reason to be provided in the exception detail
* @param secureObject that was being called
* @param configAttribs that were defined for the secureObject
*/
private void credentialsNotFound(String reason, Object secureObject, ConfigAttributeDefinition configAttribs) {
AuthenticationCredentialsNotFoundException exception = new AuthenticationCredentialsNotFoundException(reason);
AuthenticationCredentialsNotFoundEvent event = new AuthenticationCredentialsNotFoundEvent(secureObject,
configAttribs, exception);
publishEvent(event);
throw exception;
}
public AccessDecisionManager getAccessDecisionManager() {
return accessDecisionManager;
}
public AfterInvocationManager getAfterInvocationManager() {
return afterInvocationManager;
}
public AuthenticationManager getAuthenticationManager() {
return this.authenticationManager;
}
public RunAsManager getRunAsManager() {
return runAsManager;
}
/**
* Indicates the type of secure objects the subclass will be presenting to
* the abstract parent for processing. This is used to ensure collaborators
* wired to the <code>AbstractSecurityInterceptor</code> all support the
* indicated secure object class.
*
* @return the type of secure object the subclass provides services for
*/
public abstract Class getSecureObjectClass();
public boolean isAlwaysReauthenticate() {
return alwaysReauthenticate;
}
public boolean isRejectPublicInvocations() {
return rejectPublicInvocations;
}
public boolean isValidateConfigAttributes() {
return validateConfigAttributes;
}
public abstract ObjectDefinitionSource obtainObjectDefinitionSource();
public void setAccessDecisionManager(AccessDecisionManager accessDecisionManager) {
this.accessDecisionManager = accessDecisionManager;
}
public void setAfterInvocationManager(AfterInvocationManager afterInvocationManager) {
this.afterInvocationManager = afterInvocationManager;
}
/**
* Indicates whether the <code>AbstractSecurityInterceptor</code> should
* ignore the {@link Authentication#isAuthenticated()} property. Defaults to
* <code>false</code>, meaning by default the
* <code>Authentication.isAuthenticated()</code> property is trusted and
* re-authentication will not occur if the principal has already been
* authenticated.
*
* @param alwaysReauthenticate <code>true</code> to force
* <code>AbstractSecurityInterceptor</code> to disregard the value of
* <code>Authentication.isAuthenticated()</code> and always
* re-authenticate the request (defaults to <code>false</code>).
*/
public void setAlwaysReauthenticate(boolean alwaysReauthenticate) {
this.alwaysReauthenticate = alwaysReauthenticate;
}
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.eventPublisher = applicationEventPublisher;
}
public void setAuthenticationManager(AuthenticationManager newManager) {
this.authenticationManager = newManager;
}
public void setMessageSource(MessageSource messageSource) {
this.messages = new MessageSourceAccessor(messageSource);
}
/**
* By rejecting public invocations (and setting this property to
* <code>true</code>), essentially you are ensuring that every secure
* object invocation advised by <code>AbstractSecurityInterceptor</code>
* has a configuration attribute defined. This is useful to ensure a "fail
* safe" mode where undeclared secure objects will be rejected and
* configuration omissions detected early. An
* <code>IllegalArgumentException</code> will be thrown by the
* <code>AbstractSecurityInterceptor</code> if you set this property to
* <code>true</code> and an attempt is made to invoke a secure object that
* has no configuration attributes.
*
* @param rejectPublicInvocations set to <code>true</code> to reject
* invocations of secure objects that have no configuration attributes (by
* default it is <code>false</code> which treats undeclared secure objects
* as "public" or unauthorized)
*/
public void setRejectPublicInvocations(boolean rejectPublicInvocations) {
this.rejectPublicInvocations = rejectPublicInvocations;
}
public void setRunAsManager(RunAsManager runAsManager) {
this.runAsManager = runAsManager;
}
public void setValidateConfigAttributes(boolean validateConfigAttributes) {
this.validateConfigAttributes = validateConfigAttributes;
}
private void publishEvent(ApplicationEvent event) {
if (this.eventPublisher != null) {
this.eventPublisher.publishEvent(event);
}
}
}
@@ -34,17 +34,17 @@ import java.util.Vector;
* Maintains a <code>List</code> of <code>ConfigAttributeDefinition</code>s associated with different HTTP request
* URL Apache Ant path-based patterns.<p>Apache Ant path expressions are used to match a HTTP request URL against a
* <code>ConfigAttributeDefinition</code>.</p>
* <p>The order of registering the Ant paths using the {@link #addSecureUrl(String, ConfigAttributeDefinition)} is
* <p>The order of registering the Ant paths using the {@link #addSecureUrl(String,ConfigAttributeDefinition)} is
* very important. The system will identify the <b>first</b> matching path for a given HTTP URL. It will not proceed
* to evaluate later paths if a match has already been found. Accordingly, the most specific paths should be
* registered first, with the most general paths registered last.</p>
* <p>If no registered paths match the HTTP URL, <code>null</code> is returned.</p>
* <p>If no registered paths match the HTTP URL, <code>null</code> is returned.</p>
*
* @author Ben Alex
* @version $Id$
*/
public class PathBasedFilterInvocationDefinitionMap extends AbstractFilterInvocationDefinitionSource
implements FilterInvocationDefinition {
implements FilterInvocationDefinition {
//~ Static fields/initializers =====================================================================================
private static final Log logger = LogFactory.getLog(PathBasedFilterInvocationDefinitionMap.class);
@@ -58,6 +58,12 @@ public class PathBasedFilterInvocationDefinitionMap extends AbstractFilterInvoca
//~ Methods ========================================================================================================
public void addSecureUrl(String antPath, ConfigAttributeDefinition attr) {
// SEC-501: If using lower case comparison, we should convert the paths to lower case
// as any upper case characters included by mistake will prevent the URL from ever being matched.
if (convertUrlToLowercaseBeforeComparison) {
antPath = antPath.toLowerCase();
}
requestMap.add(new EntryHolder(antPath, attr));
if (logger.isDebugEnabled()) {
@@ -110,7 +116,7 @@ public class PathBasedFilterInvocationDefinitionMap extends AbstractFilterInvoca
if (logger.isDebugEnabled()) {
logger.debug("Candidate is: '" + url + "'; pattern is " + entryHolder.getAntPath() + "; matched="
+ matched);
+ matched);
}
if (matched) {
@@ -79,7 +79,7 @@ public class FilterBasedLdapUserSearch implements LdapUserSearch {
//~ Constructors ===================================================================================================
public FilterBasedLdapUserSearch(String searchBase, String searchFilter,
InitialDirContextFactory initialDirContextFactory) {
InitialDirContextFactory initialDirContextFactory) {
Assert.notNull(initialDirContextFactory, "initialDirContextFactory must not be null");
Assert.notNull(searchFilter, "searchFilter must not be null.");
Assert.notNull(searchBase, "searchBase must not be null (an empty string is acceptable).");
@@ -106,10 +106,8 @@ public class FilterBasedLdapUserSearch implements LdapUserSearch {
* @throws UsernameNotFoundException if no matching entry is found.
*/
public LdapUserDetails searchForUser(String username) {
DirContext ctx = initialDirContextFactory.newInitialDirContext();
if (logger.isDebugEnabled()) {
logger.debug("Searching for user '" + username + "', in context " + ctx + ", with user search "
logger.debug("Searching for user '" + username + "', with user search "
+ this.toString());
}
@@ -167,7 +165,7 @@ public class FilterBasedLdapUserSearch implements LdapUserSearch {
sb.append("[ searchFilter: '").append(searchFilter).append("', ");
sb.append("searchBase: '").append(searchBase).append("'");
sb.append(", scope: ")
.append((searchControls.getSearchScope() == SearchControls.SUBTREE_SCOPE) ? "subtree" : "single-level, ");
.append(searchControls.getSearchScope() == SearchControls.SUBTREE_SCOPE ? "subtree" : "single-level, ");
sb.append("searchTimeLimit: ").append(searchControls.getTimeLimit());
sb.append("derefLinkFlag: ").append(searchControls.getDerefLinkFlag()).append(" ]");
@@ -193,6 +193,7 @@ public class ProviderManager extends AbstractAuthenticationManager implements In
try {
result = provider.authenticate(authentication);
copyDetails(authentication, result);
sessionController.checkAuthenticationAllowed(result);
} catch (AuthenticationException ae) {
lastException = ae;
@@ -245,6 +246,21 @@ public class ProviderManager extends AbstractAuthenticationManager implements In
throw lastException;
}
/**
* Copies the authentication details from a source Authentication object to a destination one, provided the
* latter does not already have one set.
*
* @param source source authentication
* @param dest the destination authentication object
*/
private void copyDetails(Authentication source, Authentication dest) {
if ((dest instanceof AbstractAuthenticationToken) && (dest.getDetails() == null)) {
AbstractAuthenticationToken token = (AbstractAuthenticationToken) dest;
token.setDetails(source.getDetails());
}
}
public List getProviders() {
return this.providers;
}
@@ -26,7 +26,6 @@ import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.core.Ordered;
import org.springframework.util.Assert;
@@ -36,7 +35,7 @@ import org.springframework.util.Assert;
* {@link org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken#getKeyHash()} must match this class'
* {@link #getKey()}.</p>
*/
public class AnonymousAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware, Ordered {
public class AnonymousAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware {
//~ Static fields/initializers =====================================================================================
private static final Log logger = LogFactory.getLog(AnonymousAuthenticationProvider.class);
@@ -45,18 +44,9 @@ public class AnonymousAuthenticationProvider implements AuthenticationProvider,
protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor();
private String key;
private int order = -1; // default: same as non-Ordered
//~ Methods ========================================================================================================
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
public void afterPropertiesSet() throws Exception {
Assert.hasLength(key, "A Key is required");
Assert.notNull(this.messages, "A message source must be set");
@@ -22,6 +22,7 @@ import org.acegisecurity.BadCredentialsException;
import org.acegisecurity.providers.AuthenticationProvider;
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.acegisecurity.providers.cas.cache.NullStatelessTicketCache;
import org.acegisecurity.ui.cas.CasProcessingFilter;
@@ -35,7 +36,6 @@ import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.core.Ordered;
import org.springframework.util.Assert;
@@ -50,7 +50,7 @@ import org.springframework.util.Assert;
* @author Ben Alex
* @version $Id$
*/
public class CasAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware, Ordered {
public class CasAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware {
//~ Static fields/initializers =====================================================================================
private static final Log logger = LogFactory.getLog(CasAuthenticationProvider.class);
@@ -60,28 +60,18 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
private CasAuthoritiesPopulator casAuthoritiesPopulator;
private CasProxyDecider casProxyDecider;
protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor();
private StatelessTicketCache statelessTicketCache;
private StatelessTicketCache statelessTicketCache = new NullStatelessTicketCache();
private String key;
private TicketValidator ticketValidator;
private int order = -1; // default: same as non-Ordered
//~ Methods ========================================================================================================
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(this.casAuthoritiesPopulator, "A casAuthoritiesPopulator must be set");
Assert.notNull(this.ticketValidator, "A ticketValidator must be set");
Assert.notNull(this.casProxyDecider, "A casProxyDecider must be set");
Assert.notNull(this.statelessTicketCache, "A statelessTicketCache must be set");
Assert.notNull(key,
"A Key is required so CasAuthenticationProvider can identify tokens it previously authenticated");
Assert.hasText(this.key, "A Key is required so CasAuthenticationProvider can identify tokens it previously authenticated");
Assert.notNull(this.messages, "A message source must be set");
}
@@ -15,9 +15,9 @@
package org.acegisecurity.providers.cas.cache;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.Element;
import net.sf.ehcache.Ehcache;
import org.acegisecurity.providers.cas.CasAuthenticationToken;
import org.acegisecurity.providers.cas.StatelessTicketCache;
@@ -45,7 +45,7 @@ public class EhCacheBasedTicketCache implements StatelessTicketCache, Initializi
//~ Instance fields ================================================================================================
private Cache cache;
private Ehcache cache;
//~ Methods ========================================================================================================
@@ -73,7 +73,7 @@ public class EhCacheBasedTicketCache implements StatelessTicketCache, Initializi
}
}
public Cache getCache() {
public Ehcache getCache() {
return cache;
}
@@ -99,7 +99,7 @@ public class EhCacheBasedTicketCache implements StatelessTicketCache, Initializi
cache.remove(serviceTicket);
}
public void setCache(Cache cache) {
public void setCache(Ehcache cache) {
this.cache = cache;
}
}
@@ -0,0 +1,63 @@
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.acegisecurity.providers.cas.cache;
import org.acegisecurity.providers.cas.CasAuthenticationProvider;
import org.acegisecurity.providers.cas.CasAuthenticationToken;
import org.acegisecurity.providers.cas.StatelessTicketCache;
/**
* Implementation of @link {@link StatelessTicketCache} that has no backing cache. Useful
* in instances where storing of tickets for stateless session management is not required.
* <p>
* This is the default StatelessTicketCache of the @link {@link CasAuthenticationProvider} to
* eliminate the unnecessary dependency on EhCache that applications have even if they are not using
* the stateless session management.
*
* @author Scott Battaglia
* @version $Id$
*
*@see CasAuthenticationProvider
*/
public final class NullStatelessTicketCache implements StatelessTicketCache {
/**
* @return null since we are not storing any tickets.
*/
public CasAuthenticationToken getByTicketId(final String serviceTicket) {
return null;
}
/**
* This is a no-op since we are not storing tickets.
*/
public void putTicketInCache(final CasAuthenticationToken token) {
// nothing to do
}
/**
* This is a no-op since we are not storing tickets.
*/
public void removeTicketFromCache(final CasAuthenticationToken token) {
// nothing to do
}
/**
* This is a no-op since we are not storing tickets.
*/
public void removeTicketFromCache(final String serviceTicket) {
// nothing to do
}
}
@@ -15,140 +15,162 @@
package org.acegisecurity.providers.dao;
import java.util.Map;
import org.acegisecurity.AuthenticationException;
import org.acegisecurity.AuthenticationServiceException;
import org.acegisecurity.BadCredentialsException;
import org.acegisecurity.providers.AuthenticationProvider;
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.acegisecurity.providers.encoding.PasswordEncoder;
import org.acegisecurity.providers.encoding.PlaintextPasswordEncoder;
import org.acegisecurity.userdetails.UserDetails;
import org.acegisecurity.userdetails.UserDetailsService;
import org.springframework.core.Ordered;
import org.springframework.context.ApplicationContext;
import org.springframework.dao.DataAccessException;
import org.springframework.util.Assert;
/**
* An {@link AuthenticationProvider} implementation that retrieves user details from an {@link UserDetailsService}.
*
* An {@link AuthenticationProvider} implementation that retrieves user details
* from an {@link UserDetailsService}.
*
* @author Ben Alex
* @version $Id$
* @version $Id: DaoAuthenticationProvider.java 1857 2007-05-24 00:47:12Z
* benalex $
*/
public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider implements Ordered {
//~ Instance fields ================================================================================================
public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
private PasswordEncoder passwordEncoder = new PlaintextPasswordEncoder();
private SaltSource saltSource;
private UserDetailsService userDetailsService;
private boolean includeDetailsObject = true;
private int order = -1; // default: same as non-Ordered
// ~ Instance fields
// ================================================================================================
//~ Methods ========================================================================================================
private PasswordEncoder passwordEncoder = new PlaintextPasswordEncoder();
protected void additionalAuthenticationChecks(UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
Object salt = null;
private SaltSource saltSource;
if (this.saltSource != null) {
salt = this.saltSource.getSalt(userDetails);
}
if (authentication.getCredentials() == null) {
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"),
includeDetailsObject ? userDetails : null);
}
String presentedPassword = authentication.getCredentials() == null ? "" : authentication.getCredentials().toString();
private UserDetailsService userDetailsService;
if (!passwordEncoder.isPasswordValid(
userDetails.getPassword(), presentedPassword, salt)) {
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"),
includeDetailsObject ? userDetails : null);
}
}
private boolean includeDetailsObject = true;
protected void doAfterPropertiesSet() throws Exception {
Assert.notNull(this.userDetailsService, "A UserDetailsService must be set");
}
// ~ Methods
// ========================================================================================================
public PasswordEncoder getPasswordEncoder() {
return passwordEncoder;
}
protected void additionalAuthenticationChecks(UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
Object salt = null;
public SaltSource getSaltSource() {
return saltSource;
}
if (this.saltSource != null) {
salt = this.saltSource.getSalt(userDetails);
}
public UserDetailsService getUserDetailsService() {
return userDetailsService;
}
if (authentication.getCredentials() == null) {
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"),
includeDetailsObject ? userDetails : null);
}
protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
UserDetails loadedUser;
String presentedPassword = authentication.getCredentials() == null ? "" : authentication.getCredentials()
.toString();
try {
loadedUser = this.getUserDetailsService().loadUserByUsername(username);
} catch (DataAccessException repositoryProblem) {
throw new AuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem);
}
if (loadedUser == null) {
throw new AuthenticationServiceException(
"UserDetailsService returned null, which is an interface contract violation");
}
return loadedUser;
}
/**
* Sets the PasswordEncoder instance to be used to encode and validate passwords. If not set, {@link
* PlaintextPasswordEncoder} will be used by default.
*
* @param passwordEncoder The passwordEncoder to use
*/
public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
}
/**
* The source of salts to use when decoding passwords. <code>null</code> is a valid value, meaning the
* <code>DaoAuthenticationProvider</code> will present <code>null</code> to the relevant
* <code>PasswordEncoder</code>.
*
* @param saltSource to use when attempting to decode passwords via the <code>PasswordEncoder</code>
*/
public void setSaltSource(SaltSource saltSource) {
this.saltSource = saltSource;
}
public void setUserDetailsService(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
public boolean isIncludeDetailsObject() {
return includeDetailsObject;
}
public void setIncludeDetailsObject(boolean includeDetailsObject) {
this.includeDetailsObject = includeDetailsObject;
}
public void setOrder(int order) {
this.order = order;
if (!passwordEncoder.isPasswordValid(userDetails.getPassword(), presentedPassword, salt)) {
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"),
includeDetailsObject ? userDetails : null);
}
}
public int getOrder() {
return order ;
protected void doAfterPropertiesSet() throws Exception {
Assert.notNull(this.userDetailsService, "A UserDetailsService must be set");
}
/**
* Introspects the <code>Applicationcontext</code> for the single instance
* of {@link AccessDeniedHandler}. If found invoke
* setAccessDeniedHandler(AccessDeniedHandler accessDeniedHandler) method by
* providing the found instance of accessDeniedHandler as a method
* parameter. If more than one instance of <code>AccessDeniedHandler</code>
* is found, the method throws <code>IllegalStateException</code>.
*
* @param applicationContext to locate the instance
*/
private void autoDetectAnyUserDetailsServiceAndUseIt(ApplicationContext applicationContext) {
if (applicationContext != null) {
Map map = applicationContext.getBeansOfType(UserDetailsService.class);
if (map.size() > 1) {
throw new IllegalArgumentException(
"More than one UserDetailsService beans detected please refer to the one using "
+ " [ principalRepositoryBeanRef ] " + "attribute");
}
else if (map.size() == 1) {
setUserDetailsService((UserDetailsService) map.values().iterator().next());
}
}
}
public PasswordEncoder getPasswordEncoder() {
return passwordEncoder;
}
public SaltSource getSaltSource() {
return saltSource;
}
public UserDetailsService getUserDetailsService() {
return userDetailsService;
}
protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
UserDetails loadedUser;
try {
loadedUser = this.getUserDetailsService().loadUserByUsername(username);
}
catch (DataAccessException repositoryProblem) {
throw new AuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem);
}
if (loadedUser == null) {
throw new AuthenticationServiceException(
"UserDetailsService returned null, which is an interface contract violation");
}
return loadedUser;
}
/**
* Sets the PasswordEncoder instance to be used to encode and validate
* passwords. If not set, {@link PlaintextPasswordEncoder} will be used by
* default.
*
* @param passwordEncoder The passwordEncoder to use
*/
public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
}
/**
* The source of salts to use when decoding passwords. <code>null</code>
* is a valid value, meaning the <code>DaoAuthenticationProvider</code>
* will present <code>null</code> to the relevant
* <code>PasswordEncoder</code>.
*
* @param saltSource to use when attempting to decode passwords via the
* <code>PasswordEncoder</code>
*/
public void setSaltSource(SaltSource saltSource) {
this.saltSource = saltSource;
}
public void setUserDetailsService(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
public boolean isIncludeDetailsObject() {
return includeDetailsObject;
}
public void setIncludeDetailsObject(boolean includeDetailsObject) {
this.includeDetailsObject = includeDetailsObject;
}
}
@@ -15,9 +15,9 @@
package org.acegisecurity.providers.dao.cache;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.Element;
import net.sf.ehcache.Ehcache;
import org.acegisecurity.providers.dao.UserCache;
@@ -47,7 +47,7 @@ public class EhCacheBasedUserCache implements UserCache, InitializingBean {
//~ Instance fields ================================================================================================
private Cache cache;
private Ehcache cache;
//~ Methods ========================================================================================================
@@ -55,7 +55,7 @@ public class EhCacheBasedUserCache implements UserCache, InitializingBean {
Assert.notNull(cache, "cache mandatory");
}
public Cache getCache() {
public Ehcache getCache() {
return cache;
}
@@ -101,7 +101,7 @@ public class EhCacheBasedUserCache implements UserCache, InitializingBean {
cache.remove(username);
}
public void setCache(Cache cache) {
public void setCache(Ehcache cache) {
this.cache = cache;
}
}
@@ -36,12 +36,8 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.*;
import org.springframework.core.Ordered;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
@@ -141,32 +137,23 @@ import javax.security.auth.login.LoginException;
* @author Ray Krueger
* @version $Id$
*/
public class JaasAuthenticationProvider implements AuthenticationProvider, InitializingBean, ApplicationContextAware,
ApplicationListener, Ordered {
public class JaasAuthenticationProvider implements AuthenticationProvider, ApplicationEventPublisherAware,
InitializingBean, ApplicationListener {
//~ Static fields/initializers =====================================================================================
protected static final Log log = LogFactory.getLog(JaasAuthenticationProvider.class);
//~ Instance fields ================================================================================================
private ApplicationContext context;
private LoginExceptionResolver loginExceptionResolver = new DefaultLoginExceptionResolver();
private Resource loginConfig;
private String loginContextName = "ACEGI";
private AuthorityGranter[] authorityGranters;
private JaasAuthenticationCallbackHandler[] callbackHandlers;
private int order = -1; // default: same as non-Ordered
private ApplicationEventPublisher applicationEventPublisher;
//~ Methods ========================================================================================================
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(loginConfig, "loginConfig must be set on " + getClass());
Assert.hasLength(loginContextName, "loginContextName must be set on " + getClass());
@@ -256,10 +243,9 @@ public class JaasAuthenticationProvider implements AuthenticationProvider, Initi
*
* @param loginConfig URL to Jaas login configuration
*
* @throws IOException DOCUMENT ME!
* @throws IOException if there is a problem reading the config resource.
*/
protected void configureJaas(Resource loginConfig)
throws IOException {
protected void configureJaas(Resource loginConfig) throws IOException {
configureJaasUsingLoop();
}
@@ -267,7 +253,6 @@ public class JaasAuthenticationProvider implements AuthenticationProvider, Initi
* Loops through the login.config.url.1,login.config.url.2 properties looking for the login configuration.
* If it is not set, it will be set to the last available login.config.url.X property.
*
* @throws IOException DOCUMENT ME!
*/
private void configureJaasUsingLoop() throws IOException {
String loginConfigUrl = loginConfig.getURL().toString();
@@ -294,10 +279,6 @@ public class JaasAuthenticationProvider implements AuthenticationProvider, Initi
}
}
public ApplicationContext getApplicationContext() {
return context;
}
/**
* Returns the AuthorityGrannter array that was passed to the {@link
* #setAuthorityGranters(AuthorityGranter[])} method, or null if it none were ever set.
@@ -385,7 +366,7 @@ public class JaasAuthenticationProvider implements AuthenticationProvider, Initi
* @param ase The {@link AcegiSecurityException} that caused the failure
*/
protected void publishFailureEvent(UsernamePasswordAuthenticationToken token, AcegiSecurityException ase) {
getApplicationContext().publishEvent(new JaasAuthenticationFailedEvent(token, ase));
applicationEventPublisher.publishEvent(new JaasAuthenticationFailedEvent(token, ase));
}
/**
@@ -395,12 +376,7 @@ public class JaasAuthenticationProvider implements AuthenticationProvider, Initi
* @param token The {@link UsernamePasswordAuthenticationToken} being processed
*/
protected void publishSuccessEvent(UsernamePasswordAuthenticationToken token) {
getApplicationContext().publishEvent(new JaasAuthenticationSuccessEvent(token));
}
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.context = applicationContext;
applicationEventPublisher.publishEvent(new JaasAuthenticationSuccessEvent(token));
}
/**
@@ -455,6 +431,14 @@ public class JaasAuthenticationProvider implements AuthenticationProvider, Initi
return UsernamePasswordAuthenticationToken.class.isAssignableFrom(aClass);
}
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
protected ApplicationEventPublisher getApplicationEventPublisher() {
return applicationEventPublisher;
}
//~ Inner Classes ==================================================================================================
/**
@@ -19,6 +19,7 @@ import org.acegisecurity.AuthenticationException;
import org.acegisecurity.BadCredentialsException;
import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.AuthenticationServiceException;
import org.acegisecurity.ldap.LdapDataAccessException;
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.acegisecurity.providers.dao.AbstractUserDetailsAuthenticationProvider;
@@ -32,7 +33,6 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.core.Ordered;
import org.springframework.dao.DataAccessException;
@@ -114,7 +114,7 @@ import org.springframework.dao.DataAccessException;
* @see org.acegisecurity.providers.ldap.authenticator.BindAuthenticator
* @see org.acegisecurity.providers.ldap.populator.DefaultLdapAuthoritiesPopulator
*/
public class LdapAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider implements Ordered {
public class LdapAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
//~ Static fields/initializers =====================================================================================
private static final Log logger = LogFactory.getLog(LdapAuthenticationProvider.class);
@@ -124,20 +124,11 @@ public class LdapAuthenticationProvider extends AbstractUserDetailsAuthenticatio
private LdapAuthenticator authenticator;
private LdapAuthoritiesPopulator authoritiesPopulator;
private boolean includeDetailsObject = true;
private int order = -1; // default: same as non-Ordered
//~ Constructors ===================================================================================================
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
/**
* Create an initialized instance to the values passed as arguments
/**
* Create an instance with the supplied authenticator and authorities populator implementations.
*
* @param authenticator the authentication strategy (bind, password comparison, etc)
* to be used by this provider for authenticating users.
@@ -149,6 +140,17 @@ public class LdapAuthenticationProvider extends AbstractUserDetailsAuthenticatio
this.setAuthoritiesPopulator(authoritiesPopulator);
}
/**
* Creates an instance with the supplied authenticator and a null authorities populator.
* In this case, the authorities must be mapped from the user context.
*
* @param authenticator the authenticator strategy.
*/
public LdapAuthenticationProvider(LdapAuthenticator authenticator) {
this.setAuthenticator(authenticator);
this.setAuthoritiesPopulator(new NullAuthoritiesPopulator());
}
//~ Methods ========================================================================================================
private void setAuthenticator(LdapAuthenticator authenticator) {
@@ -244,4 +246,13 @@ public class LdapAuthenticationProvider extends AbstractUserDetailsAuthenticatio
public void setIncludeDetailsObject(boolean includeDetailsObject) {
this.includeDetailsObject = includeDetailsObject;
}
//~ Inner Classes ==================================================================================================
private static class NullAuthoritiesPopulator implements LdapAuthoritiesPopulator {
public GrantedAuthority[] getGrantedAuthorities(LdapUserDetails userDetails) throws LdapDataAccessException {
return new GrantedAuthority[0];
}
}
}
@@ -87,6 +87,7 @@ public class BindAuthenticator extends AbstractLdapAuthenticator {
LdapUserDetailsImpl.Essence user = (LdapUserDetailsImpl.Essence) template.retrieveEntry(userDn,
getUserDetailsMapper(), getUserAttributes());
user.setUsername(username);
user.setPassword(password);
return user.createUserDetails();
} catch (BadCredentialsException e) {
@@ -29,7 +29,8 @@ import java.security.MessageDigest;
/**
* A version of {@link ShaPasswordEncoder} which supports Ldap SHA and SSHA (salted-SHA) encodings. The values are
* base-64 encoded and have the label "{SHA}" (or "{SSHA}") prepended to the encoded hash.
* base-64 encoded and have the label "{SHA}" (or "{SSHA}") prepended to the encoded hash. These can be made lower-case
* in the encoded password, if required, by setting the <tt>forceLowerCasePrefix</tt> property to true.
*
* @author Luke Taylor
* @version $Id$
@@ -40,7 +41,12 @@ public class LdapShaPasswordEncoder implements PasswordEncoder {
/** The number of bytes in a SHA hash */
private static final int SHA_LENGTH = 20;
private static final String SSHA_PREFIX = "{SSHA}";
private static final String SSHA_PREFIX_LC = SSHA_PREFIX.toLowerCase();
private static final String SHA_PREFIX = "{SHA}";
private static final String SHA_PREFIX_LC = SHA_PREFIX.toLowerCase();
//~ Instance fields ================================================================================================
private boolean forceLowerCasePrefix;
//~ Constructors ===================================================================================================
@@ -76,7 +82,7 @@ public class LdapShaPasswordEncoder implements PasswordEncoder {
try {
sha = MessageDigest.getInstance("SHA");
} catch (java.security.NoSuchAlgorithmException e) {
throw new LdapDataAccessException("No SHA implementation available!");
throw new LdapDataAccessException("No SHA implementation available!", e);
}
sha.update(rawPass.getBytes());
@@ -88,7 +94,15 @@ public class LdapShaPasswordEncoder implements PasswordEncoder {
byte[] hash = combineHashAndSalt(sha.digest(), (byte[]) salt);
return ((salt == null) ? SHA_PREFIX : SSHA_PREFIX) + new String(Base64.encodeBase64(hash));
String prefix;
if (salt == null) {
prefix = forceLowerCasePrefix ? SHA_PREFIX_LC : SHA_PREFIX;
} else {
prefix = forceLowerCasePrefix ? SSHA_PREFIX_LC : SSHA_PREFIX;
}
return prefix + new String(Base64.encodeBase64(hash));
}
private byte[] extractSalt(String encPass) {
@@ -106,19 +120,28 @@ public class LdapShaPasswordEncoder implements PasswordEncoder {
* Checks the validity of an unencoded password against an encoded one in the form
* "{SSHA}sQuQF8vj8Eg2Y1hPdh3bkQhCKQBgjhQI".
*
* @param encPass the SSHA or SHA encoded password
* @param encPass the actual SSHA or SHA encoded password
* @param rawPass unencoded password to be verified.
* @param salt ignored. If the format is SSHA the salt bytes will be extracted from the encoded password.
*
* @return true if they match.
* @return true if they match (independent of the case of the prefix).
*/
public boolean isPasswordValid(String encPass, String rawPass, Object salt) {
if (encPass.startsWith(SSHA_PREFIX)) {
String encPassWithoutPrefix;
if (encPass.startsWith(SSHA_PREFIX) || encPass.startsWith(SSHA_PREFIX_LC)) {
encPassWithoutPrefix = encPass.substring(6);
salt = extractSalt(encPass);
} else {
encPassWithoutPrefix = encPass.substring(5);
salt = null;
}
return encPass.equals(encodePassword(rawPass, salt));
// Compare the encoded passwords without the prefix
return encodePassword(rawPass, salt).endsWith(encPassWithoutPrefix);
}
public void setForceLowerCasePrefix(boolean forceLowerCasePrefix) {
this.forceLowerCasePrefix = forceLowerCasePrefix;
}
}
@@ -40,9 +40,11 @@ import javax.naming.directory.SearchControls;
/**
* The default strategy for obtaining user role information from the directory.
* <p>It obtains roles by performing a search for "groups" the user is a member of.
* </p>
* <p>A typical group search scenario would be where each group/role is specified using the <tt>groupOfNames</tt>
* <p/>
* <p>It obtains roles by performing a search for "groups" the user is a member of.</p>
* <p/>
* <p/>
* A typical group search scenario would be where each group/role is specified using the <tt>groupOfNames</tt>
* (or <tt>groupOfUniqueNames</tt>) LDAP objectClass and the user's DN is listed in the <tt>member</tt> (or
* <tt>uniqueMember</tt>) attribute to indicate that they should be assigned that role. The following LDIF sample has
* the groups stored under the DN <tt>ou=groups,dc=acegisecurity,dc=org</tt> and a group called "developers" with
@@ -54,11 +56,14 @@ import javax.naming.directory.SearchControls;
* member: uid=ben,ou=people,dc=acegisecurity,dc=orgmember: uid=marissa,ou=people,dc=acegisecurity,dc=orgou: developer
* </pre>
* </p>
* <p>The group search is performed within a DN specified by the <tt>groupSearchBase</tt> property, which should
* <p/>
* The group search is performed within a DN specified by the <tt>groupSearchBase</tt> property, which should
* be relative to the root DN of its <tt>InitialDirContextFactory</tt>. If the search base is null, group searching is
* disabled. The filter used in the search is defined by the <tt>groupSearchFilter</tt> property, with the filter
* argument {0} being the full DN of the user. You can also specify which attribute defines the role name by setting
* argument {0} being the full DN of the user. You can also optionally use the parameter {1}, which will be substituted
* with the username. You can also specify which attribute defines the role name by setting
* the <tt>groupRoleAttribute</tt> property (the default is "cn").</p>
* <p/>
* <p>The configuration below shows how the group search might be performed with the above schema.
* <pre>
* &lt;bean id="ldapAuthoritiesPopulator"
@@ -73,7 +78,11 @@ import javax.naming.directory.SearchControls;
* &lt;/bean>
* </pre>
* A search for roles for user "uid=ben,ou=people,dc=acegisecurity,dc=org" would return the single granted authority
* "ROLE_DEVELOPER".</p>
* "ROLE_DEVELOPER".
* </p>
* <p/>
* The single-level search is performed by default. Setting the <tt>searchSubTree</tt> property to true will enable
* a search of the entire subtree under <tt>groupSearchBase</tt>.
*
* @author Luke Taylor
* @version $Id$
@@ -85,10 +94,14 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
//~ Instance fields ================================================================================================
/** A default role which will be assigned to all authenticated users if set */
/**
* A default role which will be assigned to all authenticated users if set
*/
private GrantedAuthority defaultRole = null;
/** An initial context factory is only required if searching for groups is required. */
/**
* An initial context factory is only required if searching for groups is required.
*/
private InitialDirContextFactory initialDirContextFactory = null;
private LdapTemplate ldapTemplate;
@@ -98,16 +111,24 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
*/
private SearchControls searchControls = new SearchControls();
/** The ID of the attribute which contains the role name for a group */
/**
* The ID of the attribute which contains the role name for a group
*/
private String groupRoleAttribute = "cn";
/** The base DN from which the search for group membership should be performed */
/**
* The base DN from which the search for group membership should be performed
*/
private String groupSearchBase = null;
/** The pattern to be used for the user search. {0} is the user's DN */
/**
* The pattern to be used for the user search. {0} is the user's DN
*/
private String groupSearchFilter = "(member={0})";
/** Attributes of the User's LDAP Object that contain role name information. */
/**
* Attributes of the User's LDAP Object that contain role name information.
*/
// private String[] userRoleAttributes = null;
private String rolePrefix = "ROLE_";
@@ -120,8 +141,8 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
* set as a property.
*
* @param initialDirContextFactory supplies the contexts used to search for user roles.
* @param groupSearchBase if this is an empty string the search will be performed from the root DN of the
* context factory.
* @param groupSearchBase if this is an empty string the search will be performed from the root DN of the
* context factory.
*/
public DefaultLdapAuthoritiesPopulator(InitialDirContextFactory initialDirContextFactory, String groupSearchBase) {
this.setInitialDirContextFactory(initialDirContextFactory);
@@ -135,7 +156,6 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
* roles for the given user (on top of those obtained from the standard
* search implemented by this class).
*
*
* @param ldapUser the user who's roles are required
* @return the extra roles which will be merged with those returned by the group search
*/
@@ -149,7 +169,6 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
* the supplied LdapUserDetails object.
*
* @param userDetails the user who's authorities are required
*
* @return the set of roles granted to the user.
*/
public final GrantedAuthority[] getGrantedAuthorities(LdapUserDetails userDetails) {
@@ -203,11 +222,11 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
if (logger.isDebugEnabled()) {
logger.debug("Searching for roles for user '" + username + "', DN = " + "'" + userDn + "', with filter "
+ groupSearchFilter + " in search base '" + getGroupSearchBase() + "'");
+ groupSearchFilter + " in search base '" + getGroupSearchBase() + "'");
}
Set userRoles = ldapTemplate.searchForSingleAttributeValues(getGroupSearchBase(), groupSearchFilter,
new String[] {userDn, username}, groupRoleAttribute);
new String[]{userDn, username}, groupRoleAttribute);
if (logger.isDebugEnabled()) {
logger.debug("Roles from search: " + userRoles);
@@ -231,12 +250,10 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
/**
* Searches for groups the user is a member of.
*
* @param userDn the user's distinguished name.
* @param userDn the user's distinguished name.
* @param userAttributes the retrieved user's attributes (unused by default).
*
* @return the set of roles obtained from a group membership search, or null if <tt>groupSearchBase</tt> has been
* set.
*
* @deprecated Subclasses should implement <tt>getAdditionalRoles</tt> instead.
*/
protected Set getGroupMembershipRoles(String userDn, Attributes userAttributes) {
@@ -264,18 +281,18 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
* Set the group search base (name to search under)
*
* @param groupSearchBase if this is an empty string the search will be performed from the root DN of the context
* factory.
* factory.
*/
private void setGroupSearchBase(String groupSearchBase) {
Assert.notNull(groupSearchBase, "The groupSearchBase (name to search under), must not be null.");
this.groupSearchBase = groupSearchBase;
if (groupSearchBase.length() == 0) {
logger.info("groupSearchBase is empty. Searches will be performed from the root: "
+ getInitialDirContextFactory().getRootDn());
+ getInitialDirContextFactory().getRootDn());
}
}
private String getGroupSearchBase() {
protected String getGroupSearchBase() {
return groupSearchBase;
}
@@ -308,6 +325,11 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
this.rolePrefix = rolePrefix;
}
/**
* If set to true, a subtree scope search will be performed. If false a single-level search is used.
*
* @param searchSubtree set to true to enable searching of the entire tree below the <tt>groupSearchBase</tt>.
*/
public void setSearchSubtree(boolean searchSubtree) {
int searchScope = searchSubtree ? SearchControls.SUBTREE_SCOPE : SearchControls.ONELEVEL_SCOPE;
searchControls.setSearchScope(searchScope);
@@ -23,7 +23,6 @@ import org.acegisecurity.providers.AuthenticationProvider;
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.Ordered;
import org.springframework.util.Assert;
@@ -44,22 +43,13 @@ import org.springframework.util.Assert;
* @author Ben Alex
* @version $Id$
*/
public class RemoteAuthenticationProvider implements AuthenticationProvider, InitializingBean, Ordered {
public class RemoteAuthenticationProvider implements AuthenticationProvider, InitializingBean {
//~ Instance fields ================================================================================================
private RemoteAuthenticationManager remoteAuthenticationManager;
private int order = -1; // default: same as non-Ordered
//~ Methods ========================================================================================================
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(this.remoteAuthenticationManager, "remoteAuthenticationManager is mandatory");
}
@@ -30,7 +30,6 @@ import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.core.Ordered;
import org.springframework.util.Assert;
@@ -41,7 +40,7 @@ import org.springframework.util.Assert;
* {@link org.acegisecurity.providers.rememberme.RememberMeAuthenticationToken#getKeyHash()} must match this class'
* {@link #getKey()}.</p>
*/
public class RememberMeAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware, Ordered {
public class RememberMeAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware {
//~ Static fields/initializers =====================================================================================
private static final Log logger = LogFactory.getLog(RememberMeAuthenticationProvider.class);
@@ -50,18 +49,9 @@ public class RememberMeAuthenticationProvider implements AuthenticationProvider,
protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor();
private String key;
private int order = -1; // default: same as non-Ordered
//~ Methods ========================================================================================================
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
public void afterPropertiesSet() throws Exception {
Assert.hasLength(key);
Assert.notNull(this.messages, "A message source must be set");
@@ -28,7 +28,6 @@ import org.acegisecurity.userdetails.UserDetails;
import org.acegisecurity.userdetails.UserDetailsService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.Ordered;
import org.springframework.dao.DataAccessException;
import org.springframework.util.Assert;
@@ -38,7 +37,7 @@ import org.springframework.util.Assert;
* @author Scott McCrory
* @version $Id: SiteminderAuthenticationProvider.java 1582 2006-07-15 15:18:51Z smccrory $
*/
public class SiteminderAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider implements Ordered {
public class SiteminderAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
/**
@@ -53,8 +52,6 @@ public class SiteminderAuthenticationProvider extends AbstractUserDetailsAuthent
*/
private UserDetailsService userDetailsService;
private int order = -1; // default: same as non-Ordered
//~ Methods ========================================================================================================
/**
@@ -133,12 +130,4 @@ public class SiteminderAuthenticationProvider extends AbstractUserDetailsAuthent
this.userDetailsService = userDetailsService;
}
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
}
@@ -33,7 +33,6 @@ import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.core.Ordered;
import org.springframework.util.Assert;
@@ -47,7 +46,7 @@ import java.security.cert.X509Certificate;
* @author Luke Taylor
* @version $Id$
*/
public class X509AuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware, Ordered {
public class X509AuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware {
//~ Static fields/initializers =====================================================================================
private static final Log logger = LogFactory.getLog(X509AuthenticationProvider.class);
@@ -57,18 +56,9 @@ public class X509AuthenticationProvider implements AuthenticationProvider, Initi
protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor();
private X509AuthoritiesPopulator x509AuthoritiesPopulator;
private X509UserCache userCache = new NullX509UserCache();
private int order = -1; // default: same as non-Ordered
//~ Methods ========================================================================================================
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(userCache, "An x509UserCache must be set");
Assert.notNull(x509AuthoritiesPopulator, "An X509AuthoritiesPopulator must be set");
@@ -15,9 +15,9 @@
package org.acegisecurity.providers.x509.cache;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.Element;
import net.sf.ehcache.Ehcache;
import org.acegisecurity.providers.x509.X509UserCache;
@@ -50,7 +50,7 @@ public class EhCacheBasedX509UserCache implements X509UserCache, InitializingBea
//~ Instance fields ================================================================================================
private Cache cache;
private Ehcache cache;
//~ Methods ========================================================================================================
@@ -102,7 +102,7 @@ public class EhCacheBasedX509UserCache implements X509UserCache, InitializingBea
cache.remove(userCert);
}
public void setCache(Cache cache) {
public void setCache(Ehcache cache) {
this.cache = cache;
}
}
@@ -18,6 +18,7 @@ package org.acegisecurity.providers.x509.populator;
import org.acegisecurity.AcegiMessageSource;
import org.acegisecurity.AuthenticationException;
import org.acegisecurity.BadCredentialsException;
import org.acegisecurity.AuthenticationServiceException;
import org.acegisecurity.providers.x509.X509AuthoritiesPopulator;
@@ -79,8 +80,7 @@ public class DaoX509AuthoritiesPopulator implements X509AuthoritiesPopulator, In
}
}
public UserDetails getUserDetails(X509Certificate clientCert)
throws AuthenticationException {
public UserDetails getUserDetails(X509Certificate clientCert) throws AuthenticationException {
String subjectDN = clientCert.getSubjectDN().getName();
PatternMatcher matcher = new Perl5Matcher();
@@ -97,7 +97,14 @@ public class DaoX509AuthoritiesPopulator implements X509AuthoritiesPopulator, In
String userName = match.group(1);
return this.userDetailsService.loadUserByUsername(userName);
UserDetails user = this.userDetailsService.loadUserByUsername(userName);
if (user == null) {
throw new AuthenticationServiceException(
"UserDetailsService returned null, which is an interface contract violation");
}
return user;
}
public void setMessageSource(MessageSource messageSource) {
@@ -106,9 +113,10 @@ public class DaoX509AuthoritiesPopulator implements X509AuthoritiesPopulator, In
/**
* Sets the regular expression which will by used to extract the user name from the certificate's Subject
* DN.<p>It should contain a single group; for example the default expression "CN=(.?)," matches the common
* DN.
* <p>It should contain a single group; for example the default expression "CN=(.?)," matches the common
* name field. So "CN=Jimi Hendrix, OU=..." will give a user name of "Jimi Hendrix".</p>
* <p>The matches are case insensitive. So "emailAddress=(.?)," will match "EMAILADDRESS=jimi@hendrix.org,
* <p>The matches are case insensitive. So "emailAddress=(.?)," will match "EMAILADDRESS=jimi@hendrix.org,
* CN=..." giving a user name "jimi@hendrix.org"</p>
*
* @param subjectDNRegex the regular expression to find in the subject
@@ -27,7 +27,6 @@ import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.core.Ordered;
import org.springframework.util.Assert;
@@ -39,23 +38,14 @@ import org.springframework.util.Assert;
* <code>RunAsImplAuthenticationProvider</code>-configured key.</p>
* <P>If the key does not match, a <code>BadCredentialsException</code> is thrown.</p>
*/
public class RunAsImplAuthenticationProvider implements InitializingBean, AuthenticationProvider, MessageSourceAware, Ordered {
public class RunAsImplAuthenticationProvider implements InitializingBean, AuthenticationProvider, MessageSourceAware {
//~ Instance fields ================================================================================================
protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor();
private String key;
private int order = -1; // default: same as non-Ordered
//~ Methods ========================================================================================================
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(key, "A Key is required and should match that configured for the RunAsManagerImpl");
}
@@ -54,424 +54,494 @@ import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Abstract processor of browser-based HTTP-based authentication requests.<p>This filter is responsible for
* processing authentication requests. If authentication is successful, the resulting {@link Authentication} object
* will be placed into the <code>SecurityContext</code>, which is guaranteed to have already been created by an
* earlier filter.</p>
* <p>If authentication fails, the <code>AuthenticationException</code> will be placed into the
* <code>HttpSession</code> with the attribute defined by {@link #ACEGI_SECURITY_LAST_EXCEPTION_KEY}.</p>
* <p>To use this filter, it is necessary to specify the following properties:</p>
* <ul>
* <li><code>defaultTargetUrl</code> indicates the URL that should be used for redirection if the
* <code>HttpSession</code> attribute named {@link #ACEGI_SAVED_REQUEST_KEY} does not indicate the target URL once
* authentication is completed successfully. eg: <code>/</code>. The <code>defaultTargetUrl</code> will be treated
* as relative to the web-app's context path, and should include the leading <code>/</code>. Alternatively,
* inclusion of a scheme name (eg http:// or https://) as the prefix will denote a fully-qualified URL and this is
* also supported.</li>
* <li><code>authenticationFailureUrl</code> indicates the URL that should be used for redirection if the
* authentication request fails. eg: <code>/login.jsp?login_error=1</code>.</li>
* <li><code>filterProcessesUrl</code> indicates the URL that this filter will respond to. This parameter
* varies by subclass.</li>
* <li><code>alwaysUseDefaultTargetUrl</code> causes successful authentication to always redirect to the
* <code>defaultTargetUrl</code>, even if the <code>HttpSession</code> attribute named {@link
* #ACEGI_SAVED_REQUEST_KEY} defines the intended target URL.</li>
* </ul>
* <p>To configure this filter to redirect to specific pages as the result of specific {@link
* AuthenticationException}s you can do the following. Configure the <code>exceptionMappings</code> property in your
* application xml. This property is a java.util.Properties object that maps a fully-qualified exception class name to
* a redirection url target.
* For example:
* Abstract processor of browser-based HTTP-based authentication requests.
* <p>
* This filter is responsible for processing authentication requests. If
* authentication is successful, the resulting {@link Authentication} object
* will be placed into the <code>SecurityContext</code>, which is guaranteed
* to have already been created by an earlier filter.
* </p>
* <p>
* If authentication fails, the <code>AuthenticationException</code> will be
* placed into the <code>HttpSession</code> with the attribute defined by
* {@link #ACEGI_SECURITY_LAST_EXCEPTION_KEY}.
* </p>
* <p>
* To use this filter, it is necessary to specify the following properties:
* </p>
* <ul>
* <li><code>defaultTargetUrl</code> indicates the URL that should be used
* for redirection if the <code>HttpSession</code> attribute named
* {@link #ACEGI_SAVED_REQUEST_KEY} does not indicate the target URL once
* authentication is completed successfully. eg: <code>/</code>. The
* <code>defaultTargetUrl</code> will be treated as relative to the web-app's
* context path, and should include the leading <code>/</code>.
* Alternatively, inclusion of a scheme name (eg http:// or https://) as the
* prefix will denote a fully-qualified URL and this is also supported.</li>
* <li><code>authenticationFailureUrl</code> indicates the URL that should be
* used for redirection if the authentication request fails. eg:
* <code>/login.jsp?login_error=1</code>.</li>
* <li><code>filterProcessesUrl</code> indicates the URL that this filter
* will respond to. This parameter varies by subclass.</li>
* <li><code>alwaysUseDefaultTargetUrl</code> causes successful
* authentication to always redirect to the <code>defaultTargetUrl</code>,
* even if the <code>HttpSession</code> attribute named {@link
* #ACEGI_SAVED_REQUEST_KEY} defines the intended target URL.</li>
* </ul>
* <p>
* To configure this filter to redirect to specific pages as the result of
* specific {@link AuthenticationException}s you can do the following.
* Configure the <code>exceptionMappings</code> property in your application
* xml. This property is a java.util.Properties object that maps a
* fully-qualified exception class name to a redirection url target. For
* example:
*
* <pre>
* &lt;property name="exceptionMappings"&gt;
* &lt;property name=&quot;exceptionMappings&quot;&gt;
* &lt;props&gt;
* &lt;prop&gt; key="org.acegisecurity.BadCredentialsException"&gt;/bad_credentials.jsp&lt;/prop&gt;
* &lt;prop&gt; key=&quot;org.acegisecurity.BadCredentialsException&quot;&gt;/bad_credentials.jsp&lt;/prop&gt;
* &lt;/props&gt;
* &lt;/property&gt;
* </pre>
* The example above would redirect all {@link org.acegisecurity.BadCredentialsException}s thrown, to a page in the
* web-application called /bad_credentials.jsp.</p>
* <p>Any {@link AuthenticationException} thrown that cannot be matched in the <code>exceptionMappings</code> will
* be redirected to the <code>authenticationFailureUrl</code></p>
* <p>If authentication is successful, an {@link
* org.acegisecurity.event.authentication.InteractiveAuthenticationSuccessEvent} will be published to the application
* context. No events will be published if authentication was unsuccessful, because this would generally be recorded
* via an <code>AuthenticationManager</code>-specific application event.</p>
*
*
* The example above would redirect all
* {@link org.acegisecurity.BadCredentialsException}s thrown, to a page in the
* web-application called /bad_credentials.jsp.
* </p>
* <p>
* Any {@link AuthenticationException} thrown that cannot be matched in the
* <code>exceptionMappings</code> will be redirected to the
* <code>authenticationFailureUrl</code>
* </p>
* <p>
* If authentication is successful, an {@link
* org.acegisecurity.event.authentication.InteractiveAuthenticationSuccessEvent}
* will be published to the application context. No events will be published if
* authentication was unsuccessful, because this would generally be recorded via
* an <code>AuthenticationManager</code>-specific application event.
* </p>
*
* @author Ben Alex
* @version $Id$
* @version $Id: AbstractProcessingFilter.java 1909 2007-06-19 04:08:19Z
* vishalpuri $
*/
public abstract class AbstractProcessingFilter implements Filter, InitializingBean, ApplicationEventPublisherAware,
MessageSourceAware {
//~ Static fields/initializers =====================================================================================
public static final String ACEGI_SAVED_REQUEST_KEY = "ACEGI_SAVED_REQUEST_KEY";
public static final String ACEGI_SECURITY_LAST_EXCEPTION_KEY = "ACEGI_SECURITY_LAST_EXCEPTION";
//~ Instance fields ================================================================================================
protected ApplicationEventPublisher eventPublisher;
protected AuthenticationDetailsSource authenticationDetailsSource = new AuthenticationDetailsSourceImpl();
private AuthenticationManager authenticationManager;
protected final Log logger = LogFactory.getLog(this.getClass());
protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor();
private Properties exceptionMappings = new Properties();
private RememberMeServices rememberMeServices = new NullRememberMeServices();
/** Where to redirect the browser to if authentication fails */
private String authenticationFailureUrl;
/**
* Where to redirect the browser to if authentication is successful but ACEGI_SAVED_REQUEST_KEY is
* <code>null</code>
*/
private String defaultTargetUrl;
/**
* The URL destination that this filter intercepts and processes (usually something like
* <code>/j_acegi_security_check</code>)
*/
private String filterProcessesUrl = getDefaultFilterProcessesUrl();
/**
* If <code>true</code>, will always redirect to the value of {@link #getDefaultTargetUrl} upon successful
* authentication, irrespective of the page that caused the authentication request (defaults to <code>false</code>).
*/
private boolean alwaysUseDefaultTargetUrl = false;
/**
* Indicates if the filter chain should be continued prior to delegation to {@link
* #successfulAuthentication(HttpServletRequest, HttpServletResponse, Authentication)}, which may be useful in
* certain environment (eg Tapestry). Defaults to <code>false</code>.
*/
private boolean continueChainBeforeSuccessfulAuthentication = false;
/**
* Specifies the buffer size to use in the event of a directory. A buffer size is used to ensure the
* response is not written back to the client immediately. This provides a way for the <code>HttpSession</code>
* to be updated before the browser redirect will be sent. Defaults to an 8 Kb buffer.
*/
private int bufferSize = 8 * 1024;
/**
* If true, causes any redirection URLs to be calculated minus the protocol and context path (defaults to false).
*/
private boolean useRelativeContext = false;
//~ Methods ========================================================================================================
public void afterPropertiesSet() throws Exception {
Assert.hasLength(filterProcessesUrl, "filterProcessesUrl must be specified");
Assert.hasLength(defaultTargetUrl, "defaultTargetUrl must be specified");
Assert.hasLength(authenticationFailureUrl, "authenticationFailureUrl must be specified");
Assert.notNull(authenticationManager, "authenticationManager must be specified");
Assert.notNull(this.rememberMeServices);
}
/**
* Performs actual authentication.
*
* @param request from which to extract parameters and perform the authentication
*
* @return the authenticated user
*
* @throws AuthenticationException if authentication fails
*/
public abstract Authentication attemptAuthentication(HttpServletRequest request)
throws AuthenticationException;
/**
* Does nothing. We use IoC container lifecycle services instead.
*/
public void destroy() {}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (!(request instanceof HttpServletRequest)) {
throw new ServletException("Can only process HttpServletRequest");
}
if (!(response instanceof HttpServletResponse)) {
throw new ServletException("Can only process HttpServletResponse");
}
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
if (requiresAuthentication(httpRequest, httpResponse)) {
if (logger.isDebugEnabled()) {
logger.debug("Request is to process authentication");
}
Authentication authResult;
try {
onPreAuthentication(httpRequest, httpResponse);
authResult = attemptAuthentication(httpRequest);
} catch (AuthenticationException failed) {
// Authentication failed
unsuccessfulAuthentication(httpRequest, httpResponse, failed);
return;
}
// Authentication success
if (continueChainBeforeSuccessfulAuthentication) {
chain.doFilter(request, response);
}
successfulAuthentication(httpRequest, httpResponse, authResult);
return;
}
chain.doFilter(request, response);
}
public String getAuthenticationFailureUrl() {
return authenticationFailureUrl;
}
public AuthenticationManager getAuthenticationManager() {
return authenticationManager;
}
/**
* Specifies the default <code>filterProcessesUrl</code> for the implementation.
*
* @return the default <code>filterProcessesUrl</code>
*/
public abstract String getDefaultFilterProcessesUrl();
/**
* Supplies the default target Url that will be used if no saved request is found or the
* <tt>alwaysUseDefaultTargetUrl</tt> propert is set to true.
* Override this method of you want to provide a customized default Url (for example if you want different Urls
* depending on the authorities of the user who has just logged in).
*
* @return the defaultTargetUrl property
*/
public String getDefaultTargetUrl() {
return defaultTargetUrl;
}
public Properties getExceptionMappings() {
return new Properties(exceptionMappings);
}
public String getFilterProcessesUrl() {
return filterProcessesUrl;
}
public RememberMeServices getRememberMeServices() {
return rememberMeServices;
}
/**
* Does nothing. We use IoC container lifecycle services instead.
*
* @param arg0 ignored
*
* @throws ServletException ignored
*/
public void init(FilterConfig arg0) throws ServletException {}
public boolean isAlwaysUseDefaultTargetUrl() {
return alwaysUseDefaultTargetUrl;
}
public boolean isContinueChainBeforeSuccessfulAuthentication() {
return continueChainBeforeSuccessfulAuthentication;
}
public static String obtainFullRequestUrl(HttpServletRequest request) {
SavedRequest savedRequest = (SavedRequest) request.getSession()
.getAttribute(AbstractProcessingFilter.ACEGI_SAVED_REQUEST_KEY);
return (savedRequest == null) ? null : savedRequest.getFullRequestUrl();
}
protected void onPreAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException, IOException {}
protected void onSuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
Authentication authResult) throws IOException {}
protected void onUnsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
AuthenticationException failed) throws IOException {}
/**
* <p>Indicates whether this filter should attempt to process a login request for the current invocation.</p>
* <p>It strips any parameters from the "path" section of the request URL (such as the jsessionid
* parameter in <em>http://host/myapp/index.html;jsessionid=blah</em>) before matching against the
* <code>filterProcessesUrl</code> property.</p>
* <p>Subclasses may override for special requirements, such as Tapestry integration.</p>
*
* @param request as received from the filter chain
* @param response as received from the filter chain
*
* @return <code>true</code> if the filter should attempt authentication, <code>false</code> otherwise
*/
protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
String uri = request.getRequestURI();
int pathParamIndex = uri.indexOf(';');
if (pathParamIndex > 0) {
// strip everything after the first semi-colon
uri = uri.substring(0, pathParamIndex);
}
if ("".equals(request.getContextPath())) {
return uri.endsWith(filterProcessesUrl);
}
return uri.endsWith(request.getContextPath() + filterProcessesUrl);
}
protected void sendRedirect(HttpServletRequest request, HttpServletResponse response, String url)
throws IOException {
String finalUrl;
if (!url.startsWith("http://") && !url.startsWith("https://")) {
if (useRelativeContext) {
finalUrl = url;
} else {
finalUrl = request.getContextPath() + url;
}
} else if (useRelativeContext) {
// Calculate the relative URL from the fully qualifed URL, minus the protocol and base context.
int len = request.getContextPath().length();
int index = url.indexOf(request.getContextPath()) + len;
finalUrl = url.substring(index);
if (finalUrl.length() > 1 && finalUrl.charAt(0) == '/') {
finalUrl = finalUrl.substring( 1 );
}
} else {
finalUrl = url;
}
Assert.isTrue(!response.isCommitted(), "Response already committed; the authentication mechanism must be able to modify buffer size");
response.setBufferSize(bufferSize);
response.sendRedirect(response.encodeRedirectURL(finalUrl));
}
public void setAlwaysUseDefaultTargetUrl(boolean alwaysUseDefaultTargetUrl) {
this.alwaysUseDefaultTargetUrl = alwaysUseDefaultTargetUrl;
}
public void setApplicationEventPublisher(ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) {
Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource required");
this.authenticationDetailsSource = authenticationDetailsSource;
}
public void setAuthenticationFailureUrl(String authenticationFailureUrl) {
this.authenticationFailureUrl = authenticationFailureUrl;
}
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
public void setContinueChainBeforeSuccessfulAuthentication(boolean continueChainBeforeSuccessfulAuthentication) {
this.continueChainBeforeSuccessfulAuthentication = continueChainBeforeSuccessfulAuthentication;
}
public void setDefaultTargetUrl(String defaultTargetUrl) {
Assert.isTrue(defaultTargetUrl.startsWith("/") | defaultTargetUrl.startsWith("http"),
"defaultTarget must start with '/' or with 'http(s)'");
this.defaultTargetUrl = defaultTargetUrl;
}
public void setExceptionMappings(Properties exceptionMappings) {
this.exceptionMappings = exceptionMappings;
}
public void setFilterProcessesUrl(String filterProcessesUrl) {
this.filterProcessesUrl = filterProcessesUrl;
}
public void setMessageSource(MessageSource messageSource) {
this.messages = new MessageSourceAccessor(messageSource);
}
public void setRememberMeServices(RememberMeServices rememberMeServices) {
this.rememberMeServices = rememberMeServices;
}
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
Authentication authResult) throws IOException {
if (logger.isDebugEnabled()) {
logger.debug("Authentication success: " + authResult.toString());
}
SecurityContextHolder.getContext().setAuthentication(authResult);
if (logger.isDebugEnabled()) {
logger.debug("Updated SecurityContextHolder to contain the following Authentication: '" + authResult + "'");
}
String targetUrl = determineTargetUrl(request);
if (logger.isDebugEnabled()) {
logger.debug("Redirecting to target URL from HTTP Session (or default): " + targetUrl);
}
onSuccessfulAuthentication(request, response, authResult);
rememberMeServices.loginSuccess(request, response, authResult);
// Fire event
if (this.eventPublisher != null) {
eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
}
sendRedirect(request, response, targetUrl);
}
protected String determineTargetUrl(HttpServletRequest request) {
// Don't attempt to obtain the url from the saved request if alwaysUsedefaultTargetUrl is set
String targetUrl = alwaysUseDefaultTargetUrl ? null : obtainFullRequestUrl(request);
if (targetUrl == null) {
targetUrl = getDefaultTargetUrl();
}
return targetUrl;
}
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
AuthenticationException failed) throws IOException {
SecurityContextHolder.getContext().setAuthentication(null);
MessageSourceAware {
// ~ Static fields/initializers
// =====================================================================================
public static final String ACEGI_SAVED_REQUEST_KEY = "ACEGI_SAVED_REQUEST_KEY";
public static final String ACEGI_SECURITY_LAST_EXCEPTION_KEY = "ACEGI_SECURITY_LAST_EXCEPTION";
// ~ Instance fields
// ================================================================================================
protected ApplicationEventPublisher eventPublisher;
protected AuthenticationDetailsSource authenticationDetailsSource = new AuthenticationDetailsSourceImpl();
private AuthenticationManager authenticationManager;
protected final Log logger = LogFactory.getLog(this.getClass());
protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor();
private Properties exceptionMappings = new Properties();
private RememberMeServices rememberMeServices = new NullRememberMeServices();
/** Where to redirect the browser to if authentication fails */
private String authenticationFailureUrl;
/**
* Where to redirect the browser to if authentication is successful but
* ACEGI_SAVED_REQUEST_KEY is <code>null</code>
*/
private String defaultTargetUrl;
/**
* The URL destination that this filter intercepts and processes (usually
* something like <code>/j_acegi_security_check</code>)
*/
private String filterProcessesUrl = getDefaultFilterProcessesUrl();
/**
* If <code>true</code>, will always redirect to the value of
* {@link #getDefaultTargetUrl} upon successful authentication, irrespective
* of the page that caused the authentication request (defaults to
* <code>false</code>).
*/
private boolean alwaysUseDefaultTargetUrl = false;
/**
* Indicates if the filter chain should be continued prior to delegation to
* {@link #successfulAuthentication(HttpServletRequest, HttpServletResponse,
* Authentication)}, which may be useful in certain environment (eg
* Tapestry). Defaults to <code>false</code>.
*/
private boolean continueChainBeforeSuccessfulAuthentication = false;
/**
* Specifies the buffer size to use in the event of a directory. A buffer
* size is used to ensure the response is not written back to the client
* immediately. This provides a way for the <code>HttpSession</code> to be
* updated before the browser redirect will be sent. Defaults to an 8 Kb
* buffer.
*/
private int bufferSize = 8 * 1024;
/**
* If true, causes any redirection URLs to be calculated minus the protocol
* and context path (defaults to false).
*/
private boolean useRelativeContext = false;
// ~ Methods
// ========================================================================================================
public void afterPropertiesSet() throws Exception {
Assert.hasLength(filterProcessesUrl, "filterProcessesUrl must be specified");
Assert.hasLength(defaultTargetUrl, "defaultTargetUrl must be specified");
Assert.hasLength(authenticationFailureUrl, "authenticationFailureUrl must be specified");
Assert.notNull(authenticationManager, "authenticationManager must be specified");
Assert.notNull(this.rememberMeServices);
}
/**
* Performs actual authentication.
*
* @param request from which to extract parameters and perform the
* authentication
*
* @return the authenticated user
*
* @throws AuthenticationException if authentication fails
*/
public abstract Authentication attemptAuthentication(HttpServletRequest request) throws AuthenticationException;
/**
* Does nothing. We use IoC container lifecycle services instead.
*/
public void destroy() {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
ServletException {
if (!(request instanceof HttpServletRequest)) {
throw new ServletException("Can only process HttpServletRequest");
}
if (!(response instanceof HttpServletResponse)) {
throw new ServletException("Can only process HttpServletResponse");
}
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
if (requiresAuthentication(httpRequest, httpResponse)) {
if (logger.isDebugEnabled()) {
logger.debug("Request is to process authentication");
}
Authentication authResult;
try {
onPreAuthentication(httpRequest, httpResponse);
authResult = attemptAuthentication(httpRequest);
}
catch (AuthenticationException failed) {
// Authentication failed
unsuccessfulAuthentication(httpRequest, httpResponse, failed);
return;
}
// Authentication success
if (continueChainBeforeSuccessfulAuthentication) {
chain.doFilter(request, response);
}
successfulAuthentication(httpRequest, httpResponse, authResult);
return;
}
chain.doFilter(request, response);
}
public String getAuthenticationFailureUrl() {
return authenticationFailureUrl;
}
public AuthenticationManager getAuthenticationManager() {
return authenticationManager;
}
/**
* Specifies the default <code>filterProcessesUrl</code> for the
* implementation.
*
* @return the default <code>filterProcessesUrl</code>
*/
public abstract String getDefaultFilterProcessesUrl();
/**
* Supplies the default target Url that will be used if no saved request is
* found or the <tt>alwaysUseDefaultTargetUrl</tt> propert is set to true.
* Override this method of you want to provide a customized default Url (for
* example if you want different Urls depending on the authorities of the
* user who has just logged in).
*
* @return the defaultTargetUrl property
*/
public String getDefaultTargetUrl() {
return defaultTargetUrl;
}
public Properties getExceptionMappings() {
return new Properties(exceptionMappings);
}
public String getFilterProcessesUrl() {
return filterProcessesUrl;
}
public RememberMeServices getRememberMeServices() {
return rememberMeServices;
}
/**
* Does nothing. We use IoC container lifecycle services instead.
*
* @param arg0 ignored
*
* @throws ServletException ignored
*/
public void init(FilterConfig arg0) throws ServletException {
}
public boolean isAlwaysUseDefaultTargetUrl() {
return alwaysUseDefaultTargetUrl;
}
public boolean isContinueChainBeforeSuccessfulAuthentication() {
return continueChainBeforeSuccessfulAuthentication;
}
public static String obtainFullRequestUrl(HttpServletRequest request) {
SavedRequest savedRequest = (SavedRequest) request.getSession().getAttribute(
AbstractProcessingFilter.ACEGI_SAVED_REQUEST_KEY);
return (savedRequest == null) ? null : savedRequest.getFullRequestUrl();
}
protected void onPreAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException, IOException {
}
protected void onSuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
Authentication authResult) throws IOException {
}
protected void onUnsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
AuthenticationException failed) throws IOException {
}
/**
* <p>
* Indicates whether this filter should attempt to process a login request
* for the current invocation.
* </p>
* <p>
* It strips any parameters from the "path" section of the request URL (such
* as the jsessionid parameter in
* <em>http://host/myapp/index.html;jsessionid=blah</em>) before matching
* against the <code>filterProcessesUrl</code> property.
* </p>
* <p>
* Subclasses may override for special requirements, such as Tapestry
* integration.
* </p>
*
* @param request as received from the filter chain
* @param response as received from the filter chain
*
* @return <code>true</code> if the filter should attempt authentication,
* <code>false</code> otherwise
*/
protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
String uri = request.getRequestURI();
int pathParamIndex = uri.indexOf(';');
if (pathParamIndex > 0) {
// strip everything after the first semi-colon
uri = uri.substring(0, pathParamIndex);
}
if ("".equals(request.getContextPath())) {
return uri.endsWith(filterProcessesUrl);
}
return uri.endsWith(request.getContextPath() + filterProcessesUrl);
}
protected void sendRedirect(HttpServletRequest request, HttpServletResponse response, String url)
throws IOException {
String finalUrl;
if (!url.startsWith("http://") && !url.startsWith("https://")) {
if (useRelativeContext) {
finalUrl = url;
}
else {
finalUrl = request.getContextPath() + url;
}
}
else if (useRelativeContext) {
// Calculate the relative URL from the fully qualifed URL, minus the
// protocol and base context.
int len = request.getContextPath().length();
int index = url.indexOf(request.getContextPath()) + len;
finalUrl = url.substring(index);
if (finalUrl.length() > 1 && finalUrl.charAt(0) == '/') {
finalUrl = finalUrl.substring(1);
}
}
else {
finalUrl = url;
}
Assert.isTrue(!response.isCommitted(),
"Response already committed; the authentication mechanism must be able to modify buffer size");
response.setBufferSize(bufferSize);
response.sendRedirect(response.encodeRedirectURL(finalUrl));
}
public void setAlwaysUseDefaultTargetUrl(boolean alwaysUseDefaultTargetUrl) {
this.alwaysUseDefaultTargetUrl = alwaysUseDefaultTargetUrl;
}
public void setApplicationEventPublisher(ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) {
Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource required");
this.authenticationDetailsSource = authenticationDetailsSource;
}
public void setAuthenticationFailureUrl(String authenticationFailureUrl) {
this.authenticationFailureUrl = authenticationFailureUrl;
}
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
public void setContinueChainBeforeSuccessfulAuthentication(boolean continueChainBeforeSuccessfulAuthentication) {
this.continueChainBeforeSuccessfulAuthentication = continueChainBeforeSuccessfulAuthentication;
}
public void setDefaultTargetUrl(String defaultTargetUrl) {
Assert.isTrue(defaultTargetUrl.startsWith("/") | defaultTargetUrl.startsWith("http"),
"defaultTarget must start with '/' or with 'http(s)'");
this.defaultTargetUrl = defaultTargetUrl;
}
public void setExceptionMappings(Properties exceptionMappings) {
this.exceptionMappings = exceptionMappings;
}
public void setFilterProcessesUrl(String filterProcessesUrl) {
this.filterProcessesUrl = filterProcessesUrl;
}
public void setMessageSource(MessageSource messageSource) {
this.messages = new MessageSourceAccessor(messageSource);
}
public void setRememberMeServices(RememberMeServices rememberMeServices) {
this.rememberMeServices = rememberMeServices;
}
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
Authentication authResult) throws IOException {
if (logger.isDebugEnabled()) {
logger.debug("Authentication success: " + authResult.toString());
}
SecurityContextHolder.getContext().setAuthentication(authResult);
if (logger.isDebugEnabled()) {
logger.debug("Updated SecurityContextHolder to contain the following Authentication: '" + authResult + "'");
}
String targetUrl = determineTargetUrl(request);
if (logger.isDebugEnabled()) {
logger.debug("Redirecting to target URL from HTTP Session (or default): " + targetUrl);
}
onSuccessfulAuthentication(request, response, authResult);
rememberMeServices.loginSuccess(request, response, authResult);
// Fire event
if (this.eventPublisher != null) {
eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
}
sendRedirect(request, response, targetUrl);
}
protected String determineTargetUrl(HttpServletRequest request) {
// Don't attempt to obtain the url from the saved request if
// alwaysUsedefaultTargetUrl is set
String targetUrl = alwaysUseDefaultTargetUrl ? null : obtainFullRequestUrl(request);
if (targetUrl == null) {
targetUrl = getDefaultTargetUrl();
}
if (logger.isDebugEnabled()) {
logger.debug("Updated SecurityContextHolder to contain null Authentication");
}
return targetUrl;
}
String failureUrl = exceptionMappings.getProperty(failed.getClass().getName(), authenticationFailureUrl);
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
AuthenticationException failed) throws IOException {
SecurityContextHolder.getContext().setAuthentication(null);
if (logger.isDebugEnabled()) {
logger.debug("Authentication request failed: " + failed.toString());
}
if (logger.isDebugEnabled()) {
logger.debug("Updated SecurityContextHolder to contain null Authentication");
}
try {
request.getSession().setAttribute(ACEGI_SECURITY_LAST_EXCEPTION_KEY, failed);
} catch (Exception ignored) {}
String failureUrl = determineFailureUrl(request, failed);
onUnsuccessfulAuthentication(request, response, failed);
if (logger.isDebugEnabled()) {
logger.debug("Authentication request failed: " + failed.toString());
}
rememberMeServices.loginFail(request, response);
try {
request.getSession().setAttribute(ACEGI_SECURITY_LAST_EXCEPTION_KEY, failed);
}
catch (Exception ignored) {
}
sendRedirect(request, response, failureUrl);
onUnsuccessfulAuthentication(request, response, failed);
rememberMeServices.loginFail(request, response);
sendRedirect(request, response, failureUrl);
}
protected String determineFailureUrl(HttpServletRequest request, AuthenticationException failed) {
return exceptionMappings.getProperty(failed.getClass().getName(), authenticationFailureUrl);
}
public AuthenticationDetailsSource getAuthenticationDetailsSource() {
// Required due to SEC-310
return authenticationDetailsSource;
}
// Required due to SEC-310
return authenticationDetailsSource;
}
public void setBufferSize(int bufferSize) {
this.bufferSize = bufferSize;
@@ -480,4 +550,5 @@ public abstract class AbstractProcessingFilter implements Filter, InitializingBe
public void setUseRelativeContext(boolean useRelativeContext) {
this.useRelativeContext = useRelativeContext;
}
}
@@ -33,10 +33,12 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.util.Assert;
import java.io.IOException;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
@@ -47,185 +49,210 @@ import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Handles any <code>AccessDeniedException</code> and <code>AuthenticationException</code> thrown within the filter
* chain.<p>This filter is necessary because it provides the bridge between Java exceptions and HTTP responses. It
* is solely concerned with maintaining the user interface. This filter does not do any actual security enforcement.</p>
* <p>If an {@link AuthenticationException} is detected, the filter will launch the
* <code>authenticationEntryPoint</code>. This allows common handling of authentication failures originating from any
* subclass of {@link org.acegisecurity.intercept.AbstractSecurityInterceptor}.</p>
* <p>If an {@link AccessDeniedException} is detected, the filter will determine whether or not the user is an
* anonymous user. If they are an anonymous user, the <code>authenticationEntryPoint</code> will be launched. If they
* are not an anonymous user, the filter will delegate to the {@link org.acegisecurity.ui.AccessDeniedHandler}. By
* default the filter will use {@link org.acegisecurity.ui.AccessDeniedHandlerImpl}.</p>
* <p>To use this filter, it is necessary to specify the following properties:</p>
* <ul>
* <li><code>authenticationEntryPoint</code> indicates the handler that should commence the authentication
* process if an <code>AuthenticationException</code> is detected. Note that this may also switch the current
* protocol from http to https for an SSL login.</li>
* <li><code>portResolver</code> is used to determine the "real" port that a request was received on.</li>
* </ul>
* <P><B>Do not use this class directly.</B> Instead configure <code>web.xml</code> to use the {@link
* org.acegisecurity.util.FilterToBeanProxy}.</p>
* Handles any <code>AccessDeniedException</code> and <code>AuthenticationException</code> thrown within the
* filter chain.
* <p>
* This filter is necessary because it provides the bridge between Java exceptions and HTTP responses.
* It is solely concerned with maintaining the user interface. This filter does not do any actual security enforcement.
* </p>
* <p>
* If an {@link AuthenticationException} is detected, the filter will launch the <code>authenticationEntryPoint</code>.
* This allows common handling of authentication failures originating from any subclass of
* {@link org.acegisecurity.intercept.AbstractSecurityInterceptor}.
* </p>
* <p>
* If an {@link AccessDeniedException} is detected, the filter will determine whether or not the user is an anonymous
* user. If they are an anonymous user, the <code>authenticationEntryPoint</code> will be launched. If they are not
* an anonymous user, the filter will delegate to the {@link org.acegisecurity.ui.AccessDeniedHandler}.
* By default the filter will use {@link org.acegisecurity.ui.AccessDeniedHandlerImpl}.
* </p>
* <p>
* To use this filter, it is necessary to specify the following properties:
* </p>
* <ul>
* <li><code>authenticationEntryPoint</code> indicates the handler that
* should commence the authentication process if an
* <code>AuthenticationException</code> is detected. Note that this may also
* switch the current protocol from http to https for an SSL login.</li>
* <li><code>portResolver</code> is used to determine the "real" port that a
* request was received on.</li>
* </ul>
* <P>
* <B>Do not use this class directly.</B> Instead configure
* <code>web.xml</code> to use the {@link
* org.acegisecurity.util.FilterToBeanProxy}.
* </p>
*
* @author Ben Alex
* @author colin sampaleanu
* @version $Id$
*/
public class ExceptionTranslationFilter implements Filter, InitializingBean {
//~ Static fields/initializers =====================================================================================
private static final Log logger = LogFactory.getLog(ExceptionTranslationFilter.class);
private static final Log logger = LogFactory.getLog(ExceptionTranslationFilter.class);
//~ Instance fields ================================================================================================
//~ Instance fields ================================================================================================
private AccessDeniedHandler accessDeniedHandler = new AccessDeniedHandlerImpl();
private AuthenticationEntryPoint authenticationEntryPoint;
private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
private PortResolver portResolver = new PortResolverImpl();
private boolean createSessionAllowed = true;
private AuthenticationEntryPoint authenticationEntryPoint;
private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
private PortResolver portResolver = new PortResolverImpl();
private boolean createSessionAllowed = true;
//~ Methods ========================================================================================================
//~ Methods ========================================================================================================
public void afterPropertiesSet() throws Exception {
Assert.notNull(authenticationEntryPoint, "authenticationEntryPoint must be specified");
Assert.notNull(portResolver, "portResolver must be specified");
Assert.notNull(authenticationTrustResolver, "authenticationTrustResolver must be specified");
public void afterPropertiesSet() throws Exception {
Assert.notNull(authenticationEntryPoint, "authenticationEntryPoint must be specified");
Assert.notNull(portResolver, "portResolver must be specified");
Assert.notNull(authenticationTrustResolver, "authenticationTrustResolver must be specified");
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
ServletException {
if (!(request instanceof HttpServletRequest)) {
throw new ServletException("HttpServletRequest required");
}
if (!(response instanceof HttpServletResponse)) {
throw new ServletException("HttpServletResponse required");
}
try {
chain.doFilter(request, response);
if (logger.isDebugEnabled()) {
logger.debug("Chain processed normally");
}
}
catch (AuthenticationException ex) {
handleException(request, response, chain, ex);
}
catch (AccessDeniedException ex) {
handleException(request, response, chain, ex);
}
catch (ServletException ex) {
if (ex.getRootCause() instanceof AuthenticationException
|| ex.getRootCause() instanceof AccessDeniedException) {
handleException(request, response, chain, (AcegiSecurityException) ex.getRootCause());
}
else {
throw ex;
}
}
catch (IOException ex) {
throw ex;
}
}
public AuthenticationEntryPoint getAuthenticationEntryPoint() {
return authenticationEntryPoint;
}
public AuthenticationTrustResolver getAuthenticationTrustResolver() {
return authenticationTrustResolver;
}
public PortResolver getPortResolver() {
return portResolver;
}
private void handleException(ServletRequest request, ServletResponse response, FilterChain chain,
AcegiSecurityException exception) throws IOException, ServletException {
if (exception instanceof AuthenticationException) {
if (logger.isDebugEnabled()) {
logger.debug("Authentication exception occurred; redirecting to authentication entry point", exception);
}
sendStartAuthentication(request, response, chain, (AuthenticationException) exception);
}
else if (exception instanceof AccessDeniedException) {
if (authenticationTrustResolver.isAnonymous(SecurityContextHolder.getContext().getAuthentication())) {
if (logger.isDebugEnabled()) {
logger.debug("Access is denied (user is anonymous); redirecting to authentication entry point",
exception);
}
sendStartAuthentication(request, response, chain, new InsufficientAuthenticationException(
"Full authentication is required to access this resource"));
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Access is denied (user is not anonymous); delegating to AccessDeniedHandler",
exception);
}
accessDeniedHandler.handle(request, response, (AccessDeniedException) exception);
}
}
}
/**
* If <code>true</code>, indicates that <code>SecurityEnforcementFilter</code> is permitted to store the target
* URL and exception information in the <code>HttpSession</code> (the default).
* In situations where you do not wish to unnecessarily create <code>HttpSession</code>s - because the user agent
* will know the failed URL, such as with BASIC or Digest authentication - you may wish to
* set this property to <code>false</code>. Remember to also set the
* {@link org.acegisecurity.context.HttpSessionContextIntegrationFilter#allowSessionCreation}
* to <code>false</code> if you set this property to <code>false</code>.
*
* @return <code>true</code> if the <code>HttpSession</code> will be
* used to store information about the failed request, <code>false</code>
* if the <code>HttpSession</code> will not be used
*/
public boolean isCreateSessionAllowed() {
return createSessionAllowed;
}
protected void sendStartAuthentication(ServletRequest request, ServletResponse response, FilterChain chain,
AuthenticationException reason) throws ServletException, IOException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
SavedRequest savedRequest = new SavedRequest(httpRequest, portResolver);
if (logger.isDebugEnabled()) {
logger.debug("Authentication entry point being called; SavedRequest added to Session: " + savedRequest);
}
if (createSessionAllowed) {
// Store the HTTP request itself. Used by AbstractProcessingFilter
// for redirection after successful authentication (SEC-29)
httpRequest.getSession().setAttribute(AbstractProcessingFilter.ACEGI_SAVED_REQUEST_KEY, savedRequest);
}
// SEC-112: Clear the SecurityContextHolder's Authentication, as the
// existing Authentication is no longer considered valid
SecurityContextHolder.getContext().setAuthentication(null);
authenticationEntryPoint.commence(httpRequest, (HttpServletResponse) response, reason);
}
public void setAccessDeniedHandler(AccessDeniedHandler accessDeniedHandler) {
Assert.notNull(accessDeniedHandler, "AccessDeniedHandler required");
this.accessDeniedHandler = accessDeniedHandler;
}
public void setAuthenticationEntryPoint(AuthenticationEntryPoint authenticationEntryPoint) {
this.authenticationEntryPoint = authenticationEntryPoint;
}
public void setAuthenticationTrustResolver(AuthenticationTrustResolver authenticationTrustResolver) {
this.authenticationTrustResolver = authenticationTrustResolver;
}
public void setCreateSessionAllowed(boolean createSessionAllowed) {
this.createSessionAllowed = createSessionAllowed;
}
public void setPortResolver(PortResolver portResolver) {
this.portResolver = portResolver;
}
public void init(FilterConfig filterConfig) throws ServletException {
}
public void destroy() {}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (!(request instanceof HttpServletRequest)) {
throw new ServletException("HttpServletRequest required");
}
if (!(response instanceof HttpServletResponse)) {
throw new ServletException("HttpServletResponse required");
}
try {
chain.doFilter(request, response);
if (logger.isDebugEnabled()) {
logger.debug("Chain processed normally");
}
} catch (AuthenticationException ex) {
handleException(request, response, chain, ex);
} catch (AccessDeniedException ex) {
handleException(request, response, chain, ex);
} catch (ServletException ex) {
if (ex.getRootCause() instanceof AuthenticationException
|| ex.getRootCause() instanceof AccessDeniedException) {
handleException(request, response, chain, (AcegiSecurityException) ex.getRootCause());
} else {
throw ex;
}
} catch (IOException ex) {
throw ex;
}
}
public AuthenticationEntryPoint getAuthenticationEntryPoint() {
return authenticationEntryPoint;
}
public AuthenticationTrustResolver getAuthenticationTrustResolver() {
return authenticationTrustResolver;
}
public PortResolver getPortResolver() {
return portResolver;
}
private void handleException(ServletRequest request, ServletResponse response, FilterChain chain,
AcegiSecurityException exception) throws IOException, ServletException {
if (exception instanceof AuthenticationException) {
if (logger.isDebugEnabled()) {
logger.debug("Authentication exception occurred; redirecting to authentication entry point", exception);
}
sendStartAuthentication(request, response, chain, (AuthenticationException) exception);
} else if (exception instanceof AccessDeniedException) {
if (authenticationTrustResolver.isAnonymous(SecurityContextHolder.getContext().getAuthentication())) {
if (logger.isDebugEnabled()) {
logger.debug("Access is denied (user is anonymous); redirecting to authentication entry point",
exception);
}
sendStartAuthentication(request, response, chain,
new InsufficientAuthenticationException("Full authentication is required to access this resource"));
} else {
if (logger.isDebugEnabled()) {
logger.debug("Access is denied (user is not anonymous); delegating to AccessDeniedHandler",
exception);
}
accessDeniedHandler.handle(request, response, (AccessDeniedException) exception);
}
}
}
public void init(FilterConfig filterConfig) throws ServletException {}
/**
* If <code>true</code>, indicates that <code>SecurityEnforcementFilter</code> is permitted to store the
* target URL and exception information in the <code>HttpSession</code> (the default). In situations where you do
* not wish to unnecessarily create <code>HttpSession</code>s - because the user agent will know the failed URL,
* such as with BASIC or Digest authentication - you may wish to set this property to <code>false</code>. Remember
* to also set the {@link org.acegisecurity.context.HttpSessionContextIntegrationFilter#allowSessionCreation} to
* <code>false</code> if you set this property to <code>false</code>.
*
* @return <code>true</code> if the <code>HttpSession</code> will be used to store information about the failed
* request, <code>false</code> if the <code>HttpSession</code> will not be used
*/
public boolean isCreateSessionAllowed() {
return createSessionAllowed;
}
protected void sendStartAuthentication(ServletRequest request, ServletResponse response, FilterChain chain,
AuthenticationException reason) throws ServletException, IOException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
SavedRequest savedRequest = new SavedRequest(httpRequest, portResolver);
if (logger.isDebugEnabled()) {
logger.debug("Authentication entry point being called; SavedRequest added to Session: " + savedRequest);
}
if (createSessionAllowed) {
// Store the HTTP request itself. Used by AbstractProcessingFilter
// for redirection after successful authentication (SEC-29)
httpRequest.getSession().setAttribute(AbstractProcessingFilter.ACEGI_SAVED_REQUEST_KEY, savedRequest);
}
// SEC-112: Clear the SecurityContextHolder's Authentication, as the
// existing Authentication is no longer considered valid
SecurityContextHolder.getContext().setAuthentication(null);
authenticationEntryPoint.commence(httpRequest, (HttpServletResponse) response, reason);
}
public void setAccessDeniedHandler(AccessDeniedHandler accessDeniedHandler) {
Assert.notNull(accessDeniedHandler, "AccessDeniedHandler required");
this.accessDeniedHandler = accessDeniedHandler;
}
public void setAuthenticationEntryPoint(AuthenticationEntryPoint authenticationEntryPoint) {
this.authenticationEntryPoint = authenticationEntryPoint;
}
public void setAuthenticationTrustResolver(AuthenticationTrustResolver authenticationTrustResolver) {
this.authenticationTrustResolver = authenticationTrustResolver;
}
public void setCreateSessionAllowed(boolean createSessionAllowed) {
this.createSessionAllowed = createSessionAllowed;
}
public void setPortResolver(PortResolver portResolver) {
this.portResolver = portResolver;
}
public void destroy() {
}
}
@@ -31,6 +31,7 @@ import org.acegisecurity.AuthenticationException;
import org.acegisecurity.AuthenticationManager;
import org.acegisecurity.context.SecurityContextHolder;
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken;
import org.acegisecurity.ui.AuthenticationDetailsSource;
import org.acegisecurity.ui.AuthenticationDetailsSourceImpl;
import org.acegisecurity.ui.AuthenticationEntryPoint;
@@ -39,7 +40,6 @@ import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.Ordered;
import org.springframework.util.Assert;
@@ -73,7 +73,7 @@ import org.springframework.util.Assert;
* @author Ben Alex
* @version $Id$
*/
public class BasicProcessingFilter implements Filter, InitializingBean, Ordered {
public class BasicProcessingFilter implements Filter, InitializingBean {
//~ Static fields/initializers =====================================================================================
private static final Log logger = LogFactory.getLog(BasicProcessingFilter.class);
@@ -85,7 +85,6 @@ public class BasicProcessingFilter implements Filter, InitializingBean, Ordered
private AuthenticationManager authenticationManager;
private RememberMeServices rememberMeServices;
private boolean ignoreFailure = false;
private int order;
//~ Methods ========================================================================================================
@@ -190,6 +189,17 @@ public class BasicProcessingFilter implements Filter, InitializingBean, Ordered
if (existingAuth instanceof UsernamePasswordAuthenticationToken && !existingAuth.getName().equals(username)) {
return true;
}
// Handle unusual condition where an AnonymousAuthenticationToken is already present
// This shouldn't happen very often, as BasicProcessingFitler is meant to be earlier in the filter
// chain than AnonymousProcessingFilter. Nevertheless, presence of both an AnonymousAuthenticationToken
// together with a BASIC authentication request header should indicate reauthentication using the
// BASIC protocol is desirable. This behaviour is also consistent with that provided by form and digest,
// both of which force re-authentication if the respective header is detected (and in doing so replace
// any existing AnonymousAuthenticationToken). See SEC-610.
if (existingAuth instanceof AnonymousAuthenticationToken) {
return true;
}
return false;
}
@@ -229,11 +239,4 @@ public class BasicProcessingFilter implements Filter, InitializingBean, Ordered
this.rememberMeServices = rememberMeServices;
}
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
}
@@ -24,11 +24,7 @@ import javax.servlet.http.HttpServletResponse;
import org.acegisecurity.AuthenticationException;
import org.acegisecurity.ui.AuthenticationEntryPoint;
import org.acegisecurity.util.OrderedUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.Ordered;
import org.springframework.util.Assert;
@@ -42,29 +38,15 @@ import org.springframework.util.Assert;
* @author Ben Alex
* @version $Id$
*/
public class BasicProcessingFilterEntryPoint implements AuthenticationEntryPoint, InitializingBean, Ordered, ApplicationContextAware {
public class BasicProcessingFilterEntryPoint implements AuthenticationEntryPoint, InitializingBean {
//~ Instance fields ================================================================================================
private static final int DEFAULT_ORDER = Integer.MAX_VALUE;
private String realmName;
private int order = DEFAULT_ORDER;
private ApplicationContext applicationContext;
//~ Methods ========================================================================================================
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
public void afterPropertiesSet() throws Exception {
Assert.hasText(realmName, "realmName must be specified");
if (order == DEFAULT_ORDER) {
OrderedUtils.copyOrderFromOtherClass(BasicProcessingFilter.class, applicationContext, this, true);
}
}
public void commence(ServletRequest request, ServletResponse response, AuthenticationException authException)
@@ -82,7 +64,4 @@ public class BasicProcessingFilterEntryPoint implements AuthenticationEntryPoint
this.realmName = realmName;
}
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
}
@@ -27,7 +27,6 @@ import javax.servlet.http.HttpServletResponse;
import org.acegisecurity.AuthenticationException;
import org.acegisecurity.ui.AuthenticationEntryPoint;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.Ordered;
import org.springframework.util.Assert;
@@ -42,23 +41,14 @@ import org.springframework.util.Assert;
* @author Ben Alex
* @version $Id$
*/
public class CasProcessingFilterEntryPoint implements AuthenticationEntryPoint, InitializingBean, Ordered{
public class CasProcessingFilterEntryPoint implements AuthenticationEntryPoint, InitializingBean {
//~ Instance fields ================================================================================================
private ServiceProperties serviceProperties;
private String loginUrl;
private int order = Integer.MAX_VALUE; // ~ default
//~ Methods ========================================================================================================
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
public void afterPropertiesSet() throws Exception {
Assert.hasLength(this.loginUrl, "loginUrl must be specified");
Assert.notNull(this.serviceProperties, "serviceProperties must be specified");
@@ -68,20 +68,20 @@ import javax.servlet.http.HttpServletResponse;
* <code>SecurityContextHolder</code>.<p>For a detailed background on what this filter is designed to process,
* refer to <a href="http://www.ietf.org/rfc/rfc2617.txt">RFC 2617</a> (which superseded RFC 2069, although this
* filter support clients that implement either RFC 2617 or RFC 2069).</p>
* <p>This filter can be used to provide Digest authentication services to both remoting protocol clients (such as
* <p>This filter can be used to provide Digest authentication services to both remoting protocol clients (such as
* Hessian and SOAP) as well as standard user agents (such as Internet Explorer and FireFox).</p>
* <p>This Digest implementation has been designed to avoid needing to store session state between invocations.
* <p>This Digest implementation has been designed to avoid needing to store session state between invocations.
* All session management information is stored in the "nonce" that is sent to the client by the {@link
* DigestProcessingFilterEntryPoint}.</p>
* <P>If authentication is successful, the resulting {@link org.acegisecurity.Authentication Authentication}
* <P>If authentication is successful, the resulting {@link org.acegisecurity.Authentication Authentication}
* object will be placed into the <code>SecurityContextHolder</code>.</p>
* <p>If authentication fails, an {@link org.acegisecurity.ui.AuthenticationEntryPoint AuthenticationEntryPoint}
* <p>If authentication fails, an {@link org.acegisecurity.ui.AuthenticationEntryPoint AuthenticationEntryPoint}
* implementation is called. This must always be {@link DigestProcessingFilterEntryPoint}, which will prompt the user
* to authenticate again via Digest authentication.</p>
* <p>Note there are limitations to Digest authentication, although it is a more comprehensive and secure solution
* <p>Note there are limitations to Digest authentication, although it is a more comprehensive and secure solution
* than Basic authentication. Please see RFC 2617 section 4 for a full discussion on the advantages of Digest
* authentication over Basic authentication, including commentary on the limitations that it still imposes.</p>
* <p><b>Do not use this class directly.</b> Instead configure <code>web.xml</code> to use the {@link
* <p><b>Do not use this class directly.</b> Instead configure <code>web.xml</code> to use the {@link
* org.acegisecurity.util.FilterToBeanProxy}.</p>
*/
public class DigestProcessingFilter implements Filter, InitializingBean, MessageSourceAware {
@@ -105,10 +105,11 @@ public class DigestProcessingFilter implements Filter, InitializingBean, Message
Assert.notNull(authenticationEntryPoint, "A DigestProcessingFilterEntryPoint is required");
}
public void destroy() {}
public void destroy() {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
throws IOException, ServletException {
if (!(request instanceof HttpServletRequest)) {
throw new ServletException("Can only process HttpServletRequest");
}
@@ -128,7 +129,7 @@ public class DigestProcessingFilter implements Filter, InitializingBean, Message
if ((header != null) && header.startsWith("Digest ")) {
String section212response = header.substring(7);
String[] headerEntries = StringUtils.commaDelimitedListToStringArray(section212response);
String[] headerEntries = StringSplitUtils.splitIgnoringQuotes(section212response, ',');
Map headerMap = StringSplitUtils.splitEachArrayElementAndCreateMap(headerEntries, "=", "\"");
String username = (String) headerMap.get("username");
@@ -144,12 +145,12 @@ public class DigestProcessingFilter implements Filter, InitializingBean, Message
if ((username == null) || (realm == null) || (nonce == null) || (uri == null) || (response == null)) {
if (logger.isDebugEnabled()) {
logger.debug("extracted username: '" + username + "'; realm: '" + username + "'; nonce: '"
+ username + "'; uri: '" + username + "'; response: '" + username + "'");
+ username + "'; uri: '" + username + "'; response: '" + username + "'");
}
fail(request, response,
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.missingMandatory",
new Object[] {section212response}, "Missing mandatory digest value; received header {0}")));
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.missingMandatory",
new Object[]{section212response}, "Missing mandatory digest value; received header {0}")));
return;
}
@@ -162,8 +163,8 @@ public class DigestProcessingFilter implements Filter, InitializingBean, Message
}
fail(request, response,
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.missingAuth",
new Object[] {section212response}, "Missing mandatory digest value; received header {0}")));
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.missingAuth",
new Object[]{section212response}, "Missing mandatory digest value; received header {0}")));
return;
}
@@ -172,9 +173,9 @@ public class DigestProcessingFilter implements Filter, InitializingBean, Message
// Check realm name equals what we expected
if (!this.getAuthenticationEntryPoint().getRealmName().equals(realm)) {
fail(request, response,
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.incorrectRealm",
new Object[] {realm, this.getAuthenticationEntryPoint().getRealmName()},
"Response realm name '{0}' does not match system realm name of '{1}'")));
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.incorrectRealm",
new Object[]{realm, this.getAuthenticationEntryPoint().getRealmName()},
"Response realm name '{0}' does not match system realm name of '{1}'")));
return;
}
@@ -182,22 +183,22 @@ public class DigestProcessingFilter implements Filter, InitializingBean, Message
// Check nonce was a Base64 encoded (as sent by DigestProcessingFilterEntryPoint)
if (!Base64.isArrayByteBase64(nonce.getBytes())) {
fail(request, response,
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.nonceEncoding",
new Object[] {nonce}, "Nonce is not encoded in Base64; received nonce {0}")));
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.nonceEncoding",
new Object[]{nonce}, "Nonce is not encoded in Base64; received nonce {0}")));
return;
}
// Decode nonce from Base64
// format of nonce is:
// format of nonce is:
// base64(expirationTime + ":" + md5Hex(expirationTime + ":" + key))
String nonceAsPlainText = new String(Base64.decodeBase64(nonce.getBytes()));
String[] nonceTokens = StringUtils.delimitedListToStringArray(nonceAsPlainText, ":");
if (nonceTokens.length != 2) {
fail(request, response,
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.nonceNotTwoTokens",
new Object[] {nonceAsPlainText}, "Nonce should have yielded two tokens but was {0}")));
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.nonceNotTwoTokens",
new Object[]{nonceAsPlainText}, "Nonce should have yielded two tokens but was {0}")));
return;
}
@@ -209,9 +210,9 @@ public class DigestProcessingFilter implements Filter, InitializingBean, Message
nonceExpiryTime = new Long(nonceTokens[0]).longValue();
} catch (NumberFormatException nfe) {
fail(request, response,
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.nonceNotNumeric",
new Object[] {nonceAsPlainText},
"Nonce token should have yielded a numeric first token, but was {0}")));
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.nonceNotNumeric",
new Object[]{nonceAsPlainText},
"Nonce token should have yielded a numeric first token, but was {0}")));
return;
}
@@ -222,8 +223,8 @@ public class DigestProcessingFilter implements Filter, InitializingBean, Message
if (!expectedNonceSignature.equals(nonceTokens[1])) {
fail(request, response,
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.nonceCompromised",
new Object[] {nonceAsPlainText}, "Nonce token compromised {0}")));
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.nonceCompromised",
new Object[]{nonceAsPlainText}, "Nonce token compromised {0}")));
return;
}
@@ -241,15 +242,15 @@ public class DigestProcessingFilter implements Filter, InitializingBean, Message
user = userDetailsService.loadUserByUsername(username);
} catch (UsernameNotFoundException notFound) {
fail(request, response,
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.usernameNotFound",
new Object[] {username}, "Username {0} not found")));
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.usernameNotFound",
new Object[]{username}, "Username {0} not found")));
return;
}
if (user == null) {
throw new AuthenticationServiceException(
"AuthenticationDao returned null, which is an interface contract violation");
"AuthenticationDao returned null, which is an interface contract violation");
}
userCache.putUserInCache(user);
@@ -266,7 +267,7 @@ public class DigestProcessingFilter implements Filter, InitializingBean, Message
if (!serverDigestMd5.equals(responseDigest) && !loadedFromDao) {
if (logger.isDebugEnabled()) {
logger.debug(
"Digest comparison failure; trying to refresh user from DAO in case password had changed");
"Digest comparison failure; trying to refresh user from DAO in case password had changed");
}
try {
@@ -274,8 +275,8 @@ public class DigestProcessingFilter implements Filter, InitializingBean, Message
} catch (UsernameNotFoundException notFound) {
// Would very rarely happen, as user existed earlier
fail(request, response,
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.usernameNotFound",
new Object[] {username}, "Username {0} not found")));
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.usernameNotFound",
new Object[]{username}, "Username {0} not found")));
}
userCache.putUserInCache(user);
@@ -289,12 +290,12 @@ public class DigestProcessingFilter implements Filter, InitializingBean, Message
if (!serverDigestMd5.equals(responseDigest)) {
if (logger.isDebugEnabled()) {
logger.debug("Expected response: '" + serverDigestMd5 + "' but received: '" + responseDigest
+ "'; is AuthenticationDao returning clear text passwords?");
+ "'; is AuthenticationDao returning clear text passwords?");
}
fail(request, response,
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.incorrectResponse",
"Incorrect response")));
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.incorrectResponse",
"Incorrect response")));
return;
}
@@ -305,15 +306,15 @@ public class DigestProcessingFilter implements Filter, InitializingBean, Message
// but the request was otherwise appearing to be valid
if (nonceExpiryTime < System.currentTimeMillis()) {
fail(request, response,
new NonceExpiredException(messages.getMessage("DigestProcessingFilter.nonceExpired",
"Nonce has expired/timed out")));
new NonceExpiredException(messages.getMessage("DigestProcessingFilter.nonceExpired",
"Nonce has expired/timed out")));
return;
}
if (logger.isDebugEnabled()) {
logger.debug("Authentication success for user: '" + username + "' with response: '" + responseDigest
+ "'");
+ "'");
}
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(user,
@@ -335,7 +336,7 @@ public class DigestProcessingFilter implements Filter, InitializingBean, Message
}
private void fail(ServletRequest request, ServletResponse response, AuthenticationException failed)
throws IOException, ServletException {
throws IOException, ServletException {
SecurityContextHolder.getContext().setAuthentication(null);
if (logger.isDebugEnabled()) {
@@ -351,24 +352,22 @@ public class DigestProcessingFilter implements Filter, InitializingBean, Message
* coding of user agents.
*
* @param passwordAlreadyEncoded true if the password argument is already encoded in the correct format. False if
* it is plain text.
* @param username the user's login name.
* @param realm the name of the realm.
* @param password the user's password in plaintext or ready-encoded.
* @param httpMethod the HTTP request method (GET, POST etc.)
* @param uri the request URI.
* @param qop the qop directive, or null if not set.
* @param nonce the nonce supplied by the server
* @param nc the "nonce-count" as defined in RFC 2617.
* @param cnonce opaque string supplied by the client when qop is set.
*
* it is plain text.
* @param username the user's login name.
* @param realm the name of the realm.
* @param password the user's password in plaintext or ready-encoded.
* @param httpMethod the HTTP request method (GET, POST etc.)
* @param uri the request URI.
* @param qop the qop directive, or null if not set.
* @param nonce the nonce supplied by the server
* @param nc the "nonce-count" as defined in RFC 2617.
* @param cnonce opaque string supplied by the client when qop is set.
* @return the MD5 of the digest authentication response, encoded in hex
*
* @throws IllegalArgumentException if the supplied qop value is unsupported.
*/
public static String generateDigest(boolean passwordAlreadyEncoded, String username, String realm, String password,
String httpMethod, String uri, String qop, String nonce, String nc, String cnonce)
throws IllegalArgumentException {
String httpMethod, String uri, String qop, String nonce, String nc, String cnonce)
throws IllegalArgumentException {
String a1Md5 = null;
String a2 = httpMethod + ":" + uri;
String a2Md5 = new String(DigestUtils.md5Hex(a2));
@@ -408,7 +407,8 @@ public class DigestProcessingFilter implements Filter, InitializingBean, Message
return userDetailsService;
}
public void init(FilterConfig ignored) throws ServletException {}
public void init(FilterConfig ignored) throws ServletException {
}
public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) {
Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource required");
@@ -15,15 +15,6 @@
package org.acegisecurity.ui.logout;
import org.acegisecurity.Authentication;
import org.acegisecurity.context.SecurityContextHolder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;
import java.io.IOException;
import javax.servlet.Filter;
@@ -35,14 +26,26 @@ import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.acegisecurity.Authentication;
import org.acegisecurity.context.SecurityContextHolder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;
/**
* Logs a principal out.<p>Polls a series of {@link LogoutHandler}s. The handlers should be specified in the order
* they are required. Generally you will want to call logout handlers <code>TokenBasedRememberMeServices</code> and
* <code>SecurityContextLogoutHandler</code> (in that order).</p>
* <p>After logout, the URL specified by {@link #logoutSuccessUrl} will be shown.</p>
* <p><b>Do not use this class directly.</b> Instead configure <code>web.xml</code> to use the {@link
* org.acegisecurity.util.FilterToBeanProxy}.</p>
* Logs a principal out.
* <p>
* Polls a series of {@link LogoutHandler}s. The handlers should be specified in the order they are required.
* Generally you will want to call logout handlers <code>TokenBasedRememberMeServices</code> and
* <code>SecurityContextLogoutHandler</code> (in that order).
* </p>
* <p>
* After logout, the URL specified by {@link #logoutSuccessUrl} will be shown.
* </p>
* <p>
* <b>Do not use this class directly.</b> Instead configure <code>web.xml</code> to use the
* {@link org.acegisecurity.util.FilterToBeanProxy}.
* </p>
*
* @author Ben Alex
* @version $Id$
@@ -72,10 +75,11 @@ public class LogoutFilter implements Filter {
/**
* Not used. Use IoC container lifecycle methods instead.
*/
public void destroy() {}
public void destroy() {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
ServletException {
if (!(request instanceof HttpServletRequest)) {
throw new ServletException("Can only process HttpServletRequest");
}
@@ -113,10 +117,11 @@ public class LogoutFilter implements Filter {
*
* @throws ServletException ignored
*/
public void init(FilterConfig arg0) throws ServletException {}
public void init(FilterConfig arg0) throws ServletException {
}
/**
* Allow subclasses to modify when a logout should tak eplace.
* Allow subclasses to modify when a logout should take place.
*
* @param request the request
* @param response the response
@@ -128,28 +133,35 @@ public class LogoutFilter implements Filter {
int pathParamIndex = uri.indexOf(';');
if (pathParamIndex > 0) {
// strip everything after the first semi-colon
// strip everything from the first semi-colon
uri = uri.substring(0, pathParamIndex);
}
if ("".equals(request.getContextPath())) {
return uri.endsWith(filterProcessesUrl);
int queryParamIndex = uri.indexOf('?');
if (queryParamIndex > 0) {
// strip everything from the first question mark
uri = uri.substring(0, queryParamIndex);
}
if ("".equals(request.getContextPath())) {
return uri.endsWith(filterProcessesUrl);
}
return uri.endsWith(request.getContextPath() + filterProcessesUrl);
}
/**
* Allow subclasses to modify the redirection message.
*
* @param request the request
* @param request the request
* @param response the response
* @param url the URL to redirect to
* @param url the URL to redirect to
*
* @throws IOException in the event of any failure
*/
protected void sendRedirect(HttpServletRequest request, HttpServletResponse response, String url)
throws IOException {
throws IOException {
if (!url.startsWith("http://") && !url.startsWith("https://")) {
url = request.getContextPath() + url;
}
@@ -161,4 +173,8 @@ public class LogoutFilter implements Filter {
Assert.hasText(filterProcessesUrl, "FilterProcessesUrl required");
this.filterProcessesUrl = filterProcessesUrl;
}
protected String getFilterProcessesUrl() {
return filterProcessesUrl;
}
}
@@ -18,7 +18,6 @@ package org.acegisecurity.ui.logout;
import org.acegisecurity.Authentication;
import org.acegisecurity.context.SecurityContextHolder;
import org.springframework.core.Ordered;
import org.springframework.util.Assert;
import javax.servlet.http.HttpServletRequest;
@@ -38,14 +37,12 @@ import javax.servlet.http.HttpSession;
* @version $Id: SecurityContextLogoutHandler.java 1784 2007-02-24 21:00:24Z
* luke_t $
*/
public class SecurityContextLogoutHandler implements LogoutHandler, Ordered {
public class SecurityContextLogoutHandler implements LogoutHandler {
// ~ Methods
// ========================================================================================================
private boolean invalidateHttpSession = true;
private int order = Integer.MAX_VALUE; //~ default
/**
* Requires the request to be passed in.
*
@@ -80,12 +77,4 @@ public class SecurityContextLogoutHandler implements LogoutHandler, Ordered {
this.invalidateHttpSession = invalidateHttpSession;
}
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
}
@@ -78,9 +78,9 @@ public class RememberMeProcessingFilter implements Filter, InitializingBean, App
//~ Methods ========================================================================================================
public void afterPropertiesSet() throws Exception {
Assert.notNull(rememberMeServices, "RememberMeServices required");
Assert.notNull(authenticationManager, "AuthenticationManager required");
}
Assert.notNull(authenticationManager, "authenticationManager must be specified");
Assert.notNull(this.rememberMeServices);
}
/**
* Does nothing - we rely on IoC lifecycle services instead.
@@ -167,4 +167,5 @@ public class RememberMeProcessingFilter implements Filter, InitializingBean, App
public void setRememberMeServices(RememberMeServices rememberMeServices) {
this.rememberMeServices = rememberMeServices;
}
}
@@ -16,6 +16,7 @@
package org.acegisecurity.ui.rememberme;
import java.util.Date;
import java.util.Map;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
@@ -23,6 +24,7 @@ import javax.servlet.http.HttpServletResponse;
import org.acegisecurity.Authentication;
import org.acegisecurity.providers.rememberme.RememberMeAuthenticationToken;
import org.acegisecurity.ui.AccessDeniedHandler;
import org.acegisecurity.ui.AuthenticationDetailsSource;
import org.acegisecurity.ui.AuthenticationDetailsSourceImpl;
import org.acegisecurity.ui.logout.LogoutHandler;
@@ -34,343 +36,451 @@ import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.Ordered;
import org.springframework.context.ApplicationContext;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.RequestUtils;
/**
* Identifies previously remembered users by a Base-64 encoded cookie.
*
*
* <p>
* This implementation does not rely on an external database, so is attractive for simple applications.
* The cookie will be valid for a specific period from the date of the last
* {@link #loginSuccess(HttpServletRequest, HttpServletResponse, Authentication)}. As per the
* interface contract, this method will only be called when the principal completes a successful interactive
* authentication. As such the time period commences from the last authentication attempt where they furnished
* credentials - not the time period they last logged in via remember-me. The implementation will only send a
* remember-me token if the parameter defined by {@link #setParameter(String)} is present.</p>
*
* <p>An {@link org.acegisecurity.userdetails.UserDetailsService} is required by this implementation, so that it
* can construct a valid <code>Authentication</code> from the returned {@link
* org.acegisecurity.userdetails.UserDetails}. This is also necessary so that the user's password is available and can
* be checked as part of the encoded cookie.</p>
*
* This implementation does not rely on an external database, so is attractive
* for simple applications. The cookie will be valid for a specific period from
* the date of the last
* {@link #loginSuccess(HttpServletRequest, HttpServletResponse, Authentication)}.
* As per the interface contract, this method will only be called when the
* principal completes a successful interactive authentication. As such the time
* period commences from the last authentication attempt where they furnished
* credentials - not the time period they last logged in via remember-me. The
* implementation will only send a remember-me token if the parameter defined by
* {@link #setParameter(String)} is present.
* </p>
*
* <p>
* An {@link org.acegisecurity.userdetails.UserDetailsService} is required by
* this implementation, so that it can construct a valid
* <code>Authentication</code> from the returned {@link
* org.acegisecurity.userdetails.UserDetails}. This is also necessary so that
* the user's password is available and can be checked as part of the encoded
* cookie.
* </p>
*
* <p>
* The cookie encoded by this implementation adopts the following form:
* <pre>username + ":" + expiryTime + ":" + Md5Hex(username + ":" + expiryTime + ":" + password + ":" + key)</pre>
*
* <pre>
* username + &quot;:&quot; + expiryTime + &quot;:&quot; + Md5Hex(username + &quot;:&quot; + expiryTime + &quot;:&quot; + password + &quot;:&quot; + key)
* </pre>
*
* </p>
* <p>
* As such, if the user changes their password any remember-me token will be invalidated. Equally, the system
* administrator may invalidate every remember-me token on issue by changing the key. This provides some reasonable
* approaches to recovering from a remember-me token being left on a public machine (eg kiosk system, Internet cafe
* etc). Most importantly, at no time is the user's password ever sent to the user agent, providing an important
* security safeguard. Unfortunately the username is necessary in this implementation (as we do not want to rely on a
* database for remember-me services) and as such high security applications should be aware of this occasionally
* undesired disclosure of a valid username.
* As such, if the user changes their password any remember-me token will be
* invalidated. Equally, the system administrator may invalidate every
* remember-me token on issue by changing the key. This provides some reasonable
* approaches to recovering from a remember-me token being left on a public
* machine (eg kiosk system, Internet cafe etc). Most importantly, at no time is
* the user's password ever sent to the user agent, providing an important
* security safeguard. Unfortunately the username is necessary in this
* implementation (as we do not want to rely on a database for remember-me
* services) and as such high security applications should be aware of this
* occasionally undesired disclosure of a valid username.
* </p>
* <p>
* This is a basic remember-me implementation which is suitable for many applications. However, we recommend a
* database-based implementation if you require a more secure remember-me approach.
* This is a basic remember-me implementation which is suitable for many
* applications. However, we recommend a database-based implementation if you
* require a more secure remember-me approach.
* </p>
* <p>
* By default the tokens will be valid for 14 days from the last successful authentication attempt. This can be
* changed using {@link #setTokenValiditySeconds(long)}.
* By default the tokens will be valid for 14 days from the last successful
* authentication attempt. This can be changed using
* {@link #setTokenValiditySeconds(long)}.
* </p>
*
*
* @author Ben Alex
* @version $Id$
* @version $Id: TokenBasedRememberMeServices.java 1871 2007-05-25 03:12:49Z
* benalex $
*/
public class TokenBasedRememberMeServices implements RememberMeServices, InitializingBean, LogoutHandler, Ordered {
//~ Static fields/initializers =====================================================================================
public class TokenBasedRememberMeServices implements RememberMeServices, InitializingBean, LogoutHandler {
// ~ Static fields/initializers
// =====================================================================================
public static final String ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY = "ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE";
public static final String DEFAULT_PARAMETER = "_acegi_security_remember_me";
protected static final Log logger = LogFactory.getLog(TokenBasedRememberMeServices.class);
public static final String ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY = "ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE";
//~ Instance fields ================================================================================================
public static final String DEFAULT_PARAMETER = "_acegi_security_remember_me";
private AuthenticationDetailsSource authenticationDetailsSource = new AuthenticationDetailsSourceImpl();
private String key;
private String parameter = DEFAULT_PARAMETER;
private UserDetailsService userDetailsService;
private long tokenValiditySeconds = 1209600; // 14 days
private boolean alwaysRemember = false;
private int order = Integer.MAX_VALUE; //~ default
private String cookieName = ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY;
protected static final Log logger = LogFactory.getLog(TokenBasedRememberMeServices.class);
//~ Methods ========================================================================================================
// ~ Instance fields
// ================================================================================================
public void afterPropertiesSet() throws Exception {
Assert.hasLength(key);
Assert.hasLength(parameter);
Assert.hasLength(cookieName);
Assert.notNull(userDetailsService);
}
protected AuthenticationDetailsSource authenticationDetailsSource = new AuthenticationDetailsSourceImpl();
public Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) {
Cookie[] cookies = request.getCookies();
private String key;
if ((cookies == null) || (cookies.length == 0)) {
return null;
}
private String parameter = DEFAULT_PARAMETER;
for (int i = 0; i < cookies.length; i++) {
if (cookieName.equals(cookies[i].getName())) {
String cookieValue = cookies[i].getValue();
private UserDetailsService userDetailsService;
for (int j = 0; j < cookieValue.length() % 4; j++) {
cookieValue = cookieValue + "=";
}
if (Base64.isArrayByteBase64(cookieValue.getBytes())) {
if (logger.isDebugEnabled()) {
logger.debug("Remember-me cookie detected");
}
protected long tokenValiditySeconds = 1209600; // 14 days
// Decode token from Base64
// format of token is:
// username + ":" + expiryTime + ":" +
// Md5Hex(username + ":" + expiryTime + ":" + password + ":" + key)
String cookieAsPlainText = new String(Base64.decodeBase64(cookieValue.getBytes()));
String[] cookieTokens = StringUtils.delimitedListToStringArray(cookieAsPlainText, ":");
private boolean alwaysRemember = false;
if (cookieTokens.length == 3) {
long tokenExpiryTime;
private static final int DEFAULT_ORDER = Integer.MAX_VALUE; // ~ default
try {
tokenExpiryTime = new Long(cookieTokens[1]).longValue();
} catch (NumberFormatException nfe) {
cancelCookie(request, response,
"Cookie token[1] did not contain a valid number (contained '" + cookieTokens[1] + "')");
private String cookieName = ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY;
return null;
}
// ~ Methods
// ========================================================================================================
// Check it has not expired
if (tokenExpiryTime < System.currentTimeMillis()) {
cancelCookie(request, response,
"Cookie token[1] has expired (expired on '" + new Date(tokenExpiryTime)
+ "'; current time is '" + new Date() + "')");
public void afterPropertiesSet() throws Exception {
Assert.hasLength(key);
Assert.hasLength(parameter);
Assert.hasLength(cookieName);
Assert.notNull(userDetailsService);
}
return null;
}
/**
* Introspects the <code>Applicationcontext</code> for the single instance
* of {@link AccessDeniedHandler}. If found invoke
* setAccessDeniedHandler(AccessDeniedHandler accessDeniedHandler) method by
* providing the found instance of accessDeniedHandler as a method
* parameter. If more than one instance of <code>AccessDeniedHandler</code>
* is found, the method throws <code>IllegalStateException</code>.
*
* @param applicationContext to locate the instance
*/
private void autoDetectAndUseAnyUserDetailsService(ApplicationContext applicationContext) {
Map map = applicationContext.getBeansOfType(UserDetailsService.class);
if (map.size() > 1) {
throw new IllegalArgumentException(
"More than one UserDetailsService beans detected please refer to the one using "
+ " [ principalRepositoryBeanRef ] " + "attribute");
}
else if (map.size() == 1) {
setUserDetailsService((UserDetailsService) map.values().iterator().next());
}
}
// Check the user exists
// Defer lookup until after expiry time checked, to possibly avoid expensive lookup
UserDetails userDetails;
public Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) {
Cookie[] cookies = request.getCookies();
try {
userDetails = this.userDetailsService.loadUserByUsername(cookieTokens[0]);
} catch (UsernameNotFoundException notFound) {
cancelCookie(request, response,
"Cookie token[0] contained username '" + cookieTokens[0] + "' but was not found");
if ((cookies == null) || (cookies.length == 0)) {
return null;
}
return null;
}
for (int i = 0; i < cookies.length; i++) {
if (cookieName.equals(cookies[i].getName())) {
String cookieValue = cookies[i].getValue();
// Immediately reject if the user is not allowed to login
if (!userDetails.isAccountNonExpired() || !userDetails.isCredentialsNonExpired()
|| !userDetails.isEnabled()) {
cancelCookie(request, response,
"Cookie token[0] contained username '" + cookieTokens[0]
+ "' but account has expired, credentials have expired, or user is disabled");
for (int j = 0; j < cookieValue.length() % 4; j++) {
cookieValue = cookieValue + "=";
}
return null;
}
if (Base64.isArrayByteBase64(cookieValue.getBytes())) {
if (logger.isDebugEnabled()) {
logger.debug("Remember-me cookie detected");
}
// Check signature of token matches remaining details
// Must do this after user lookup, as we need the DAO-derived password
// If efficiency was a major issue, just add in a UserCache implementation,
// but recall this method is usually only called one per HttpSession
// (as if the token is valid, it will cause SecurityContextHolder population, whilst
// if invalid, will cause the cookie to be cancelled)
String expectedTokenSignature = DigestUtils.md5Hex(userDetails.getUsername() + ":"
+ tokenExpiryTime + ":" + userDetails.getPassword() + ":" + this.key);
// Decode token from Base64
// format of token is:
// username + ":" + expiryTime + ":" +
// Md5Hex(username + ":" + expiryTime + ":" + password + ":"
// + key)
String cookieAsPlainText = new String(Base64.decodeBase64(cookieValue.getBytes()));
String[] cookieTokens = StringUtils.delimitedListToStringArray(cookieAsPlainText, ":");
if (!expectedTokenSignature.equals(cookieTokens[2])) {
cancelCookie(request, response,
"Cookie token[2] contained signature '" + cookieTokens[2] + "' but expected '"
+ expectedTokenSignature + "'");
if (cookieTokens.length == 3) {
return null;
}
long tokenExpiryTime;
// By this stage we have a valid token
if (logger.isDebugEnabled()) {
logger.debug("Remember-me cookie accepted");
}
try {
tokenExpiryTime = new Long(cookieTokens[1]).longValue();
}
catch (NumberFormatException nfe) {
cancelCookie(request, response,
"Cookie token[1] did not contain a valid number (contained '" + cookieTokens[1]
+ "')");
RememberMeAuthenticationToken auth = new RememberMeAuthenticationToken(this.key, userDetails,
userDetails.getAuthorities());
auth.setDetails(authenticationDetailsSource.buildDetails((HttpServletRequest) request));
return null;
}
return auth;
} else {
cancelCookie(request, response,
"Cookie token did not contain 3 tokens; decoded value was '" + cookieAsPlainText + "'");
if (isTokenExpired(tokenExpiryTime)) {
cancelCookie(request, response, "Cookie token[1] has expired (expired on '"
+ new Date(tokenExpiryTime) + "'; current time is '" + new Date() + "')");
return null;
}
} else {
cancelCookie(request, response,
"Cookie token was not Base64 encoded; value was '" + cookieValue + "'");
return null;
}
return null;
}
}
}
// Check the user exists
// Defer lookup until after expiry time checked, to
// possibly avoid expensive lookup
UserDetails userDetails = loadUserDetails(request, response, cookieTokens);
return null;
}
if (userDetails == null) {
cancelCookie(request, response, "Cookie token[0] contained username '" + cookieTokens[0]
+ "' but was not found");
return null;
}
private void cancelCookie(HttpServletRequest request, HttpServletResponse response, String reasonForLog) {
if ((reasonForLog != null) && logger.isDebugEnabled()) {
logger.debug("Cancelling cookie for reason: " + reasonForLog);
}
if (!isValidUserDetails(request, response, userDetails, cookieTokens)) {
return null;
}
response.addCookie(makeCancelCookie(request));
}
// Check signature of token matches remaining details
// Must do this after user lookup, as we need the
// DAO-derived password
// If efficiency was a major issue, just add in a
// UserCache implementation,
// but recall this method is usually only called one per
// HttpSession
// (as if the token is valid, it will cause
// SecurityContextHolder population, whilst
// if invalid, will cause the cookie to be cancelled)
String expectedTokenSignature = makeTokenSignature(tokenExpiryTime, userDetails);
public String getKey() {
return key;
}
if (!expectedTokenSignature.equals(cookieTokens[2])) {
cancelCookie(request, response, "Cookie token[2] contained signature '" + cookieTokens[2]
+ "' but expected '" + expectedTokenSignature + "'");
public String getParameter() {
return parameter;
}
return null;
}
public long getTokenValiditySeconds() {
return tokenValiditySeconds;
}
// By this stage we have a valid token
if (logger.isDebugEnabled()) {
logger.debug("Remember-me cookie accepted");
}
public UserDetailsService getUserDetailsService() {
return userDetailsService;
}
RememberMeAuthenticationToken auth = new RememberMeAuthenticationToken(this.key, userDetails,
userDetails.getAuthorities());
auth.setDetails(authenticationDetailsSource.buildDetails((HttpServletRequest) request));
public void loginFail(HttpServletRequest request, HttpServletResponse response) {
cancelCookie(request, response, "Interactive authentication attempt was unsuccessful");
}
return auth;
}
else {
cancelCookie(request, response, "Cookie token did not contain 3 tokens; decoded value was '"
+ cookieAsPlainText + "'");
protected boolean rememberMeRequested(HttpServletRequest request, String parameter) {
if (alwaysRemember) {
return true;
}
return null;
}
}
else {
cancelCookie(request, response, "Cookie token was not Base64 encoded; value was '" + cookieValue
+ "'");
return RequestUtils.getBooleanParameter(request, parameter, false);
}
return null;
}
}
}
public void loginSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication successfulAuthentication) {
// Exit if the principal hasn't asked to be remembered
if (!rememberMeRequested(request, parameter)) {
if (logger.isDebugEnabled()) {
logger.debug("Did not send remember-me cookie (principal did not set parameter '" + this.parameter
+ "')");
}
return null;
}
return;
}
/**
* @param tokenExpiryTime
* @param userDetails
* @return
*/
protected String makeTokenSignature(long tokenExpiryTime, UserDetails userDetails) {
String expectedTokenSignature = DigestUtils.md5Hex(userDetails.getUsername() + ":" + tokenExpiryTime + ":"
+ userDetails.getPassword() + ":" + this.key);
return expectedTokenSignature;
}
// Determine username and password, ensuring empty strings
Assert.notNull(successfulAuthentication.getPrincipal());
Assert.notNull(successfulAuthentication.getCredentials());
protected boolean isValidUserDetails(HttpServletRequest request, HttpServletResponse response,
UserDetails userDetails, String[] cookieTokens) {
// Immediately reject if the user is not allowed to
// login
if (!userDetails.isAccountNonExpired() || !userDetails.isCredentialsNonExpired() || !userDetails.isEnabled()) {
cancelCookie(request, response, "Cookie token[0] contained username '" + cookieTokens[0]
+ "' but account has expired, credentials have expired, or user is disabled");
String username;
String password;
return false;
}
return true;
}
if (successfulAuthentication.getPrincipal() instanceof UserDetails) {
username = ((UserDetails) successfulAuthentication.getPrincipal()).getUsername();
password = ((UserDetails) successfulAuthentication.getPrincipal()).getPassword();
} else {
username = successfulAuthentication.getPrincipal().toString();
password = successfulAuthentication.getCredentials().toString();
}
// If unable to find a username and password, just abort as TokenBasedRememberMeServices unable to construct a valid token in this case
if (!StringUtils.hasLength(username) || !StringUtils.hasLength(password)) {
return;
}
protected UserDetails loadUserDetails(HttpServletRequest request, HttpServletResponse response,
String[] cookieTokens) {
UserDetails userDetails = null;
Assert.hasLength(username);
Assert.hasLength(password);
try {
userDetails = this.userDetailsService.loadUserByUsername(cookieTokens[0]);
}
catch (UsernameNotFoundException notFound) {
cancelCookie(request, response, "Cookie token[0] contained username '" + cookieTokens[0]
+ "' but was not found");
long expiryTime = System.currentTimeMillis() + (tokenValiditySeconds * 1000);
return null;
}
return userDetails;
}
// construct token to put in cookie; format is:
// username + ":" + expiryTime + ":" + Md5Hex(username + ":" + expiryTime + ":" + password + ":" + key)
String signatureValue = DigestUtils.md5Hex(username + ":" + expiryTime + ":" + password + ":" + key);
String tokenValue = username + ":" + expiryTime + ":" + signatureValue;
String tokenValueBase64 = new String(Base64.encodeBase64(tokenValue.getBytes()));
response.addCookie(makeValidCookie(tokenValueBase64, request, tokenValiditySeconds));
protected boolean isTokenExpired(long tokenExpiryTime) {
// Check it has not expired
if (tokenExpiryTime < System.currentTimeMillis()) {
return true;
}
return false;
}
if (logger.isDebugEnabled()) {
logger.debug("Added remember-me cookie for user '" + username
+ "', expiry: '" + new Date(expiryTime) + "'");
}
}
protected void cancelCookie(HttpServletRequest request, HttpServletResponse response, String reasonForLog) {
if ((reasonForLog != null) && logger.isDebugEnabled()) {
logger.debug("Cancelling cookie for reason: " + reasonForLog);
}
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
cancelCookie(request, response, "Logout of user "
+ (authentication == null ? "Unknown" : authentication.getName()));
}
response.addCookie(makeCancelCookie(request));
}
protected Cookie makeCancelCookie(HttpServletRequest request) {
Cookie cookie = new Cookie(cookieName, null);
cookie.setMaxAge(0);
cookie.setPath(StringUtils.hasLength(request.getContextPath()) ? request.getContextPath() : "/");
public String getKey() {
return key;
}
return cookie;
}
public String getParameter() {
return parameter;
}
protected Cookie makeValidCookie(String tokenValueBase64, HttpServletRequest request, long maxAge) {
Cookie cookie = new Cookie(cookieName, tokenValueBase64);
cookie.setMaxAge(new Long(maxAge).intValue());
cookie.setPath(StringUtils.hasLength(request.getContextPath()) ? request.getContextPath() : "/");
public long getTokenValiditySeconds() {
return tokenValiditySeconds;
}
return cookie;
}
public UserDetailsService getUserDetailsService() {
return userDetailsService;
}
public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) {
Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource required");
this.authenticationDetailsSource = authenticationDetailsSource;
}
public void loginFail(HttpServletRequest request, HttpServletResponse response) {
cancelCookie(request, response, "Interactive authentication attempt was unsuccessful");
}
public void setKey(String key) {
this.key = key;
}
protected boolean rememberMeRequested(HttpServletRequest request, String parameter) {
if (alwaysRemember) {
return true;
}
public void setParameter(String parameter) {
this.parameter = parameter;
}
return RequestUtils.getBooleanParameter(request, parameter, false);
}
public void setCookieName(String cookieName) {
public void loginSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication successfulAuthentication) {
// Exit if the principal hasn't asked to be remembered
if (!rememberMeRequested(request, parameter)) {
if (logger.isDebugEnabled()) {
logger.debug("Did not send remember-me cookie (principal did not set parameter '" + this.parameter
+ "')");
}
return;
}
// Determine username and password, ensuring empty strings
Assert.notNull(successfulAuthentication.getPrincipal());
Assert.notNull(successfulAuthentication.getCredentials());
String username = retrieveUserName(successfulAuthentication);
String password = retrievePassword(successfulAuthentication);
// If unable to find a username and password, just abort as
// TokenBasedRememberMeServices unable to construct a valid token in
// this case
if (!StringUtils.hasLength(username) || !StringUtils.hasLength(password)) {
return;
}
long expiryTime = System.currentTimeMillis() + (tokenValiditySeconds * 1000);
// construct token to put in cookie; format is:
// username + ":" + expiryTime + ":" + Md5Hex(username + ":" +
// expiryTime + ":" + password + ":" + key)
String signatureValue = DigestUtils.md5Hex(username + ":" + expiryTime + ":" + password + ":" + key);
String tokenValue = username + ":" + expiryTime + ":" + signatureValue;
String tokenValueBase64 = new String(Base64.encodeBase64(tokenValue.getBytes()));
response.addCookie(makeValidCookie(tokenValueBase64, request, tokenValiditySeconds));
if (logger.isDebugEnabled()) {
logger
.debug("Added remember-me cookie for user '" + username + "', expiry: '" + new Date(expiryTime)
+ "'");
}
}
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
cancelCookie(request, response, "Logout of user "
+ (authentication == null ? "Unknown" : authentication.getName()));
}
protected String retrieveUserName(Authentication successfulAuthentication) {
if (isInstanceOfUserDetails(successfulAuthentication)) {
return ((UserDetails) successfulAuthentication.getPrincipal()).getUsername();
}
else {
return successfulAuthentication.getPrincipal().toString();
}
}
protected String retrievePassword(Authentication successfulAuthentication) {
if (isInstanceOfUserDetails(successfulAuthentication)) {
return ((UserDetails) successfulAuthentication.getPrincipal()).getPassword();
}
else {
return successfulAuthentication.getCredentials().toString();
}
}
private boolean isInstanceOfUserDetails(Authentication authentication) {
return authentication.getPrincipal() instanceof UserDetails;
}
protected Cookie makeCancelCookie(HttpServletRequest request) {
Cookie cookie = new Cookie(cookieName, null);
cookie.setMaxAge(0);
cookie.setPath(StringUtils.hasLength(request.getContextPath()) ? request.getContextPath() : "/");
return cookie;
}
protected Cookie makeValidCookie(String tokenValueBase64, HttpServletRequest request, long maxAge) {
Cookie cookie = new Cookie(cookieName, tokenValueBase64);
cookie.setMaxAge(new Long(maxAge).intValue());
cookie.setPath(StringUtils.hasLength(request.getContextPath()) ? request.getContextPath() : "/");
return cookie;
}
public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) {
Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource required");
this.authenticationDetailsSource = authenticationDetailsSource;
}
public void setKey(String key) {
this.key = key;
}
public void setParameter(String parameter) {
this.parameter = parameter;
}
public void setCookieName(String cookieName) {
this.cookieName = cookieName;
}
public void setTokenValiditySeconds(long tokenValiditySeconds) {
this.tokenValiditySeconds = tokenValiditySeconds;
}
public void setUserDetailsService(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
public boolean isAlwaysRemember() {
return alwaysRemember;
}
public void setAlwaysRemember(boolean alwaysRemember) {
this.alwaysRemember = alwaysRemember;
}
public int getOrder() {
return order;
this.tokenValiditySeconds = tokenValiditySeconds;
}
public void setOrder(int order) {
this.order = order;
public void setUserDetailsService(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
public boolean isAlwaysRemember() {
return alwaysRemember;
}
public void setAlwaysRemember(boolean alwaysRemember) {
this.alwaysRemember = alwaysRemember;
}
public String getCookieName() {
return cookieName;
}
}
@@ -28,10 +28,12 @@ import javax.servlet.http.HttpServletRequest;
/**
* Processes an authentication form.<p>Login forms must present two parameters to this filter: a username and
* Processes an authentication form.
* <p>Login forms must present two parameters to this filter: a username and
* password. The parameter names to use are contained in the static fields {@link #ACEGI_SECURITY_FORM_USERNAME_KEY}
* and {@link #ACEGI_SECURITY_FORM_PASSWORD_KEY}.</p>
* <P><B>Do not use this class directly.</B> Instead configure <code>web.xml</code> to use the {@link
*
* <p><b>Do not use this class directly.</b> Instead configure <code>web.xml</code> to use the {@link
* org.acegisecurity.util.FilterToBeanProxy}.</p>
*
* @author Ben Alex
@@ -47,8 +49,7 @@ public class AuthenticationProcessingFilter extends AbstractProcessingFilter {
//~ Methods ========================================================================================================
public Authentication attemptAuthentication(HttpServletRequest request)
throws AuthenticationException {
public Authentication attemptAuthentication(HttpServletRequest request) throws AuthenticationException {
String username = obtainUsername(request);
String password = obtainPassword(request);
@@ -60,6 +61,8 @@ public class AuthenticationProcessingFilter extends AbstractProcessingFilter {
password = "";
}
username = username.trim();
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
// Place the last username attempted into HttpSession for views
@@ -28,7 +28,6 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.Ordered;
import org.springframework.util.Assert;
@@ -41,211 +40,226 @@ import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* <p>Used by the <code>SecurityEnforcementFilter</code> to commence authentication via the {@link
* AuthenticationProcessingFilter}. This object holds the location of the login form, relative to the web app context
* path, and is used to commence a redirect to that form.</p>
* <p>By setting the <em>forceHttps</em> property to true, you may configure the class to force the protocol used
* for the login form to be <code>HTTPS</code>, even if the original intercepted request for a resource used the
* <code>HTTP</code> protocol. When this happens, after a successful login (via HTTPS), the original resource will
* still be accessed as HTTP, via the original request URL. For the forced HTTPS feature to work, the {@link
* PortMapper} is consulted to determine the HTTP:HTTPS pairs.</p>
*
* <p>
* Used by the <code>SecurityEnforcementFilter</code> to commence
* authentication via the {@link AuthenticationProcessingFilter}. This object
* holds the location of the login form, relative to the web app context path,
* and is used to commence a redirect to that form.
* </p>
* <p>
* By setting the <em>forceHttps</em> property to true, you may configure the
* class to force the protocol used for the login form to be <code>HTTPS</code>,
* even if the original intercepted request for a resource used the
* <code>HTTP</code> protocol. When this happens, after a successful login
* (via HTTPS), the original resource will still be accessed as HTTP, via the
* original request URL. For the forced HTTPS feature to work, the {@link
* PortMapper} is consulted to determine the HTTP:HTTPS pairs.
* </p>
*
* @author Ben Alex
* @author colin sampaleanu
* @author Omri Spector
* @version $Id$
* @version $Id: AuthenticationProcessingFilterEntryPoint.java 1873 2007-05-25
* 03:21:17Z benalex $
*/
public class AuthenticationProcessingFilterEntryPoint implements AuthenticationEntryPoint, InitializingBean, Ordered {
//~ Static fields/initializers =====================================================================================
public class AuthenticationProcessingFilterEntryPoint implements AuthenticationEntryPoint, InitializingBean {
// ~ Static fields/initializers
// =====================================================================================
private static final Log logger = LogFactory.getLog(AuthenticationProcessingFilterEntryPoint.class);
private static final Log logger = LogFactory.getLog(AuthenticationProcessingFilterEntryPoint.class);
//~ Instance fields ================================================================================================
// ~ Instance fields
// ================================================================================================
private PortMapper portMapper = new PortMapperImpl();
private PortResolver portResolver = new PortResolverImpl();
private String loginFormUrl;
private boolean forceHttps = false;
private boolean serverSideRedirect = false;
private int order = Integer.MAX_VALUE; // ~ default
private PortMapper portMapper = new PortMapperImpl();
//~ Methods ========================================================================================================
private PortResolver portResolver = new PortResolverImpl();
private String loginFormUrl;
private boolean forceHttps = false;
private boolean serverSideRedirect = false;
// ~ Methods
// ========================================================================================================
public void afterPropertiesSet() throws Exception {
Assert.hasLength(loginFormUrl, "loginFormUrl must be specified");
Assert.notNull(portMapper, "portMapper must be specified");
Assert.notNull(portResolver, "portResolver must be specified");
}
Assert.hasLength(loginFormUrl, "loginFormUrl must be specified");
Assert.notNull(portMapper, "portMapper must be specified");
Assert.notNull(portResolver, "portResolver must be specified");
}
/**
* Allows subclasses to modify the login form URL that should be applicable for a given request.
* Allows subclasses to modify the login form URL that should be applicable
* for a given request.
*
* @param request the request
* @param response the response
* @param exception the exception
* @return the URL (cannot be null or empty; defaults to {@link #getLoginFormUrl()})
* @return the URL (cannot be null or empty; defaults to
* {@link #getLoginFormUrl()})
*/
protected String determineUrlToUseForThisRequest(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) {
protected String determineUrlToUseForThisRequest(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) {
return getLoginFormUrl();
}
public void commence(ServletRequest request, ServletResponse response, AuthenticationException authException)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
String scheme = request.getScheme();
String serverName = request.getServerName();
int serverPort = portResolver.getServerPort(request);
String contextPath = req.getContextPath();
boolean inHttp = "http".equals(scheme.toLowerCase());
boolean inHttps = "https".equals(scheme.toLowerCase());
public void commence(ServletRequest request, ServletResponse response, AuthenticationException authException)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
String scheme = request.getScheme();
String serverName = request.getServerName();
int serverPort = portResolver.getServerPort(request);
String contextPath = req.getContextPath();
boolean includePort = true;
boolean inHttp = "http".equals(scheme.toLowerCase());
boolean inHttps = "https".equals(scheme.toLowerCase());
String redirectUrl = null;
boolean doForceHttps = false;
Integer httpsPort = null;
boolean includePort = true;
if (inHttp && (serverPort == 80)) {
includePort = false;
} else if (inHttps && (serverPort == 443)) {
includePort = false;
}
String redirectUrl = null;
boolean doForceHttps = false;
Integer httpsPort = null;
if (forceHttps && inHttp) {
httpsPort = (Integer) portMapper.lookupHttpsPort(new Integer(serverPort));
if (httpsPort != null) {
doForceHttps = true;
if (httpsPort.intValue() == 443) {
includePort = false;
} else {
includePort = true;
}
}
}
String loginForm = determineUrlToUseForThisRequest(req, resp, authException);
if ( serverSideRedirect ) {
if (inHttp && (serverPort == 80)) {
includePort = false;
}
else if (inHttps && (serverPort == 443)) {
includePort = false;
}
if ( doForceHttps ) {
// before doing server side redirect, we need to do client redirect to https.
String servletPath = req.getServletPath();
String pathInfo = req.getPathInfo();
String query = req.getQueryString();
if (forceHttps && inHttp) {
httpsPort = (Integer) portMapper.lookupHttpsPort(new Integer(serverPort));
redirectUrl = "https://" + serverName + ((includePort) ? (":" + httpsPort) : "") + contextPath
+ servletPath + (pathInfo == null ? "" : pathInfo ) + (query == null ? "" : "?"+query );
if (httpsPort != null) {
doForceHttps = true;
if (httpsPort.intValue() == 443) {
includePort = false;
}
else {
includePort = true;
}
}
} else {
}
if (logger.isDebugEnabled()) {
logger.debug("Server side forward to: " + loginForm);
}
String loginForm = determineUrlToUseForThisRequest(req, resp, authException);
RequestDispatcher dispatcher = req.getRequestDispatcher(loginForm);
if (serverSideRedirect) {
dispatcher.forward(request, response);
return;
if (doForceHttps) {
}
// before doing server side redirect, we need to do client
// redirect to https.
} else {
String servletPath = req.getServletPath();
String pathInfo = req.getPathInfo();
String query = req.getQueryString();
if ( doForceHttps ) {
redirectUrl = "https://" + serverName + ((includePort) ? (":" + httpsPort) : "") + contextPath
+ servletPath + (pathInfo == null ? "" : pathInfo) + (query == null ? "" : "?" + query);
redirectUrl = "https://" + serverName + ((includePort) ? (":" + httpsPort) : "") + contextPath
+ loginForm;
}
else {
} else {
if (logger.isDebugEnabled()) {
logger.debug("Server side forward to: " + loginForm);
}
redirectUrl = scheme + "://" + serverName + ((includePort) ? (":" + serverPort) : "") + contextPath
+ loginForm;
RequestDispatcher dispatcher = req.getRequestDispatcher(loginForm);
}
}
dispatcher.forward(request, response);
if (logger.isDebugEnabled()) {
logger.debug("Redirecting to: " + redirectUrl);
}
return;
((HttpServletResponse) response).sendRedirect(((HttpServletResponse) response).encodeRedirectURL(redirectUrl));
}
}
public boolean getForceHttps() {
return forceHttps;
}
}
else {
public String getLoginFormUrl() {
return loginFormUrl;
}
if (doForceHttps) {
public PortMapper getPortMapper() {
return portMapper;
}
redirectUrl = "https://" + serverName + ((includePort) ? (":" + httpsPort) : "") + contextPath
+ loginForm;
public PortResolver getPortResolver() {
return portResolver;
}
}
else {
public boolean isServerSideRedirect() {
return serverSideRedirect;
}
redirectUrl = scheme + "://" + serverName + ((includePort) ? (":" + serverPort) : "") + contextPath
+ loginForm;
/**
* Set to true to force login form access to be via https. If this value is ture (the default is false),
* and the incoming request for the protected resource which triggered the interceptor was not already
* <code>https</code>, then
*
* @param forceHttps
*/
public void setForceHttps(boolean forceHttps) {
this.forceHttps = forceHttps;
}
}
}
/**
* The URL where the <code>AuthenticationProcessingFilter</code> login page can be found. Should be
* relative to the web-app context path, and include a leading <code>/</code>
*
* @param loginFormUrl
*/
public void setLoginFormUrl(String loginFormUrl) {
this.loginFormUrl = loginFormUrl;
}
if (logger.isDebugEnabled()) {
logger.debug("Redirecting to: " + redirectUrl);
}
public void setPortMapper(PortMapper portMapper) {
this.portMapper = portMapper;
}
public void setPortResolver(PortResolver portResolver) {
this.portResolver = portResolver;
}
/**
* Tells if we are to do a server side include of the <code>loginFormUrl</code> instead of a 302
* redirect.
*
* @param serverSideRedirect
*/
public void setServerSideRedirect(boolean serverSideRedirect) {
this.serverSideRedirect = serverSideRedirect;
}
public int getOrder() {
return order;
((HttpServletResponse) response).sendRedirect(((HttpServletResponse) response).encodeRedirectURL(redirectUrl));
}
public void setOrder(int order) {
this.order = order;
public boolean getForceHttps() {
return forceHttps;
}
public String getLoginFormUrl() {
return loginFormUrl;
}
public PortMapper getPortMapper() {
return portMapper;
}
public PortResolver getPortResolver() {
return portResolver;
}
public boolean isServerSideRedirect() {
return serverSideRedirect;
}
/**
* Set to true to force login form access to be via https. If this value is
* ture (the default is false), and the incoming request for the protected
* resource which triggered the interceptor was not already
* <code>https</code>, then
*
* @param forceHttps
*/
public void setForceHttps(boolean forceHttps) {
this.forceHttps = forceHttps;
}
/**
* The URL where the <code>AuthenticationProcessingFilter</code> login
* page can be found. Should be relative to the web-app context path, and
* include a leading <code>/</code>
*
* @param loginFormUrl
*/
public void setLoginFormUrl(String loginFormUrl) {
this.loginFormUrl = loginFormUrl;
}
public void setPortMapper(PortMapper portMapper) {
this.portMapper = portMapper;
}
public void setPortResolver(PortResolver portResolver) {
this.portResolver = portResolver;
}
/**
* Tells if we are to do a server side include of the
* <code>loginFormUrl</code> instead of a 302 redirect.
*
* @param serverSideRedirect
*/
public void setServerSideRedirect(boolean serverSideRedirect) {
this.serverSideRedirect = serverSideRedirect;
}
}
@@ -26,7 +26,6 @@ import org.acegisecurity.AuthenticationException;
import org.acegisecurity.ui.AuthenticationEntryPoint;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.Ordered;
/**
* In the X.509 authentication case (unlike CAS, for example) the certificate
@@ -47,28 +46,15 @@ import org.springframework.core.Ordered;
*
* @see org.acegisecurity.ui.ExceptionTranslationFilter
*/
public class X509ProcessingFilterEntryPoint implements AuthenticationEntryPoint, Ordered {
public class X509ProcessingFilterEntryPoint implements AuthenticationEntryPoint {
// ~ Static fields/initializers
// =====================================================================================
private static final Log logger = LogFactory.getLog(X509ProcessingFilterEntryPoint.class);
// ~ instance fields
// =====================================================================================
private int order = Integer.MAX_VALUE; // ~ default
// ~ Methods
// ========================================================================================================
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
/**
* Returns a 403 error code to the client.
*
@@ -27,7 +27,7 @@ import org.acegisecurity.BadCredentialsException;
public class UsernameNotFoundException extends BadCredentialsException {
//~ Constructors ===================================================================================================
/**
/**
* Constructs a <code>UsernameNotFoundException</code> with the specified
* message.
*
@@ -37,7 +37,18 @@ public class UsernameNotFoundException extends BadCredentialsException {
super(msg);
}
/**
/**
* Constructs a <code>UsernameNotFoundException</code>, making use of the <tt>extraInformation</tt>
* property of the superclass.
*
* @param msg the detail message
* @param extraInformation additional information such as the username.
*/
public UsernameNotFoundException(String msg, Object extraInformation) {
super(msg, extraInformation);
}
/**
* Constructs a <code>UsernameNotFoundException</code> with the specified
* message and root cause.
*
@@ -1,65 +0,0 @@
package org.acegisecurity.util;
import java.lang.reflect.Method;
import java.util.Map;
import org.springframework.context.ApplicationContext;
import org.springframework.core.Ordered;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
/**
* Proivdes common logic for manipulating classes implementing the Spring
* {@link Ordered} interface.
*
* @author Ben Alex
*/
public abstract class OrderedUtils {
/**
* Introspects the application context for a single instance of <code>sourceClass</code>. If found, the order from the source
* class instance is copied into the <code>destinationObject</code>. If more than one instance of <code>sourceClass</code>
* is found, the method throws <code>IllegalStateException</code>.
*
* <p>The <code>destinationObject</code> is required to provide a public <code>setOrder(int)</code> method to permit
* mutation of the order property.
*
* @param sourceClass to locate in the application context (must be assignable to Ordered)
* @param applicationContext to locate the class
* @param destinationObject to copy the order into (must provide public setOrder(int) method)
* @param skipIfMoreThanOneCandidateSourceClassInstance if the application context provides more than one potential source, skip modifications (if false, the first located matching source will be used)
* @return whether or not the destination class was updated
*/
public static boolean copyOrderFromOtherClass(Class sourceClass, ApplicationContext applicationContext, Object destinationObject, boolean skipIfMoreThanOneCandidateSourceClassInstance) {
Assert.notNull(sourceClass, "Source class required");
Assert.notNull(applicationContext, "ApplicationContext required");
Assert.notNull(destinationObject, "Destination object required");
Assert.isTrue(Ordered.class.isAssignableFrom(sourceClass), "Source class " + sourceClass + " must be assignable to Ordered");
Map map = applicationContext.getBeansOfType(sourceClass);
if (map.size() == 0) {
return false;
} else if (map.size() > 1 && skipIfMoreThanOneCandidateSourceClassInstance) {
return false;
} else {
copyOrderFromOtherObject((Ordered)map.values().iterator().next(), destinationObject);
return true;
}
}
/**
* Copies the order property from the <code>sourceObject</code> into the <code>destinationObject</code>.
*
* <p>The <code>destinationObject</code> is required to provide a public <code>setOrder(int)</code> method to permit
* mutation of the order property.
*
* @param sourceObject to copy the order from
* @param destinationObject to copy the order into (must provide public setOrder(int) method)
*/
public static void copyOrderFromOtherObject(Ordered sourceObject, Object destinationObject) {
Assert.notNull(sourceObject, "Source object required");
Assert.notNull(destinationObject, "Destination object required");
Method m = ReflectionUtils.findMethod(destinationObject.getClass(), "setOrder", new Class[] {int.class});
Assert.notNull(m, "Method setOrder(int) not found on " + destinationObject.getClass());
ReflectionUtils.invokeMethod(m, destinationObject, new Object[] {new Integer(sourceObject.getOrder())});
}
}
@@ -20,6 +20,8 @@ import org.springframework.util.StringUtils;
import java.util.HashMap;
import java.util.Map;
import java.util.ArrayList;
import java.util.List;
/**
@@ -29,6 +31,9 @@ import java.util.Map;
* @version $Id$
*/
public final class StringSplitUtils {
//~ Static fields/initializers =====================================================================================
private static final String[] EMPTY_STRING_ARRAY = new String[0];
//~ Constructors ===================================================================================================
private StringSplitUtils() {
@@ -40,12 +45,10 @@ public final class StringSplitUtils {
* Splits a <code>String</code> at the first instance of the delimiter.<p>Does not include the delimiter in
* the response.</p>
*
* @param toSplit the string to split
* @param toSplit the string to split
* @param delimiter to split the string up with
*
* @return a two element array with index 0 being before the delimiter, and index 1 being after the delimiter
* (neither element includes the delimiter)
*
* @throws IllegalArgumentException if an argument was invalid
*/
public static String[] split(String toSplit, String delimiter) {
@@ -65,7 +68,7 @@ public final class StringSplitUtils {
String beforeDelimiter = toSplit.substring(0, offset);
String afterDelimiter = toSplit.substring(offset + 1);
return new String[] {beforeDelimiter, afterDelimiter};
return new String[]{beforeDelimiter, afterDelimiter};
}
/**
@@ -74,11 +77,10 @@ public final class StringSplitUtils {
* then generated, with the left of the delimiter providing the key, and the right of the delimiter providing the
* value.<p>Will trim both the key and value before adding to the <code>Map</code>.</p>
*
* @param array the array to process
* @param delimiter to split each element using (typically the equals symbol)
* @param array the array to process
* @param delimiter to split each element using (typically the equals symbol)
* @param removeCharacters one or more characters to remove from each element prior to attempting the split
* operation (typically the quotation mark symbol) or <code>null</code> if no removal should occur
*
* operation (typically the quotation mark symbol) or <code>null</code> if no removal should occur
* @return a <code>Map</code> representing the array contents, or <code>null</code> if the array to process was
* null or empty
*/
@@ -135,4 +137,58 @@ public final class StringSplitUtils {
return str.substring(pos + separator.length());
}
/**
* Splits a given string on the given separator character, skips the contents of quoted substrings
* when looking for separators.
* Introduced for use in DigestProcessingFilter (see SEC-506).
* <p/>
* This was copied and modified from commons-lang StringUtils
*/
public static String[] splitIgnoringQuotes(String str, char separatorChar) {
if (str == null) {
return null;
}
int len = str.length();
if (len == 0) {
return EMPTY_STRING_ARRAY;
}
List list = new ArrayList();
int i = 0;
int start = 0;
boolean match = false;
while (i < len) {
if (str.charAt(i) == '"') {
i++;
while (i < len) {
if (str.charAt(i) == '"') {
i++;
break;
}
i++;
}
match = true;
continue;
}
if (str.charAt(i) == separatorChar) {
if (match) {
list.add(str.substring(start, i));
match = false;
}
start = ++i;
continue;
}
match = true;
i++;
}
if (match) {
list.add(str.substring(start, i));
}
return (String[]) list.toArray(new String[list.size()]);
}
}
@@ -15,40 +15,43 @@
package org.acegisecurity.vote;
import java.util.Iterator;
import java.util.List;
import org.acegisecurity.AccessDecisionManager;
import org.acegisecurity.AccessDeniedException;
import org.acegisecurity.AcegiMessageSource;
import org.acegisecurity.ConfigAttribute;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.util.Assert;
import java.util.Iterator;
import java.util.List;
/**
* Abstract implementation of {@link AccessDecisionManager}.<p>Handles configuration of a bean context defined list
* of {@link AccessDecisionVoter}s and the access control behaviour if all voters abstain from voting (defaults to
* deny access).</p>
* Abstract implementation of {@link AccessDecisionManager}.
* <p/>
* Handles configuration of a bean context defined list of
* {@link AccessDecisionVoter}s and the access control behaviour if all voters
* abstain from voting (defaults to deny access).
* </p>
*/
public abstract class AbstractAccessDecisionManager implements AccessDecisionManager, InitializingBean,
MessageSourceAware {
//~ Instance fields ================================================================================================
MessageSourceAware {
// ~ Instance fields
// ================================================================================================
private List decisionVoters;
protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor();
private boolean allowIfAllAbstainDecisions = false;
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
public void afterPropertiesSet() throws Exception {
checkIfValidList(this.decisionVoters);
Assert.notEmpty(this.decisionVoters, "A list of AccessDecisionVoters is required");
Assert.notNull(this.messages, "A message source must be set");
}
@@ -59,12 +62,6 @@ public abstract class AbstractAccessDecisionManager implements AccessDecisionMan
}
}
private void checkIfValidList(List listToCheck) {
if ((listToCheck == null) || (listToCheck.size() == 0)) {
throw new IllegalArgumentException("A list of AccessDecisionVoters is required");
}
}
public List getDecisionVoters() {
return this.decisionVoters;
}
@@ -78,21 +75,14 @@ public abstract class AbstractAccessDecisionManager implements AccessDecisionMan
}
public void setDecisionVoters(List newList) {
checkIfValidList(newList);
Assert.notEmpty(newList);
Iterator iter = newList.iterator();
while (iter.hasNext()) {
Object currentObject = null;
try {
currentObject = iter.next();
AccessDecisionVoter attemptToCast = (AccessDecisionVoter) currentObject;
} catch (ClassCastException cce) {
throw new IllegalArgumentException("AccessDecisionVoter " + currentObject.getClass().getName()
Object currentObject = iter.next();
Assert.isInstanceOf(AccessDecisionVoter.class, currentObject, "AccessDecisionVoter " + currentObject.getClass().getName()
+ " must implement AccessDecisionVoter");
}
}
this.decisionVoters = newList;
@@ -117,11 +107,14 @@ public abstract class AbstractAccessDecisionManager implements AccessDecisionMan
}
/**
* Iterates through all <code>AccessDecisionVoter</code>s and ensures each can support the presented class.<p>If
* one or more voters cannot support the presented class, <code>false</code> is returned.</p>
* Iterates through all <code>AccessDecisionVoter</code>s and ensures
* each can support the presented class.
* <p/>
* If one or more voters cannot support the presented class,
* <code>false</code> is returned.
* </p>
*
* @param clazz DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public boolean supports(Class clazz) {
@@ -47,11 +47,10 @@ public class AuthenticatedVoter implements AccessDecisionVoter {
public static final String IS_AUTHENTICATED_FULLY = "IS_AUTHENTICATED_FULLY";
public static final String IS_AUTHENTICATED_REMEMBERED = "IS_AUTHENTICATED_REMEMBERED";
public static final String IS_AUTHENTICATED_ANONYMOUSLY = "IS_AUTHENTICATED_ANONYMOUSLY";
//~ Instance fields ================================================================================================
private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
//~ Methods ========================================================================================================
private boolean isFullyAuthenticated(Authentication authentication) {
@@ -25,10 +25,10 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import java.util.Map;
/**
@@ -57,7 +57,7 @@ public class LabelBasedAclVoter extends AbstractAclVoter {
//~ Instance fields ================================================================================================
private HashMap labelMap = null;
private Map labelMap = null;
private String attributeIndicatingLabeledOperation = null;
private boolean allowAccessIfNoAttributesAreLabeled = true;
@@ -136,7 +136,7 @@ public class LabelBasedAclVoter extends AbstractAclVoter {
* @param labelMap a map structured as in the above example.
*
*/
public void setLabelMap(HashMap labelMap) {
public void setLabelMap(Map labelMap) {
this.labelMap = labelMap;
}
@@ -15,92 +15,108 @@
package org.acegisecurity.vote;
import java.util.Iterator;
import org.acegisecurity.Authentication;
import org.acegisecurity.ConfigAttribute;
import org.acegisecurity.ConfigAttributeDefinition;
import java.util.Iterator;
/**
* <p>Votes if any {@link ConfigAttribute#getAttribute()} starts with a prefix indicating that it is a role. The
* default prefix string is <Code>ROLE_</code>, but this may be overriden to any value. It may also be set to empty,
* which means that essentially any attribute will be voted on. As described further below, the effect of an empty
* prefix may not be quite desireable.</p>
* <p>Abstains from voting if no configuration attribute commences with the role prefix. Votes to grant access if
* there is an exact matching {@link org.acegisecurity.GrantedAuthority} to a <code>ConfigAttribute</code> starting
* with the role prefix. Votes to deny access if there is no exact matching <code>GrantedAuthority</code> to a
* <code>ConfigAttribute</code> starting with the role prefix.</p>
* <p>An empty role prefix means that the voter will vote for every ConfigAttribute. When there are different
* categories of ConfigAttributes used, this will not be optimal since the voter will be voting for attributes which
* do not represent roles. However, this option may be of some use when using preexisting role names without a prefix,
* and no ability exists to prefix them with a role prefix on reading them in, such as provided for example in {@link
* org.acegisecurity.userdetails.jdbc.JdbcDaoImpl}.</p>
* <p>All comparisons and prefixes are case sensitive.</p>
*
* <p>
* Votes if any {@link ConfigAttribute#getAttribute()} starts with a prefix
* indicating that it is a role. The default prefix string is <Code>ROLE_</code>,
* but this may be overriden to any value. It may also be set to empty, which
* means that essentially any attribute will be voted on. As described further
* below, the effect of an empty prefix may not be quite desireable.
* </p>
* <p>
* Abstains from voting if no configuration attribute commences with the role
* prefix. Votes to grant access if there is an exact matching
* {@link org.acegisecurity.GrantedAuthority} to a <code>ConfigAttribute</code>
* starting with the role prefix. Votes to deny access if there is no exact
* matching <code>GrantedAuthority</code> to a <code>ConfigAttribute</code>
* starting with the role prefix.
* </p>
* <p>
* An empty role prefix means that the voter will vote for every
* ConfigAttribute. When there are different categories of ConfigAttributes
* used, this will not be optimal since the voter will be voting for attributes
* which do not represent roles. However, this option may be of some use when
* using preexisting role names without a prefix, and no ability exists to
* prefix them with a role prefix on reading them in, such as provided for
* example in {@link org.acegisecurity.userdetails.jdbc.JdbcDaoImpl}.
* </p>
* <p>
* All comparisons and prefixes are case sensitive.
* </p>
*
* @author Ben Alex
* @author colin sampaleanu
* @version $Id$
*/
public class RoleVoter implements AccessDecisionVoter {
//~ Instance fields ================================================================================================
// ~ Instance fields
// ================================================================================================
private String rolePrefix = "ROLE_";
private String rolePrefix = "ROLE_";
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
public String getRolePrefix() {
return rolePrefix;
}
public String getRolePrefix() {
return rolePrefix;
}
/**
* Allows the default role prefix of <code>ROLE_</code> to be overriden. May be set to an empty value,
* although this is usually not desireable.
*
* @param rolePrefix the new prefix
*/
public void setRolePrefix(String rolePrefix) {
this.rolePrefix = rolePrefix;
}
/**
* Allows the default role prefix of <code>ROLE_</code> to be overriden.
* May be set to an empty value, although this is usually not desireable.
*
* @param rolePrefix the new prefix
*/
public void setRolePrefix(String rolePrefix) {
this.rolePrefix = rolePrefix;
}
public boolean supports(ConfigAttribute attribute) {
if ((attribute.getAttribute() != null) && attribute.getAttribute().startsWith(getRolePrefix())) {
return true;
} else {
return false;
}
}
public boolean supports(ConfigAttribute attribute) {
if ((attribute.getAttribute() != null) && attribute.getAttribute().startsWith(getRolePrefix())) {
return true;
}
else {
return false;
}
}
/**
* This implementation supports any type of class, because it does not query the presented secure object.
*
* @param clazz the secure object
*
* @return always <code>true</code>
*/
public boolean supports(Class clazz) {
return true;
}
/**
* This implementation supports any type of class, because it does not query
* the presented secure object.
*
* @param clazz the secure object
*
* @return always <code>true</code>
*/
public boolean supports(Class clazz) {
return true;
}
public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config) {
int result = ACCESS_ABSTAIN;
Iterator iter = config.getConfigAttributes();
public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config) {
int result = ACCESS_ABSTAIN;
Iterator iter = config.getConfigAttributes();
while (iter.hasNext()) {
ConfigAttribute attribute = (ConfigAttribute) iter.next();
while (iter.hasNext()) {
ConfigAttribute attribute = (ConfigAttribute) iter.next();
if (this.supports(attribute)) {
result = ACCESS_DENIED;
if (this.supports(attribute)) {
result = ACCESS_DENIED;
// Attempt to find a matching granted authority
for (int i = 0; i < authentication.getAuthorities().length; i++) {
if (attribute.getAttribute().equals(authentication.getAuthorities()[i].getAuthority())) {
return ACCESS_GRANTED;
}
}
}
}
// Attempt to find a matching granted authority
for (int i = 0; i < authentication.getAuthorities().length; i++) {
if (attribute.getAttribute().equals(authentication.getAuthorities()[i].getAuthority())) {
return ACCESS_GRANTED;
}
}
}
}
return result;
}
return result;
}
}
@@ -1 +0,0 @@
http\://www.springframework.org/schema/security=org.acegisecurity.config.SecurityNamespaceHandler
@@ -1,2 +0,0 @@
http\://www.springframework.org/schema/security/spring-security-2.0.xsd=org/acegisecurity/config/spring-security-2.0.xsd
@@ -0,0 +1,47 @@
AuthByAdapterProvider.incorrectKey=Pou\u017eit\u00e1 implementace AuthByAdapter neobsahuje o\u010dek\u00e1van\u00fd kl\u00ed\u010d
BasicAclEntryAfterInvocationProvider.noPermission=Autentizovan\u00fd u\u017eivatel {0} nem\u00e1 \u017d\u00c1DN\u00c1 pr\u00e1va k objektu {1}
BasicAclEntryAfterInvocationProvider.insufficientPermission=Autentizovan\u00fd u\u017eivatel {0} nem\u00e1 po\u017eadovan\u00e1 opr\u00e1vn\u011bn\u00ed k objektu {1}
ConcurrentSessionControllerImpl.exceededAllowed=Maxim\u00e1ln\u00ed po\u010det sou\u010dasn\u00fdch p\u0159ihl\u00e1\u0161en\u00ed {0} tohoto u\u017eivatele je p\u0159ekro\u010den.
ProviderManager.providerNotFound=Nebyl nalezen \u017e\u00e1dn\u00fd AuthenticationProvider pro {0}
AnonymousAuthenticationProvider.incorrectKey=Pou\u017eit\u00fd AnonymousAuthenticationToken neobsahuje o\u010dek\u00e1van\u00fd kl\u00ed\u010d
CasAuthenticationProvider.incorrectKey=Pou\u017eit\u00fd CasAuthenticationToken neobsahuje o\u010dek\u00e1van\u00fd kl\u00ed\u010d
CasAuthenticationProvider.noServiceTicket=Nepoda\u0159ilo se z\u00edskat otisk CAS (centr\u00e1ln\u00ed autentiza\u010dn\u00ed autority) k ov\u011b\u0159en\u00ed autenticity u\u017eivatele.
NamedCasProxyDecider.untrusted=Nelze v\u011b\u0159it nejbli\u017e\u0161\u00ed proxy {0}
RejectProxyTickets.reject=Otisky proxy jsou odm\u00edtnuty
AbstractSecurityInterceptor.authenticationNotFound=Nebyl nalezen \u017e\u00e1dn\u00fd Authentication objekt v SecurityContext
AbstractUserDetailsAuthenticationProvider.onlySupports=Je podporov\u00e1n pouze UsernamePasswordAuthenticationToken
AbstractUserDetailsAuthenticationProvider.locked=U\u017eivatelsk\u00fd \u00fa\u010det je uzam\u010den
AbstractUserDetailsAuthenticationProvider.disabled=U\u017eivatelsk\u00fd \u00fa\u010det nen\u00ed aktivn\u00ed
AbstractUserDetailsAuthenticationProvider.expired=Platnost u\u017eivatelsk\u00e9ho \u00fa\u010dtu vypr\u0161ela
AbstractUserDetailsAuthenticationProvider.credentialsExpired=Platnost u\u017eivatelsk\u00e9ho hesla vypr\u0161ela
AbstractUserDetailsAuthenticationProvider.badCredentials=\u0160patn\u00e9 p\u0159ihla\u0161ovac\u00ed \u00fadaje
X509AuthenticationProvider.certificateNull=Certifik\u00e1t je pr\u00e1zdn\u00fd
DaoX509AuthoritiesPopulator.noMatching=V subjectDN nebyl nalezen \u017e\u00e1dn\u00fd \u0159et\u011bzec odpov\u00eddaj\u00edc\u00ed vy\u017eadovan\u00e9 masce: {0}
RememberMeAuthenticationProvider.incorrectKey=Pou\u017eit\u00fd RememberMeAuthenticationToken neobsahuje o\u010dek\u00e1van\u00fd kl\u00ed\u010d
RunAsImplAuthenticationProvider.incorrectKey=Pou\u017eit\u00fd RunAsUserToken neobsahuje o\u010dek\u00e1van\u00fd kl\u00ed\u010d
DigestProcessingFilter.missingMandatory=Chyb\u00ed povinn\u00e1 kl\u00ed\u010dov\u00e1 polo\u017eka; p\u0159ijat\u00e1 hlavi\u010dka {0}
DigestProcessingFilter.missingAuth=Chyb\u00ed povinn\u00e1 kl\u00ed\u010dov\u00e1 polo\u017eka 'auth' QOP (\u00farove\u0148 bezpe\u010dnosti RFC 2617); p\u0159ijat\u00e1 hlavi\u010dka {0}
DigestProcessingFilter.incorrectRealm=Oblast odpov\u011bdi {0} neodpov\u00edd\u00e1 syst\u00e9mov\u00e9 oblasti {1}
DigestProcessingFilter.nonceExpired=Kryptovan\u00fd kl\u00ed\u010d (nonce) vypr\u0161el
DigestProcessingFilter.nonceEncoding=Kryptovan\u00fd kl\u00ed\u010d (nonce) nen\u00ed p\u0159ek\u00e9dov\u00e1n do Base60; p\u0159ijat\u00fd kl\u00ed\u010d {0}
DigestProcessingFilter.nonceNotTwoTokens=Kryptovan\u00fd kl\u00ed\u010d (nonce) by m\u011bl b\u00fdt slo\u017een ze dvou \u010d\u00e1st\u00ed {0}
DigestProcessingFilter.nonceNotNumeric=Kryptovan\u00fd kl\u00ed\u010d (nonce) by m\u011bl m\u00edt prvn\u00ed \u010d\u00e1st \u010d\u00edselnou, ale je {0}
DigestProcessingFilter.nonceCompromised=Kryptovan\u00fd kl\u00ed\u010d (nonce) je znehodnocen\u00fd {0}
DigestProcessingFilter.usernameNotFound=U\u017eivatelsk\u00e9 jm\u00e9no {0} nebylo nalezeno
DigestProcessingFilter.incorrectResponse=Vadn\u00e1 odpov\u011b\u010f
SwitchUserProcessingFilter.noCurrentUser=\u017d\u00e1dn\u00fd u\u017eivatel nen\u00ed asociov\u00e1n s t\u00edmto po\u017eadavkem
SwitchUserProcessingFilter.noOriginalAuthentication=Nepoda\u0159ilo se nal\u00e9zt p\u016fvodn\u00ed Authentication objekt
SwitchUserProcessingFilter.usernameNotFound=U\u017eivatelsk\u00e9 jm\u00e9no {0} nebylo nalezeno
SwitchUserProcessingFilter.locked=U\u017eivatelsk\u00fd \u00fa\u010det je uzam\u010den
SwitchUserProcessingFilter.disabled=U\u017eivatelsk\u00fd \u00fa\u010det nen\u00ed aktivn\u00ed
SwitchUserProcessingFilter.expired=Platnost u\u017eivatelsk\u00e9ho \u00fa\u010dtu vypr\u0161ela
SwitchUserProcessingFilter.credentialsExpired=Platnost u\u017eivatelsk\u00e9ho hesla vypr\u0161ela
AbstractAccessDecisionManager.accessDenied=P\u0159\u00edstup odep\u0159en
LdapAuthenticationProvider.emptyUsername=Nen\u00ed povoleno pr\u00e1zdn\u00e9 u\u017eivatelsk\u00e9 jm\u00e9no
LdapAuthenticationProvider.emptyPassword=\u0160patn\u00e9 p\u0159ihla\u0161ovac\u00ed \u00fadaje
DefaultIntitalDirContextFactory.communicationFailure=Nen\u00ed mo\u017en\u00e9 se p\u0159ipojit k LDAP serveru
DefaultIntitalDirContextFactory.badCredentials=\u0160patn\u00e9 p\u0159ihla\u0161ovac\u00ed \u00fadaje
DefaultIntitalDirContextFactory.unexpectedException=Nepoda\u0159ilo se z\u00edskat InitialDirContext d\u00edky neo\u010dek\u00e1van\u00e9 vyj\u00edmce
PasswordComparisonAuthenticator.badCredentials=\u0160patn\u00e9 p\u0159ihla\u0161ovac\u00ed \u00fadaje
BindAuthenticator.badCredentials=\u0160patn\u00e9 p\u0159ihla\u0161ovac\u00ed \u00fadaje
BindAuthenticator.failedToLoadAttributes=\u0160patn\u00e9 p\u0159ihla\u0161ovac\u00ed \u00fadaje
@@ -0,0 +1,48 @@
AuthByAdapterProvider.incorrectKey=Die angegebene AuthByAdapter-Implementierung enthält nicht den erwarteten Schlüssel
BasicAclEntryAfterInvocationProvider.noPermission=Authentifikation {0} hat KEINE Berechtigungen bezüglich des Domänen-Objekts {1}
BasicAclEntryAfterInvocationProvider.insufficientPermission=Authentifikation {0} hat keine ausreichenden ACL-Berechtigungen bezüglich des Domänen-Objekts {1}
ConcurrentSessionControllerImpl.exceededAllowed=Die maximale Sitzungs-Anzahl von {0} für diesen Nutzer wurde überschritten
ProviderManager.providerNotFound=Für {0} wurde kein AuthenticationProvider gefunden
AnonymousAuthenticationProvider.incorrectKey=Das angegebene AnonymousAuthenticationToken enthält nicht den erwarteten Schlüssel
CasAuthenticationProvider.incorrectKey=Das angegebene CasAuthenticationToken enthält nicht den erwarteten Schlüssel
CasAuthenticationProvider.noServiceTicket=Es konnte kein CAS Service-Ticket zur Prüfung geliefert werden
NamedCasProxyDecider.untrusted=Der nächste Proxy {0} ist nicht vertrauenswürdig
RejectProxyTickets.reject=Proxy-Tickets sind abgelehnt
AbstractSecurityInterceptor.authenticationNotFound=Im SecurityContext wurde keine Authentifikation gefunden
AbstractUserDetailsAuthenticationProvider.onlySupports=Nur UsernamePasswordAuthenticationToken wird unterstützt
AbstractUserDetailsAuthenticationProvider.locked=Das Benutzerkonto ist gesperrt
AbstractUserDetailsAuthenticationProvider.disabled=Der Benutzer ist deaktiviert
AbstractUserDetailsAuthenticationProvider.expired=Die Gültigkeit des Benutzerkontos ist abgelaufen
AbstractUserDetailsAuthenticationProvider.credentialsExpired=Die Gültigkeit der Benutzerberechtigungen ist abgelaufen
AbstractUserDetailsAuthenticationProvider.badCredentials=Ungültige Benutzerberechtigungen
X509AuthenticationProvider.certificateNull=Zertifikat ist nicht gesetzt
DaoX509AuthoritiesPopulator.noMatching=Kein passendes Muster gefunden in subjectDN: {0}
RememberMeAuthenticationProvider.incorrectKey=Das angegebene RememberMeAuthenticationToken enthält nicht den erwarteten Schlüssel
RunAsImplAuthenticationProvider.incorrectKey=Das angegebene RunAsUserToken enthält nicht den erwarteten Schlüssel
DigestProcessingFilter.missingMandatory=Erforderlicher Digest-Wert fehlt; Empfangener Header {0}
DigestProcessingFilter.missingAuth=Erforderlicher Digest-Wert fehlt für 'auth' QOP; Empfangener Header {0}
DigestProcessingFilter.incorrectRealm=Realm-Name in Antwort {0} entspricht nicht dem Namen des System-Realms {1}
DigestProcessingFilter.nonceExpired=Die Nonce ist nicht mehr gültig
DigestProcessingFilter.nonceEncoding=Die Nonce ist nicht in Base64 kodiert; Empfangene Nonce {0}
DigestProcessingFilter.nonceNotTwoTokens=Nonce sollte zwei Elemente beinhalten. Gefundener Inhalt: {0}
DigestProcessingFilter.nonceNotNumeric=Das erste Element der Nonce sollte numerisch sein. Gefundener Inhalt: {0}
DigestProcessingFilter.nonceCompromised=Das Nonce-Element ist kompromittiert {0}
DigestProcessingFilter.usernameNotFound=Benutzername {0} wurde nicht gefunden
DigestProcessingFilter.incorrectResponse=Fehlerhafte Antwort
SwitchUserProcessingFilter.noCurrentUser=Mit dieser Anfrage ist kein aktueller Benutzer assoziiert
SwitchUserProcessingFilter.noOriginalAuthentication=Kann das ursprüngliche Authentifikationsobjekt nicht finden
SwitchUserProcessingFilter.usernameNotFound=Benutzername {0} wurde nicht gefunden
SwitchUserProcessingFilter.locked=Das Benutzerkonto ist gesperrt
SwitchUserProcessingFilter.disabled=Der Benutzer ist deaktiviert
SwitchUserProcessingFilter.expired=Die Gültigkeit des Benutzerkontos ist abgelaufen
SwitchUserProcessingFilter.credentialsExpired=Die Gültigkeit der Benutzerberechtigungen ist abgelaufen
AbstractAccessDecisionManager.accessDenied=Zugriff verweigert
LdapAuthenticationProvider.emptyUsername=Ein leerer Benutzername ist nicht erlaubt
LdapAuthenticationProvider.emptyPassword=Ungültige Benutzerberechtigungen
DefaultIntitalDirContextFactory.communicationFailure=Kann keine Verbindung zum LDAP-Server herstellen
DefaultIntitalDirContextFactory.badCredentials=Ungültige Benutzerberechtigungen
DefaultIntitalDirContextFactory.unexpectedException=Auf den InitialDirContext kann aufgrund eines unerwarteten Fehlers nicht zugegriffen werden
PasswordComparisonAuthenticator.badCredentials=Ungültige Benutzerberechtigungen
BindAuthenticator.badCredentials=Ungültige Benutzerberechtigungen
BindAuthenticator.failedToLoadAttributes=Ungültige Benutzerberechtigungen
@@ -0,0 +1,43 @@
# Acegi security
# Messages in French
# Translation by Laurent Pireyn (laurent.pireyn@pisolutions.eu)
AuthByAdapterProvider.incorrectKey = L'implémentation de AuthByAdapter présentée ne contient pas la clé attendue
BasicAclEntryAfterInvocationProvider.noPermission = L'authentification {0} n'a AUCUNE permission pour l'objet domaine {1}
BasicAclEntryAfterInvocationProvider.insufficientPermission = L'authentification {0} a des permissions ACL pour l'objet domaine, mais pas la permission ACL requise pour l'objet domaine {1}
ConcurrentSessionControllerImpl.exceededAllowed = Le maximum de {0} sessions a été dépassé pour cet utilisateur
ProviderManager.providerNotFound = Aucun AuthenticationProvider n'a été trouvé pour {0}
AnonymousAuthenticationProvider.incorrectKey = L'AnonymousAuthenticationToken présenté ne contient pas la clé attendue
CasAuthenticationProvider.incorrectKey = Le CasAuthenticationToken présenté ne contient pas la clé attendue
CasAuthenticationProvider.noServiceTicket = Echec d'obtention d'un ticket CAS à valider
NamedCasProxyDecider.untrusted = Le proxy {0} le plus proche n'est pas fiable
RejectProxyTickets.reject = Des tickets proxy ont été rejetés
AbstractSecurityInterceptor.authenticationNotFound = Aucun objet Authentication n'a été trouvé dans le SecurityContext
AbstractUserDetailsAuthenticationProvider.onlySupports = Seul UsernamePasswordAuthenticationToken est pris en charge
AbstractUserDetailsAuthenticationProvider.locked = Le compte utilisateur est bloqué
AbstractUserDetailsAuthenticationProvider.disabled = Le compte utilisateur est désactivé
AbstractUserDetailsAuthenticationProvider.expired = Le compte utilisateur a expiré
AbstractUserDetailsAuthenticationProvider.credentialsExpired = Les créances de l'utilisateur ont expiré
AbstractUserDetailsAuthenticationProvider.badCredentials = Les créances sont erronées
X509AuthenticationProvider.certificateNull = Le certificat est null
DaoX509AuthoritiesPopulator.noMatching = Aucun motif concordant n'a été trouvé dans le subjectDN: {0}
RememberMeAuthenticationProvider.incorrectKey = Le RememberMeAuthenticationToken présenté ne contient pas la clé attendue
RunAsImplAuthenticationProvider.incorrectKey = Le RunAsUserToken présenté ne contient pas la clé attendue
DigestProcessingFilter.missingMandatory = Une valeur obligatoire manque au condensé; reçu l'entête {0}
DigestProcessingFilter.missingAuth = Une valeur obligatoire manque au condensé pour 'auth' QOP; reçu l'entête {0}
DigestProcessingFilter.incorrectRealm = Le nom de domaine de la réponse {0} ne correspond pas au nom de domaine du système {1}
DigestProcessingFilter.nonceExpired = Le nonce a expiré
DigestProcessingFilter.nonceEncoding = Le nonce n'est pas encodé en Base64; reçu le nonce {0}
DigestProcessingFilter.nonceNotTwoTokens = Le nonce aurait dû générer deux jetons, mais était {0}
DigestProcessingFilter.nonceNotNumeric = Le jeton nonce aurait dû générer d'abord un jeton numérique, mais était {0}
DigestProcessingFilter.nonceCompromised = Le jeton nonce est compromis {0}
DigestProcessingFilter.usernameNotFound = Le nom d'utilisateur {0} n'a pas été trouvé
DigestProcessingFilter.incorrectResponse = Réponse incorrecte
SwitchUserProcessingFilter.noCurrentUser = Aucun utilisateur n'est associé à la requête en cours
SwitchUserProcessingFilter.noOriginalAuthentication = L'objet Authentication original n'a pas été trouvé
SwitchUserProcessingFilter.usernameNotFound = Le nom d'utilisateur {0} n'a pas été trouvé
SwitchUserProcessingFilter.locked = Le compte utilisateur est bloqué
SwitchUserProcessingFilter.disabled = Le compte utilisateur est désactivé
SwitchUserProcessingFilter.expired = Le compte utilisateur a expiré
SwitchUserProcessingFilter.credentialsExpired = Les créances de l'utilisateur ont expiré
AbstractAccessDecisionManager.accessDenied = Accès refusé
+12
View File
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<project name="Acegi Security Core">
<body>
<menu ref="parent"/>
<menu ref="reports"/>
</body>
</project>
@@ -24,7 +24,6 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.acegisecurity.ui.AuthenticationEntryPoint;
import org.springframework.core.Ordered;
/**
@@ -33,22 +32,13 @@ import org.springframework.core.Ordered;
* @author Ben Alex
* @version $Id$
*/
public class MockAuthenticationEntryPoint implements AuthenticationEntryPoint, Ordered {
public class MockAuthenticationEntryPoint implements AuthenticationEntryPoint {
//~ Instance fields ================================================================================================
private String url;
private int order = Integer.MAX_VALUE; // ~ default
//~ Constructors ===================================================================================================
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
public MockAuthenticationEntryPoint(String url) {
this.url = url;
}
@@ -17,7 +17,7 @@ package org.acegisecurity.acl.basic.cache;
import junit.framework.TestCase;
import net.sf.ehcache.Cache;
import net.sf.ehcache.Ehcache;
import org.acegisecurity.MockApplicationContext;
@@ -56,14 +56,10 @@ public class EhCacheBasedAclEntryCacheTests extends TestCase {
//~ Methods ========================================================================================================
private Cache getCache() {
private Ehcache getCache() {
ApplicationContext ctx = MockApplicationContext.getContext();
return (Cache) ctx.getBean("eHCacheBackend");
}
public static void main(String[] args) {
junit.textui.TestRunner.run(EhCacheBasedAclEntryCacheTests.class);
return (Ehcache) ctx.getBean("eHCacheBackend");
}
public final void setUp() throws Exception {
@@ -99,7 +95,7 @@ public class EhCacheBasedAclEntryCacheTests extends TestCase {
assertTrue(true);
}
Cache myCache = getCache();
Ehcache myCache = getCache();
cache.setCache(myCache);
assertEquals(myCache, cache.getCache());
}
@@ -51,7 +51,9 @@ public class AlwaysTestAfterTimeInMillisCaptchaChannelProcessorTests extends Tes
context.setHuman();
assertFalse(alwaysTestAfterTimeInMillisCaptchaChannelProcessor.isContextValidConcerningHumanity(context));
}
/* Commented out as it makes assumptions about the speed of the build server and fails intermittently on
build.springframework.org - L.T.
public void testIsContextValidConcerningHumanity()
throws Exception {
CaptchaSecurityContext context = new CaptchaSecurityContextImpl();
@@ -72,7 +74,7 @@ public class AlwaysTestAfterTimeInMillisCaptchaChannelProcessorTests extends Tes
assertFalse(alwaysTestAfterTimeInMillisCaptchaChannelProcessor.isContextValidConcerningHumanity(context));
}
*/
public void testNewContext() {
CaptchaSecurityContext context = new CaptchaSecurityContextImpl();
@@ -43,7 +43,7 @@ public class AlwaysTestBelowAverageTimeInMillisBetweenRequestsChannelProcessorTe
context.setHuman();
long now = System.currentTimeMillis();
/*
while ((System.currentTimeMillis() - now) <= 100) {
assertTrue(alwaysTestBelowAverageTimeInMillisBetweenRequestsChannelProcessor
.isContextValidConcerningHumanity(context));
@@ -64,8 +64,9 @@ public class AlwaysTestBelowAverageTimeInMillisBetweenRequestsChannelProcessorTe
assertFalse(alwaysTestBelowAverageTimeInMillisBetweenRequestsChannelProcessor.isContextValidConcerningHumanity(
context));
alwaysTestBelowAverageTimeInMillisBetweenRequestsChannelProcessor.setThresold(0);
*/
}
/*
public void testIsContextValidConcerningHumanity()
throws Exception {
CaptchaSecurityContext context = new CaptchaSecurityContextImpl();
@@ -112,4 +113,5 @@ public class AlwaysTestBelowAverageTimeInMillisBetweenRequestsChannelProcessorTe
.isContextValidConcerningHumanity(context));
}
}
*/
}
@@ -0,0 +1,213 @@
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.acegisecurity.concurrent;
import junit.framework.TestCase;
import java.util.Set;
import java.util.Collections;
import java.util.HashSet;
import java.util.Random;
/**
* Tests concurrency access to SessionRegistryImpl.
*
* @author Luke Taylor
* @version $Id$
*/
public class SessionRegistryImplMultithreadedTests extends TestCase {
private static final Random rnd = new Random();
private static boolean errorOccurred;
protected void setUp() throws Exception {
errorOccurred = false;
}
/**
* Reproduces the NPE mentioned in SEC-484 where a sessionId is removed from
* the set of sessions before it is removed from the list of sessions for a principal.
* getAllSessions(principal, false) then finds the sessionId in the principal's session list
* but reads null for the SessionInformation with the same Id.
* Note that this is not guaranteed to produce the error but is a good testing point. Increasing the number
* of sessions makes a failure more likely, but slows the test considerably.
* Inserting temporary sleep statements in SessionRegistryClassImpl will also help.
*/
public void testConcurrencyOfReadAndRemoveIsSafe() {
Object principal = "Joe Principal";
SessionRegistryImpl sessionregistry = new SessionRegistryImpl();
Set sessions = Collections.synchronizedSet(new HashSet());
// Register some sessions
for (int i = 0; i < 50; i++) {
String sessionId = Integer.toString(i);
sessions.add(sessionId);
sessionregistry.registerNewSession(sessionId, principal);
}
// Pile of readers to hammer the getAllSessions method.
for (int i=0; i < 10; i++) {
Thread reader = new Thread(new SessionRegistryReader(principal, sessionregistry));
reader.start();
}
Thread remover = new Thread(new SessionRemover("remover", sessionregistry, sessions));
remover.start();
while(remover.isAlive()) {
pause(250);
}
assertFalse("Thread errors detected; review log output for details", errorOccurred);
}
public void testConcurrentRemovalIsSafe() {
Object principal = "Some principal object";
SessionRegistryImpl sessionregistry = new SessionRegistryImpl();
// The session list (effectivelly the containers sessions).
Set sessions = Collections.synchronizedSet(new HashSet());
Thread registerer = new Thread(new SessionRegisterer(principal, sessionregistry, 100, sessions));
registerer.start();
int nRemovers = 4;
SessionRemover[] removers = new SessionRemover[nRemovers];
Thread[] removerThreads = new Thread[nRemovers];
for (int i = 0; i < removers.length; i++) {
removers[i] = new SessionRemover("remover" + i, sessionregistry, sessions);
removerThreads[i] = new Thread(removers[i], "remover" + i);
removerThreads[i].start();
}
while (stillRunning(removerThreads)) {
pause(500);
}
}
private boolean stillRunning(Thread[] threads) {
for (int i = 0; i < threads.length; i++) {
if (threads[i].isAlive()) {
return true;
}
}
return false;
}
private static class SessionRegisterer implements Runnable {
private SessionRegistry sessionregistry;
private int nIterations;
private Set sessionList;
private Object principal;
public SessionRegisterer(Object principal, SessionRegistry sessionregistry, int nIterations, Set sessionList) {
this.sessionregistry = sessionregistry;
this.nIterations = nIterations;
this.sessionList = sessionList;
this.principal = principal;
}
public void run() {
for (int i=0; i < nIterations && !errorOccurred; i++) {
String sessionId = Integer.toString(i);
sessionList.add(sessionId);
try {
sessionregistry.registerNewSession(sessionId,principal);
pause(20);
Thread.yield();
} catch(Exception e) {
e.printStackTrace();
errorOccurred = true;
}
}
}
}
private static class SessionRegistryReader implements Runnable {
private SessionRegistry sessionRegistry;
private Object principal;
public SessionRegistryReader(Object principal, SessionRegistry sessionregistry) {
this.sessionRegistry = sessionregistry;
this.principal = principal;
}
public void run() {
while (!errorOccurred) {
try {
sessionRegistry.getAllSessions(principal, false);
sessionRegistry.getAllPrincipals();
sessionRegistry.getAllSessions(principal, true);
Thread.yield();
} catch (Exception e) {
e.printStackTrace();
errorOccurred = true;
}
}
}
}
private static class SessionRemover implements Runnable {
private SessionRegistry sessionregistry;
private Set sessionList;
private String name;
public SessionRemover(String name, SessionRegistry sessionregistry, Set sessionList) {
this.name = name;
this.sessionregistry = sessionregistry;
this.sessionList = sessionList;
}
public void run() {
boolean finished = false;
while (!finished && !errorOccurred) {
if (sessionList.isEmpty()) {
finished = true;
// List of sessions appears to be empty but give it a chance to fill up again
System.out.println(name + ": Session list empty. Waiting.");
pause(500);
}
Object[] sessions = sessionList.toArray();
if (sessions.length > 0) {
finished = false;
String sessionId = (String) sessions[0];
// System.out.println(name + ": removing " + sessionId);
try {
sessionregistry.removeSessionInformation(sessionId);
pause(rnd.nextInt(100));
sessionList.remove(sessionId);
Thread.yield();
} catch (Exception e) {
e.printStackTrace();
errorOccurred = true;
}
}
}
}
}
private static void pause(int length) {
try {
Thread.sleep(length);
} catch (InterruptedException ignore) {}
}
}
@@ -27,7 +27,7 @@ import java.util.Date;
/**
* Tests {@link SessionRegistryImpl}.
*
* @author Ben Alex
* @author Ben Alex
* @version $Id$
*/
public class SessionRegistryImplTests extends TestCase {
@@ -23,6 +23,7 @@ import org.acegisecurity.GrantedAuthorityImpl;
import org.acegisecurity.MockFilterConfig;
import org.acegisecurity.adapters.PrincipalAcegiUserToken;
import org.jmock.Mock;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
@@ -36,271 +37,350 @@ import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
/**
* Tests {@link HttpSessionContextIntegrationFilter}.
*
* @author Ben Alex
* @version $Id$
* @version $Id: HttpSessionContextIntegrationFilterTests.java 1858 2007-05-24
* 02:04:47Z benalex $
*/
public class HttpSessionContextIntegrationFilterTests extends TestCase {
//~ Constructors ===================================================================================================
//~ Constructors ===================================================================================================
public HttpSessionContextIntegrationFilterTests() {
super();
}
public HttpSessionContextIntegrationFilterTests() {
}
public HttpSessionContextIntegrationFilterTests(String arg0) {
super(arg0);
}
public HttpSessionContextIntegrationFilterTests(String arg0) {
super(arg0);
}
//~ Methods ========================================================================================================
//~ Methods ========================================================================================================
private void executeFilterInContainerSimulator(FilterConfig filterConfig, Filter filter, ServletRequest request,
ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
filter.init(filterConfig);
filter.doFilter(request, response, filterChain);
filter.destroy();
}
private static void executeFilterInContainerSimulator(
FilterConfig filterConfig, Filter filter, ServletRequest request,
ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
filter.init(filterConfig);
filter.doFilter(request, response, filterChain);
filter.destroy();
}
public static void main(String[] args) {
junit.textui.TestRunner.run(HttpSessionContextIntegrationFilterTests.class);
}
public void testDetectsIncompatibleSessionProperties() throws Exception {
HttpSessionContextIntegrationFilter filter = new HttpSessionContextIntegrationFilter();
public void testDetectsIncompatibleSessionProperties()
throws Exception {
HttpSessionContextIntegrationFilter filter = new HttpSessionContextIntegrationFilter();
try {
filter.setAllowSessionCreation(false);
filter.setForceEagerSessionCreation(true);
filter.afterPropertiesSet();
fail("Shown have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
assertTrue(true);
}
try {
filter.setAllowSessionCreation(false);
filter.setForceEagerSessionCreation(true);
filter.afterPropertiesSet();
fail("Shown have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
assertTrue(true);
}
filter.setAllowSessionCreation(true);
filter.afterPropertiesSet();
assertTrue(true);
}
filter.setAllowSessionCreation(true);
filter.afterPropertiesSet();
assertTrue(true);
}
public void testDetectsMissingOrInvalidContext() throws Exception {
HttpSessionContextIntegrationFilter filter = new HttpSessionContextIntegrationFilter();
public void testDetectsMissingOrInvalidContext() throws Exception {
HttpSessionContextIntegrationFilter filter = new HttpSessionContextIntegrationFilter();
try {
filter.setContext(null);
filter.afterPropertiesSet();
fail("Shown have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
assertTrue(true);
}
try {
filter.setContext(null);
filter.afterPropertiesSet();
fail("Shown have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
assertTrue(true);
}
try {
filter.setContext(Integer.class);
assertEquals(Integer.class, filter.getContext());
filter.afterPropertiesSet();
fail("Shown have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
assertTrue(true);
}
}
try {
filter.setContext(Integer.class);
assertEquals(Integer.class, filter.getContext());
filter.afterPropertiesSet();
fail("Shown have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
assertTrue(true);
}
}
public void testExceptionWithinFilterChainStillClearsSecurityContextHolder() throws Exception {
// Build an Authentication object we simulate came from HttpSession
PrincipalAcegiUserToken sessionPrincipal = new PrincipalAcegiUserToken(
"key",
"someone",
"password",
new GrantedAuthority[] { new GrantedAuthorityImpl("SOME_ROLE") },
null);
public void testExceptionWithinFilterChainStillClearsSecurityContextHolder()
throws Exception {
// Build an Authentication object we simulate came from HttpSession
PrincipalAcegiUserToken sessionPrincipal = new PrincipalAcegiUserToken("key", "someone", "password",
new GrantedAuthority[] {new GrantedAuthorityImpl("SOME_ROLE")}, null);
// Build a Context to store in HttpSession (simulating prior request)
SecurityContext sc = new SecurityContextImpl();
sc.setAuthentication(sessionPrincipal);
// Build a Context to store in HttpSession (simulating prior request)
SecurityContext sc = new SecurityContextImpl();
sc.setAuthentication(sessionPrincipal);
// Build a mock request
MockHttpServletRequest request = new MockHttpServletRequest();
request.getSession().setAttribute(
HttpSessionContextIntegrationFilter.ACEGI_SECURITY_CONTEXT_KEY,
sc);
// Build a mock request
MockHttpServletRequest request = new MockHttpServletRequest();
request.getSession().setAttribute(HttpSessionContextIntegrationFilter.ACEGI_SECURITY_CONTEXT_KEY, sc);
MockHttpServletResponse response = new MockHttpServletResponse();
FilterChain chain = new MockFilterChain(sessionPrincipal, null,
new IOException());
MockHttpServletResponse response = new MockHttpServletResponse();
FilterChain chain = new MockFilterChain(sessionPrincipal, null, new IOException());
// Prepare filter
HttpSessionContextIntegrationFilter filter = new HttpSessionContextIntegrationFilter();
filter.setContext(SecurityContextImpl.class);
filter.afterPropertiesSet();
// Prepare filter
HttpSessionContextIntegrationFilter filter = new HttpSessionContextIntegrationFilter();
filter.setContext(SecurityContextImpl.class);
filter.afterPropertiesSet();
// Execute filter
try {
executeFilterInContainerSimulator(new MockFilterConfig(), filter,
request, response, chain);
fail("We should have received the IOException thrown inside the filter chain here");
} catch (IOException ioe) {
assertTrue(true);
}
// Execute filter
try {
executeFilterInContainerSimulator(new MockFilterConfig(), filter, request, response, chain);
fail("We should have received the IOException thrown inside the filter chain here");
} catch (IOException ioe) {
assertTrue(true);
}
// Check the SecurityContextHolder is null, even though an exception was
// thrown during chain
assertEquals(new SecurityContextImpl(), SecurityContextHolder.getContext());
assertNull("Should have cleared FILTER_APPLIED",
request.getAttribute(HttpSessionContextIntegrationFilter.FILTER_APPLIED));
}
// Check the SecurityContextHolder is null, even though an exception was thrown during chain
assertEquals(new SecurityContextImpl(), SecurityContextHolder.getContext());
assertNull("Should have cleared FILTER_APPLIED", request.getAttribute(HttpSessionContextIntegrationFilter.FILTER_APPLIED));
}
public void testExistingContextContentsCopiedIntoContextHolderFromSessionAndChangesToContextCopiedBackToSession()
throws Exception {
// Build an Authentication object we simulate came from HttpSession
PrincipalAcegiUserToken sessionPrincipal = new PrincipalAcegiUserToken(
"key",
"someone",
"password",
new GrantedAuthority[] { new GrantedAuthorityImpl("SOME_ROLE") },
null);
public void testExistingContextContentsCopiedIntoContextHolderFromSessionAndChangesToContextCopiedBackToSession()
throws Exception {
// Build an Authentication object we simulate came from HttpSession
PrincipalAcegiUserToken sessionPrincipal = new PrincipalAcegiUserToken("key", "someone", "password",
new GrantedAuthority[] {new GrantedAuthorityImpl("SOME_ROLE")}, null);
// Build an Authentication object we simulate our Authentication changed
// it to
PrincipalAcegiUserToken updatedPrincipal = new PrincipalAcegiUserToken(
"key", "someone", "password",
new GrantedAuthority[] { new GrantedAuthorityImpl(
"SOME_DIFFERENT_ROLE") }, null);
// Build an Authentication object we simulate our Authentication changed it to
PrincipalAcegiUserToken updatedPrincipal = new PrincipalAcegiUserToken("key", "someone", "password",
new GrantedAuthority[] {new GrantedAuthorityImpl("SOME_DIFFERENT_ROLE")}, null);
// Build a Context to store in HttpSession (simulating prior request)
SecurityContext sc = new SecurityContextImpl();
sc.setAuthentication(sessionPrincipal);
// Build a Context to store in HttpSession (simulating prior request)
SecurityContext sc = new SecurityContextImpl();
sc.setAuthentication(sessionPrincipal);
// Build a mock request
MockHttpServletRequest request = new MockHttpServletRequest();
request.getSession().setAttribute(
HttpSessionContextIntegrationFilter.ACEGI_SECURITY_CONTEXT_KEY,
sc);
// Build a mock request
MockHttpServletRequest request = new MockHttpServletRequest();
request.getSession().setAttribute(HttpSessionContextIntegrationFilter.ACEGI_SECURITY_CONTEXT_KEY, sc);
MockHttpServletResponse response = new MockHttpServletResponse();
FilterChain chain = new MockFilterChain(sessionPrincipal,
updatedPrincipal, null);
MockHttpServletResponse response = new MockHttpServletResponse();
FilterChain chain = new MockFilterChain(sessionPrincipal, updatedPrincipal, null);
// Prepare filter
HttpSessionContextIntegrationFilter filter = new HttpSessionContextIntegrationFilter();
filter.setContext(SecurityContextImpl.class);
filter.afterPropertiesSet();
// Prepare filter
HttpSessionContextIntegrationFilter filter = new HttpSessionContextIntegrationFilter();
filter.setContext(SecurityContextImpl.class);
filter.afterPropertiesSet();
// Execute filter
executeFilterInContainerSimulator(new MockFilterConfig(), filter,
request, response, chain);
// Execute filter
executeFilterInContainerSimulator(new MockFilterConfig(), filter, request, response, chain);
// Obtain new/update Authentication from HttpSession
SecurityContext context = (SecurityContext) request.getSession().getAttribute(
HttpSessionContextIntegrationFilter.ACEGI_SECURITY_CONTEXT_KEY);
assertEquals(updatedPrincipal, ((SecurityContext) context).getAuthentication());
}
// Obtain new/update Authentication from HttpSession
SecurityContext context = (SecurityContext) request.getSession()
.getAttribute(HttpSessionContextIntegrationFilter.ACEGI_SECURITY_CONTEXT_KEY);
assertEquals(updatedPrincipal, ((SecurityContext) context).getAuthentication());
}
public void testHttpSessionCreatedWhenContextHolderChanges() throws Exception {
// Build an Authentication object we simulate our Authentication changed it to
PrincipalAcegiUserToken updatedPrincipal = new PrincipalAcegiUserToken(
"key", "someone", "password",
new GrantedAuthority[] { new GrantedAuthorityImpl(
"SOME_DIFFERENT_ROLE") }, null);
public void testHttpSessionCreatedWhenContextHolderChanges()
throws Exception {
// Build an Authentication object we simulate our Authentication changed it to
PrincipalAcegiUserToken updatedPrincipal = new PrincipalAcegiUserToken("key", "someone", "password",
new GrantedAuthority[] {new GrantedAuthorityImpl("SOME_DIFFERENT_ROLE")}, null);
// Build a mock request
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
FilterChain chain = new MockFilterChain(null, updatedPrincipal, null);
// Build a mock request
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
FilterChain chain = new MockFilterChain(null, updatedPrincipal, null);
// Prepare filter
HttpSessionContextIntegrationFilter filter = new HttpSessionContextIntegrationFilter();
filter.setContext(SecurityContextImpl.class);
// don't call afterPropertiesSet to test case when Spring filter.afterPropertiesSet(); isn't called
// Prepare filter
HttpSessionContextIntegrationFilter filter = new HttpSessionContextIntegrationFilter();
filter.setContext(SecurityContextImpl.class);
// don't call afterPropertiesSet to test case when not instantiated by Spring
//filter.afterPropertiesSet();
// Execute filter
executeFilterInContainerSimulator(new MockFilterConfig(), filter, request, response, chain);
// Execute filter
executeFilterInContainerSimulator(new MockFilterConfig(), filter, request, response, chain);
// Obtain new/updated Authentication from HttpSession
SecurityContext context = (SecurityContext) request.getSession(false).getAttribute(
HttpSessionContextIntegrationFilter.ACEGI_SECURITY_CONTEXT_KEY);
assertEquals(updatedPrincipal, ((SecurityContext) context).getAuthentication());
}
// Obtain new/updated Authentication from HttpSession
SecurityContext context = (SecurityContext) request.getSession(false)
.getAttribute(HttpSessionContextIntegrationFilter.ACEGI_SECURITY_CONTEXT_KEY);
assertEquals(updatedPrincipal, ((SecurityContext) context).getAuthentication());
}
public void testHttpSessionEagerlyCreatedWhenDirected() throws Exception {
// Build a mock request
MockHttpServletRequest request = new MockHttpServletRequest(null, null);
MockHttpServletResponse response = new MockHttpServletResponse();
FilterChain chain = new MockFilterChain(null, null, null);
public void testHttpSessionEagerlyCreatedWhenDirected()
throws Exception {
// Build a mock request
MockHttpServletRequest request = new MockHttpServletRequest(null, null);
MockHttpServletResponse response = new MockHttpServletResponse();
FilterChain chain = new MockFilterChain(null, null, null);
// Prepare filter
HttpSessionContextIntegrationFilter filter = new HttpSessionContextIntegrationFilter();
filter.setContext(SecurityContextImpl.class);
filter.setForceEagerSessionCreation(true); // non-default
filter.afterPropertiesSet();
// Prepare filter
HttpSessionContextIntegrationFilter filter = new HttpSessionContextIntegrationFilter();
filter.setContext(SecurityContextImpl.class);
filter.setForceEagerSessionCreation(true); // non-default
filter.afterPropertiesSet();
// Execute filter
executeFilterInContainerSimulator(new MockFilterConfig(), filter,
request, response, chain);
// Execute filter
executeFilterInContainerSimulator(new MockFilterConfig(), filter, request, response, chain);
// Check the session is not null
assertNotNull(request.getSession(false));
}
// Check the session is not null
assertNotNull(request.getSession(false));
}
public void testHttpSessionNotCreatedUnlessContextHolderChanges() throws Exception {
// Build a mock request
MockHttpServletRequest request = new MockHttpServletRequest(null, null);
MockHttpServletResponse response = new MockHttpServletResponse();
FilterChain chain = new MockFilterChain(null, null, null);
public void testHttpSessionNotCreatedUnlessContextHolderChanges()
throws Exception {
// Build a mock request
MockHttpServletRequest request = new MockHttpServletRequest(null, null);
MockHttpServletResponse response = new MockHttpServletResponse();
FilterChain chain = new MockFilterChain(null, null, null);
// Prepare filter
HttpSessionContextIntegrationFilter filter = new HttpSessionContextIntegrationFilter();
filter.setContext(SecurityContextImpl.class);
filter.afterPropertiesSet();
// Prepare filter
HttpSessionContextIntegrationFilter filter = new HttpSessionContextIntegrationFilter();
filter.setContext(SecurityContextImpl.class);
filter.afterPropertiesSet();
// Execute filter
executeFilterInContainerSimulator(new MockFilterConfig(), filter,
request, response, chain);
// Execute filter
executeFilterInContainerSimulator(new MockFilterConfig(), filter, request, response, chain);
// Check the session is null
assertNull(request.getSession(false));
}
// Check the session is null
assertNull(request.getSession(false));
}
public void testHttpSessionWithNonContextInWellKnownLocationIsOverwritten() throws Exception {
// Build an Authentication object we simulate our Authentication changed
// it to
PrincipalAcegiUserToken updatedPrincipal = new PrincipalAcegiUserToken(
"key", "someone", "password",
new GrantedAuthority[] { new GrantedAuthorityImpl(
"SOME_DIFFERENT_ROLE") }, null);
public void testHttpSessionWithNonContextInWellKnownLocationIsOverwritten()
throws Exception {
// Build an Authentication object we simulate our Authentication changed it to
PrincipalAcegiUserToken updatedPrincipal = new PrincipalAcegiUserToken("key", "someone", "password",
new GrantedAuthority[] {new GrantedAuthorityImpl("SOME_DIFFERENT_ROLE")}, null);
// Build a mock request
MockHttpServletRequest request = new MockHttpServletRequest();
request.getSession().setAttribute(
HttpSessionContextIntegrationFilter.ACEGI_SECURITY_CONTEXT_KEY,
"NOT_A_CONTEXT_OBJECT");
// Build a mock request
MockHttpServletRequest request = new MockHttpServletRequest();
request.getSession()
.setAttribute(HttpSessionContextIntegrationFilter.ACEGI_SECURITY_CONTEXT_KEY, "NOT_A_CONTEXT_OBJECT");
MockHttpServletResponse response = new MockHttpServletResponse();
FilterChain chain = new MockFilterChain(null, updatedPrincipal, null);
MockHttpServletResponse response = new MockHttpServletResponse();
FilterChain chain = new MockFilterChain(null, updatedPrincipal, null);
// Prepare filter
HttpSessionContextIntegrationFilter filter = new HttpSessionContextIntegrationFilter();
filter.setContext(SecurityContextImpl.class);
filter.afterPropertiesSet();
// Prepare filter
HttpSessionContextIntegrationFilter filter = new HttpSessionContextIntegrationFilter();
filter.setContext(SecurityContextImpl.class);
filter.afterPropertiesSet();
// Execute filter
executeFilterInContainerSimulator(new MockFilterConfig(), filter, request, response, chain);
// Execute filter
executeFilterInContainerSimulator(new MockFilterConfig(), filter, request, response, chain);
// Obtain new/update Authentication from HttpSession
SecurityContext context = (SecurityContext) request.getSession().getAttribute(
HttpSessionContextIntegrationFilter.ACEGI_SECURITY_CONTEXT_KEY);
assertEquals(updatedPrincipal, ((SecurityContext) context).getAuthentication());
}
// Obtain new/update Authentication from HttpSession
SecurityContext context = (SecurityContext) request.getSession()
.getAttribute(HttpSessionContextIntegrationFilter.ACEGI_SECURITY_CONTEXT_KEY);
assertEquals(updatedPrincipal, ((SecurityContext) context).getAuthentication());
}
public void testConcurrentThreadsLazilyChangeFilterAppliedValueToTrue() throws Exception {
PrincipalAcegiUserToken sessionPrincipal = new PrincipalAcegiUserToken(
"key",
"someone",
"password",
new GrantedAuthority[] { new GrantedAuthorityImpl("SOME_ROLE") },
null);
//~ Inner Classes ==================================================================================================
// Build a Context to store in HttpSession (simulating prior request)
SecurityContext sc = new SecurityContextImpl();
sc.setAuthentication(sessionPrincipal);
private class MockFilterChain extends TestCase implements FilterChain {
private Authentication changeContextHolder;
private Authentication expectedOnContextHolder;
private IOException toThrowDuringChain;
MockHttpServletRequest request = new MockHttpServletRequest();
request.getSession().setAttribute(
HttpSessionContextIntegrationFilter.ACEGI_SECURITY_CONTEXT_KEY,
sc);
MockHttpServletResponse response = new MockHttpServletResponse();
public MockFilterChain(Authentication expectedOnContextHolder, Authentication changeContextHolder,
IOException toThrowDuringChain) {
this.expectedOnContextHolder = expectedOnContextHolder;
this.changeContextHolder = changeContextHolder;
this.toThrowDuringChain = toThrowDuringChain;
}
// Prepare filter
HttpSessionContextIntegrationFilter filter = new HttpSessionContextIntegrationFilter();
filter.setContext(SecurityContextImpl.class);
filter.afterPropertiesSet();
private MockFilterChain() {}
for (int i = 0; i < 3; i++) {
ThreadRunner runner = new ThreadRunner(request, response, filter,
new MockFilterChain(sessionPrincipal, null, null));
runner.start();
}
public void doFilter(ServletRequest arg0, ServletResponse arg1)
throws IOException, ServletException {
if (expectedOnContextHolder != null) {
assertEquals(expectedOnContextHolder, SecurityContextHolder.getContext().getAuthentication());
}
}
if (changeContextHolder != null) {
SecurityContext sc = SecurityContextHolder.getContext();
sc.setAuthentication(changeContextHolder);
SecurityContextHolder.setContext(sc);
}
// ~ Inner Classes
// ==================================================================================================
if (toThrowDuringChain != null) {
throw toThrowDuringChain;
}
}
}
private class MockFilterChain extends TestCase implements FilterChain {
private Authentication changeContextHolder;
private Authentication expectedOnContextHolder;
private IOException toThrowDuringChain;
public MockFilterChain(Authentication expectedOnContextHolder,
Authentication changeContextHolder,
IOException toThrowDuringChain) {
this.expectedOnContextHolder = expectedOnContextHolder;
this.changeContextHolder = changeContextHolder;
this.toThrowDuringChain = toThrowDuringChain;
}
public void doFilter(ServletRequest arg0, ServletResponse arg1) throws IOException, ServletException {
if (expectedOnContextHolder != null) {
assertEquals(expectedOnContextHolder, SecurityContextHolder.getContext().getAuthentication());
}
if (changeContextHolder != null) {
SecurityContext sc = SecurityContextHolder.getContext();
sc.setAuthentication(changeContextHolder);
SecurityContextHolder.setContext(sc);
}
if (toThrowDuringChain != null) {
throw toThrowDuringChain;
}
}
}
private static class ThreadRunner extends Thread {
private MockHttpServletRequest request;
private MockHttpServletResponse response;
private HttpSessionContextIntegrationFilter filter;
private MockFilterChain chain;
public ThreadRunner(MockHttpServletRequest request,
MockHttpServletResponse response,
HttpSessionContextIntegrationFilter filter,
MockFilterChain chain) {
this.request = request;
this.response = response;
this.filter = filter;
this.chain = chain;
}
public void run() {
try {
// Execute filter
executeFilterInContainerSimulator(new MockFilterConfig(), filter, request, response, chain);
// Check the session is not null
assertNotNull(request.getSession(false));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
@@ -36,7 +36,6 @@ public class PathBasedFilterDefinitionMapTests extends TestCase {
//~ Constructors ===================================================================================================
public PathBasedFilterDefinitionMapTests() {
super();
}
public PathBasedFilterDefinitionMapTests(String arg0) {
@@ -59,7 +58,6 @@ public class PathBasedFilterDefinitionMapTests extends TestCase {
public void testLookupNotRequiringExactMatchSuccessIfNotMatching() {
PathBasedFilterInvocationDefinitionMap map = new PathBasedFilterInvocationDefinitionMap();
map.setConvertUrlToLowercaseBeforeComparison(true);
assertTrue(map.isConvertUrlToLowercaseBeforeComparison());
ConfigAttributeDefinition def = new ConfigAttributeDefinition();
def.addConfigAttribute(new SecurityConfig("ROLE_ONE"));
@@ -71,10 +69,26 @@ public class PathBasedFilterDefinitionMapTests extends TestCase {
assertEquals(def, response);
}
/**
* SEC-501
*/
public void testLookupNotRequiringExactMatchSucceedsIfSecureUrlPathContainsUpperCase() {
PathBasedFilterInvocationDefinitionMap map = new PathBasedFilterInvocationDefinitionMap();
map.setConvertUrlToLowercaseBeforeComparison(true);
ConfigAttributeDefinition def = new ConfigAttributeDefinition();
def.addConfigAttribute(new SecurityConfig("ROLE_ONE"));
map.addSecureUrl("/SeCuRE/super/**", def);
FilterInvocation fi = createFilterinvocation("/secure/super/somefile.html");
ConfigAttributeDefinition response = map.lookupAttributes(fi.getRequestUrl());
assertEquals(def, response);
}
public void testLookupRequiringExactMatchFailsIfNotMatching() {
PathBasedFilterInvocationDefinitionMap map = new PathBasedFilterInvocationDefinitionMap();
assertFalse(map.isConvertUrlToLowercaseBeforeComparison());
ConfigAttributeDefinition def = new ConfigAttributeDefinition();
def.addConfigAttribute(new SecurityConfig("ROLE_ONE"));
map.addSecureUrl("/secure/super/**", def);
@@ -87,13 +101,11 @@ public class PathBasedFilterDefinitionMapTests extends TestCase {
public void testLookupRequiringExactMatchIsSuccessful() {
PathBasedFilterInvocationDefinitionMap map = new PathBasedFilterInvocationDefinitionMap();
assertFalse(map.isConvertUrlToLowercaseBeforeComparison());
ConfigAttributeDefinition def = new ConfigAttributeDefinition();
def.addConfigAttribute(new SecurityConfig("ROLE_ONE"));
map.addSecureUrl("/secure/super/**", def);
map.addSecureUrl("/SeCurE/super/**", def);
FilterInvocation fi = createFilterinvocation("/secure/super/somefile.html");
FilterInvocation fi = createFilterinvocation("/SeCurE/super/somefile.html");
ConfigAttributeDefinition response = map.lookupAttributes(fi.getRequestUrl());
assertEquals(def, response);
@@ -101,8 +113,6 @@ public class PathBasedFilterDefinitionMapTests extends TestCase {
public void testLookupRequiringExactMatchWithAdditionalSlashesIsSuccessful() {
PathBasedFilterInvocationDefinitionMap map = new PathBasedFilterInvocationDefinitionMap();
assertFalse(map.isConvertUrlToLowercaseBeforeComparison());
ConfigAttributeDefinition def = new ConfigAttributeDefinition();
def.addConfigAttribute(new SecurityConfig("ROLE_ONE"));
map.addSecureUrl("/someAdminPage.html**", def);
@@ -113,11 +123,11 @@ public class PathBasedFilterDefinitionMapTests extends TestCase {
assertEquals(def, response); // see SEC-161 (it should truncate after ? sign)
}
/** Check fixes for SEC-321 */
/**
* Check fixes for SEC-321
*/
public void testExtraQuestionMarkStillMatches() {
PathBasedFilterInvocationDefinitionMap map = new PathBasedFilterInvocationDefinitionMap();
assertFalse(map.isConvertUrlToLowercaseBeforeComparison());
ConfigAttributeDefinition def = new ConfigAttributeDefinition();
def.addConfigAttribute(new SecurityConfig("ROLE_ONE"));
map.addSecureUrl("/someAdminPage.html*", def);
@@ -23,8 +23,6 @@ import java.util.Hashtable;
/**
*
*
* @author Luke Taylor
* @version $Id$
*/
@@ -36,7 +34,7 @@ public abstract class AbstractLdapServerTestCase extends TestCase {
protected static final String MANAGER_PASSWORD = "acegisecurity";
// External server config
// private static final String PROVIDER_URL = "ldap://monkeymachine:389/"+ROOT_DN;
// private static final String PROVIDER_URL = "ldap://gorille:389/"+ROOT_DN;
// private static final String CONTEXT_FACTORY = "com.sun.jndi.ldap.LdapCtxFactory";
// private static final Hashtable EXTRA_ENV = new Hashtable();
@@ -52,7 +50,8 @@ public abstract class AbstractLdapServerTestCase extends TestCase {
//~ Constructors ===================================================================================================
protected AbstractLdapServerTestCase() {}
protected AbstractLdapServerTestCase() {
}
protected AbstractLdapServerTestCase(String string) {
super(string);
@@ -64,7 +63,8 @@ public abstract class AbstractLdapServerTestCase extends TestCase {
return idf;
}
protected void onSetUp() {}
protected void onSetUp() {
}
public final void setUp() {
idf = new DefaultInitialDirContextFactory(PROVIDER_URL);
@@ -22,8 +22,6 @@ import javax.naming.directory.DirContext;
/**
*
*
* @author Luke Taylor
* @version $Id$
*/
@@ -67,22 +65,24 @@ public class LdapTemplateTests extends AbstractLdapServerTestCase {
public void testNamingExceptionIsTranslatedCorrectly() {
try {
template.execute(new LdapCallback() {
public Object doInDirContext(DirContext dirContext)
public Object doInDirContext(DirContext dirContext)
throws NamingException {
throw new NamingException();
}
});
throw new NamingException();
}
});
fail("Expected LdapDataAccessException on NamingException");
} catch (LdapDataAccessException expected) {}
} catch (LdapDataAccessException expected) {
}
}
public void testSearchForSingleAttributeValues() {
String param = "uid=ben,ou=people,dc=acegisecurity,dc=org";
Set values = template.searchForSingleAttributeValues("ou=groups", "(member={0})", new String[] {param}, "ou");
Set values = template.searchForSingleAttributeValues("ou=groups", "(member={0})", new String[]{param}, "ou");
assertEquals("Expected 2 results from search", 2, values.size());
assertEquals("Expected 3 results from search", 3, values.size());
assertTrue(values.contains("developer"));
assertTrue(values.contains("manager"));
assertTrue(values.contains("submanager"));
}
}
@@ -64,7 +64,7 @@ public class LdapTestServer {
//~ Methods ========================================================================================================
public void createGroup(String cn, String ou, String[] memberDns) {
public void createGroup(String cn, String groupContext, String ou, String[] memberDns) {
Attributes group = new BasicAttributes("cn", cn);
Attribute members = new BasicAttribute("member");
Attribute orgUnit = new BasicAttribute("ou", ou);
@@ -82,7 +82,8 @@ public class LdapTestServer {
group.put(orgUnit);
try {
serverContext.createSubcontext("cn=" + cn + ",ou=groups", group);
DirContext ctx = serverContext.createSubcontext("cn=" + cn + "," + groupContext, group);
System.out.println("Created group " + ctx.getNameInNamespace());
} catch (NameAlreadyBoundException ignore) {
// System.out.println(" group " + cn + " already exists.");
} catch (NamingException ne) {
@@ -122,7 +123,7 @@ public class LdapTestServer {
ou.put(objectClass);
try {
serverContext.createSubcontext("ou=" + name, ou);
serverContext.createSubcontext(name, ou);
} catch (NameAlreadyBoundException ignore) {
// System.out.println(" ou " + name + " already exists.");
} catch (NamingException ne) {
@@ -188,16 +189,19 @@ public class LdapTestServer {
}
private void initTestData() {
createOu("people");
createOu("groups");
createOu("ou=people");
createOu("ou=groups");
createOu("ou=subgroups,ou=groups");
createUser("bob", "Bob Hamilton", "bobspassword");
createUser("ben", "Ben Alex", "{SHA}nFCebWjxfaLbHHG1Qk5UU4trbvQ=");
String[] developers = new String[] {
String[] developers = new String[]{
"uid=ben,ou=people,dc=acegisecurity,dc=org", "uid=bob,ou=people,dc=acegisecurity,dc=org"
};
createGroup("developers", "developer", developers);
createGroup("managers", "manager", new String[] {developers[0]});
};
createGroup("developers", "ou=groups", "developer", developers);
createGroup("managers", "ou=groups", "manager", new String[]{developers[0]});
createGroup("submanagers", "ou=subgroups,ou=groups", "submanager", new String[]{developers[0]});
}
public static void main(String[] args) {
@@ -243,11 +247,13 @@ public class LdapTestServer {
}
}
/** Recursively deletes a directory */
/**
* Recursively deletes a directory
*/
private boolean deleteDir(File dir) {
if (dir.isDirectory()) {
String[] children = dir.list();
for (int i=0; i<children.length; i++) {
for (int i = 0; i < children.length; i++) {
boolean success = deleteDir(new File(dir, children[i]));
if (!success) {
return false;
@@ -42,7 +42,6 @@ public class FilterBasedLdapUserSearchTests extends AbstractLdapServerTestCase {
}
public FilterBasedLdapUserSearchTests() {
super();
}
//~ Methods ========================================================================================================
@@ -246,6 +246,8 @@ public class CasAuthenticationProviderTests extends TestCase {
public void testDetectsMissingStatelessTicketCache()
throws Exception {
CasAuthenticationProvider cap = new CasAuthenticationProvider();
// set this explicitly to null to test failure
cap.setStatelessTicketCache(null);
cap.setCasAuthoritiesPopulator(new MockAuthoritiesPopulator());
cap.setCasProxyDecider(new MockProxyDecider());
cap.setKey("qwerty");
@@ -17,7 +17,7 @@ package org.acegisecurity.providers.cas.cache;
import junit.framework.TestCase;
import net.sf.ehcache.Cache;
import net.sf.ehcache.Ehcache;
import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.GrantedAuthorityImpl;
@@ -43,7 +43,6 @@ public class EhCacheBasedTicketCacheTests extends TestCase {
//~ Constructors ===================================================================================================
public EhCacheBasedTicketCacheTests() {
super();
}
public EhCacheBasedTicketCacheTests(String arg0) {
@@ -52,10 +51,10 @@ public class EhCacheBasedTicketCacheTests extends TestCase {
//~ Methods ========================================================================================================
private Cache getCache() {
private Ehcache getCache() {
ApplicationContext ctx = MockApplicationContext.getContext();
return (Cache) ctx.getBean("eHCacheBackend");
return (Ehcache) ctx.getBean("eHCacheBackend");
}
private CasAuthenticationToken getToken() {
@@ -70,10 +69,6 @@ public class EhCacheBasedTicketCacheTests extends TestCase {
proxyList, "PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt");
}
public static void main(String[] args) {
junit.textui.TestRunner.run(EhCacheBasedTicketCacheTests.class);
}
public final void setUp() throws Exception {
super.setUp();
}
@@ -106,7 +101,7 @@ public class EhCacheBasedTicketCacheTests extends TestCase {
assertTrue(true);
}
Cache myCache = getCache();
Ehcache myCache = getCache();
cache.setCache(myCache);
assertEquals(myCache, cache.getCache());
}
@@ -0,0 +1,61 @@
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.acegisecurity.providers.cas.cache;
import java.util.ArrayList;
import java.util.List;
import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.GrantedAuthorityImpl;
import org.acegisecurity.providers.cas.CasAuthenticationToken;
import org.acegisecurity.providers.cas.StatelessTicketCache;
import org.acegisecurity.userdetails.User;
import junit.framework.TestCase;
/**
* Test cases for the @link {@link NullStatelessTicketCache}
*
* @author Scott Battaglia
* @version $Id$
*
*/
public class NullStatelessTicketCacheTests extends TestCase {
private StatelessTicketCache cache = new NullStatelessTicketCache();
public void testGetter() {
assertNull(cache.getByTicketId(null));
assertNull(cache.getByTicketId("test"));
}
public void testInsertAndGet() {
final CasAuthenticationToken token = getToken();
cache.putTicketInCache(token);
assertNull(cache.getByTicketId((String) token.getCredentials()));
}
private CasAuthenticationToken getToken() {
List proxyList = new ArrayList();
proxyList.add("https://localhost/newPortal/j_spring_cas_security_check");
User user = new User("marissa", "password", true, true, true, true,
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl("ROLE_TWO")});
return new CasAuthenticationToken("key", user, "ST-0-ER94xMJmn6pha35CQRoZ",
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl("ROLE_TWO")}, user,
proxyList, "PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt");
}
}

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