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.
|
* the authentication check.
|
||||||
*/
|
*/
|
||||||
public void authenticateUser (
|
public void authenticateUser (
|
||||||
User user, String username, String password, boolean persist)
|
User user, String username, Password password, boolean persist)
|
||||||
throws AuthenticationFailedException;
|
throws AuthenticationFailedException;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,22 +10,12 @@ package com.samskivert.servlet.user;
|
|||||||
public class Password
|
public class Password
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Creates a password from the supplied username and (unencrypted)
|
* Returns the clear password text. This method may return null if
|
||||||
* password.
|
* none was provided when creating the password.
|
||||||
*/
|
*/
|
||||||
public Password (String username, String password)
|
public String getCleartext ()
|
||||||
{
|
{
|
||||||
_encrypted = UserUtil.encryptPassword(username, password);
|
return _cleartext;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -36,5 +26,31 @@ public class Password
|
|||||||
return _encrypted;
|
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;
|
protected String _encrypted;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,7 +76,17 @@ public class User
|
|||||||
*/
|
*/
|
||||||
public void setPassword (String password)
|
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");
|
_dirty.setModified("password");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,20 +109,38 @@ public class User
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compares the supplied (unencrypted) password with the password
|
* Converts a legacy password.
|
||||||
* associated with this user record.
|
*
|
||||||
|
* @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.
|
* @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);
|
return this.password.equals(password.getEncrypted());
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ public class UserManager
|
|||||||
{
|
{
|
||||||
// documentation inherited
|
// documentation inherited
|
||||||
public void authenticateUser (
|
public void authenticateUser (
|
||||||
User user, String username, String password, boolean persist)
|
User user, String username, Password password, boolean persist)
|
||||||
throws InvalidPasswordException
|
throws InvalidPasswordException
|
||||||
{
|
{
|
||||||
// don't care
|
// don't care
|
||||||
@@ -75,7 +75,7 @@ public class UserManager
|
|||||||
{
|
{
|
||||||
// documentation inherited
|
// documentation inherited
|
||||||
public void authenticateUser (
|
public void authenticateUser (
|
||||||
User user, String username, String password, boolean persist)
|
User user, String username, Password password, boolean persist)
|
||||||
throws AuthenticationFailedException
|
throws AuthenticationFailedException
|
||||||
{
|
{
|
||||||
if (!user.passwordsMatch(password)) {
|
if (!user.passwordsMatch(password)) {
|
||||||
@@ -224,7 +224,7 @@ public class UserManager
|
|||||||
* or some other error occurs, an exception will be thrown.
|
* or some other error occurs, an exception will be thrown.
|
||||||
*
|
*
|
||||||
* @param username The username supplied by the user.
|
* @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
|
* @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
|
* false, the cookie will expire at the end of the user's browser
|
||||||
* session.
|
* session.
|
||||||
@@ -235,7 +235,7 @@ public class UserManager
|
|||||||
*
|
*
|
||||||
* @return the user object of the authenticated user.
|
* @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,
|
HttpServletRequest req, HttpServletResponse rsp,
|
||||||
Authenticator auth)
|
Authenticator auth)
|
||||||
throws PersistenceException, AuthenticationFailedException
|
throws PersistenceException, AuthenticationFailedException
|
||||||
@@ -246,6 +246,13 @@ public class UserManager
|
|||||||
throw new NoSuchUserException("error.no_such_user");
|
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
|
// run the user through the authentication gamut
|
||||||
auth.authenticateUser(user, username, password, persist);
|
auth.authenticateUser(user, username, password, persist);
|
||||||
|
|
||||||
|
|||||||
@@ -91,29 +91,6 @@ public class UserRepository extends JORARepository
|
|||||||
getUserClass().getName(), "users", session, "userId");
|
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
|
* Like {@link #createUser(String,String,String,String,int)} except
|
||||||
* that the supplied password has already been encrypted.
|
* that the supplied password has already been encrypted.
|
||||||
@@ -139,11 +116,11 @@ public class UserRepository extends JORARepository
|
|||||||
String realname, String email, int siteId)
|
String realname, String email, int siteId)
|
||||||
{
|
{
|
||||||
user.username = name.getUsername();
|
user.username = name.getUsername();
|
||||||
user.password = pass.getEncrypted();
|
user.setPassword(pass);
|
||||||
user.realname = realname;
|
user.setRealName(realname);
|
||||||
user.email = email;
|
user.setEmail(email);
|
||||||
user.created = new Date(System.currentTimeMillis());
|
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.loadUser("mdb"));
|
||||||
System.out.println(rep.loadUserBySession("auth"));
|
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",
|
"Michael Bayne", "mdb@samskivert.com",
|
||||||
SiteIdentifier.DEFAULT_SITE_ID);
|
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",
|
"mdb@samskivert.com",
|
||||||
SiteIdentifier.DEFAULT_SITE_ID);
|
SiteIdentifier.DEFAULT_SITE_ID);
|
||||||
|
|
||||||
|
|||||||
@@ -48,26 +48,17 @@ public class UserUtil
|
|||||||
* that would be stored in the user record were the password to be
|
* that would be stored in the user record were the password to be
|
||||||
* updated via {@link User#setPassword}.
|
* 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.
|
* Encrypts passwords the way we used to.
|
||||||
*
|
|
||||||
* @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".
|
|
||||||
*/
|
*/
|
||||||
public static String encryptPassword (
|
public static String legacyEncrypt (String username, String password,
|
||||||
String username, String password, boolean ignoreUserCase)
|
boolean ignoreUserCase)
|
||||||
{
|
{
|
||||||
// perhaps we'll not be too (case) sensitive
|
|
||||||
if (ignoreUserCase) {
|
if (ignoreUserCase) {
|
||||||
username = username.toLowerCase();
|
username = username.toLowerCase();
|
||||||
}
|
}
|
||||||
@@ -77,10 +68,9 @@ public class UserUtil
|
|||||||
public static void main (String[] args)
|
public static void main (String[] args)
|
||||||
{
|
{
|
||||||
if (args.length < 2) {
|
if (args.length < 2) {
|
||||||
System.err.println("Usage: UserUtil username password");
|
System.err.println("Usage: UserUtil password");
|
||||||
System.exit(-1);
|
System.exit(-1);
|
||||||
}
|
}
|
||||||
System.out.println("Encrypted password: " +
|
System.out.println("Encrypted password: " + encryptPassword(args[0]));
|
||||||
encryptPassword(args[0], args[1], true));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user