Yay! Yet another change to the password system. This time we're switching

entirely from old-and-busted crypt() to the new hotness MD5. This allows
us to interoperate cleanly with our soon-to-be new forum system which MD5
encodes a user's password before it ever leaves the web browser. Those
security conscious little devils.


git-svn-id: https://samskivert.googlecode.com/svn/trunk@1551 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
mdb
2004-12-18 02:33:10 +00:00
parent 37858624b8
commit a495745c67
6 changed files with 96 additions and 76 deletions
@@ -44,6 +44,6 @@ public interface Authenticator
* the authentication check.
*/
public void authenticateUser (
User user, String username, String password, boolean persist)
User user, String username, Password password, boolean persist)
throws AuthenticationFailedException;
}
@@ -10,22 +10,12 @@ package com.samskivert.servlet.user;
public class Password
{
/**
* Creates a password from the supplied username and (unencrypted)
* password.
* Returns the clear password text. This method may return null if
* none was provided when creating the password.
*/
public Password (String username, String password)
public String getCleartext ()
{
_encrypted = UserUtil.encryptPassword(username, password);
}
/**
* Creates a password directly from the encrypted text. This text
* <em>must</em> have been created using {@link
* UserUtil#encryptPassword} or via the same algorithm.
*/
public Password (String encrypted)
{
_encrypted = encrypted;
return _cleartext;
}
/**
@@ -36,5 +26,31 @@ public class Password
return _encrypted;
}
/**
* Creates a password instance from the supplied plaintext.
*/
public static Password makeFromClear (String password)
{
return new Password(password, UserUtil.encryptPassword(password));
}
/**
* Creates a password instance from the supplied already encrypted
* text. <em>Note:</em> the encrypted text must be obtained from
* {@link UserUtil#encryptPassword}.
*/
public static Password makeFromCrypto (String encrypted)
{
return new Password(null, encrypted);
}
/** Creates a password instance. */
protected Password (String cleartext, String encrypted)
{
_cleartext = cleartext;
_encrypted = encrypted;
}
protected String _cleartext;
protected String _encrypted;
}
@@ -76,7 +76,17 @@ public class User
*/
public void setPassword (String password)
{
this.password = UserUtil.encryptPassword(username, password);
setPassword(Password.makeFromClear(password));
}
/**
* Updates the user's password.
*
* @param password The user's new password.
*/
public void setPassword (Password password)
{
this.password = password.getEncrypted();
_dirty.setModified("password");
}
@@ -99,20 +109,38 @@ public class User
}
/**
* Compares the supplied (unencrypted) password with the password
* associated with this user record.
* Converts a legacy password.
*
* @return true if a conversion took place, false if not.
*/
public boolean updateLegacyPassword (String password)
{
// we may have an old crypt() encrypted password
if (this.password.length() == 13) {
// check both the case sensitive and insensitive versions for
// further legacy reasons
if (this.password.equals(
UserUtil.legacyEncrypt(username, password, false)) ||
this.password.equals(
UserUtil.legacyEncrypt(username, password, true))) {
// update our password with the new encryption method
setPassword(password);
return true;
}
}
return false;
}
/**
* Compares the supplied password with the password associated with
* this user record.
*
* @return true if the passwords match, false if they do not.
*/
public boolean passwordsMatch (String password)
public boolean passwordsMatch (Password password)
{
String epasswd = UserUtil.encryptPassword(username, password);
String oldpasswd = UserUtil.encryptPassword(username, password, false);
// because we used to be case sensitive, we have to check both
// versions of the encrypted password here for backward
// compatibility
return (this.password.equals(epasswd) ||
this.password.equals(oldpasswd));
return this.password.equals(password.getEncrypted());
}
/**
@@ -60,7 +60,7 @@ public class UserManager
{
// documentation inherited
public void authenticateUser (
User user, String username, String password, boolean persist)
User user, String username, Password password, boolean persist)
throws InvalidPasswordException
{
// don't care
@@ -75,7 +75,7 @@ public class UserManager
{
// documentation inherited
public void authenticateUser (
User user, String username, String password, boolean persist)
User user, String username, Password password, boolean persist)
throws AuthenticationFailedException
{
if (!user.passwordsMatch(password)) {
@@ -224,7 +224,7 @@ public class UserManager
* or some other error occurs, an exception will be thrown.
*
* @param username The username supplied by the user.
* @param password The plaintext password supplied by the user.
* @param password The password supplied by the user.
* @param persist If true, the cookie will expire in one month, if
* false, the cookie will expire at the end of the user's browser
* session.
@@ -235,7 +235,7 @@ public class UserManager
*
* @return the user object of the authenticated user.
*/
public User login (String username, String password, boolean persist,
public User login (String username, Password password, boolean persist,
HttpServletRequest req, HttpServletResponse rsp,
Authenticator auth)
throws PersistenceException, AuthenticationFailedException
@@ -246,6 +246,13 @@ public class UserManager
throw new NoSuchUserException("error.no_such_user");
}
// potentially convert the user's legacy password
if (password.getCleartext() != null &&
user.updateLegacyPassword(password.getCleartext())) {
Log.info("Updated legacy password " + user.username + ".");
_repository.updateUser(user);
}
// run the user through the authentication gamut
auth.authenticateUser(user, username, password, persist);
@@ -91,29 +91,6 @@ public class UserRepository extends JORARepository
getUserClass().getName(), "users", session, "userId");
}
/**
* Requests that a new user be created in the repository.
*
* @param uname the username of the new user to create.
* @param pass the (unencrypted) password for the new user.
* @param realname the user's real name.
* @param email the user's email address.
* @param siteId the unique identifier of the site through which this
* account is being created. The resulting user will be tracked as
* originating from this site for accounting purposes ({@link
* SiteIdentifier#DEFAULT_SITE_ID} can be used by systems that don't
* desire to perform site tracking.
*
* @return The userid of the newly created user.
*/
public int createUser (Username uname, String pass, String realname,
String email, int siteId)
throws UserExistsException, PersistenceException
{
return createUser(uname, new Password(uname.getUsername(), pass),
realname, email, siteId);
}
/**
* Like {@link #createUser(String,String,String,String,int)} except
* that the supplied password has already been encrypted.
@@ -139,11 +116,11 @@ public class UserRepository extends JORARepository
String realname, String email, int siteId)
{
user.username = name.getUsername();
user.password = pass.getEncrypted();
user.realname = realname;
user.email = email;
user.setPassword(pass);
user.setRealName(realname);
user.setEmail(email);
user.created = new Date(System.currentTimeMillis());
user.siteId = siteId;
user.setSiteId(siteId);
}
/**
@@ -606,10 +583,12 @@ public class UserRepository extends JORARepository
System.out.println(rep.loadUser("mdb"));
System.out.println(rep.loadUserBySession("auth"));
rep.createUser(new Username("samskivert"), "foobar",
rep.createUser(new Username("samskivert"),
Password.makeFromClear("foobar"),
"Michael Bayne", "mdb@samskivert.com",
SiteIdentifier.DEFAULT_SITE_ID);
rep.createUser(new Username("mdb"), "foobar", "Michael Bayne",
rep.createUser(new Username("mdb"),
Password.makeFromClear("foobar"), "Michael Bayne",
"mdb@samskivert.com",
SiteIdentifier.DEFAULT_SITE_ID);
@@ -48,26 +48,17 @@ public class UserUtil
* that would be stored in the user record were the password to be
* updated via {@link User#setPassword}.
*/
public static String encryptPassword (String username, String password)
public static String encryptPassword (String password)
{
return encryptPassword(username, password, true);
return StringUtil.md5hex(password);
}
/**
* Encrypts the user's password according to our preferred scheme.
*
* @param username the username of the user whose password is to be
* encrypted.
* @param password the plaintext of the password to be encrypted.
* @param ignoreUserCase true if the username should be uncapitalized
* prior to performing the encryption so that future password checks
* will work if the user registers as "Bluebeard" but logs in as
* "bluebeard".
* Encrypts passwords the way we used to.
*/
public static String encryptPassword (
String username, String password, boolean ignoreUserCase)
public static String legacyEncrypt (String username, String password,
boolean ignoreUserCase)
{
// perhaps we'll not be too (case) sensitive
if (ignoreUserCase) {
username = username.toLowerCase();
}
@@ -77,10 +68,9 @@ public class UserUtil
public static void main (String[] args)
{
if (args.length < 2) {
System.err.println("Usage: UserUtil username password");
System.err.println("Usage: UserUtil password");
System.exit(-1);
}
System.out.println("Encrypted password: " +
encryptPassword(args[0], args[1], true));
System.out.println("Encrypted password: " + encryptPassword(args[0]));
}
}