From e0d57de330288f2ad638fc922c45d39418960bea Mon Sep 17 00:00:00 2001 From: Ben Alex Date: Fri, 23 Apr 2004 05:01:57 +0000 Subject: [PATCH] Add DaoAuthenticationProvider caching support. --- .../cas/applicationContext-invalid.xml | 1 + .../adapters/cas/applicationContext-valid.xml | 1 + changelog.txt | 2 + .../dao/DaoAuthenticationProvider.java | 86 ++++++- .../providers/dao/DaoAuthenticationToken.java | 148 ++++++++++++ .../adapters/adaptertest-valid.xml | 1 + .../dao/DaoAuthenticationProviderTests.java | 129 ++++++++++- .../dao/DaoAuthenticationTokenTests.java | 217 ++++++++++++++++++ .../ui/basicauth/filtertest-valid.xml | 1 + .../contacts/etc/ca/applicationContext.xml | 1 + .../etc/filter/applicationContext.xml | 1 + upgrade-04-05.txt | 6 + 12 files changed, 577 insertions(+), 17 deletions(-) create mode 100644 core/src/main/java/org/acegisecurity/providers/dao/DaoAuthenticationToken.java create mode 100644 core/src/test/java/org/acegisecurity/providers/dao/DaoAuthenticationTokenTests.java diff --git a/adapters/cas/src/test/java/org/acegisecurity/adapters/cas/applicationContext-invalid.xml b/adapters/cas/src/test/java/org/acegisecurity/adapters/cas/applicationContext-invalid.xml index 872d1f5977..f48ec3281c 100644 --- a/adapters/cas/src/test/java/org/acegisecurity/adapters/cas/applicationContext-invalid.xml +++ b/adapters/cas/src/test/java/org/acegisecurity/adapters/cas/applicationContext-invalid.xml @@ -33,6 +33,7 @@ + my_password diff --git a/adapters/cas/src/test/java/org/acegisecurity/adapters/cas/applicationContext-valid.xml b/adapters/cas/src/test/java/org/acegisecurity/adapters/cas/applicationContext-valid.xml index 33132386f3..66c709eebb 100644 --- a/adapters/cas/src/test/java/org/acegisecurity/adapters/cas/applicationContext-valid.xml +++ b/adapters/cas/src/test/java/org/acegisecurity/adapters/cas/applicationContext-valid.xml @@ -33,6 +33,7 @@ + my_password diff --git a/changelog.txt b/changelog.txt index 468a722f2d..3a7448d617 100644 --- a/changelog.txt +++ b/changelog.txt @@ -3,6 +3,7 @@ Changes in version 0.5 (2004-xx-xx) * Added single sign on support via Yale Central Authentication Service (CAS) * Added full support for HTTP Basic Authentication +* Added caching for DaoAuthenticationProvider successful authentications * Added Burlap and Hessian remoting to Contacts sample application * Added pluggable password encoders including plaintext, SHA and MD5 * Added pluggable salt sources to enhance security of hashed passwords @@ -14,6 +15,7 @@ Changes in version 0.5 (2004-xx-xx) * Added Apache Ant path syntax support to SecurityEnforcementFilter * Updated JAR to Spring 1.0.1 * Refactored filters to use Spring application context lifecycle support +* Improved constructor detection of nulls in User and other key objects * Fixed FilterInvocation.getRequestUrl() to also include getPathInfo() * Fixed Contacts sample application tags * Established acegisecurity-developer mailing list diff --git a/core/src/main/java/org/acegisecurity/providers/dao/DaoAuthenticationProvider.java b/core/src/main/java/org/acegisecurity/providers/dao/DaoAuthenticationProvider.java index 36b58f1aba..c172870672 100644 --- a/core/src/main/java/org/acegisecurity/providers/dao/DaoAuthenticationProvider.java +++ b/core/src/main/java/org/acegisecurity/providers/dao/DaoAuthenticationProvider.java @@ -29,6 +29,8 @@ import org.springframework.beans.factory.InitializingBean; import org.springframework.dao.DataAccessException; +import java.util.Date; + /** * An {@link AuthenticationProvider} implementation that retrieves user details @@ -39,6 +41,21 @@ import org.springframework.dao.DataAccessException; * UsernamePasswordAuthenticationToken} requests contain the correct username, * password and the user is not disabled. *

+ * + *

+ * Upon successful validation, a DaoAuthenticationToken will be + * created and returned to the caller. This token will be signed with the key + * configured by {@link #getKey()} and expire {@link + * #getRefreshTokenInterval()} milliseconds into the future. The token will be + * assumed to remain valid whilstever it has not expired, and no requests of + * the AuthenticationProvider will need to be made. Once the + * token has expired, the relevant AuthenticationProvider will be + * called again to provide an updated enabled/disabled status, and list of + * granted authorities. It should be noted the credentials will not be + * revalidated, as the user presented correct credentials in the originial + * UsernamePasswordAuthenticationToken. This avoids complications + * if the user changes their password during the session. + *

* * @author Ben Alex * @version $Id$ @@ -50,6 +67,8 @@ public class DaoAuthenticationProvider implements AuthenticationProvider, private AuthenticationDao authenticationDao; private PasswordEncoder passwordEncoder = new PlaintextPasswordEncoder(); private SaltSource saltSource; + private String key; + private long refreshTokenInterval = 60000; // 60 seconds //~ Methods ================================================================ @@ -61,6 +80,14 @@ public class DaoAuthenticationProvider implements AuthenticationProvider, return authenticationDao; } + public void setKey(String key) { + this.key = key; + } + + public String getKey() { + return key; + } + /** * Sets the PasswordEncoder instance to be used to encode and validate * passwords. If not set, {@link PlaintextPasswordEncoder} will be used by @@ -76,6 +103,22 @@ public class DaoAuthenticationProvider implements AuthenticationProvider, return passwordEncoder; } + public void setRefreshTokenInterval(long refreshTokenInterval) { + this.refreshTokenInterval = refreshTokenInterval; + } + + /** + * Indicates the number of seconds a created + * DaoAuthenticationToken will remain valid for. Whilstever + * the token is valid, the DaoAuthenticationProvider will + * only check it presents the expected key hash code. + * + * @return Returns the refreshTokenInterval. + */ + public long getRefreshTokenInterval() { + return refreshTokenInterval; + } + /** * The source of salts to use when decoding passwords. null * is a valid value, meaning the DaoAuthenticationProvider @@ -98,10 +141,29 @@ public class DaoAuthenticationProvider implements AuthenticationProvider, throw new IllegalArgumentException( "An Authentication DAO must be set"); } + + if ((this.key == null) || "".equals(key)) { + throw new IllegalArgumentException("A key must be set"); + } } public Authentication authenticate(Authentication authentication) throws AuthenticationException { + // If an existing DaoAuthenticationToken, check we created it and it hasn't expired + if (authentication instanceof DaoAuthenticationToken) { + if (this.key.hashCode() == ((DaoAuthenticationToken) authentication) + .getKeyHash()) { + if (((DaoAuthenticationToken) authentication).getExpires() + .after(new Date())) { + return authentication; + } + } else { + throw new BadCredentialsException( + "The presented DaoAuthenticationToken does not contain the expected key"); + } + } + + // We need to authenticate or refresh the token User user = null; try { @@ -114,23 +176,29 @@ public class DaoAuthenticationProvider implements AuthenticationProvider, .getMessage(), repositoryProblem); } - Object salt = null; + if (!(authentication instanceof DaoAuthenticationToken)) { + // Must validate credentials, as this is not simply a token refresh + Object salt = null; - if (this.saltSource != null) { - salt = this.saltSource.getSalt(user); - } + if (this.saltSource != null) { + salt = this.saltSource.getSalt(user); + } - if (!passwordEncoder.isPasswordValid(user.getPassword(), - authentication.getCredentials().toString(), salt)) { - throw new BadCredentialsException("Bad credentials presented"); + if (!passwordEncoder.isPasswordValid(user.getPassword(), + authentication.getCredentials().toString(), salt)) { + throw new BadCredentialsException("Bad credentials presented"); + } } if (!user.isEnabled()) { throw new DisabledException("User is disabled"); } - return new UsernamePasswordAuthenticationToken(user.getUsername(), - authentication.getCredentials().toString(), user.getAuthorities()); + Date expiry = new Date(new Date().getTime() + + this.getRefreshTokenInterval()); + + return new DaoAuthenticationToken(this.getKey(), expiry, + user.getUsername(), user.getPassword(), user.getAuthorities()); } public boolean supports(Class authentication) { diff --git a/core/src/main/java/org/acegisecurity/providers/dao/DaoAuthenticationToken.java b/core/src/main/java/org/acegisecurity/providers/dao/DaoAuthenticationToken.java new file mode 100644 index 0000000000..51bc393f5e --- /dev/null +++ b/core/src/main/java/org/acegisecurity/providers/dao/DaoAuthenticationToken.java @@ -0,0 +1,148 @@ +/* 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. + */ + +package net.sf.acegisecurity.providers.dao; + +import net.sf.acegisecurity.GrantedAuthority; +import net.sf.acegisecurity.providers.AbstractAuthenticationToken; + +import java.io.Serializable; + +import java.util.Date; + + +/** + * Represents a successful DAO-based Authentication. + * + * @author Ben Alex + * @version $Id$ + */ +public class DaoAuthenticationToken extends AbstractAuthenticationToken + implements Serializable { + //~ Instance fields ======================================================== + + private Date expires; + private Object credentials; + private Object principal; + private GrantedAuthority[] authorities; + private int keyHash; + + //~ Constructors =========================================================== + + /** + * Constructor. + * + * @param key to identify if this object made by a given {@link + * DaoAuthenticationProvider} + * @param expires when the token is due to expire + * @param principal the username from the {@link User} object + * @param credentials the password from the {@link User} object + * @param authorities the authorities granted to the user, from the {@link + * User} object + * + * @throws IllegalArgumentException if a null was passed + */ + public DaoAuthenticationToken(String key, Date expires, Object principal, + Object credentials, GrantedAuthority[] authorities) { + if ((key == null) || ("".equals(key)) || (expires == null) + || (principal == null) || "".equals(principal) + || (credentials == null) || "".equals(credentials) + || (authorities == null)) { + throw new IllegalArgumentException( + "Cannot pass null or empty values to constructor"); + } + + for (int i = 0; i < authorities.length; i++) { + if (authorities[i] == null) { + throw new IllegalArgumentException("Granted authority element " + + i + + " is null - GrantedAuthority[] cannot contain any null elements"); + } + } + + this.keyHash = key.hashCode(); + this.expires = expires; + this.principal = principal; + this.credentials = credentials; + this.authorities = authorities; + } + + protected DaoAuthenticationToken() { + throw new IllegalArgumentException("Cannot use default constructor"); + } + + //~ Methods ================================================================ + + /** + * Ignored (always true). + * + * @param isAuthenticated ignored + */ + public void setAuthenticated(boolean isAuthenticated) { + // ignored + } + + /** + * Always returns true. + * + * @return true + */ + public boolean isAuthenticated() { + return true; + } + + public GrantedAuthority[] getAuthorities() { + return this.authorities; + } + + public Object getCredentials() { + return this.credentials; + } + + public Date getExpires() { + return this.expires; + } + + public int getKeyHash() { + return this.keyHash; + } + + public Object getPrincipal() { + return this.principal; + } + + public boolean equals(Object obj) { + if (!super.equals(obj)) { + return false; + } + + if (obj instanceof DaoAuthenticationToken) { + DaoAuthenticationToken test = (DaoAuthenticationToken) obj; + + if (this.getKeyHash() != test.getKeyHash()) { + return false; + } + + // expires never null due to constructor + if (this.getExpires() != test.getExpires()) { + return false; + } + + return true; + } + + return false; + } +} diff --git a/core/src/test/java/org/acegisecurity/adapters/adaptertest-valid.xml b/core/src/test/java/org/acegisecurity/adapters/adaptertest-valid.xml index 38a23ee9ca..7f0c51c608 100644 --- a/core/src/test/java/org/acegisecurity/adapters/adaptertest-valid.xml +++ b/core/src/test/java/org/acegisecurity/adapters/adaptertest-valid.xml @@ -36,6 +36,7 @@ + my_password diff --git a/core/src/test/java/org/acegisecurity/providers/dao/DaoAuthenticationProviderTests.java b/core/src/test/java/org/acegisecurity/providers/dao/DaoAuthenticationProviderTests.java index 9986dbf8e5..2e007dd371 100644 --- a/core/src/test/java/org/acegisecurity/providers/dao/DaoAuthenticationProviderTests.java +++ b/core/src/test/java/org/acegisecurity/providers/dao/DaoAuthenticationProviderTests.java @@ -31,6 +31,8 @@ import net.sf.acegisecurity.providers.encoding.ShaPasswordEncoder; import org.springframework.dao.DataAccessException; import org.springframework.dao.DataRetrievalFailureException; +import java.util.Date; + /** * Tests {@link DaoAuthenticationProvider}. @@ -54,6 +56,7 @@ public class DaoAuthenticationProviderTests extends TestCase { "KOala"); DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); + provider.setKey("x"); provider.setAuthenticationDao(new MockAuthenticationDaoUserMarissa()); try { @@ -69,6 +72,7 @@ public class DaoAuthenticationProviderTests extends TestCase { "opal"); DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); + provider.setKey("x"); provider.setAuthenticationDao(new MockAuthenticationDaoUserPeter()); try { @@ -84,6 +88,7 @@ public class DaoAuthenticationProviderTests extends TestCase { "koala"); DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); + provider.setKey("x"); provider.setAuthenticationDao(new MockAuthenticationDaoSimulateBackendError()); try { @@ -99,6 +104,7 @@ public class DaoAuthenticationProviderTests extends TestCase { "INVALID_PASSWORD"); DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); + provider.setKey("x"); provider.setAuthenticationDao(new MockAuthenticationDaoUserMarissa()); try { @@ -114,6 +120,7 @@ public class DaoAuthenticationProviderTests extends TestCase { "koala"); DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); + provider.setKey("x"); provider.setAuthenticationDao(new MockAuthenticationDaoUserMarissa()); try { @@ -129,6 +136,7 @@ public class DaoAuthenticationProviderTests extends TestCase { "koala"); DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); + provider.setKey("x"); provider.setAuthenticationDao(new MockAuthenticationDaoUserMarissa()); try { @@ -144,20 +152,55 @@ public class DaoAuthenticationProviderTests extends TestCase { "koala"); DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); + provider.setKey("x"); provider.setAuthenticationDao(new MockAuthenticationDaoUserMarissa()); Authentication result = provider.authenticate(token); - if (!(result instanceof UsernamePasswordAuthenticationToken)) { - fail( - "Should have returned instance of UsernamePasswordAuthenticationToken"); + if (!(result instanceof DaoAuthenticationToken)) { + fail("Should have returned instance of DaoAuthenticationToken"); } - UsernamePasswordAuthenticationToken castResult = (UsernamePasswordAuthenticationToken) result; + DaoAuthenticationToken castResult = (DaoAuthenticationToken) result; assertEquals("marissa", castResult.getPrincipal()); assertEquals("koala", castResult.getCredentials()); assertEquals("ROLE_ONE", castResult.getAuthorities()[0].getAuthority()); assertEquals("ROLE_TWO", castResult.getAuthorities()[1].getAuthority()); + assertEquals(provider.getKey().hashCode(), castResult.getKeyHash()); + } + + public void testAuthenticatesThenAcceptsCreatedTokenAutomatically() { + UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("marissa", + "koala"); + + DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); + provider.setKey("x"); + provider.setAuthenticationDao(new MockAuthenticationDaoUserMarissa()); + + Authentication result = provider.authenticate(token); + + if (!(result instanceof DaoAuthenticationToken)) { + fail("Should have returned instance of DaoAuthenticationToken"); + } + + DaoAuthenticationToken castResult = (DaoAuthenticationToken) result; + assertEquals("marissa", castResult.getPrincipal()); + assertEquals(provider.getKey().hashCode(), castResult.getKeyHash()); + assertTrue(castResult.getExpires().after(new Date())); + + // Now try to re-authenticate + // Set provider to null, so we get a NullPointerException if it tries to re-authenticate + provider.setAuthenticationDao(null); + + Authentication secondResult = provider.authenticate(result); + + if (!(secondResult instanceof DaoAuthenticationToken)) { + fail("Should have returned instance of DaoAuthenticationToken"); + } + + // Should still have the same expiry time as original + assertEquals(castResult.getExpires(), + ((DaoAuthenticationToken) secondResult).getExpires()); } public void testAuthenticatesWhenASaltIsUsed() { @@ -168,21 +211,77 @@ public class DaoAuthenticationProviderTests extends TestCase { salt.setSystemWideSalt("SYSTEM_SALT_VALUE"); DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); + provider.setKey("x"); provider.setAuthenticationDao(new MockAuthenticationDaoUserMarissaWithSalt()); provider.setSaltSource(salt); Authentication result = provider.authenticate(token); - if (!(result instanceof UsernamePasswordAuthenticationToken)) { + if (!(result instanceof DaoAuthenticationToken)) { fail( - "Should have returned instance of UsernamePasswordAuthenticationToken"); + "Should have returned instance of DaoPasswordAuthenticationToken"); } - UsernamePasswordAuthenticationToken castResult = (UsernamePasswordAuthenticationToken) result; + DaoAuthenticationToken castResult = (DaoAuthenticationToken) result; assertEquals("marissa", castResult.getPrincipal()); - assertEquals("koala", castResult.getCredentials()); + assertEquals("koala{SYSTEM_SALT_VALUE}", castResult.getCredentials()); assertEquals("ROLE_ONE", castResult.getAuthorities()[0].getAuthority()); assertEquals("ROLE_TWO", castResult.getAuthorities()[1].getAuthority()); + assertEquals(provider.getKey().hashCode(), castResult.getKeyHash()); + } + + public void testDaoAuthenticationTokensThatHaveExpiredAreRefreshed() + throws Exception { + UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("marissa", + "koala"); + + DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); + provider.setKey("x"); + provider.setRefreshTokenInterval(0); // never cache + provider.setAuthenticationDao(new MockAuthenticationDaoUserMarissa()); + + Authentication result = provider.authenticate(token); + + if (!(result instanceof DaoAuthenticationToken)) { + fail("Should have returned instance of DaoAuthenticationToken"); + } + + DaoAuthenticationToken castResult = (DaoAuthenticationToken) result; + assertEquals("marissa", castResult.getPrincipal()); + assertEquals(provider.getKey().hashCode(), castResult.getKeyHash()); + Thread.sleep(1000); + assertTrue(castResult.getExpires().before(new Date())); // already expired + + // Now try to re-authenticate + Authentication secondResult = provider.authenticate(result); + + if (!(secondResult instanceof DaoAuthenticationToken)) { + fail("Should have returned instance of DaoAuthenticationToken"); + } + + // Should still have a later expiry time than original + assertTrue(castResult.getExpires().before(((DaoAuthenticationToken) secondResult) + .getExpires())); + } + + public void testDaoAuthenticationTokensWithWrongKeyAreRejected() + throws Exception { + DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); + provider.setKey("x"); + provider.setRefreshTokenInterval(0); // never cache + provider.setAuthenticationDao(new MockAuthenticationDaoUserMarissa()); + + DaoAuthenticationToken token = new DaoAuthenticationToken("key", + new Date(), "Test", "Password", + new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl( + "ROLE_TWO")}); + + try { + provider.authenticate(token); + fail("Should have thrown BadCredentialsException"); + } catch (BadCredentialsException expected) { + assertTrue(true); + } } public void testGettersSetters() { @@ -199,6 +298,19 @@ public class DaoAuthenticationProviderTests extends TestCase { public void testStartupFailsIfNoAuthenticationDao() throws Exception { DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); + provider.setKey("xxx"); + + try { + provider.afterPropertiesSet(); + fail("Should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException expected) { + assertTrue(true); + } + } + + public void testStartupFailsIfNoKeySet() throws Exception { + DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); + provider.setAuthenticationDao(new MockAuthenticationDaoUserMarissa()); try { provider.afterPropertiesSet(); @@ -211,6 +323,7 @@ public class DaoAuthenticationProviderTests extends TestCase { public void testStartupSuccess() throws Exception { DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); AuthenticationDao dao = new MockAuthenticationDaoUserMarissa(); + provider.setKey("x"); provider.setAuthenticationDao(dao); assertEquals(dao, provider.getAuthenticationDao()); provider.afterPropertiesSet(); diff --git a/core/src/test/java/org/acegisecurity/providers/dao/DaoAuthenticationTokenTests.java b/core/src/test/java/org/acegisecurity/providers/dao/DaoAuthenticationTokenTests.java new file mode 100644 index 0000000000..f9eac4951d --- /dev/null +++ b/core/src/test/java/org/acegisecurity/providers/dao/DaoAuthenticationTokenTests.java @@ -0,0 +1,217 @@ +/* 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. + */ + +package net.sf.acegisecurity.providers.dao; + +import junit.framework.TestCase; + +import net.sf.acegisecurity.GrantedAuthority; +import net.sf.acegisecurity.GrantedAuthorityImpl; +import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken; + +import java.util.Date; + + +/** + * Tests {@link DaoAuthenticationToken}. + * + * @author Ben Alex + * @version $Id$ + */ +public class DaoAuthenticationTokenTests extends TestCase { + //~ Constructors =========================================================== + + public DaoAuthenticationTokenTests() { + super(); + } + + public DaoAuthenticationTokenTests(String arg0) { + super(arg0); + } + + //~ Methods ================================================================ + + public final void setUp() throws Exception { + super.setUp(); + } + + public static void main(String[] args) { + junit.textui.TestRunner.run(DaoAuthenticationTokenTests.class); + } + + public void testConstructorRejectsNulls() { + try { + new DaoAuthenticationToken(null, new Date(), "Test", "Password", + new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl( + "ROLE_TWO")}); + fail("Should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException expected) { + assertTrue(true); + } + + try { + new DaoAuthenticationToken("key", null, "Test", "Password", + new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl( + "ROLE_TWO")}); + fail("Should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException expected) { + assertTrue(true); + } + + try { + new DaoAuthenticationToken("key", new Date(), null, "Password", + new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl( + "ROLE_TWO")}); + fail("Should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException expected) { + assertTrue(true); + } + + try { + new DaoAuthenticationToken("key", new Date(), "Test", null, + new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl( + "ROLE_TWO")}); + fail("Should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException expected) { + assertTrue(true); + } + + try { + new DaoAuthenticationToken("key", new Date(), "Test", "Password", + null); + fail("Should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException expected) { + assertTrue(true); + } + + try { + new DaoAuthenticationToken("key", new Date(), "Test", "Password", + new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), null}); + fail("Should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException expected) { + assertTrue(true); + } + } + + public void testEqualsWhenEqual() { + Date date = new Date(); + + DaoAuthenticationToken token1 = new DaoAuthenticationToken("key", date, + "Test", "Password", + new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl( + "ROLE_TWO")}); + + DaoAuthenticationToken token2 = new DaoAuthenticationToken("key", date, + "Test", "Password", + new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl( + "ROLE_TWO")}); + + assertEquals(token1, token2); + } + + public void testGetters() { + Date date = new Date(); + DaoAuthenticationToken token = new DaoAuthenticationToken("key", date, + "Test", "Password", + new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl( + "ROLE_TWO")}); + assertEquals("key".hashCode(), token.getKeyHash()); + assertEquals("Test", token.getPrincipal()); + assertEquals("Password", token.getCredentials()); + assertEquals("ROLE_ONE", token.getAuthorities()[0].getAuthority()); + assertEquals("ROLE_TWO", token.getAuthorities()[1].getAuthority()); + assertEquals(date, token.getExpires()); + } + + public void testNoArgConstructor() { + try { + new DaoAuthenticationToken(); + fail("Should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException expected) { + assertTrue(true); + } + } + + public void testNotEqualsDueToAbstractParentEqualsCheck() { + Date date = new Date(); + + DaoAuthenticationToken token1 = new DaoAuthenticationToken("key", date, + "Test", "Password", + new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl( + "ROLE_TWO")}); + + DaoAuthenticationToken token2 = new DaoAuthenticationToken("key", date, + "DIFFERENT_PRINCIPAL", "Password", + new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl( + "ROLE_TWO")}); + + assertTrue(!token1.equals(token2)); + } + + public void testNotEqualsDueToDifferentAuthenticationClass() { + DaoAuthenticationToken token1 = new DaoAuthenticationToken("key", + new Date(), "Test", "Password", + new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl( + "ROLE_TWO")}); + + UsernamePasswordAuthenticationToken token2 = new UsernamePasswordAuthenticationToken("Test", + "Password", + new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl( + "ROLE_TWO")}); + token2.setAuthenticated(true); + + assertTrue(!token1.equals(token2)); + } + + public void testNotEqualsDueToDifferentExpiresDate() { + DaoAuthenticationToken token1 = new DaoAuthenticationToken("key", + new Date(50000), "Test", "Password", + new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl( + "ROLE_TWO")}); + + DaoAuthenticationToken token2 = new DaoAuthenticationToken("key", + new Date(60000), "Test", "Password", + new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl( + "ROLE_TWO")}); + + assertTrue(!token1.equals(token2)); + } + + public void testNotEqualsDueToKey() { + Date date = new Date(); + + DaoAuthenticationToken token1 = new DaoAuthenticationToken("key", date, + "Test", "Password", + new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl( + "ROLE_TWO")}); + + DaoAuthenticationToken token2 = new DaoAuthenticationToken("DIFFERENT_KEY", + date, "Test", "Password", + new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl( + "ROLE_TWO")}); + + assertTrue(!token1.equals(token2)); + } + + public void testSetAuthenticatedIgnored() { + DaoAuthenticationToken token = new DaoAuthenticationToken("key", + new Date(), "Test", "Password", + new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl( + "ROLE_TWO")}); + assertTrue(token.isAuthenticated()); + token.setAuthenticated(false); // ignored + assertTrue(token.isAuthenticated()); + } +} diff --git a/core/src/test/java/org/acegisecurity/ui/basicauth/filtertest-valid.xml b/core/src/test/java/org/acegisecurity/ui/basicauth/filtertest-valid.xml index 08c20c8af0..b9bd6b67bc 100644 --- a/core/src/test/java/org/acegisecurity/ui/basicauth/filtertest-valid.xml +++ b/core/src/test/java/org/acegisecurity/ui/basicauth/filtertest-valid.xml @@ -36,6 +36,7 @@ + my_password diff --git a/samples/contacts/etc/ca/applicationContext.xml b/samples/contacts/etc/ca/applicationContext.xml index 46802b2516..48906619eb 100644 --- a/samples/contacts/etc/ca/applicationContext.xml +++ b/samples/contacts/etc/ca/applicationContext.xml @@ -48,6 +48,7 @@ + my_password diff --git a/samples/contacts/etc/filter/applicationContext.xml b/samples/contacts/etc/filter/applicationContext.xml index 08209c45a9..ecc2831420 100644 --- a/samples/contacts/etc/filter/applicationContext.xml +++ b/samples/contacts/etc/filter/applicationContext.xml @@ -43,6 +43,7 @@ + my_password diff --git a/upgrade-04-05.txt b/upgrade-04-05.txt index b8f3a4228b..f22eddbb7f 100644 --- a/upgrade-04-05.txt +++ b/upgrade-04-05.txt @@ -24,6 +24,12 @@ applications: requested username. The new PlaintextPasswordEncoder offers a setter for ignoring the password case (defaults to require exact case matches). +- DaoAuthenticationProvider now provides caching. Successful authentications + return DaoAuthenticationTokens. You must set the mandatory "key" property + on DaoAuthenticationProvider so these tokens can be validated. You may + also wish to change the "refreshTokenInterval" property from the default + of 60,000 milliseconds. + - If you're using container adapters, please refer to the reference documentation as additional JARs are now required in your container classloader.