diff --git a/core/src/main/java/org/acegisecurity/providers/dao/BaseDigestPasswordEncoder.java b/core/src/main/java/org/acegisecurity/providers/dao/BaseDigestPasswordEncoder.java new file mode 100644 index 0000000000..0c49fce06a --- /dev/null +++ b/core/src/main/java/org/acegisecurity/providers/dao/BaseDigestPasswordEncoder.java @@ -0,0 +1,44 @@ +/* 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; + +/** + *
+ * Convenience base for Digest password encoders + *
+ * + * @author colin sampaleanu + * @version $Id$ + */ +public abstract class BaseDigestPasswordEncoder implements PasswordEncoder { + + //~ Instance fields ======================================================== + private boolean encodeHashAsBase64 = false; + + //~ Methods ================================================================ + + /** + * The encoded password is normally returned as Hex (32 char) version of the + * hash bytes. Setting this property to true will cause the encoded pass to + * be returned as Base64 text, which will consume 24 characters. + */ + public void setEncodeHashAsBase64(boolean encodeHashAsBase64) { + this.encodeHashAsBase64 = encodeHashAsBase64; + } + public boolean getEncodeHashAsBase64() { + return encodeHashAsBase64; + } + +} 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 0f06d285f3..3f428834b6 100644 --- a/core/src/main/java/org/acegisecurity/providers/dao/DaoAuthenticationProvider.java +++ b/core/src/main/java/org/acegisecurity/providers/dao/DaoAuthenticationProvider.java @@ -47,8 +47,6 @@ public class DaoAuthenticationProvider implements AuthenticationProvider, private AuthenticationDao authenticationDao; private PasswordEncoder passwordEncoder = new PlaintextPasswordEncoder(); - private boolean ignorePasswordCase = false; - private boolean ignoreUsernameCase = true; //~ Methods ================================================================ @@ -60,36 +58,6 @@ public class DaoAuthenticationProvider implements AuthenticationProvider, return authenticationDao; } - /** - * Indicates whether the password comparison is case sensitive. Defaults to - *false, meaning an exact case match is required.
- *
- * @param ignorePasswordCase set to true for less stringent
- * comparison
- */
- public void setIgnorePasswordCase(boolean ignorePasswordCase) {
- this.ignorePasswordCase = ignorePasswordCase;
- }
-
- public boolean isIgnorePasswordCase() {
- return ignorePasswordCase;
- }
-
- /**
- * Indicates whether the username search is case sensitive. Default to
- * true, meaning an exact case match is not required.
- *
- * @param ignoreUsernameCase set to false for more stringent
- * comparison
- */
- public void setIgnoreUsernameCase(boolean ignoreUsernameCase) {
- this.ignoreUsernameCase = ignoreUsernameCase;
- }
-
- public boolean isIgnoreUsernameCase() {
- return ignoreUsernameCase;
- }
-
/**
* Sets the PasswordEncoder instance to be used to encode and validate
* passwords. If not set, {@link PlaintextPasswordEncoder} will be used by
@@ -123,20 +91,12 @@ public class DaoAuthenticationProvider implements AuthenticationProvider,
throw new BadCredentialsException("Bad credentials presented");
} catch (DataAccessException repositoryProblem) {
throw new AuthenticationServiceException(repositoryProblem
- .getMessage());
- }
-
- if ((!this.ignoreUsernameCase)
- && (!user.getUsername().equals(authentication.getPrincipal()
- .toString()))) {
- throw new BadCredentialsException("Bad credentials presented");
+ .getMessage(), repositoryProblem);
}
if (!passwordEncoder.isPasswordValid(user.getPassword(),
- authentication.getCredentials().toString(), user,
- ignorePasswordCase)) {
+ authentication.getCredentials().toString(), user))
throw new BadCredentialsException("Bad credentials presented");
- }
if (!user.isEnabled()) {
throw new DisabledException("User is disabled");
diff --git a/core/src/main/java/org/acegisecurity/providers/dao/MD5PasswordEncoder.java b/core/src/main/java/org/acegisecurity/providers/dao/MD5PasswordEncoder.java
index 4eb4745191..c3694bbe94 100644
--- a/core/src/main/java/org/acegisecurity/providers/dao/MD5PasswordEncoder.java
+++ b/core/src/main/java/org/acegisecurity/providers/dao/MD5PasswordEncoder.java
@@ -15,6 +15,7 @@
package net.sf.acegisecurity.providers.dao;
+import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
@@ -24,34 +25,39 @@ import org.apache.commons.codec.digest.DigestUtils;
*
*
* - * The ignorePasswordCase parameter is not used for this implementation. - *
- * - ** A null password is encoded to the same value as an empty ("") password. *
* * @author colin sampaleanu * @version $Id$ */ -public class MD5PasswordEncoder implements PasswordEncoder { +public class MD5PasswordEncoder extends BaseDigestPasswordEncoder implements PasswordEncoder { //~ Methods ================================================================ - /* (non-Javadoc) - * @see net.sf.acegisecurity.providers.dao.PasswordEncoder#isPasswordValid(net.sf.acegisecurity.providers.dao.User, java.lang.String, boolean) - */ - public boolean isPasswordValid(String encPass, String rawPass, - Object saltSource, boolean ignorePasswordCase) { - String pass1 = "" + encPass; - String pass2 = DigestUtils.md5Hex("" + rawPass); + /* (non-Javadoc) + * @see net.sf.acegisecurity.providers.dao.PasswordEncoder#isPasswordValid(java.lang.String, java.lang.String, java.lang.Object) + */ + public boolean isPasswordValid(String encPass, String rawPass, Object saltSource) { - return pass1.equals(pass2); - } + String pass1 = "" + encPass; + String pass2 = encodeInternal("" + rawPass); - /* (non-Javadoc) - * @see net.sf.acegisecurity.providers.dao.PasswordEncoder#encodePassword(java.lang.String, java.lang.Object) - */ - public String encodePassword(String rawPass, Object saltSource) { - return DigestUtils.md5Hex("" + rawPass); - } + return pass1.equals(pass2); + } + + /* (non-Javadoc) + * @see net.sf.acegisecurity.providers.dao.PasswordEncoder#encodePassword(java.lang.String, java.lang.Object) + */ + public String encodePassword(String rawPass, Object saltSource) { + return encodeInternal("" + rawPass); + } + + private String encodeInternal(String input) { + + if (!getEncodeHashAsBase64()) + return DigestUtils.md5Hex(input); + + byte[] encoded = Base64.encodeBase64(DigestUtils.md5(input)); + return new String(encoded); + } } diff --git a/core/src/main/java/org/acegisecurity/providers/dao/PasswordEncoder.java b/core/src/main/java/org/acegisecurity/providers/dao/PasswordEncoder.java index c7a91340ad..ba0db95c00 100644 --- a/core/src/main/java/org/acegisecurity/providers/dao/PasswordEncoder.java +++ b/core/src/main/java/org/acegisecurity/providers/dao/PasswordEncoder.java @@ -31,8 +31,8 @@ public interface PasswordEncoder { //~ Methods ================================================================ public boolean isPasswordValid(String encPass, String rawPass, - Object saltSource, boolean ignorePasswordCase) + Object saltSource) throws DataAccessException; - public String encodePassword(String rawPass, Object saltSource); + public String encodePassword(String rawPass, Object saltSource) throws DataAccessException; } diff --git a/core/src/main/java/org/acegisecurity/providers/dao/PlaintextPasswordEncoder.java b/core/src/main/java/org/acegisecurity/providers/dao/PlaintextPasswordEncoder.java index e289740732..91c15bf87f 100644 --- a/core/src/main/java/org/acegisecurity/providers/dao/PlaintextPasswordEncoder.java +++ b/core/src/main/java/org/acegisecurity/providers/dao/PlaintextPasswordEncoder.java @@ -24,13 +24,32 @@ package net.sf.acegisecurity.providers.dao; * @version $Id$ */ public class PlaintextPasswordEncoder implements PasswordEncoder { + + //~ Instance fields ======================================================== + private boolean ignorePasswordCase = false; + //~ Methods ================================================================ + + /** + * Indicates whether the password comparison is case sensitive. Defaults to + *false, meaning an exact case match is required.
+ *
+ * @param ignorePasswordCase set to true for less stringent
+ * comparison
+ */
+ public void setIgnorePasswordCase(boolean ignorePasswordCase) {
+ this.ignorePasswordCase = ignorePasswordCase;
+ }
+
+ public boolean isIgnorePasswordCase() {
+ return ignorePasswordCase;
+ }
/* (non-Javadoc)
- * @see net.sf.acegisecurity.providers.dao.PasswordEncoder#isPasswordValid(net.sf.acegisecurity.providers.dao.User, java.lang.String, boolean)
+ * @see net.sf.acegisecurity.providers.dao.PasswordEncoder#isPasswordValid(java.lang.String, java.lang.String, java.lang.Object)
*/
- public boolean isPasswordValid(String encPass, String rawPass,
- Object saltSource, boolean ignorePasswordCase) {
+ public boolean isPasswordValid(String encPass, String rawPass, Object saltSource) {
+
String pass1 = "" + encPass;
String pass2 = "" + rawPass;
diff --git a/core/src/main/java/org/acegisecurity/providers/dao/SHAPasswordEncoder.java b/core/src/main/java/org/acegisecurity/providers/dao/SHAPasswordEncoder.java
index 19695ee9eb..2fd7ad523e 100644
--- a/core/src/main/java/org/acegisecurity/providers/dao/SHAPasswordEncoder.java
+++ b/core/src/main/java/org/acegisecurity/providers/dao/SHAPasswordEncoder.java
@@ -15,35 +15,30 @@
package net.sf.acegisecurity.providers.dao;
+import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
-
/**
* * SHA implementation of PasswordEncoder. *
* *- * The ignorePasswordCase parameter is not used for this implementation. - *
- * - ** A null password is encoded to the same value as an empty ("") password. *
* * @author colin sampaleanu * @version $Id$ */ -public class SHAPasswordEncoder implements PasswordEncoder { - //~ Methods ================================================================ - +public class SHAPasswordEncoder extends BaseDigestPasswordEncoder implements PasswordEncoder { + /* (non-Javadoc) - * @see net.sf.acegisecurity.providers.dao.PasswordEncoder#isPasswordValid(net.sf.acegisecurity.providers.dao.User, java.lang.String, boolean) + * @see net.sf.acegisecurity.providers.dao.PasswordEncoder#isPasswordValid(java.lang.String, java.lang.String, java.lang.Object) */ - public boolean isPasswordValid(String encPass, String rawPass, - Object saltSource, boolean ignorePasswordCase) { + public boolean isPasswordValid(String encPass, String rawPass, Object saltSource) { + String pass1 = "" + encPass; - String pass2 = DigestUtils.shaHex("" + rawPass); + String pass2 = encodeInternal("" + rawPass); return pass1.equals(pass2); } @@ -52,6 +47,15 @@ public class SHAPasswordEncoder implements PasswordEncoder { * @see net.sf.acegisecurity.providers.dao.PasswordEncoder#encodePassword(java.lang.String, java.lang.Object) */ public String encodePassword(String rawPass, Object saltSource) { - return DigestUtils.shaHex("" + rawPass); + return encodeInternal("" + rawPass); + } + + private String encodeInternal(String input) { + + if (!getEncodeHashAsBase64()) + return DigestUtils.shaHex(input); + + byte[] encoded = Base64.encodeBase64(DigestUtils.sha(input)); + return new String(encoded); } } diff --git a/core/src/main/java/org/acegisecurity/userdetails/UserDetailsService.java b/core/src/main/java/org/acegisecurity/userdetails/UserDetailsService.java index 0c7f9597f2..057128acf2 100644 --- a/core/src/main/java/org/acegisecurity/userdetails/UserDetailsService.java +++ b/core/src/main/java/org/acegisecurity/userdetails/UserDetailsService.java @@ -34,9 +34,11 @@ public interface AuthenticationDao { //~ Methods ================================================================ /** - * Locates the user based on the username. The search is case insensitive, - * meaning the implementation must return any matching object irrespective - * of the mixture of uppercase and lowercase characters in the username. + * Locates the user based on the username. In the actual implementation, + * the search may possibly be case insensitive, or case insensitive + * depending on how the implementaion instance is configured. In this case, + * the User object that comes back may have a username that is of a different + * case than what was actually requested.. * * @param username the username presented to the {@link * DaoAuthenticationProvider} diff --git a/core/src/main/resources/org/acegisecurity/adapters/acegisecurity.xml b/core/src/main/resources/org/acegisecurity/adapters/acegisecurity.xml index df0c6150ca..38a23ee9ca 100644 --- a/core/src/main/resources/org/acegisecurity/adapters/acegisecurity.xml +++ b/core/src/main/resources/org/acegisecurity/adapters/acegisecurity.xml @@ -36,8 +36,6 @@+ * TestCase for PlaintextPasswordEncoder. + *
+ * + * @author colin sampaleanu + * @version $Id$ + */ +public class MD5PasswordEncoderTest extends TestCase { + + public void testBasicFunctionality() { + + MD5PasswordEncoder pe = new MD5PasswordEncoder(); + String raw = "abc123"; + String badRaw = "abc321"; + String encoded = pe.encodePassword(raw, null); // no SALT source + assertTrue(pe.isPasswordValid(encoded, raw, null)); + assertFalse(pe.isPasswordValid(encoded, badRaw, null)); + assertTrue(encoded.length() == 32); + + // now try Base64 + pe.setEncodeHashAsBase64(true); + encoded = pe.encodePassword(raw, null); // no SALT source + assertTrue(pe.isPasswordValid(encoded, raw, null)); + assertFalse(pe.isPasswordValid(encoded, badRaw, null)); + assertTrue(encoded.length() != 32); + } + +} diff --git a/core/src/test/java/org/acegisecurity/providers/dao/PlaintextPasswordEncoderTest.java b/core/src/test/java/org/acegisecurity/providers/dao/PlaintextPasswordEncoderTest.java new file mode 100644 index 0000000000..8646d4dda8 --- /dev/null +++ b/core/src/test/java/org/acegisecurity/providers/dao/PlaintextPasswordEncoderTest.java @@ -0,0 +1,59 @@ +/* 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; + +/** + *+ * TestCase for PlaintextPasswordEncoder. + *
+ * + * @author colin sampaleanu + * @version $Id$ + */ +public class PlaintextPasswordEncoderTest extends TestCase { + + public void testBasicFunctionality() { + PlaintextPasswordEncoder pe = new PlaintextPasswordEncoder(); + + String raw = "abc123"; + String rawDiffCase = "AbC123"; + String badRaw = "abc321"; + // should be able to validate even without encoding + String encoded = raw; + assertTrue(pe.isPasswordValid(encoded, raw, null)); // no SALT source + assertFalse(pe.isPasswordValid(encoded, badRaw, null)); + + // now make sure encoded version it gives us back is comparable as well + encoded = pe.encodePassword(raw, null); + assertTrue(pe.isPasswordValid(encoded, raw, null)); // no SALT source + assertFalse(pe.isPasswordValid(encoded, badRaw, null)); + + // make sure default is not to ignore password case + encoded = pe.encodePassword(rawDiffCase, null); + assertFalse(pe.isPasswordValid(encoded, raw, null)); + + // now check for ignore password case + pe = new PlaintextPasswordEncoder(); + pe.setIgnorePasswordCase(true); + + // should be able to validate even without encoding + encoded = pe.encodePassword(rawDiffCase, null); + assertTrue(pe.isPasswordValid(encoded, raw, null)); + assertFalse(pe.isPasswordValid(encoded, badRaw, null)); + } + +} \ No newline at end of file diff --git a/core/src/test/java/org/acegisecurity/providers/dao/SHAPasswordEncoderTest.java b/core/src/test/java/org/acegisecurity/providers/dao/SHAPasswordEncoderTest.java new file mode 100644 index 0000000000..67f1707dfa --- /dev/null +++ b/core/src/test/java/org/acegisecurity/providers/dao/SHAPasswordEncoderTest.java @@ -0,0 +1,47 @@ +/* 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; + +/** + *+ * TestCase for SHAPasswordEncoder. + *
+ * + * @author colin sampaleanu + * @version $Id$ + */ +public class SHAPasswordEncoderTest extends TestCase { + + public void testBasicFunctionality() { + + SHAPasswordEncoder pe = new SHAPasswordEncoder(); + String raw = "abc123"; + String badRaw = "abc321"; + String encoded = pe.encodePassword(raw, null); // no SALT source + assertTrue(pe.isPasswordValid(encoded, raw, null)); + assertFalse(pe.isPasswordValid(encoded, badRaw, null)); + assertTrue(encoded.length() == 40); + + // now try Base64 + pe.setEncodeHashAsBase64(true); + encoded = pe.encodePassword(raw, null); // no SALT source + assertTrue(pe.isPasswordValid(encoded, raw, null)); + assertFalse(pe.isPasswordValid(encoded, badRaw, null)); + assertTrue(encoded.length() != 40); + + } +} diff --git a/core/src/test/java/org/acegisecurity/ui/webapp/filtertest-valid.xml b/core/src/test/java/org/acegisecurity/ui/webapp/filtertest-valid.xml index df0c6150ca..38a23ee9ca 100644 --- a/core/src/test/java/org/acegisecurity/ui/webapp/filtertest-valid.xml +++ b/core/src/test/java/org/acegisecurity/ui/webapp/filtertest-valid.xml @@ -36,8 +36,6 @@