gov.va.isaac.config.profiles.UserProfile.java Source code

Java tutorial

Introduction

Here is the source code for gov.va.isaac.config.profiles.UserProfile.java

Source

/**
 * Copyright Notice
 *
 * This is a work of the U.S. Government and is not subject to copyright
 * protection in the United States. Foreign copyrights may apply.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package gov.va.isaac.config.profiles;

import gov.va.isaac.AppContext;
import gov.va.isaac.config.generated.RoleOption;
import gov.va.isaac.config.generated.StatedInferredOptions;
import gov.va.isaac.config.profiles.UserProfileBindings.RelationshipDirection;
import gov.va.isaac.util.PasswordHasher;

import org.ihtsdo.otf.tcc.api.coordinate.Status;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * {@link UserProfile}
 *
 * @author <a href="mailto:daniel.armbrust.list@gmail.com">Dan Armbrust</a>
 */

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class UserProfile {
    private static Logger logger = LoggerFactory.getLogger(UserProfile.class);

    //This is a cache of what they typed when the logged in - so it can be used later for logging into workflow, or sync.
    private transient char[] clearTextPassword;

    //What we actually perform login comparisons against - what is stored in the user prefs file.
    @XmlElement
    private String hashedPassword;

    @XmlElement
    private String userLogonName;

    @XmlElement
    private UUID conceptUUID;

    @XmlElement
    private StatedInferredOptions statedInferredPolicy = UserProfileDefaults.getDefaultStatedInferredPolicy();

    @XmlElement
    private boolean displayFSN = UserProfileDefaults.getDefaultDisplayFSN();

    @XmlElement
    private RelationshipDirection displayRelDirection = UserProfileDefaults.getDefaultDisplayRelDirection();

    @XmlElement
    private String workflowUsername = null;

    //This field will be encrypted using the clearTextPassword
    @XmlElement
    private String workflowPasswordEncrypted = null;

    @XmlElement
    private String syncUsername = null;

    //This field will be encrypted using the clearTextPassword
    @XmlElement
    private String syncPasswordEncrypted = null;

    @XmlElement
    private boolean launchWorkflowForEachCommit = UserProfileDefaults.getDefaultLaunchWorkflowForEachCommit();

    @XmlElement
    private boolean runDroolsBeforeEachCommit = UserProfileDefaults.getDefaultRunDroolsBeforeEachCommit();

    @XmlElement
    private String workflowServerDeploymentId = null;

    @XmlElement
    private UUID viewCoordinatePath = null;

    @XmlElement
    private Long viewCoordinateTime = null;

    @XmlElement
    private UUID editCoordinatePath = null;

    @XmlElement
    private UUID workflowPromotionPath = null;

    @XmlElement
    public String workflowServerUrl = null;

    @XmlElement
    public String changeSetUrl = null;

    @XmlElement
    public String releaseVersion = null;

    @XmlElement
    public String extensionNamespace = null;

    @XmlElement
    public Status[] viewCoordinateStatuses = null;

    @XmlElement
    public UUID[] viewCoordinateModules = null;

    /*
     *  *** Update clone() method when adding parameters
     *  
     */
    @Override
    protected UserProfile clone() {
        UserProfile clone = new UserProfile();
        clone.userLogonName = this.userLogonName;
        clone.clearTextPassword = this.clearTextPassword;
        clone.hashedPassword = this.hashedPassword;
        clone.statedInferredPolicy = this.statedInferredPolicy;
        clone.displayFSN = this.displayFSN;
        clone.displayRelDirection = this.displayRelDirection;
        clone.syncPasswordEncrypted = this.syncPasswordEncrypted;
        clone.syncUsername = this.syncUsername;
        clone.workflowPasswordEncrypted = this.workflowPasswordEncrypted;
        clone.workflowUsername = this.workflowUsername;
        clone.conceptUUID = this.conceptUUID;
        clone.launchWorkflowForEachCommit = this.launchWorkflowForEachCommit;
        clone.runDroolsBeforeEachCommit = this.runDroolsBeforeEachCommit;
        clone.workflowServerDeploymentId = this.workflowServerDeploymentId;
        clone.viewCoordinatePath = this.viewCoordinatePath;
        clone.viewCoordinateTime = this.viewCoordinateTime;
        clone.editCoordinatePath = this.editCoordinatePath;
        clone.workflowPromotionPath = this.workflowPromotionPath;
        clone.workflowServerUrl = this.workflowServerUrl;
        clone.changeSetUrl = this.changeSetUrl;
        clone.releaseVersion = this.releaseVersion;
        clone.extensionNamespace = this.extensionNamespace;
        if (this.viewCoordinateStatuses != null) {
            clone.viewCoordinateStatuses = Arrays.copyOf(this.viewCoordinateStatuses,
                    this.viewCoordinateStatuses.length);
        } else {
            clone.viewCoordinateStatuses = null;
        }
        if (this.viewCoordinateModules != null) {
            clone.viewCoordinateModules = new UUID[this.viewCoordinateModules.length];
            for (int i = 0; i < this.viewCoordinateModules.length; ++i) {
                clone.viewCoordinateModules[i] = UUID.fromString(this.viewCoordinateModules[i].toString());
            }
        } else {
            clone.viewCoordinateModules = null;
        }

        return clone;
    }

    /**
     * do not use - only for jaxb
     */
    private UserProfile() {
        //for jaxb and clone
    }

    /**
     * Do not call - use {@link UserProfileManager#createUserProfile(gov.va.isaac.config.generated.User, gov.va.isaac.config.generated.NewUserDefaults)}
     * 
     * Only public due to a testing quirk  - BaseTest - in workflow needs this, as may some JUnit tests, eventually.
     */
    public UserProfile(String userLogonName, String password, UUID conceptUUID) {
        this.userLogonName = userLogonName;
        this.conceptUUID = conceptUUID;
        try {
            this.hashedPassword = PasswordHasher.getSaltedHash(password);
            this.clearTextPassword = password.toCharArray();
        } catch (Exception e) {
            logger.error("Unexpected error hashing password", e);
            this.hashedPassword = "foo";
        }
    }

    public boolean isCorrectPassword(String password) {
        try {
            boolean result = PasswordHasher.check(password, hashedPassword);
            if (result) {
                clearTextPassword = password.toCharArray();
            }
            return result;
        } catch (Exception e) {
            logger.error("Unexpected error validating password", e);
            return false;
        }
    }

    /**
     * This call sets both the clearTextPassword field, and the hashedPassword field - hashing as appropriate.
     * 
     * This call saves the changes to the preferences file.
     */
    public void setPassword(String currentPassword, String newPassword) throws InvalidPasswordException {
        if (!isCorrectPassword(currentPassword)) {
            throw new InvalidPasswordException("Incorrect current password");
        }
        if (newPassword == null || newPassword.length() == 0) {
            throw new InvalidPasswordException("The password must be provided");
        }
        try {
            //Need to decrypt and reencrypt the workflow and sync passwords, since these are encrypted with the clearTextPassword.
            String wfPass = getWorkflowPassword();
            String syncPass = getSyncPassword();

            this.clearTextPassword = newPassword.toCharArray();
            this.hashedPassword = PasswordHasher.getSaltedHash(newPassword);

            if (wfPass != null) {
                setWorkflowPassword(wfPass);
            }
            if (syncPass != null) {
                setSyncPassword(syncPass);
            }
            AppContext.getService(UserProfileManager.class).saveChanges(this);
        } catch (Exception e) {
            logger.error("Unexpected error hashing password", e);
            throw new RuntimeException(e);
        }
    }

    public String getUserLogonName() {
        return userLogonName;
    }

    public StatedInferredOptions getStatedInferredPolicy() {
        return statedInferredPolicy;
    }

    public void setStatedInferredPolicy(StatedInferredOptions statedInferredPolicy) {
        this.statedInferredPolicy = statedInferredPolicy;
    }

    public void setDisplayFSN(boolean displayFSN) {
        this.displayFSN = displayFSN;
    }

    public boolean getDisplayFSN() {
        return displayFSN;
    }

    public void setDisplayRelDirection(RelationshipDirection displayRelationshipDirection) {
        this.displayRelDirection = displayRelationshipDirection;
    }

    public RelationshipDirection getDisplayRelDirection() {
        return displayRelDirection;
    }

    public String getWorkflowUsername() {
        return workflowUsername;
    }

    public void setWorkflowUsername(String workflowUsername) {
        this.workflowUsername = workflowUsername;
    }

    public String getSyncUsername() {
        return syncUsername;
    }

    public void setSyncUsername(String syncUsername) {
        this.syncUsername = syncUsername;
    }

    public void setWorkflowPassword(String workflowPassword) {
        if (clearTextPassword == null) {
            throw new RuntimeException("Cannot encrypt a workflow password until successfully logged in");
        }
        try {
            this.workflowPasswordEncrypted = PasswordHasher.encrypt(new String(clearTextPassword),
                    workflowPassword);
        } catch (Exception e) {
            logger.error("Unexpected error encrypting password", e);
            throw new RuntimeException("Unexpected error encrypting workflow password");
        }
    }

    public String getWorkflowPassword() throws InvalidPasswordException {
        if (clearTextPassword == null) {
            throw new RuntimeException("Cannot decrypt a workflow password until successfully logged in");
        }
        if (workflowPasswordEncrypted == null) {
            return null;
        }
        try {
            return PasswordHasher.decryptToString(new String(clearTextPassword), this.workflowPasswordEncrypted);
        } catch (Exception e) {
            throw new InvalidPasswordException("Invalid password for decrypting the workflow password");
        }
    }

    public void setSyncPassword(String syncPassword) {
        if (clearTextPassword == null) {
            throw new RuntimeException("Cannot encrypt a sync password until successfully logged in");
        }
        try {
            this.syncPasswordEncrypted = PasswordHasher.encrypt(new String(clearTextPassword), syncPassword);
        } catch (Exception e) {
            logger.error("Unexpected error encrypting password", e);
            throw new RuntimeException("Unexpected error encrypting sync password");
        }
    }

    public String getSyncPassword() {
        if (clearTextPassword == null) {
            throw new RuntimeException("Cannot decrypt a sync password until successfully logged in");
        }
        if (syncPasswordEncrypted == null) {
            return null;
        }
        try {
            return PasswordHasher.decryptToString(new String(clearTextPassword), this.syncPasswordEncrypted);
        } catch (Exception e) {
            throw new InvalidPasswordException("Invalid password for decrypting the sync password");
        }
    }

    /**
     * The UUID of the concept in the DB that represents this user.
     */
    public UUID getConceptUUID() {
        return conceptUUID;
    }

    public boolean hasRole(RoleOption role) {
        if (role == null) {
            return false;
        }
        //TODO implement role checking - probably store these on the user concept in a refex?
        return true;
    }

    /**
     * @return the launchWorkflowForEachCommit
     */
    public boolean isLaunchWorkflowForEachCommit() {
        return launchWorkflowForEachCommit;
    }

    /**
     * @param launchWorkflowForEachCommit the launchWorkflowForEachCommit to set
     */
    public void setLaunchWorkflowForEachCommit(boolean launchWorkflowForEachCommit) {
        this.launchWorkflowForEachCommit = launchWorkflowForEachCommit;
    }

    /**
     * @return the runDroolsBeforeEachCommit
     */
    public boolean isRunDroolsBeforeEachCommit() {
        return runDroolsBeforeEachCommit;
    }

    /**
     * @param runDroolsBeforeEachCommit the runDroolsBeforeEachCommit to set
     */
    public void setRunDroolsBeforeEachCommit(boolean runDroolsBeforeEachCommit) {
        this.runDroolsBeforeEachCommit = runDroolsBeforeEachCommit;
    }

    /**
     * @return workflowServerDeploymentId
     */
    public String getWorkflowServerDeploymentId() {
        if (StringUtils.isBlank(workflowServerDeploymentId)) {
            return UserProfileDefaults.getDefaultWorkflowServerDeploymentId();
        }
        return workflowServerDeploymentId;
    }

    /**
     * @param workflowServerDeploymentId
     */
    public void setWorkflowServerDeploymentId(String workflowServerDeploymentId) {
        this.workflowServerDeploymentId = workflowServerDeploymentId;
    }

    public Long getViewCoordinateTime() {
        if (viewCoordinateTime == null) {
            return UserProfileDefaults.getDefaultViewCoordinateTime();
        }
        return viewCoordinateTime;
    }

    public void setViewCoordinateTime(Long time) {
        this.viewCoordinateTime = time;
    }

    /**
     * @return viewCoordinatePath
     */
    public UUID getViewCoordinatePath() {
        if (viewCoordinatePath == null) {
            return UserProfileDefaults.getDefaultViewCoordinatePath();
        }
        return viewCoordinatePath;
    }

    /**
     * @param viewCoordinatePath
     */
    public void setViewCoordinatePath(UUID viewCoordinatePath) {
        this.viewCoordinatePath = viewCoordinatePath;
    }

    /**
     * @return editCoordinatePath
     */
    public UUID getEditCoordinatePath() {
        if (editCoordinatePath == null) {
            return UserProfileDefaults.getDefaultEditCoordinatePath();
        }
        return editCoordinatePath;
    }

    /**
     * @param editCoordinatePath
     */
    public void setEditCoordinatePath(UUID editCoordinatePath) {
        this.editCoordinatePath = editCoordinatePath;
    }

    /**
     * @return workflowPromotionPath
     */
    public UUID getWorkflowPromotionPath() {
        if (workflowPromotionPath == null) {
            return UserProfileDefaults.getDefaultWorkflowPromotionPath();
        }
        return workflowPromotionPath;
    }

    /**
     * @param workflowPromotionPath
     */
    public void setWorkflowPromotionPath(UUID workflowPromotionPath) {
        this.workflowPromotionPath = workflowPromotionPath;
    }

    /**
     * @return workflowServerUrl
     */
    public String getWorkflowServerUrl() {
        if (StringUtils.isBlank(workflowServerUrl)) {
            return UserProfileDefaults.getDefaultWorkflowServerUrl();
        }
        return workflowServerUrl;
    }

    /**
     * @param workflowServerUrl
     */
    public void setWorkflowServerUrl(String workflowServerUrl) {
        this.workflowServerUrl = workflowServerUrl;
    }

    /**
     * @return changeSetUrl
     */
    public String getChangeSetUrl() {
        if (StringUtils.isBlank(changeSetUrl)) {
            return UserProfileDefaults.getDefaultChangeSetUrl();
        }
        return changeSetUrl;
    }

    /**
     * @param changeSetUrl
     */
    public void setChangeSetUrl(String changeSetUrl) {
        this.changeSetUrl = changeSetUrl;
    }

    /**
     * @return releaseVersion
     */
    public String getReleaseVersion() {
        if (StringUtils.isBlank(releaseVersion)) {
            return UserProfileDefaults.getDefaultReleaseVersion();
        }
        return releaseVersion;
    }

    /**
     * @param releaseVersion
     */
    public void setReleaseVersion(String releaseVersion) {
        this.releaseVersion = releaseVersion;
    }

    /**
     * @return extensionNamespace
     */
    public String getExtensionNamespace() {
        if (StringUtils.isBlank(extensionNamespace)) {
            return UserProfileDefaults.getDefaultExtensionNamespace();
        }
        return extensionNamespace;
    }

    /**
     * @param extensionNamespace
     */
    public void setExtensionNamespace(String extensionNamespace) {
        this.extensionNamespace = extensionNamespace;
    }

    /**
     * @return viewCoordinateStatuses unmodifiable set of viewCoordinateStatus
     * 
     * Always returns a unique unmodifiable set of 1 or more Status values.
     * Returns unmodifiable set of default status values if stored array is null or contains no non-null values
     */
    public Set<Status> getViewCoordinateStatuses() {
        Set<Status> statuses = new HashSet<>();
        if (viewCoordinateStatuses != null) {
            for (Status status : viewCoordinateStatuses) {
                if (status != null) {
                    statuses.add(status);
                }
            }
        }

        if (statuses.size() == 0) {
            statuses.addAll(UserProfileDefaults.getDefaultViewCoordinateStatuses());
        }

        return Collections.unmodifiableSet(statuses);
    }

    /**
     * @param viewCoordinateStatuses set of viewCoordinateStatus
     * 
     * Sets a unique set of non-null Status values.
     * If passed set is null or contains no non-null values then empty array is used.
     */
    public void setViewCoordinateStatuses(Set<Status> viewCoordinateStatusesSet) {
        setViewCoordinateStatuses(viewCoordinateStatusesSet != null
                ? viewCoordinateStatusesSet.toArray(new Status[viewCoordinateStatusesSet.size()])
                : new Status[0]);
    }

    /**
     * @param viewCoordinateStatuses variable length parameter array of viewCoordinateStatus
     * 
     * Sets a unique set of non-null Status values.
     * If passed parameter array is null or contains no non-null values then empty array is used.
     */
    public void setViewCoordinateStatuses(Status... viewCoordinateStatusesSet) {
        Set<Status> validPassedStatuses = new HashSet<>();
        if (viewCoordinateStatusesSet != null) {
            for (Status status : viewCoordinateStatusesSet) {
                if (status != null) {
                    validPassedStatuses.add(status);
                }
            }
        }

        if (validPassedStatuses.size() > 0) {
            viewCoordinateStatuses = validPassedStatuses.toArray(new Status[validPassedStatuses.size()]);
        } else {
            viewCoordinateStatuses = new Status[0];
        }
    }

    /**
     * @return viewCoordinateModules unmodifiable set of viewCoordinateModules
     * 
     * Always returns a unique unmodifiable set of 0 or more module UUIDs.
     * An empty returned set means NO RESTRICTION for the purposes of filtering.
     */
    public Set<UUID> getViewCoordinateModules() {
        Set<UUID> modules = new HashSet<>();
        if (viewCoordinateModules != null) {
            for (UUID uuid : viewCoordinateModules) {
                if (uuid != null) {
                    modules.add(uuid);
                }
            }
        } else {
            modules.addAll(UserProfileDefaults.getDefaultViewCoordinateModules());
        }

        return Collections.unmodifiableSet(modules);
    }

    /**
     * @param viewCoordinateModules set of viewCoordinateModule UUIDs
     * 
     * Sets a unique set of zero or more non-null UUID values.
     * If passed set is null or contains no non-null values then empty array is used.
     */
    public void setViewCoordinateModules(Set<UUID> viewCoordinateModulesSet) {
        setViewCoordinateModules(viewCoordinateModulesSet != null
                ? viewCoordinateModulesSet.toArray(new UUID[viewCoordinateModulesSet.size()])
                : new UUID[0]);
    }

    /**
     * @param viewCoordinateModules variable length parameter array of viewCoordinateModule UUIDs
     * 
     * Sets a unique set of zero or more non-null UUID values.
     * If passed variable length parameter array is null or contains no non-null values then empty array is used.
     */
    public void setViewCoordinateModules(UUID... viewCoordinateModulesSet) {
        Set<UUID> validPassedUuids = new HashSet<>();
        if (viewCoordinateModulesSet != null) {
            for (UUID uuid : viewCoordinateModulesSet) {
                if (uuid != null) {
                    validPassedUuids.add(uuid);
                }
            }
        }

        if (validPassedUuids.size() > 0) {
            viewCoordinateModules = validPassedUuids.toArray(new UUID[validPassedUuids.size()]);
        } else {
            viewCoordinateModules = new UUID[0];
        }
    }

    // Persistence methods
    protected void store(File fileToWrite) throws IOException {
        try {
            JAXBContext jaxbContext = JAXBContext.newInstance(UserProfile.class);
            Marshaller jaxbMarshaller = jaxbContext.createMarshaller();

            // output pretty printed
            jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

            jaxbMarshaller.marshal(this, fileToWrite);
        } catch (Exception e) {
            throw new IOException("Problem storings UserProfile to " + fileToWrite.getAbsolutePath(), e);
        }
    }

    protected static UserProfile read(File path) throws IOException {
        try {
            JAXBContext jaxbContext = JAXBContext.newInstance(UserProfile.class);

            Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
            return (UserProfile) jaxbUnmarshaller.unmarshal(path);
        } catch (Exception e) {
            logger.error("Problem reading user profile from " + path.getAbsolutePath(), e);
            throw new IOException("Problem reading user profile", e);
        }
    }

}