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:
@@ -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]));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user