alsutton/enterprisepasswordsafe

View on GitHub
src/main/java/com/enterprisepasswordsafe/database/User.java

Summary

Maintainability
A
3 hrs
Test Coverage
F
53%
/*
 * Copyright (c) 2017 Carbon Security Ltd. <opensource@carbonsecurity.co.uk>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package com.enterprisepasswordsafe.database;

import com.enterprisepasswordsafe.engine.AccessControlDecryptor;
import com.enterprisepasswordsafe.engine.users.PasswordHasher;
import com.enterprisepasswordsafe.engine.users.UserAccessKeyEncryptionHandler;
import com.enterprisepasswordsafe.engine.users.UserClassifier;
import com.enterprisepasswordsafe.engine.users.UserPasswordEncryptionHandler;
import com.enterprisepasswordsafe.engine.utils.IDGenerator;

import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * Object representing a user in the system.
 */
public final class User
    implements Comparable<User>, EntityWithAccessRights, AccessControlDecryptor {

    /**
     * The size of the group access key in bits.
     */

    public static final int USER_KEY_SIZE = 128;

    /**
     * The password last changed value designed to force a password change.
     */

    public static final long PASSWORD_LAST_CHANGED_FORCE = Long.MIN_VALUE;

    /**
     * The dummy value for when the password last changed has not been initialised.
     */

    private static final long PASSWORD_LAST_CHANGED_DUMMY = 1;

    /**
     * The value to use when users are deleted.
     */

    static final String DELETED_VALUE = "D";

    /**
     * The algorithm used for the group key.
     */

    static public final String USER_KEY_ALGORITHM = "AES";

    /**
     * The ID of this user.
     */

    private final String userId;

    /**
     * The users login name.
     */

    private final String userName;

    /**
     * The users Email address.
     */

    private String email;

    /**
     * The users full name.
     */

    private String fullName;

    /**
     * The users password.
     */

    private byte[] password;

    /**
     * The users access key.
     */

    private SecretKey accessKey = null;

    /**
     * The encoded access key.
     */

    private byte[] encodedAccessKey;

    /**
     * The encoded access key.
     */

    private byte[] encodedAdminAccessKey;

    /**
     * The last time the user logged on.
     */

    private final long lastLogin;

    /**
     * The authentication source for the user.
     */

    private String authSource;

    /**
     * The last time the users password was changed.
     */

    private long passwordLastChanged;

    /**
     * Flag to say if the user has been disabled.
     */

    private boolean disabled;

    private final PasswordHasher passwordHasher = new PasswordHasher();

    /**
     * Creates a new User object.
     *
     * @param newUserName The name of the user.
     * @param newPassword The users password.
     * @param newFullName The users full name.
     * @param newEmail The users email.
     *
     * @throws NoSuchAlgorithmException Thrown if there is a problem generating the users
     *  access key.
     */
    public User(final String newUserName, final String newPassword,
            final String newFullName, final String newEmail)
        throws NoSuchAlgorithmException {
        userId = IDGenerator.getID();
        userName = newUserName;
        fullName = newFullName;
        email = newEmail;
        lastLogin = 0;
        authSource = null;
        disabled = false;

        setLoginPassword(newPassword);

        KeyGenerator keyGenerator = KeyGenerator.getInstance(USER_KEY_ALGORITHM);
        keyGenerator.init(USER_KEY_SIZE);
        accessKey = keyGenerator.generateKey();
    }

    /**
     * Creates an instance of a user object from a ResultSet.
     *
     * @param rs
     *            The ResultSet to extract the data from.
     * @param startIdx
     *            The start index of the data in the result set.
     *
     * @throws SQLException Thrown if there is a problem extracting the data from the ResultSet.
     */
    public User(final ResultSet rs, final int startIdx)
        throws SQLException {
        int idx = startIdx;
        userId = rs.getString(idx++);
        userName = rs.getString(idx++);
        password = rs.getBytes(idx++);
        email = rs.getString(idx++);
        fullName = rs.getString(idx++);
        encodedAccessKey = rs.getBytes(idx++);
        encodedAdminAccessKey = rs.getBytes(idx++);
        lastLogin = rs.getLong(idx++);
        authSource = rs.getString(idx++);

        String isDisabled = rs.getString(idx++);
        disabled = !(rs.wasNull() || isDisabled.charAt(0) == 'N');

        passwordLastChanged = rs.getLong(idx);
        if( rs.wasNull() ) {
            passwordLastChanged = PASSWORD_LAST_CHANGED_DUMMY;
        }
    }

    /**
     * Compares this object to another object. If the other object is a user it
     * compares the user_name with the other objects user_name, if it's not then
     * it compares hash codes.
     *
     * @param otherUser The other user to compare this one to.
     *
     * @return <0 if the other object is considered of a lesser value, 0 if it is
     *         equals, or >0 if it is greater than the value of this object.
     */

    @Override
    public int compareTo(final User otherUser) {
        return userName.compareToIgnoreCase(otherUser.userName);
    }

    /**
     * Generate the hash code for this user. Due to the ID being unique the
     * hash code of the id is used.
     *
     * @return The hash code for this object.
     */

    @Override
    public int hashCode() {
        return userId.hashCode();
    }

    /**
     * Check to see if another object is equal to this one.
     *
     * @param o
     *            The other object.
     * @return true if the objects are equal, false if not.
     */

    @Override
    public boolean equals(final Object o) {
        if (!(o instanceof User)) {
            return false;
        }

        User otherUser = (User) o;
        return otherUser.userId.equals(userId);
    }

    /**
     * Returns whether or not this user is a enabled.
     *
     * @return true if the user is enabled, false if not.
     */

    public boolean isEnabled() {
        return !disabled;
    }

    /**
     * Sets the user enablement flag.
     *
     * @param isEnabled true if the user should be enabled, false if not.
     */

    public void setEnabled(final boolean isEnabled) {
        disabled = !isEnabled;
    }

    public AuthenticationSource getAuthenticationSource()
            throws SQLException {
        String userAuthSource = getAuthSource();
        if (!new UserClassifier().isMasterAdmin(this) && userAuthSource != null) {
            return AuthenticationSourceDAO.getInstance().getById(userAuthSource);
        } else {
            return AuthenticationSource.DEFAULT_SOURCE;
        }
    }

    public void setLoginPassword(final String newPassword)
            throws NoSuchAlgorithmException {
        password = passwordHasher.createHashWithRandomSalt(newPassword);
    }

    /**
     * Forces the user to change their password at the next login.
     */

    public void forcePasswordChangeAtNextLogin( ) {
        passwordLastChanged =  PASSWORD_LAST_CHANGED_FORCE;
    }

    /**
     * Whether or not the password given is the users password.
     *
     * @param userPassword
     *            The password to test.
     *
     * @return true if the password is correct, false if not.
     *
     * @throws NoSuchAlgorithmException Thrown if the password hashing algorithm is unavailable.
     */

    public boolean checkPassword(final char[] userPassword)
            throws NoSuchAlgorithmException {
        return checkPassword(new String(userPassword));
    }

    public boolean checkPassword(final String password)
            throws NoSuchAlgorithmException {
        if (password == null) {
            return false;
        }
        return passwordHasher.equalsSaltedHash(password, getPassword());
    }

    /**
     * Get the AccessKey.
     *
     * @return Returns the accessKey.
     */
    public SecretKey getAccessKey() {
        return accessKey;
    }

    /**
     * Set the AccessKey.
     *
     * @param newKey The access key to use.
     */
    public void setAccessKey(SecretKey newKey) {
        accessKey = newKey;
    }

    /**
     * Decrypts the access key using the specified password.
     *
     * @param decryptionPassword The to decrypt the access key with.
     */
    public void decryptAccessKey(final String decryptionPassword)
        throws GeneralSecurityException {
        if( accessKey != null ) {
            return;
        }

        if( encodedAccessKey == null ) {
            throw new RuntimeException("Encoded access key unavailable, access key not present.");
        }

        byte[] keyBytes = new UserPasswordEncryptionHandler(decryptionPassword).decrypt(encodedAccessKey);
        accessKey = new SecretKeySpec(keyBytes, USER_KEY_ALGORITHM);
    }

    /**
     * Decrypts the admin access key using the admin group.
     *
     * @param adminGroup The group to decrypt the access key with.
     */
    public void decryptAdminAccessKey(final Group adminGroup)
        throws GeneralSecurityException {
        if( accessKey != null ) {
            return;
        }

        if( encodedAdminAccessKey == null ) {
            throw new RuntimeException("Encoded admin access key unavailable, access key not present.");
        }

        byte[] keyBytes = adminGroup.getKeyDecrypter().decrypt( encodedAdminAccessKey );
        accessKey = new SecretKeySpec(keyBytes, USER_KEY_ALGORITHM);
    }

    /**
     * @return Returns the authSource.
     */
    public String getAuthSource() {
        return authSource;
    }

    /**
     * @return Returns the email.
     */
    public String getEmail() {
        return email;
    }

    /**
     * @return Returns the fullName.
     */
    public String getFullName() {
        return fullName;
    }

    /**
     * @return Returns the lastLogin.
     */
    public long getLastLogin() {
        return lastLogin;
    }

    public byte[] getPassword() {
        return password;
    }

    @Override
    public String getId() {
        return userId;
    }

    /**
     * @return Returns the userName.
     */
    public String getUserName() {
        return userName;
    }

    /**
     * Gets the last time the users password was changed
     *
     * @return A String for the last time a users password was changed.
     */

    public long getPasswordLastChanged() {
        return passwordLastChanged;
    }

    /**
     * Prints this user name.
     *
     * @return A string representation of the user.
     */

    @Override
    public String toString() {
        String theUsername = userName;
        if (theUsername == null) {
            theUsername = "<<UNKNOWN USER>>";
        }

        return theUsername;
    }

    /**
     * @param newAuthSource The authSource to set.
     */
    public void setAuthSource(final String newAuthSource) {
        authSource = newAuthSource;
    }

    /**
     * @param newEmail The email to set.
     */
    public void setEmail(final String newEmail) {
        email = newEmail;
    }

    /**
     * @param newFullName The fullName to set.
     */
    public void setFullName(final String newFullName) {
        fullName = newFullName;
    }

    /**
     * Gets the decrypter for this user which uses the admin key.
     *
     * @return The decrypter.
     */

    @Override
    public Decrypter getKeyDecrypter() {
        return new UserAccessKeyEncryptionHandler(getAccessKey());
    }

    /**
     * Gets the decrypter for this user which uses the admin key.
     *
     * @return The decrypter.
     */

    @Override
    public Encrypter getKeyEncrypter() {
        return new UserAccessKeyEncryptionHandler(getAccessKey());
    }

}