move and rename password encoding classes.

change saltSource arument to salt argument, which impl may or may not use.
This commit is contained in:
Colin Sampaleanu 2004-04-16 03:44:04 +00:00
parent 5d9d734735
commit 3d089aaa67
13 changed files with 415 additions and 70 deletions

View File

@ -22,6 +22,7 @@ import net.sf.acegisecurity.BadCredentialsException;
import net.sf.acegisecurity.DisabledException;
import net.sf.acegisecurity.providers.AuthenticationProvider;
import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import net.sf.acegisecurity.providers.encoding.*;
import org.springframework.beans.factory.InitializingBean;
@ -95,8 +96,9 @@ public class DaoAuthenticationProvider implements AuthenticationProvider,
}
if (!passwordEncoder.isPasswordValid(user.getPassword(),
authentication.getCredentials().toString(), user))
authentication.getCredentials().toString(), user)) {
throw new BadCredentialsException("Bad credentials presented");
}
if (!user.isEnabled()) {
throw new DisabledException("User is disabled");

View File

@ -0,0 +1,50 @@
/* 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.encoding;
import net.sf.acegisecurity.providers.encoding.*;
/**
* <p>
* Convenience base for Digest password encoders
* </p>
*
* @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.
*
* @param encodeHashAsBase64 DOCUMENT ME!
*/
public void setEncodeHashAsBase64(boolean encodeHashAsBase64) {
this.encodeHashAsBase64 = encodeHashAsBase64;
}
public boolean getEncodeHashAsBase64() {
return encodeHashAsBase64;
}
}

View File

@ -0,0 +1,64 @@
/* 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.encoding;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
/**
* <p>
* MD5 implementation of PasswordEncoder.
* </p>
*
* <p>
* A null password is encoded to the same value as an empty ("") password.
* </p>
*
* @author colin sampaleanu
* @version $Id$
*/
public class Md5PasswordEncoder extends BaseDigestPasswordEncoder
implements PasswordEncoder {
//~ Methods ================================================================
/* (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 salt) {
String pass1 = "" + encPass;
String pass2 = encodeInternal("" + 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 salt) {
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);
}
}

View File

@ -0,0 +1,87 @@
/* 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.encoding;
import org.springframework.dao.DataAccessException;
/**
* <p>
* Interface for performing authentication operations on a password, so that
* digest algorithms may be abstracted.
* </p>
*
* @author colin sampaleanu
* @version $Id$
*/
public interface PasswordEncoder {
//~ Methods ================================================================
/**
* <p>
* Validates a specified 'raw' password against an encoded password
* previously returned form {@link #encodePassword(String, Object)}. The
* raw password will first be encoded, and then both values will be
* compared.
* </p>
*
* <p>
* The specified salt will potentially be used by the implementation to
* 'salt' the initial value before encoding. If a salt value is provided,
* it must be the same as the value used when calling {@link
* #encodePassword(String, Object)} to produce the first encoded value.
* Note that a specific implementation may choose to ignore the salt
* value, or provide its own.
* </p>
*
* @param encPass a pre-encoded password
* @param rawPass a raw password to encode and compare against the
* pre-encoded password
* @param an object optionally used by the implementation to 'salt' the raw
* password before encoding. A null value is legal.
*
* @return DOCUMENT ME!
*/
public boolean isPasswordValid(String encPass, String rawPass,
Object saltSource) throws DataAccessException;
/**
* <p>
* Encodes the specified raw password with an implementation specific
* algorithm. This will generally be a one-way message digest such as MD5
* or SHA, but may also be a plaintext variant which does no encoding at
* all, but rather returns the same password it was fed. The latter is
* useful to plug in when the original password must be stored as-is.
* </p>
*
* <p>
* The specified salt will potentially be used by the implementation to
* 'salt' the initial value before encoding, in order to prevent
* dictionary attacks. If a salt value is provided, the same salt value
* must be use when calling the {@link #isPasswordValid(String, String,
* Object)} function. Note that a specific implementation may choose to
* ignore the salt value, or provide its own.
* </p>
*
* @param rawPass the password to encode
* @param an object optionally used by the implementation to 'salt' the raw
* password before encoding. A null value is legal.
*
* @return DOCUMENT ME!
*/
public String encodePassword(String rawPass, Object salt)
throws DataAccessException;
}

View File

@ -0,0 +1,68 @@
/* 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.encoding;
/**
* <p>
* Plaintext implementation of PasswordEncoder.
* </p>
*
* @author colin sampaleanu
* @version $Id$
*/
public class PlaintextPasswordEncoder implements PasswordEncoder {
//~ Instance fields ========================================================
private boolean ignorePasswordCase = false;
//~ Methods ================================================================
/**
* Indicates whether the password comparison is case sensitive. Defaults to
* <code>false</code>, meaning an exact case match is required.
*
* @param ignorePasswordCase set to <code>true</code> 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(java.lang.String, java.lang.String, java.lang.Object)
*/
public boolean isPasswordValid(String encPass, String rawPass, Object salt) {
String pass1 = "" + encPass;
String pass2 = "" + rawPass;
if (!ignorePasswordCase) {
return pass1.equals(pass2);
} else {
return pass1.equalsIgnoreCase(pass2);
}
}
/* (non-Javadoc)
* @see net.sf.acegisecurity.providers.dao.PasswordEncoder#encodePassword(java.lang.String, java.lang.Object)
*/
public String encodePassword(String rawPass, Object salt) {
return rawPass;
}
}

View File

@ -0,0 +1,64 @@
/* 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.encoding;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
/**
* <p>
* SHA implementation of PasswordEncoder.
* </p>
*
* <p>
* A null password is encoded to the same value as an empty ("") password.
* </p>
*
* @author colin sampaleanu
* @version $Id$
*/
public class ShaPasswordEncoder extends BaseDigestPasswordEncoder
implements PasswordEncoder {
//~ Methods ================================================================
/* (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 salt) {
String pass1 = "" + encPass;
String pass2 = encodeInternal("" + 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 salt) {
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);
}
}

View File

@ -36,9 +36,9 @@ public interface AuthenticationDao {
/**
* 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..
* 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}

View File

@ -37,7 +37,6 @@ import org.springframework.dao.DataRetrievalFailureException;
* @version $Id$
*/
public class DaoAuthenticationProviderTests extends TestCase {
//~ Methods ================================================================
public final void setUp() throws Exception {

View File

@ -12,10 +12,14 @@
* 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.providers.encoding.*;
/**
* <p>
* TestCase for PlaintextPasswordEncoder.
@ -25,23 +29,22 @@ import junit.framework.TestCase;
* @version $Id$
*/
public class MD5PasswordEncoderTest extends TestCase {
//~ Methods ================================================================
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);
}
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);
}
}

