Commit 39ce623f authored by Matt Tucker's avatar Matt Tucker Committed by matt

Refactored the UserProvider and AuthProvider interfaces to better separate...

Refactored the UserProvider and AuthProvider interfaces to better separate concerns. All password tasks are now handled by AuthProvider. Also did some refactoring work on the JDBC* providers. (JM-751)

git-svn-id: http://svn.igniterealtime.org/svn/repos/wildfire/trunk@4263 b35dd754-fafc-0310-a699-88a17e54d16e
parent 76819e5d
......@@ -208,7 +208,7 @@ public class DbConnectionManager {
}
/**
* Closes a prepared statement. This method should be called within the finally section of
* Closes a statement. This method should be called within the finally section of
* your database logic, as in the following example:
*
* <pre>
......@@ -226,12 +226,12 @@ public class DbConnectionManager {
* }
* } </pre>
*
* @param pstmt the prepared statement.
* @param stmt the statement.
*/
public static void closePreparedStatement(PreparedStatement pstmt) {
public static void closeStatement(Statement stmt) {
try {
if (pstmt != null) {
pstmt.close();
if (stmt != null) {
stmt.close();
}
}
catch (Exception e) {
......@@ -240,7 +240,7 @@ public class DbConnectionManager {
}
/**
* Closes a result set, prepared statement and database connection (returning the connection to
* Closes a result set, statement and database connection (returning the connection to
* the connection pool). This method should be called within the finally section of
* your database logic, as in the following example:
*
......@@ -261,17 +261,17 @@ public class DbConnectionManager {
* ConnectionManager.closeConnection(rs, pstmt, con);
* }</pre>
*
* @param pstmt the prepared statement.
* @param stmt the statement.
* @param con the connection.
*/
public static void closeConnection(ResultSet rs, PreparedStatement pstmt, Connection con) {
public static void closeConnection(ResultSet rs, Statement stmt, Connection con) {
closeResultSet(rs);
closePreparedStatement(pstmt);
closeStatement(stmt);
closeConnection(con);
}
/**
* Closes a prepared statement and database connection (returning the connection to
* Closes a statement and database connection (returning the connection to
* the connection pool). This method should be called within the finally section of
* your database logic, as in the following example:
* <p/>
......@@ -290,13 +290,13 @@ public class DbConnectionManager {
* DbConnectionManager.closeConnection(pstmt, con);
* }</pre>
*
* @param pstmt the prepated statement.
* @param con the connection.
* @param stmt the statement.
* @param con the connection.
*/
public static void closeConnection(PreparedStatement pstmt, Connection con) {
public static void closeConnection(Statement stmt, Connection con) {
try {
if (pstmt != null) {
pstmt.close();
if (stmt != null) {
stmt.close();
}
}
catch (Exception e) {
......@@ -536,9 +536,9 @@ public class DbConnectionManager {
* different JDBC drivers have different capabilities and methods for
* setting large text values.
*
* @param pstmt the PreparedStatement to set the text field in.
* @param pstmt the PreparedStatement to set the text field in.
* @param parameterIndex the index corresponding to the text field.
* @param value the String to set.
* @param value the String to set.
*/
public static void setLargeTextField(PreparedStatement pstmt, int parameterIndex,
String value) throws SQLException {
......
......@@ -14,7 +14,6 @@ package org.jivesoftware.wildfire.auth;
import org.jivesoftware.util.*;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.wildfire.user.UserNotFoundException;
import org.jivesoftware.wildfire.user.UserManager;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
......@@ -40,6 +39,8 @@ public class AuthFactory {
private static AuthProvider authProvider = null;
private static MessageDigest digest;
private static final Object DIGEST_LOCK = new Object();
private static Blowfish cipher = null;
static {
// Load an auth provider.
......@@ -62,6 +63,18 @@ public class AuthFactory {
}
}
/**
* Returns the currently-installed AuthProvider. <b>Warning:</b> in virtually all
* cases the auth provider should not be used directly. Instead, the appropriate
* methods in AuthFactory should be called. Direct access to the auth provider is
* only provided for special-case logic.
*
* @return the current UserProvider.
*/
public static AuthProvider getAuthProvider() {
return authProvider;
}
/**
* Returns true if the currently installed {@link AuthProvider} supports authentication
* using plain-text passwords according to JEP-0078. Plain-text authentication is
......@@ -95,7 +108,7 @@ public class AuthFactory {
*/
public static String getPassword(String username) throws UserNotFoundException,
UnsupportedOperationException {
return UserManager.getUserProvider().getPassword(username);
return authProvider.getPassword(username);
}
/**
......@@ -144,9 +157,80 @@ public class AuthFactory {
* @return the digested result as a hex string.
*/
public static String createDigest(String token, String password) {
synchronized (digest) {
synchronized (DIGEST_LOCK) {
digest.update(token.getBytes());
return StringUtils.encodeHex(digest.digest(password.getBytes()));
}
}
/**
* Returns an encrypted version of the plain-text password. Encryption is performed
* using the Blowfish algorithm. The encryption key is stored as the Jive property
* "passwordKey". If the key is not present, it will be automatically generated.
*
* @param password the plain-text password.
* @return the encrypted password.
* @throws UnsupportedOperationException if encryption/decryption is not possible;
* for example, during setup mode.
*/
public static String encryptPassword(String password) {
Blowfish cipher = getCipher();
if (cipher == null) {
throw new UnsupportedOperationException();
}
return cipher.encryptString(password);
}
/**
* Returns a decrypted version of the encrypted password. Encryption is performed
* using the Blowfish algorithm. The encryption key is stored as the Jive property
* "passwordKey". If the key is not present, it will be automatically generated.
*
* @param encryptedPassword the encrypted password.
* @return the encrypted password.
* @throws UnsupportedOperationException if encryption/decryption is not possible;
* for example, during setup mode.
*/
public static String decryptPassword(String encryptedPassword) {
Blowfish cipher = getCipher();
if (cipher == null) {
throw new UnsupportedOperationException();
}
return cipher.decryptString(encryptedPassword);
}
/**
* Returns a Blowfish cipher that can be used for encrypting and decrypting passwords.
* The encryption key is stored as the Jive property "passwordKey". If it's not present,
* it will be automatically generated.
*
* @return the Blowfish cipher, or <tt>null</tt> if Wildfire is not able to create a Cipher;
* for example, during setup mode.
*/
private static synchronized Blowfish getCipher() {
if (cipher != null) {
return cipher;
}
// Get the password key, stored as a database property. Obviously,
// protecting your database is critical for making the
// encryption fully secure.
String keyString;
try {
keyString = JiveGlobals.getProperty("passwordKey");
if (keyString == null) {
keyString = StringUtils.randomString(15);
JiveGlobals.setProperty("passwordKey", keyString);
// Check to make sure that setting the property worked. It won't work,
// for example, when in setup mode.
if (!keyString.equals(JiveGlobals.getProperty("passwordKey"))) {
return null;
}
}
cipher = new Blowfish(keyString);
}
catch (Exception e) {
Log.error(e);
}
return cipher;
}
}
\ No newline at end of file
......@@ -11,6 +11,8 @@
package org.jivesoftware.wildfire.auth;
import org.jivesoftware.wildfire.user.UserNotFoundException;
/**
* Provider interface for authentication. Users that wish to integrate with
* their own authentication system must implement this class and then register
......@@ -77,4 +79,40 @@ public interface AuthProvider {
*/
void authenticate(String username, String token, String digest)
throws UnauthorizedException;
/**
* Returns the user's password. This method should throw an UnsupportedOperationException
* if this operation is not supported by the backend user store.
*
* @param username the username of the user.
* @return the user's password.
* @throws UserNotFoundException if the given user could not be loaded.
* @throws UnsupportedOperationException if the provider does not
* support the operation (this is an optional operation).
*/
public String getPassword(String username) throws UserNotFoundException,
UnsupportedOperationException;
/**
* Sets the users's password. This method should throw an UnsupportedOperationException
* if this operation is not supported by the backend user store.
*
* @param username the username of the user.
* @param password the new plaintext password for the user.
* @throws UserNotFoundException if the given user could not be loaded.
* @throws UnsupportedOperationException if the provider does not
* support the operation (this is an optional operation).
*/
public void setPassword(String username, String password)
throws UserNotFoundException, UnsupportedOperationException;
/**
* Returns true if this UserProvider is able to retrieve user passwords from
* the backend user store. If this operation is not supported then {@link #getPassword(String)}
* will throw an {@link UnsupportedOperationException} if invoked.
*
* @return true if this UserProvider is able to retrieve user passwords from the
* backend user store.
*/
public boolean supportsPasswordRetrieval();
}
\ No newline at end of file
......@@ -12,7 +12,11 @@
package org.jivesoftware.wildfire.auth;
import org.jivesoftware.wildfire.user.UserNotFoundException;
import org.jivesoftware.wildfire.user.DefaultUserProvider;
import org.jivesoftware.database.DbConnectionManager;
import org.jivesoftware.util.Log;
import org.jivesoftware.util.JiveGlobals;
import java.sql.*;
/**
* Default AuthProvider implementation. It authenticates against the <tt>jiveUser</tt>
......@@ -25,18 +29,16 @@ import org.jivesoftware.wildfire.user.DefaultUserProvider;
*/
public class DefaultAuthProvider implements AuthProvider {
private DefaultUserProvider userProvider;
private static final String LOAD_PASSWORD =
"SELECT password,encryptedPassword FROM jiveUser WHERE username=?";
private static final String UPDATE_PASSWORD =
"UPDATE jiveUser SET password=?, encryptedPassword=? WHERE username=?";
/**
* Constructs a new DefaultAuthProvider.
*/
public DefaultAuthProvider() {
// Create a new default user provider since we need it to get the
// user's password. We always create our own user provider because
// we don't know what user provider is configured for the system and
// the contract of this class is to authenticate against the jiveUser
// database table.
userProvider = new DefaultUserProvider();
}
public void authenticate(String username, String password) throws UnauthorizedException {
......@@ -45,7 +47,7 @@ public class DefaultAuthProvider implements AuthProvider {
}
username = username.trim().toLowerCase();
try {
if (!password.equals(userProvider.getPassword(username))) {
if (!password.equals(getPassword(username))) {
throw new UnauthorizedException();
}
}
......@@ -61,7 +63,7 @@ public class DefaultAuthProvider implements AuthProvider {
}
username = username.trim().toLowerCase();
try {
String password = userProvider.getPassword(username);
String password = getPassword(username);
String anticipatedDigest = AuthFactory.createDigest(token, password);
if (!digest.equalsIgnoreCase(anticipatedDigest)) {
throw new UnauthorizedException();
......@@ -80,4 +82,93 @@ public class DefaultAuthProvider implements AuthProvider {
public boolean isDigestSupported() {
return true;
}
public String getPassword(String username) throws UserNotFoundException {
if (!supportsPasswordRetrieval()) {
// Reject the operation since the provider is read-only
throw new UnsupportedOperationException();
}
Connection con = null;
PreparedStatement pstmt = null;
try {
con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement(LOAD_PASSWORD);
pstmt.setString(1, username);
ResultSet rs = pstmt.executeQuery();
if (!rs.next()) {
throw new UserNotFoundException(username);
}
String plainText = rs.getString(1);
String encrypted = rs.getString(2);
if (encrypted != null) {
try {
return AuthFactory.decryptPassword(encrypted);
}
catch (UnsupportedOperationException uoe) {
// Ignore and return plain password instead.
}
}
return plainText;
}
catch (SQLException sqle) {
throw new UserNotFoundException(sqle);
}
finally {
try { if (pstmt != null) pstmt.close(); }
catch (Exception e) { Log.error(e); }
try { if (con != null) con.close(); }
catch (Exception e) { Log.error(e); }
}
}
public void setPassword(String username, String password) throws UserNotFoundException {
// Determine if the password should be stored as plain text or encrypted.
boolean usePlainPassword = JiveGlobals.getBooleanProperty("user.usePlainPassword");
String encryptedPassword = null;
if (!usePlainPassword) {
try {
encryptedPassword = AuthFactory.encryptPassword(password);
// Set password to null so that it's inserted that way.
password = null;
}
catch (UnsupportedOperationException uoe) {
// Encryption may fail. In that case, ignore the error and
// the plain password will be stored.
}
}
Connection con = null;
PreparedStatement pstmt = null;
try {
con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement(UPDATE_PASSWORD);
if (password == null) {
pstmt.setNull(1, Types.VARCHAR);
}
else {
pstmt.setString(1, password);
}
if (encryptedPassword == null) {
pstmt.setNull(2, Types.VARCHAR);
}
else {
pstmt.setString(2, encryptedPassword);
}
pstmt.setString(3, username);
pstmt.executeUpdate();
}
catch (SQLException sqle) {
throw new UserNotFoundException(sqle);
}
finally {
try { if (pstmt != null) pstmt.close(); }
catch (Exception e) { Log.error(e); }
try { if (con != null) con.close(); }
catch (Exception e) { Log.error(e); }
}
}
public boolean supportsPasswordRetrieval() {
return true;
}
}
\ No newline at end of file
......@@ -13,6 +13,7 @@ package org.jivesoftware.wildfire.auth;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.ClassUtils;
import org.jivesoftware.util.Log;
import org.jivesoftware.wildfire.user.UserNotFoundException;
import java.util.Set;
import java.util.HashSet;
......@@ -232,4 +233,19 @@ public class HybridAuthProvider implements AuthProvider {
throw new UnauthorizedException("Digest authentication not supported.");
}
public String getPassword(String username)
throws UserNotFoundException, UnsupportedOperationException
{
throw new UnsupportedOperationException();
}
public void setPassword(String username, String password)
throws UserNotFoundException, UnsupportedOperationException
{
throw new UnsupportedOperationException();
}
public boolean supportsPasswordRetrieval() {
return false;
}
}
\ No newline at end of file
......@@ -14,6 +14,7 @@ import org.jivesoftware.util.Log;
import org.jivesoftware.util.StringUtils;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.wildfire.user.*;
import org.jivesoftware.database.DbConnectionManager;
import java.sql.DriverManager;
import java.sql.Connection;
......@@ -40,11 +41,12 @@ import java.sql.SQLException;
* You'll also need to set your JDBC driver, connection string, and SQL statements:
*
* <pre>
* &lt;jdbcProvider&gt;
* &lt;driver&gt;com.mysql.jdbc.Driver&lt;/driver&gt;
* &lt;connectionString&gt;jdbc:mysql://localhost/dbname?user=username&amp;password=secret&lt;/connectionString&gt;
* &lt;/jdbcProvider&gt;
*
* &lt;jdbcAuthProvider&gt;
* &lt;jdbcDriver&gt;
* &lt;className&gt;com.mysql.jdbc.Driver&lt;/className&gt;
* &lt;/jdbcDrivec&gt;
* &lt;jdbcConnString&gt;jdbc:mysql:://localhost/dbname?user=username&amp;amp;password=secret&lt;/jdbcConnString&gt;
* &lt;passwordSQL&gt;SELECT password FROM user_account WHERE username=?&lt;passwordSQL&gt;
* &lt;passwordType&gt;plain&lt;passwordType&gt;
* &lt;/jdbcAuthProvider&gt;</pre>
......@@ -60,7 +62,8 @@ import java.sql.SQLException;
*/
public class JDBCAuthProvider implements AuthProvider {
private String jdbcConnectionString;
private String connectionString;
private String passwordSQL;
private PasswordType passwordType = PasswordType.plain;
......@@ -68,8 +71,8 @@ public class JDBCAuthProvider implements AuthProvider {
* Constructs a new JDBC authentication provider.
*/
public JDBCAuthProvider() {
// Load the JDBC driver
String jdbcDriver = JiveGlobals.getXMLProperty("jdbcAuthProvider.jdbcDriver.className");
// Load the JDBC driver and connection string.
String jdbcDriver = JiveGlobals.getXMLProperty("jdbcProvider.jdbcDriver.className");
try {
Class.forName(jdbcDriver).newInstance();
}
......@@ -77,9 +80,9 @@ public class JDBCAuthProvider implements AuthProvider {
Log.error("Unable to load JDBC driver: " + jdbcDriver, e);
return;
}
connectionString = JiveGlobals.getXMLProperty("jdbcProvider.jdbcConnString");
// Grab connection string and SQL config.
jdbcConnectionString = JiveGlobals.getXMLProperty("jdbcAuthProvider.jdbcConnString");
// Load SQL statements.
passwordSQL = JiveGlobals.getXMLProperty("jdbcAuthProvider.passwordSQL");
passwordType = PasswordType.plain;
try {
......@@ -95,7 +98,13 @@ public class JDBCAuthProvider implements AuthProvider {
throw new UnauthorizedException();
}
username = username.trim().toLowerCase();
String userPassword = getPassword(username);
String userPassword;
try {
userPassword = getPassword(username);
}
catch (UserNotFoundException unfe) {
throw new UnauthorizedException();
}
// If the user's password doesn't match the password passed in, authentication
// should fail.
if (passwordType == PasswordType.md5) {
......@@ -123,7 +132,13 @@ public class JDBCAuthProvider implements AuthProvider {
throw new UnauthorizedException();
}
username = username.trim().toLowerCase();
String password = getPassword(username);
String password;
try {
password = getPassword(username);
}
catch (UserNotFoundException unfe) {
throw new UnauthorizedException();
}
String anticipatedDigest = AuthFactory.createDigest(token, password);
if (!digest.equalsIgnoreCase(anticipatedDigest)) {
throw new UnauthorizedException();
......@@ -143,71 +158,71 @@ public class JDBCAuthProvider implements AuthProvider {
return (passwordSQL != null && passwordType == PasswordType.plain);
}
/**
* Indicates how the password is stored.
*/
public enum PasswordType {
/**
* The password is stored as plain text.
*/
plain,
/**
* The password is stored as a hex-encoded MD5 hash.
*/
md5,
/**
* The password is stored as a hex-encoded SHA-1 hash.
*/
sha1
}
private String getPassword(String username) throws UnauthorizedException {
public String getPassword(String username) throws UserNotFoundException,
UnsupportedOperationException
{
String password = null;
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
con = DriverManager.getConnection(jdbcConnectionString);
con = DriverManager.getConnection(connectionString);
pstmt = con.prepareStatement(passwordSQL);
pstmt.setString(1, username);
ResultSet rs = pstmt.executeQuery();
rs = pstmt.executeQuery();
// If the query had no results, the username and password
// did not match a user record. Therefore, throw an exception.
if (!rs.next()) {
throw new UnauthorizedException();
throw new UserNotFoundException();
}
password = rs.getString(1);
rs.close();
}
catch (SQLException e) {
Log.error("Exception in JDBCAuthProvider", e);
throw new UnauthorizedException();
throw new UserNotFoundException();
}
finally {
try {
if (pstmt != null) {
pstmt.close();
}
}
catch (Exception e) {
Log.error(e);
}
try {
if (con != null) {
con.close();
}
}
catch (Exception e) {
Log.error(e);
}
DbConnectionManager.closeConnection(rs, pstmt, con);
}
return password;
}
public void setPassword(String username, String password)
throws UserNotFoundException, UnsupportedOperationException {
}
public boolean supportsPasswordRetrieval() {
return false;
}
/**
* Indicates how the password is stored.
*/
public enum PasswordType {
/**
* The password is stored as plain text.
*/
plain,
/**
* The password is stored as a hex-encoded MD5 hash.
*/
md5,
/**
* The password is stored as a hex-encoded SHA-1 hash.
*/
sha1
}
/**
* Checks to see if the user exists; if not, a new user is created.
*
* @param username the username.
*/
private static void createUser(String username) {
// See if the user exists in the database. If not, automatically create them.
UserManager userManager = UserManager.getInstance();
......
......@@ -164,4 +164,18 @@ public class NativeAuthProvider implements AuthProvider {
public boolean isDigestSupported() {
return false;
}
public String getPassword(String username)
throws UserNotFoundException, UnsupportedOperationException
{
throw new UnsupportedOperationException();
}
public void setPassword(String username, String password) throws UserNotFoundException {
throw new UnsupportedOperationException();
}
public boolean supportsPasswordRetrieval() {
return false;
}
}
......@@ -204,4 +204,18 @@ public class POP3AuthProvider implements AuthProvider {
public boolean isDigestSupported() {
return false;
}
public String getPassword(String username)
throws UserNotFoundException, UnsupportedOperationException
{
throw new UnsupportedOperationException();
}
public void setPassword(String username, String password) throws UserNotFoundException {
throw new UnsupportedOperationException();
}
public boolean supportsPasswordRetrieval() {
return false;
}
}
\ No newline at end of file
......@@ -14,6 +14,7 @@ package org.jivesoftware.wildfire.ldap;
import org.jivesoftware.util.*;
import org.jivesoftware.wildfire.auth.AuthProvider;
import org.jivesoftware.wildfire.auth.UnauthorizedException;
import org.jivesoftware.wildfire.user.UserNotFoundException;
/**
* Implementation of auth provider interface for LDAP authentication service plug-in.
......@@ -105,7 +106,17 @@ public class LdapAuthProvider implements AuthProvider {
throw new UnsupportedOperationException("Digest authentication not currently supported.");
}
public void updatePassword(String username, String password) throws UnsupportedOperationException {
throw new UnsupportedOperationException("Cannot update password in LDAP");
public String getPassword(String username) throws UserNotFoundException,
UnsupportedOperationException
{
throw new UnsupportedOperationException();
}
public void setPassword(String username, String password) throws UserNotFoundException {
throw new UnsupportedOperationException();
}
public boolean supportsPasswordRetrieval() {
return false;
}
}
\ No newline at end of file
......@@ -297,16 +297,6 @@ public class LdapUserProvider implements UserProvider {
return new UserCollection(usernames.toArray(new String[usernames.size()]));
}
public String getPassword(String username) throws UserNotFoundException,
UnsupportedOperationException
{
throw new UnsupportedOperationException();
}
public void setPassword(String username, String password) throws UserNotFoundException {
throw new UnsupportedOperationException();
}
public void setName(String username, String name) throws UserNotFoundException {
throw new UnsupportedOperationException();
}
......@@ -491,8 +481,4 @@ public class LdapUserProvider implements UserProvider {
public boolean isReadOnly() {
return true;
}
public boolean supportsPasswordRetrieval() {
return false;
}
}
\ No newline at end of file
......@@ -22,7 +22,6 @@ import org.jivesoftware.wildfire.auth.AuthFactory;
import org.jivesoftware.wildfire.auth.AuthToken;
import org.jivesoftware.wildfire.auth.UnauthorizedException;
import org.jivesoftware.wildfire.server.IncomingServerSession;
import org.jivesoftware.wildfire.user.UserManager;
import org.xmpp.packet.JID;
import javax.net.ssl.SSLPeerUnverifiedException;
......@@ -126,7 +125,7 @@ public class SASLAuthentication {
if (mech.equals("CRAM-MD5") || mech.equals("DIGEST-MD5")) {
// Check if the user provider in use supports passwords retrieval. Accessing
// to the users passwords will be required by the CallbackHandler
if (!UserManager.getUserProvider().supportsPasswordRetrieval()) {
if (!AuthFactory.getAuthProvider().supportsPasswordRetrieval()) {
continue;
}
}
......
......@@ -13,6 +13,7 @@ package org.jivesoftware.wildfire.user;
import org.jivesoftware.database.DbConnectionManager;
import org.jivesoftware.util.*;
import org.jivesoftware.wildfire.auth.AuthFactory;
import java.sql.*;
import java.util.*;
......@@ -54,39 +55,6 @@ public class DefaultUserProvider implements UserProvider {
"UPDATE jiveUser SET creationDate=? WHERE username=?";
private static final String UPDATE_MODIFICATION_DATE =
"UPDATE jiveUser SET modificationDate=? WHERE username=?";
private static final String LOAD_PASSWORD =
"SELECT password,encryptedPassword FROM jiveUser WHERE username=?";
private static final String UPDATE_PASSWORD =
"UPDATE jiveUser SET password=?, encryptedPassword=? WHERE username=?";
private static Blowfish cipher = null;
private static synchronized Blowfish getCipher() {
if (cipher != null) {
return cipher;
}
// Get the password key, stored as a database property. Obviously,
// protecting your database is critical for making the
// encryption fully secure.
String keyString;
try {
keyString = JiveGlobals.getProperty("passwordKey");
if (keyString == null) {
keyString = StringUtils.randomString(15);
JiveGlobals.setProperty("passwordKey", keyString);
// Check to make sure that setting the property worked. It won't work,
// for example, when in setup mode.
if (!keyString.equals(JiveGlobals.getProperty("passwordKey"))) {
return null;
}
}
cipher = new Blowfish(keyString);
}
catch (Exception e) {
Log.error(e);
}
return cipher;
}
public User loadUser(String username) throws UserNotFoundException {
Connection con = null;
......@@ -137,12 +105,15 @@ public class DefaultUserProvider implements UserProvider {
boolean usePlainPassword = JiveGlobals.getBooleanProperty("user.usePlainPassword");
String encryptedPassword = null;
if (!usePlainPassword) {
Blowfish cipher = getCipher();
if (cipher != null) {
encryptedPassword = cipher.encryptString(password);
try {
encryptedPassword = AuthFactory.encryptPassword(password);
// Set password to null so that it's inserted that way.
password = null;
}
catch (UnsupportedOperationException uoe) {
// Encrypting the password may have failed if in setup mode. Therefore,
// use the plain password.
}
}
Date now = new Date();
......@@ -411,91 +382,6 @@ public class DefaultUserProvider implements UserProvider {
}
}
public String getPassword(String username) throws UserNotFoundException {
if (!supportsPasswordRetrieval()) {
// Reject the operation since the provider is read-only
throw new UnsupportedOperationException();
}
Connection con = null;
PreparedStatement pstmt = null;
try {
con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement(LOAD_PASSWORD);
pstmt.setString(1, username);
ResultSet rs = pstmt.executeQuery();
if (!rs.next()) {
throw new UserNotFoundException(username);
}
String plainText = rs.getString(1);
String encrypted = rs.getString(2);
if (encrypted != null) {
Blowfish cipher = getCipher();
if (cipher != null) {
return cipher.decryptString(encrypted);
}
}
return plainText;
}
catch (SQLException sqle) {
throw new UserNotFoundException(sqle);
}
finally {
try { if (pstmt != null) pstmt.close(); }
catch (Exception e) { Log.error(e); }
try { if (con != null) con.close(); }
catch (Exception e) { Log.error(e); }
}
}
public void setPassword(String username, String password) throws UserNotFoundException {
if (isReadOnly()) {
// Reject the operation since the provider is read-only
throw new UnsupportedOperationException();
}
// Determine if the password should be stored as plain text or encrypted.
boolean usePlainPassword = JiveGlobals.getBooleanProperty("user.usePlainPassword");
String encryptedPassword = null;
if (!usePlainPassword) {
Blowfish cipher = getCipher();
if (cipher != null) {
encryptedPassword = cipher.encryptString(password);
// Set password to null so that it's inserted that way.
password = null;
}
}
Connection con = null;
PreparedStatement pstmt = null;
try {
con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement(UPDATE_PASSWORD);
if (password == null) {
pstmt.setNull(1, Types.VARCHAR);
}
else {
pstmt.setString(1, password);
}
if (encryptedPassword == null) {
pstmt.setNull(2, Types.VARCHAR);
}
else {
pstmt.setString(2, encryptedPassword);
}
pstmt.setString(3, username);
pstmt.executeUpdate();
}
catch (SQLException sqle) {
throw new UserNotFoundException(sqle);
}
finally {
try { if (pstmt != null) pstmt.close(); }
catch (Exception e) { Log.error(e); }
try { if (con != null) con.close(); }
catch (Exception e) { Log.error(e); }
}
}
public Set<String> getSearchFields() throws UnsupportedOperationException {
return new LinkedHashSet<String>(Arrays.asList("Username", "Name", "Email"));
}
......@@ -637,8 +523,4 @@ public class DefaultUserProvider implements UserProvider {
public boolean isReadOnly() {
return false;
}
public boolean supportsPasswordRetrieval() {
return true;
}
}
\ No newline at end of file
......@@ -24,11 +24,5 @@ package org.jivesoftware.wildfire.user;
*/
public class NativeUserProvider extends DefaultUserProvider {
public void setPassword(String username, String password) throws UserNotFoundException {
throw new UnsupportedOperationException();
}
public boolean supportsPasswordRetrieval() {
return false;
}
}
......@@ -38,12 +38,4 @@ public class POP3UserProvider extends DefaultUserProvider {
public void setEmail(String username, String email) throws UserNotFoundException {
throw new UnsupportedOperationException();
}
public void setPassword(String username, String password) throws UserNotFoundException {
throw new UnsupportedOperationException();
}
public boolean supportsPasswordRetrieval() {
return false;
}
}
\ No newline at end of file
......@@ -16,6 +16,7 @@ import org.jivesoftware.util.CacheSizes;
import org.jivesoftware.util.Cacheable;
import org.jivesoftware.util.Log;
import org.jivesoftware.wildfire.XMPPServer;
import org.jivesoftware.wildfire.auth.AuthFactory;
import org.jivesoftware.wildfire.event.UserEventDispatcher;
import org.jivesoftware.wildfire.roster.Roster;
......@@ -136,14 +137,17 @@ public class User implements Cacheable {
}
try {
UserManager.getUserProvider().setPassword(username, password);
AuthFactory.getAuthProvider().setPassword(username, password);
// Fire event.
Map params = new HashMap();
Map<String,Object> params = new HashMap<String,Object>();
params.put("type", "passwordModified");
UserEventDispatcher.dispatchEvent(this, UserEventDispatcher.EventType.user_modified,
params);
}
catch (UnsupportedOperationException uoe) {
Log.error(uoe);
}
catch (UserNotFoundException unfe) {
Log.error(unfe);
}
......@@ -164,7 +168,7 @@ public class User implements Cacheable {
this.name = name;
// Fire event.
Map params = new HashMap();
Map<String,String> params = new HashMap<String,String>();
params.put("type", "nameModified");
params.put("originalValue", originalName);
UserEventDispatcher.dispatchEvent(this, UserEventDispatcher.EventType.user_modified,
......@@ -189,7 +193,7 @@ public class User implements Cacheable {
UserManager.getUserProvider().setEmail(username, email);
this.email = email;
// Fire event.
Map params = new HashMap();
Map<String,String> params = new HashMap<String,String>();
params.put("type", "emailModified");
params.put("originalValue", originalEmail);
UserEventDispatcher.dispatchEvent(this, UserEventDispatcher.EventType.user_modified,
......@@ -215,7 +219,7 @@ public class User implements Cacheable {
this.creationDate = creationDate;
// Fire event.
Map params = new HashMap();
Map<String,Object> params = new HashMap<String,Object>();
params.put("type", "creationDateModified");
params.put("originalValue", originalCreationDate);
UserEventDispatcher.dispatchEvent(this, UserEventDispatcher.EventType.user_modified,
......@@ -241,7 +245,7 @@ public class User implements Cacheable {
this.modificationDate = modificationDate;
// Fire event.
Map params = new HashMap();
Map<String,Object> params = new HashMap<String,Object>();
params.put("type", "nameModified");
params.put("originalValue", originalModificationDate);
UserEventDispatcher.dispatchEvent(this, UserEventDispatcher.EventType.user_modified,
......@@ -325,12 +329,12 @@ public class User implements Cacheable {
private class PropertiesMap extends AbstractMap {
public Object put(Object key, Object value) {
Map eventParams = new HashMap();
Map<String,Object> eventParams = new HashMap<String,Object>();
Object answer;
String keyString = (String) key;
synchronized (keyString.intern()) {
if (properties.containsKey(key)) {
String originalValue = properties.get(key);
if (properties.containsKey(keyString)) {
String originalValue = properties.get(keyString);
answer = properties.put(keyString, (String)value);
updateProperty(keyString, (String)value);
// Configure event.
......@@ -389,7 +393,7 @@ public class User implements Cacheable {
deleteProperty(key);
iter.remove();
// Fire event.
Map params = new HashMap();
Map<String,Object> params = new HashMap<String,Object>();
params.put("type", "propertyDeleted");
params.put("propertyKey", key);
UserEventDispatcher.dispatchEvent(User.this,
......
......@@ -98,32 +98,6 @@ public interface UserProvider {
*/
public Collection<User> getUsers(int startIndex, int numResults);
/**
* Returns the user's password. This method should throw an UnsupportedOperationException
* if this operation is not supported by the backend user store.
*
* @param username the username of the user.
* @return the user's password.
* @throws UserNotFoundException if the given user could not be loaded.
* @throws UnsupportedOperationException if the provider does not
* support the operation (this is an optional operation).
*/
public String getPassword(String username) throws UserNotFoundException,
UnsupportedOperationException;
/**
* Sets the users's password. This method should throw an UnsupportedOperationException
* if this operation is not supported by the backend user store.
*
* @param username the username of the user.
* @param password the new plaintext password for the user.
* @throws UserNotFoundException if the given user could not be loaded.
* @throws UnsupportedOperationException if the provider does not
* support the operation (this is an optional operation).
*/
public void setPassword(String username, String password)
throws UserNotFoundException, UnsupportedOperationException;
/**
* Sets the user's name. This method should throw an UnsupportedOperationException
* if this operation is not supported by the backend user store.
......@@ -235,14 +209,4 @@ public interface UserProvider {
* @return true if the user provider is read-only.
*/
public boolean isReadOnly();
/**
* Returns true if this UserProvider is able to retrieve user passwords from
* the backend user store. If this operation is not supported then {@link #getPassword(String)}
* will throw an {@link UnsupportedOperationException} if invoked.
*
* @return true if this UserProvider is able to retrieve user passwords from the
* backend user store.
*/
public boolean supportsPasswordRetrieval();
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment