package org.cagrid.dorian.service.idp; import gov.nih.nci.cagrid.common.Utils; import java.io.IOException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.ArrayList; import java.util.Date; import java.util.List; import org.apache.commons.lang.StringUtils; import org.cagrid.core.common.FaultHelper; import org.cagrid.dorian.common.AuditConstants; import org.cagrid.dorian.model.exceptions.DorianInternalException; import org.cagrid.dorian.model.exceptions.InvalidUserPropertyException; import org.cagrid.dorian.model.exceptions.NoSuchUserException; import org.cagrid.dorian.model.exceptions.PermissionDeniedException; import org.cagrid.dorian.model.idp.CountryCode; import org.cagrid.dorian.model.idp.IdentityProviderAudit; import org.cagrid.dorian.model.idp.LocalUser; import org.cagrid.dorian.model.idp.LocalUserFilter; import org.cagrid.dorian.model.idp.LocalUserRole; import org.cagrid.dorian.model.idp.LocalUserStatus; import org.cagrid.dorian.model.idp.PasswordSecurity; import org.cagrid.dorian.model.idp.PasswordStatus; import org.cagrid.dorian.model.idp.StateCode; import org.cagrid.dorian.service.util.AddressValidator; import org.cagrid.dorian.service.util.Crypt; import org.cagrid.gaards.authentication.BasicAuthentication; import org.cagrid.gaards.authentication.Credential; import org.cagrid.gaards.authentication.faults.CredentialNotSupportedException; import org.cagrid.gaards.authentication.faults.InvalidCredentialException; import org.cagrid.tools.database.Database; import org.cagrid.tools.events.EventManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author <A href="mailto:langella@bmi.osu.edu">Stephen Langella </A> * @author <A href="mailto:oster@bmi.osu.edu">Scott Oster </A> * @author <A href="mailto:hastings@bmi.osu.edu">Shannon Hastings </A> * @version $Id: ArgumentManagerTable.java,v 1.2 2004/10/15 16:35:16 langella * Exp $ */ public class UserManager { public final static String PASSWORD_ERROR_MESSAGE = "The uid or password is incorrect."; public final static int SYSTEM_MAX_PASSWORD_LENGTH = 30; public final static String SYSTEM_MAX_PASSWORD_ERROR_PREFIX = "Unacceptable password, the length of the password cannot exceed the maximum system password length of "; public static String INVALID_PASSWORD_MESSAGE = "Invalid password, a valid password CANNOT contain a dictionary word and MUST contain at least one upper case letter, at least one lower case letter, at least one number, and at least one symbol (~!@#$%^&*()_-+={}[]|:;<>,.?)"; public static String ADMIN_USER_ID = "dorian"; public static String ADMIN_PASSWORD = "DorianAdmin$1"; public static final String IDP_USERS_TABLE = "idp_users"; private final static Logger log = LoggerFactory .getLogger(UserManager.class); private Database db; private boolean dbBuilt = false; private IdentityProviderProperties conf; private PasswordSecurityManager passwordSecurityManager; public UserManager(Database db, IdentityProviderProperties conf) throws DorianInternalException { this.db = db; this.conf = conf; this.passwordSecurityManager = new PasswordSecurityManager(db, conf.getPasswordSecurityPolicy()); } public LocalUser authenticateAndVerifyUser(Credential credential, EventManager eventManager) throws DorianInternalException, InvalidCredentialException, CredentialNotSupportedException { if (credential.getClass().equals(BasicAuthentication.class)) { BasicAuthentication cred = (BasicAuthentication) credential; try { LocalUser u = getUser(cred.getUserId()); PasswordSecurity entry = this.passwordSecurityManager.getEntry( u.getUserId(), true); PasswordStatus status = entry.getPasswordStatus(); String suppliedPassword = cred.getPassword(); if (suppliedPassword.length() > SYSTEM_MAX_PASSWORD_LENGTH) { InvalidCredentialException fault = FaultHelper .createFaultException( InvalidCredentialException.class, PASSWORD_ERROR_MESSAGE); throw fault; } if (status.equals(PasswordStatus.VALID)) { String digest = null; boolean crypt = false; if ((entry.getDigestAlgorithm() == null) || (entry.getDigestAlgorithm() .equals(PasswordSecurityManager.CRYPT_DIGEST_ALGORITHM))) { crypt = true; digest = Crypt.crypt(suppliedPassword); } else if (entry.getDigestAlgorithm().equals( PasswordSecurityManager.PASSWORD_DIGEST_ALGORITHM)) { try { digest = PasswordSecurityManager.encrypt( suppliedPassword, entry.getDigestSalt()); } catch (Exception e) { log.error(e.getMessage(), e); DorianInternalException fault = FaultHelper .createFaultException( DorianInternalException.class, "Unexpected error calculating password digest!!!"); throw fault; } } else { DorianInternalException fault = FaultHelper .createFaultException( DorianInternalException.class, "Could not obtain password digest, unknown digest algorithm (" + entry.getDigestAlgorithm() + ")!!!"); throw fault; } if (!u.getPassword().equals(digest)) { this.passwordSecurityManager .reportInvalidLoginAttempt(u.getUserId()); PasswordSecurity ps = this.passwordSecurityManager .getEntry(u.getUserId()); if (ps.getPasswordStatus() .equals(PasswordStatus.LOCKED)) { Date unlock = new Date(ps.getLockoutExpiration()); if (eventManager != null) { eventManager .logEvent( u.getUserId(), AuditConstants.SYSTEM_ID, IdentityProviderAudit.LOCAL_ACCOUNT_LOCKED .value(), "Account locked because of to many consecutive invalid logins (" + conf.getPasswordSecurityPolicy() .getConsecutiveInvalidLogins() + "). The lock will expire on " + unlock.toString() + "."); } } else if (ps.getPasswordStatus().equals( PasswordStatus.LOCKED_UNTIL_CHANGED)) { if (eventManager != null) { eventManager .logEvent( u.getUserId(), AuditConstants.SYSTEM_ID, IdentityProviderAudit.LOCAL_ACCOUNT_LOCKED .value(), "Account locked because of to many total invalid logins (" + conf.getPasswordSecurityPolicy() .getTotalInvalidLogins() + "). The lock will not expire until the account password is reset by an administrator."); } } InvalidCredentialException fault = FaultHelper .createFaultException( InvalidCredentialException.class, PASSWORD_ERROR_MESSAGE); throw fault; } else { this.passwordSecurityManager .reportSuccessfulLoginAttempt(u.getUserId()); if (crypt) { u.setPassword(suppliedPassword); try { updateUser(u); if (eventManager != null) { eventManager .logEvent( u.getUserId(), AuditConstants.SYSTEM_ID, IdentityProviderAudit.LOCAL_ACCOUNT_UPDATED .value(), "Password encryption algorithm updated from crypt to " + PasswordSecurityManager.PASSWORD_DIGEST_ALGORITHM + "."); } } catch (Exception e) { log.error(e.getMessage(), e); DorianInternalException fault = FaultHelper .createFaultException( DorianInternalException.class, "Unexpected error upgrading password digest."); throw fault; } } } } else if (status.equals(PasswordStatus.LOCKED_UNTIL_CHANGED)) { InvalidCredentialException fault = FaultHelper .createFaultException( InvalidCredentialException.class, "This account has been locked because the maximum number of invalid logins has been exceeded, please contact an administrator to have your password reset."); throw fault; } else if (status.equals(PasswordStatus.LOCKED)) { InvalidCredentialException fault = FaultHelper .createFaultException( InvalidCredentialException.class, "This account has been temporarily locked because the maximum number of consecutive invalid logins has been exceeded."); throw fault; } else { InvalidCredentialException fault = FaultHelper .createFaultException( InvalidCredentialException.class, "Unexpected security status code received."); throw fault; } try { verifyUser(u); } catch (PermissionDeniedException pde) { InvalidCredentialException fault = FaultHelper .createFaultException( InvalidCredentialException.class, FaultHelper.getMessage(pde)); throw fault; } return u; } catch (NoSuchUserException nsue) { InvalidCredentialException fault = FaultHelper .createFaultException(InvalidCredentialException.class, "User Id or password is incorrect"); throw fault; } catch (DorianInternalException e) { String msg = FaultHelper.getMessage(e); log.error(msg, e); DorianInternalException fault = FaultHelper .createFaultException(DorianInternalException.class, "An unexpected database error occurred."); FaultHelper.addCause(fault, e.getFault()); throw fault; } } else { CredentialNotSupportedException fault = FaultHelper .createFaultException( CredentialNotSupportedException.class, "The credential provided is not supported."); throw fault; } } public void verifyUser(LocalUser u) throws DorianInternalException, PermissionDeniedException { if (!u.getStatus().equals(LocalUserStatus.ACTIVE)) { if (u.getStatus().equals(LocalUserStatus.SUSPENDED)) { PermissionDeniedException fault = FaultHelper .createFaultException(PermissionDeniedException.class, "The account has been suspended."); throw fault; } else if (u.getStatus().equals(LocalUserStatus.REJECTED)) { PermissionDeniedException fault = FaultHelper .createFaultException(PermissionDeniedException.class, "The application for the account was rejected."); throw fault; } else if (u.getStatus().equals(LocalUserStatus.PENDING)) { PermissionDeniedException fault = FaultHelper .createFaultException(PermissionDeniedException.class, "The application for this account has not yet been reviewed."); throw fault; } else { PermissionDeniedException fault = FaultHelper .createFaultException(PermissionDeniedException.class, "Unknown Reason"); throw fault; } } } private void validateSpecifiedField(String fieldName, String name) throws InvalidUserPropertyException { try { AddressValidator.validateField(fieldName, name); } catch (Exception e) { InvalidUserPropertyException fault = FaultHelper .createFaultException(InvalidUserPropertyException.class, e.getMessage()); throw fault; } } private void validateEmail(String email) throws InvalidUserPropertyException { try { AddressValidator.validateEmail(email); } catch (Exception e) { InvalidUserPropertyException fault = FaultHelper .createFaultException(InvalidUserPropertyException.class, e.getMessage()); throw fault; } } private void validatePassword(LocalUser user) throws DorianInternalException, InvalidUserPropertyException { String password = user.getPassword(); if (password == null) { InvalidUserPropertyException fault = FaultHelper .createFaultException(InvalidUserPropertyException.class, "Unacceptable password, the length of the password must be between " + conf.getPasswordSecurityPolicy() .getMinPasswordLength() + " and " + conf.getPasswordSecurityPolicy() .getMaxPasswordLength() + " characters."); throw fault; } else if (password.length() > SYSTEM_MAX_PASSWORD_LENGTH) { InvalidUserPropertyException fault = FaultHelper .createFaultException(InvalidUserPropertyException.class, SYSTEM_MAX_PASSWORD_ERROR_PREFIX + +SYSTEM_MAX_PASSWORD_LENGTH + "."); throw fault; } else if ((conf.getPasswordSecurityPolicy().getMinPasswordLength() > password .length()) || (conf.getPasswordSecurityPolicy().getMaxPasswordLength() < password .length())) { InvalidUserPropertyException fault = FaultHelper .createFaultException(InvalidUserPropertyException.class, "Unacceptable password, the length of the password must be between " + conf.getPasswordSecurityPolicy() .getMinPasswordLength() + " and " + conf.getPasswordSecurityPolicy() .getMaxPasswordLength() + " characters."); throw fault; } else { boolean hasDictionaryWord = true; try { hasDictionaryWord = DictionaryCheck .doesStringContainDictionaryWord(password); } catch (IOException e) { log.error(e.getMessage(), e); DorianInternalException fault = FaultHelper .createFaultException( DorianInternalException.class, "Unexpected error validating the user's password, please contact an administrator."); throw fault; } boolean hasCapital = PasswordUtils.hasCapitalLetter(password); boolean hasLowerCase = PasswordUtils.hasLowerCaseLetter(password); boolean hasNumber = PasswordUtils.hasNumber(password); boolean hasSymbol = PasswordUtils.hasSymbol(password); if ((!hasCapital) || (!hasLowerCase) || (!hasNumber) || (!hasSymbol) || (hasDictionaryWord)) { InvalidUserPropertyException fault = FaultHelper .createFaultException( InvalidUserPropertyException.class, INVALID_PASSWORD_MESSAGE); throw fault; } } } private void validateUserId(LocalUser user) throws InvalidUserPropertyException { String uid = user.getUserId(); if ((uid == null) || (conf.getMinUserIdLength() > uid.length()) || (conf.getMaxUserIdLength() < uid.length())) { InvalidUserPropertyException fault = FaultHelper .createFaultException(InvalidUserPropertyException.class, "Unacceptable User ID, the length of the user id must be between " + conf.getMinUserIdLength() + " and " + conf.getMaxUserIdLength() + " characters."); throw fault; } } private void validateUser(LocalUser user) throws DorianInternalException, InvalidUserPropertyException { validateUserId(user); validatePassword(user); validateSpecifiedField("First Name", user.getFirstName()); validateSpecifiedField("Last Name", user.getLastName()); validateSpecifiedField("Address", user.getAddress()); validateSpecifiedField("City", user.getCity()); validateSpecifiedField("Organization", user.getOrganization()); validateSpecifiedField("Zip Code", user.getZipcode()); validateSpecifiedField("Phone", user.getPhoneNumber()); validateEmail(user.getEmail()); } public synchronized void addUser(LocalUser user) throws DorianInternalException, InvalidUserPropertyException { this.buildDatabase(); this.validateUser(user); if (userExists(user.getUserId())) { InvalidUserPropertyException fault = FaultHelper .createFaultException(InvalidUserPropertyException.class, "The user " + user.getUserId() + " already exists."); throw fault; } Connection c = null; try { String passwordSalt = PasswordSecurityManager.getRandomSalt(); String passwordDigest = PasswordSecurityManager.encrypt( user.getPassword(), passwordSalt); c = db.getConnection(); PreparedStatement ps = c .prepareStatement("INSERT INTO " + IDP_USERS_TABLE + " SET UID = ?, EMAIL= ?, PASSWORD= ?, FIRST_NAME= ?, LAST_NAME= ?, ORGANIZATION= ?, ADDRESS= ?, ADDRESS2= ?,CITY= ?, STATE= ?, ZIP_CODE= ?, COUNTRY= ?, PHONE_NUMBER= ?, STATUS= ?, ROLE= ?"); ps.setString(1, user.getUserId()); ps.setString(2, user.getEmail()); ps.setString(3, passwordDigest); ps.setString(4, user.getFirstName()); ps.setString(5, user.getLastName()); ps.setString(6, user.getOrganization()); ps.setString(7, user.getAddress()); if (Utils.clean(user.getAddress2()) == null) { ps.setString(8, ""); } else { ps.setString(8, user.getAddress2()); } ps.setString(9, user.getCity()); ps.setString(10, user.getState().value()); ps.setString(11, user.getZipcode()); ps.setString(12, user.getCountry().value()); ps.setString(13, user.getPhoneNumber()); ps.setString(14, user.getStatus().value()); ps.setString(15, user.getRole().value()); ps.executeUpdate(); ps.close(); this.passwordSecurityManager.resetEntry(user.getUserId(), passwordSalt); user.setPasswordSecurity(this.passwordSecurityManager.getEntry( user.getUserId(), true)); } catch (Exception e) { try { this.removeUser(user.getUserId()); } catch (Exception ex) { } try { this.passwordSecurityManager.deleteEntry(user.getUserId()); } catch (Exception ex) { } log.error(e.getMessage(), e); DorianInternalException fault = FaultHelper.createFaultException( DorianInternalException.class, "Unexpected Error, Could not add user!!!"); FaultHelper.addMessage(fault, e.getMessage()); throw fault; } finally { db.releaseConnection(c); } } public synchronized void removeUser(String uid) throws DorianInternalException { this.buildDatabase(); Connection c = null; try { c = db.getConnection(); PreparedStatement ps = c.prepareStatement("DELETE FROM " + IDP_USERS_TABLE + " WHERE UID= ?"); ps.setString(1, uid); ps.executeUpdate(); ps.close(); this.passwordSecurityManager.deleteEntry(uid); } catch (Exception e) { log.error(e.getMessage(), e); DorianInternalException fault = FaultHelper.createFaultException( DorianInternalException.class, "Unexpected Error, Could not delete user!!!"); FaultHelper.addMessage(fault, e.getMessage()); throw fault; } finally { db.releaseConnection(c); } } public LocalUser[] getUsers(LocalUserFilter filter) throws DorianInternalException { return getUsers(filter, true); } public LocalUser[] getUsers(LocalUserFilter filter, boolean includePassword) throws DorianInternalException { this.buildDatabase(); Connection c = null; List<LocalUser> users = new ArrayList<LocalUser>(); try { c = db.getConnection(); PreparedStatement ps = null; if (filter != null) { ps = c.prepareStatement("select * from " + IDP_USERS_TABLE + " WHERE UID LIKE ? AND EMAIL LIKE ? AND FIRST_NAME LIKE ? AND LAST_NAME LIKE ? AND ORGANIZATION LIKE ? AND ADDRESS LIKE ? AND ADDRESS2 LIKE ? AND CITY LIKE ? AND STATE LIKE ? AND ZIP_CODE LIKE ? AND COUNTRY LIKE ? AND PHONE_NUMBER LIKE ? AND STATUS LIKE ? AND ROLE LIKE ?"); if (filter.getUserId() != null) { ps.setString(1, "%" + filter.getUserId() + "%"); } else { ps.setString(1, "%"); } if (filter.getEmail() != null) { ps.setString(2, "%" + filter.getEmail() + "%"); } else { ps.setString(2, "%"); } if (filter.getFirstName() != null) { ps.setString(3, "%" + filter.getFirstName() + "%"); } else { ps.setString(3, "%"); } if (filter.getLastName() != null) { ps.setString(4, "%" + filter.getLastName() + "%"); } else { ps.setString(4, "%"); } if (filter.getOrganization() != null) { ps.setString(5, "%" + filter.getOrganization() + "%"); } else { ps.setString(5, "%"); } if (filter.getAddress() != null) { ps.setString(6, "%" + filter.getAddress() + "%"); } else { ps.setString(6, "%"); } if (filter.getAddress2() != null) { ps.setString(7, "%" + filter.getAddress2() + "%"); } else { ps.setString(7, "%"); } if (filter.getCity() != null) { ps.setString(8, "%" + filter.getCity() + "%"); } else { ps.setString(8, "%"); } if (filter.getState() != null) { ps.setString(9, "%" + filter.getState() + "%"); } else { ps.setString(9, "%"); } if (filter.getZipcode() != null) { ps.setString(10, "%" + filter.getZipcode() + "%"); } else { ps.setString(10, "%"); } if (filter.getCountry() != null) { ps.setString(11, "%" + filter.getCountry() + "%"); } else { ps.setString(11, "%"); } if (filter.getPhoneNumber() != null) { ps.setString(12, "%" + filter.getPhoneNumber() + "%"); } else { ps.setString(12, "%"); } if (filter.getStatus() != null) { ps.setString(13, filter.getStatus().value()); } else { ps.setString(13, "%"); } if (filter.getRole() != null) { ps.setString(14, filter.getRole().value()); } else { ps.setString(14, "%"); } } else { ps = c.prepareStatement("select * from " + IDP_USERS_TABLE); } // System.out.println(ps.toString()); ResultSet rs = ps.executeQuery(); while (rs.next()) { LocalUser user = new LocalUser(); user.setUserId(rs.getString("UID")); user.setEmail(rs.getString("EMAIL")); if (includePassword) { user.setPassword(rs.getString("PASSWORD")); } user.setFirstName(rs.getString("FIRST_NAME")); user.setLastName(rs.getString("LAST_NAME")); user.setOrganization(rs.getString("ORGANIZATION")); user.setAddress(rs.getString("ADDRESS")); user.setAddress2(rs.getString("ADDRESS2")); user.setCity(rs.getString("CITY")); user.setState(StateCode.fromValue(rs.getString("STATE"))); user.setZipcode(rs.getString("ZIP_CODE")); user.setCountry(CountryCode.fromValue(rs.getString("COUNTRY"))); user.setPhoneNumber(rs.getString("PHONE_NUMBER")); user.setStatus(LocalUserStatus.fromValue(rs.getString("STATUS"))); user.setRole(LocalUserRole.fromValue(rs.getString("ROLE"))); if (includePassword) { user.setPasswordSecurity(this.passwordSecurityManager .getEntry(user.getUserId(), true)); } else { user.setPasswordSecurity(this.passwordSecurityManager .getEntry(user.getUserId(), false)); } users.add(user); } rs.close(); ps.close(); LocalUser[] list = new LocalUser[users.size()]; for (int i = 0; i < list.length; i++) { list[i] = (LocalUser) users.get(i); } return list; } catch (Exception e) { log.error(e.getMessage(), e); DorianInternalException fault = FaultHelper.createFaultException( DorianInternalException.class, "Unexpected Error, could not obtain a list of users"); FaultHelper.addMessage(fault, e.getMessage()); throw fault; } finally { db.releaseConnection(c); } } public LocalUser getUser(String uid) throws DorianInternalException, NoSuchUserException { return this.getUser(uid, true); } public LocalUser getUser(String uid, boolean includePassword) throws DorianInternalException, NoSuchUserException { this.buildDatabase(); LocalUser user = new LocalUser(); Connection c = null; try { c = db.getConnection(); PreparedStatement s = c.prepareStatement("select * from " + IDP_USERS_TABLE + " where UID= ?"); s.setString(1, uid); ResultSet rs = s.executeQuery(); if (rs.next()) { user.setUserId(uid); user.setEmail(rs.getString("EMAIL")); if (includePassword) { user.setPassword(rs.getString("PASSWORD")); } user.setFirstName(rs.getString("FIRST_NAME")); user.setLastName(rs.getString("LAST_NAME")); user.setOrganization(rs.getString("ORGANIZATION")); user.setAddress(rs.getString("ADDRESS")); user.setAddress2(rs.getString("ADDRESS2")); user.setCity(rs.getString("CITY")); user.setState(StateCode.fromValue(rs.getString("STATE"))); user.setZipcode(rs.getString("ZIP_CODE")); user.setCountry(CountryCode.fromValue(rs.getString("COUNTRY"))); user.setPhoneNumber(rs.getString("PHONE_NUMBER")); user.setStatus(LocalUserStatus.fromValue(rs.getString("STATUS"))); user.setRole(LocalUserRole.fromValue(rs.getString("ROLE"))); if (includePassword) { user.setPasswordSecurity(this.passwordSecurityManager .getEntry(uid, true)); } else { user.setPasswordSecurity(this.passwordSecurityManager .getEntry(uid, false)); } } else { NoSuchUserException fault = FaultHelper.createFaultException( NoSuchUserException.class, "The user " + uid + " does not exist."); throw fault; } rs.close(); s.close(); } catch (NoSuchUserException f) { throw f; } catch (Exception e) { log.error(e.getMessage(), e); DorianInternalException fault = FaultHelper.createFaultException( DorianInternalException.class, "Unexpected Error, could not obtain the user " + uid + "."); FaultHelper.addMessage(fault, e.getMessage()); throw fault; } finally { db.releaseConnection(c); } return user; } public void buildDatabase() throws DorianInternalException { if (!dbBuilt) { try { if (!this.db.tableExists(IDP_USERS_TABLE)) { String applications = "CREATE TABLE " + IDP_USERS_TABLE + " (" + "UID VARCHAR(255) NOT NULL PRIMARY KEY," + "EMAIL VARCHAR(255) NOT NULL," + "PASSWORD VARCHAR(255) NOT NULL," + "FIRST_NAME VARCHAR(255) NOT NULL," + "LAST_NAME VARCHAR(255) NOT NULL," + "ORGANIZATION VARCHAR(255) NOT NULL," + "ADDRESS VARCHAR(255) NOT NULL," + "ADDRESS2 VARCHAR(255)," + "CITY VARCHAR(255) NOT NULL," + "STATE VARCHAR(20) NOT NULL," + "ZIP_CODE VARCHAR(20) NOT NULL," + "COUNTRY VARCHAR(2) NOT NULL," + "PHONE_NUMBER VARCHAR(20) NOT NULL," + "STATUS VARCHAR(20) NOT NULL," + "ROLE VARCHAR(20) NOT NULL," + "INDEX document_index (EMAIL));"; db.update(applications); try { LocalUser u = new LocalUser(); u.setUserId(ADMIN_USER_ID); u.setPassword(ADMIN_PASSWORD); u.setEmail("dorian@dorian.org"); u.setFirstName("Mr."); u.setLastName("Administrator"); u.setOrganization("caBIG"); u.setAddress("3184 Graves Hall"); u.setAddress2("333 W. Tenth Avenue"); u.setCity("Columbus"); u.setState(StateCode.OH); u.setZipcode("43210"); u.setCountry(CountryCode.US); u.setPhoneNumber("555-555-5555"); u.setStatus(LocalUserStatus.ACTIVE); u.setRole(LocalUserRole.ADMINISTRATOR); this.addUser(u); } catch (Exception e) { log.error(e.getMessage(), e); DorianInternalException fault = FaultHelper .createFaultException( DorianInternalException.class, "Unexpected Error, Could not add initial IdP user!!!"); FaultHelper.addMessage(fault, e.getMessage()); throw fault; } } this.dbBuilt = true; } catch (DorianInternalException e) { throw e; } catch (Exception e) { log.error(e.getMessage(), e); DorianInternalException fault = FaultHelper .createFaultException(DorianInternalException.class, "An unexpected database error occurred."); FaultHelper.addMessage(fault, e.getMessage()); throw fault; } } } public synchronized void updateUser(LocalUser u) throws DorianInternalException, NoSuchUserException, InvalidUserPropertyException { this.buildDatabase(); if (u.getUserId() == null) { NoSuchUserException fault = FaultHelper.createFaultException( NoSuchUserException.class, "Could not update user, the user " + u.getUserId() + " does not exist."); throw fault; } else if (userExists(u.getUserId())) { String passwordSalt = null; StringBuffer sb = new StringBuffer(); sb.append("update " + IDP_USERS_TABLE + " SET "); int changes = 0; LocalUser curr = this.getUser(u.getUserId()); boolean passwordChanged = false; if (!StringUtils.isBlank(u.getPassword())) { String newPasswordDigest = null; String existingDigestAlgorithm = curr.getPasswordSecurity() .getDigestAlgorithm(); if ((existingDigestAlgorithm == null) || (existingDigestAlgorithm .equals(PasswordSecurityManager.CRYPT_DIGEST_ALGORITHM))) { newPasswordDigest = Crypt.crypt(u.getPassword()); } else if (existingDigestAlgorithm .equals(PasswordSecurityManager.PASSWORD_DIGEST_ALGORITHM)) { try { newPasswordDigest = PasswordSecurityManager.encrypt(u .getPassword(), curr.getPasswordSecurity() .getDigestSalt()); } catch (Exception e) { log.error(e.getMessage(), e); DorianInternalException fault = FaultHelper .createFaultException( DorianInternalException.class, "Unexpected error calculating password digest!!!"); throw fault; } } else { DorianInternalException fault = FaultHelper .createFaultException( DorianInternalException.class, "Could not obtain password digest, unknown digest algorithm (" + existingDigestAlgorithm + ")!!!"); throw fault; } if (!newPasswordDigest.equals(curr.getPassword())) { validatePassword(u); String newPass = null; try { passwordSalt = PasswordSecurityManager.getRandomSalt(); newPass = PasswordSecurityManager.encrypt( u.getPassword(), passwordSalt); } catch (Exception e) { log.error(e.getMessage(), e); DorianInternalException fault = FaultHelper .createFaultException( DorianInternalException.class, "Could not update user, unexpected error calculating the password digest."); throw fault; } curr.setPassword(newPass); passwordChanged = true; } } if ((u.getEmail() != null) && (!u.getEmail().equals(curr.getEmail()))) { validateEmail(u.getEmail()); curr.setEmail(u.getEmail()); } if ((u.getFirstName() != null) && (!u.getFirstName().equals(curr.getFirstName()))) { validateSpecifiedField("First Name", u.getFirstName()); curr.setFirstName(u.getFirstName()); } if ((u.getLastName() != null) && (!u.getLastName().equals(curr.getLastName()))) { validateSpecifiedField("Last Name", u.getLastName()); curr.setLastName(u.getLastName()); } if ((u.getOrganization() != null) && (!u.getOrganization().equals(curr.getOrganization()))) { validateSpecifiedField("Organization", u.getOrganization()); curr.setOrganization(u.getOrganization()); } if ((u.getAddress() != null) && (!u.getAddress().equals(curr.getAddress()))) { validateSpecifiedField("Address", u.getAddress()); curr.setAddress(u.getAddress()); } if ((u.getAddress2() != null) && (!u.getAddress2().equals(curr.getAddress2()))) { curr.setAddress2(u.getAddress2()); } if ((u.getCity() != null) && (!u.getCity().equals(curr.getCity()))) { validateSpecifiedField("City", u.getCity()); curr.setCity(u.getCity()); } if ((u.getState() != null) && (!u.getState().equals(curr.getState()))) { curr.setState(u.getState()); } if ((u.getCountry() != null) && (!u.getCountry().equals(curr.getCountry()))) { curr.setCountry(u.getCountry()); } if ((u.getZipcode() != null) && (!u.getZipcode().equals(curr.getZipcode()))) { validateSpecifiedField("Zip Code", u.getZipcode()); curr.setZipcode(u.getZipcode()); } if ((u.getPhoneNumber() != null) && (!u.getPhoneNumber().equals(curr.getPhoneNumber()))) { validateSpecifiedField("Phone Number", u.getPhoneNumber()); curr.setPhoneNumber(u.getPhoneNumber()); } if ((u.getStatus() != null) && (!u.getStatus().equals(curr.getStatus()))) { if (accountCreated(curr.getStatus()) && !accountCreated(u.getStatus())) { InvalidUserPropertyException fault = FaultHelper .createFaultException( InvalidUserPropertyException.class, "Error, cannot change " + u.getUserId() + "'s status from a post-created account status (" + curr.getStatus() + ") to a pre-created account status (" + u.getStatus() + ")."); throw fault; } curr.setStatus(u.getStatus()); } if ((u.getRole() != null) && (!u.getRole().equals(curr.getRole()))) { if (changes > 0) { sb.append(","); } curr.setRole(u.getRole()); } Connection c = null; try { c = db.getConnection(); PreparedStatement ps = c .prepareStatement("UPDATE " + IDP_USERS_TABLE + " SET UID = ?, EMAIL= ?, PASSWORD= ?, FIRST_NAME= ?, LAST_NAME= ?, ORGANIZATION= ?, ADDRESS= ?, ADDRESS2= ?,CITY= ?, STATE= ?, ZIP_CODE= ?, COUNTRY= ?, PHONE_NUMBER= ?, STATUS= ?, ROLE= ? WHERE UID = ?"); ps.setString(1, curr.getUserId()); ps.setString(2, curr.getEmail()); ps.setString(3, curr.getPassword()); ps.setString(4, curr.getFirstName()); ps.setString(5, curr.getLastName()); ps.setString(6, curr.getOrganization()); ps.setString(7, curr.getAddress()); ps.setString(8, curr.getAddress2()); ps.setString(9, curr.getCity()); ps.setString(10, curr.getState().value()); ps.setString(11, curr.getZipcode()); ps.setString(12, curr.getCountry().value()); ps.setString(13, curr.getPhoneNumber()); ps.setString(14, curr.getStatus().value()); ps.setString(15, curr.getRole().value()); ps.setString(16, curr.getUserId()); ps.executeUpdate(); ps.close(); if (passwordChanged) { this.passwordSecurityManager.resetEntry(curr.getUserId(), passwordSalt); u.setPasswordSecurity(this.passwordSecurityManager .getEntry(curr.getUserId(), false)); } } catch (Exception e) { log.error(e.getMessage(), e); DorianInternalException fault = FaultHelper .createFaultException(DorianInternalException.class, "Unexpected Error, Could not update user!!!"); FaultHelper.addMessage(fault, e.getMessage()); throw fault; } finally { db.releaseConnection(c); } } else { NoSuchUserException fault = FaultHelper.createFaultException( NoSuchUserException.class, "Could not update user, the user " + u.getUserId() + " does not exist."); throw fault; } } private boolean accountCreated(LocalUserStatus status) { if (status.equals(LocalUserStatus.SUSPENDED)) { return true; } else if (status.equals(LocalUserStatus.ACTIVE)) { return true; } else { return false; } } public boolean userExists(String uid) throws DorianInternalException { this.buildDatabase(); Connection c = null; boolean exists = false; try { c = db.getConnection(); PreparedStatement s = c.prepareStatement("select count(*) from " + IDP_USERS_TABLE + " where UID= ?"); s.setString(1, uid); ResultSet rs = s.executeQuery(); if (rs.next()) { int count = rs.getInt(1); if (count > 0) { exists = true; } } rs.close(); s.close(); } catch (Exception e) { log.error(e.getMessage(), e); DorianInternalException fault = FaultHelper.createFaultException( DorianInternalException.class, "Unexpected Database Error, could not determine if the user " + uid + " exists."); FaultHelper.addMessage(fault, e.getMessage()); throw fault; } finally { db.releaseConnection(c); } return exists; } public void clearDatabase() throws DorianInternalException { this.buildDatabase(); try { db.update("drop TABLE " + IDP_USERS_TABLE); this.passwordSecurityManager.clearDatabase(); } catch (Exception e) { log.error(e.getMessage(), e); DorianInternalException fault = FaultHelper.createFaultException( DorianInternalException.class, "An unexpected database error occurred."); FaultHelper.addMessage(fault, e.getMessage()); throw fault; } } public PasswordSecurityManager getPasswordSecurityManager() { return passwordSecurityManager; } }