View File

@ -12,10 +12,14 @@
* 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.providers.encoding.*;
/**
* <p>
* TestCase for PlaintextPasswordEncoder.
@ -25,35 +29,36 @@ import junit.framework.TestCase;
* @version $Id$
*/
public class PlaintextPasswordEncoderTest extends TestCase {
//~ Methods ================================================================
public void testBasicFunctionality() {
PlaintextPasswordEncoder pe = new PlaintextPasswordEncoder();
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));
String raw = "abc123";
String rawDiffCase = "AbC123";
String badRaw = "abc321";
// 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
String encoded = raw;
assertTrue(pe.isPasswordValid(encoded, raw, null)); // no SALT source
assertFalse(pe.isPasswordValid(encoded, badRaw, null));
// 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));
}
// 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));
}
}

View File

@ -12,36 +12,39 @@
* 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.providers.encoding.*;
/**
* <p>
* TestCase for SHAPasswordEncoder.
* TestCase for ShaPasswordEncoder.
* </p>
*
* @author colin sampaleanu
* @version $Id$
*/
public class SHAPasswordEncoderTest extends TestCase {
//~ Methods ================================================================
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);
}
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);
}
}

View File

@ -30,7 +30,7 @@ import java.util.Map;
/**
* Demonstrates accessing the {@link ContactManager} via remoting protocols.
*
*
* <P>
* Based on Spring's JPetStore sample, written by Juergen Hoeller.
* </p>

View File

@ -1,5 +1,5 @@
#HSQL database
#Thu Apr 15 12:20:59 EDT 2004
#Thu Apr 15 23:43:47 EDT 2004
sql.strict_fk=true
readonly=false
sql.strong_fk=true