From a495745c67bd06b65c1fae1fe2c83043c072dc9f Mon Sep 17 00:00:00 2001 From: mdb Date: Sat, 18 Dec 2004 02:33:10 +0000 Subject: [PATCH] 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 --- .../servlet/user/Authenticator.java | 2 +- .../com/samskivert/servlet/user/Password.java | 44 ++++++++++------ .../com/samskivert/servlet/user/User.java | 50 +++++++++++++++---- .../samskivert/servlet/user/UserManager.java | 15 ++++-- .../servlet/user/UserRepository.java | 37 +++----------- .../com/samskivert/servlet/user/UserUtil.java | 24 +++------ 6 files changed, 96 insertions(+), 76 deletions(-) diff --git a/projects/samskivert/src/java/com/samskivert/servlet/user/Authenticator.java b/projects/samskivert/src/java/com/samskivert/servlet/user/Authenticator.java index 39d1bdad..979bb750 100644 --- a/projects/samskivert/src/java/com/samskivert/servlet/user/Authenticator.java +++ b/projects/samskivert/src/java/com/samskivert/servlet/user/Authenticator.java @@ -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; } diff --git a/projects/samskivert/src/java/com/samskivert/servlet/user/Password.java b/projects/samskivert/src/java/com/samskivert/servlet/user/Password.java index c8f1ff13..1fbaf996 100644 --- a/projects/samskivert/src/java/com/samskivert/servlet/user/Password.java +++ b/projects/samskivert/src/java/com/samskivert/servlet/user/Password.java @@ -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 - * must 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. Note: 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; } diff --git a/projects/samskivert/src/java/com/samskivert/servlet/user/User.java b/projects/samskivert/src/java/com/samskivert/servlet/user/User.java index 284aaf8e..1f58764a 100644 --- a/projects/samskivert/src/java/com/samskivert/servlet/user/User.java +++ b/projects/samskivert/src/java/com/samskivert/servlet/user/User.java @@ -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()); } /** diff --git a/projects/samskivert/src/java/com/samskivert/servlet/user/UserManager.java b/projects/samskivert/src/java/com/samskivert/servlet/user/UserManager.java index 439f723f..e0375f75 100644 --- a/projects/samskivert/src/java/com/samskivert/servlet/user/UserManager.java +++ b/projects/samskivert/src/java/com/samskivert/servlet/user/UserManager.java @@ -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); diff --git a/projects/samskivert/src/java/com/samskivert/servlet/user/UserRepository.java b/projects/samskivert/src/java/com/samskivert/servlet/user/UserRepository.java index e6447b2e..1d3543e6 100644 --- a/projects/samskivert/src/java/com/samskivert/servlet/user/UserRepository.java +++ b/projects/samskivert/src/java/com/samskivert/servlet/user/UserRepository.java @@ -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); diff --git a/projects/samskivert/src/java/com/samskivert/servlet/user/UserUtil.java b/projects/samskivert/src/java/com/samskivert/servlet/user/UserUtil.java index 416d5438..288d79bb 100644 --- a/projects/samskivert/src/java/com/samskivert/servlet/user/UserUtil.java +++ b/projects/samskivert/src/java/com/samskivert/servlet/user/UserUtil.java @@ -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])); } }