org.ejbca.core.ejb.ra.UserData.java Source code

Java tutorial

Introduction

Here is the source code for org.ejbca.core.ejb.ra.UserData.java

Source

/*************************************************************************
 *                                                                       *
 *  EJBCA Community: The OpenSource Certificate Authority                *
 *                                                                       *
 *  This software is free software; you can redistribute it and/or       *
 *  modify it under the terms of the GNU Lesser General Public           *
 *  License as published by the Free Software Foundation; either         *
 *  version 2.1 of the License, or any later version.                    *
 *                                                                       *
 *  See terms of license at gnu.org.                                     *
 *                                                                       *
 *************************************************************************/

package org.ejbca.core.ejb.ra;

import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.List;

import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.PostLoad;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import javax.persistence.Query;
import javax.persistence.Table;
import javax.persistence.Transient;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.cesecore.certificates.endentity.EndEntityConstants;
import org.cesecore.certificates.endentity.EndEntityInformation;
import org.cesecore.certificates.endentity.EndEntityType;
import org.cesecore.certificates.endentity.ExtendedInformation;
import org.cesecore.dbprotection.ProtectedData;
import org.cesecore.dbprotection.ProtectionStringBuilder;
import org.cesecore.util.CertTools;
import org.cesecore.util.QueryResultWrapper;
import org.cesecore.util.StringTools;
import org.ejbca.core.model.SecConst;
import org.ejbca.util.crypto.BCrypt;
import org.ejbca.util.crypto.CryptoTools;
import org.ejbca.util.crypto.SupportedPasswordHashAlgorithm;

/**
 * Representation of a User.
 * 
 * Passwords should me manipulated through helper functions setPassword() and setOpenPassword(). The setPassword() function sets the hashed password,
 * while the setOpenPassword() method sets both the hashed password and the clear text password. The method comparePassword() is used to verify a
 * password against the hashed password.
 * 
 * @version $Id: UserData.java 19902 2014-09-30 14:32:24Z anatom $
 */
@Entity
@Table(name = "UserData")
public class UserData extends ProtectedData implements Serializable {

    private static final long serialVersionUID = 1L;
    private static final Logger log = Logger.getLogger(UserData.class);

    private String username;
    private String subjectDN;
    private int caId;
    private String subjectAltName;
    private String cardNumber;
    private String subjectEmail;
    private int status;
    private int type;
    private String clearPassword;
    private String passwordHash;
    private long timeCreated;
    private long timeModified;
    private int endEntityProfileId;
    private int certificateProfileId;
    private int tokenType;
    private int hardTokenIssuerId;
    private String extendedInformationData;
    private String keyStorePassword;
    private int rowVersion = 0;
    private String rowProtection;

    /**
     * Entity Bean holding info about a User. Create by sending in the instance, username, password and subject DN. SubjectEmail, Status and Type are
     * set to default values (null, STATUS_NEW, USER_INVALID). and should be set using the respective set-methods. Clear text password is not set at
     * all and must be set using setClearPassword();
     * 
     * @param username the unique username used for authentication.
     * @param password the password used for authentication. If clearpwd is false this only sets passwordhash, if clearpwd is true it also sets
     *            cleartext password.
     * @param clearpwd true if clear password should be set for CA generated tokens (p12, jks, pem), false otherwise for only storing hashed
     *            passwords.
     * @param dn the DN the subject is given in his certificate.
     * @param cardnumber the number printed on the card.
     * @param altname string of alternative names, i.e. rfc822name=foo2bar.com,dnsName=foo.bar.com, can be null
     * @param email user email address, can be null
     * @param type user type, i.e. EndEntityTypes.USER_ENDUSER etc
     * @param eeprofileid end entity profile id, can be 0
     * @param certprofileid certificate profile id, can be 0
     * @param tokentype token type to issue to the user, i.e. SecConst.TOKEN_SOFT_BROWSERGEN
     * @param hardtokenissuerid hard token issuer id if hard token issuing is used, 0 otherwise
     * @param extendedInformation ExtendedInformation object
     * 
     * @throws NoSuchAlgorithmException
     */
    public UserData(String username, String password, boolean clearpwd, String dn, int caid, String cardnumber,
            String altname, String email, int type, int eeprofileid, int certprofileid, int tokentype,
            int hardtokenissuerid, ExtendedInformation extendedInformation) {
        long time = new Date().getTime();
        setUsername(username);
        if (clearpwd) {
            setOpenPassword(password);
        } else {
            setPasswordHash(CryptoTools.makePasswordHash(password));
            setClearPassword(null);
        }
        setSubjectDN(CertTools.stringToBCDNString(dn));
        setCaId(caid);
        setSubjectAltName(altname);
        setSubjectEmail(email);
        setStatus(EndEntityConstants.STATUS_NEW);
        setType(type);
        setTimeCreated(time);
        setTimeModified(time);
        setEndEntityProfileId(eeprofileid);
        setCertificateProfileId(certprofileid);
        setTokenType(tokentype);
        setHardTokenIssuerId(hardtokenissuerid);
        setExtendedInformation(extendedInformation);
        setCardNumber(cardnumber);
        if (log.isDebugEnabled()) {
            log.debug("Created user " + username);
        }
    }

    public UserData() {
    }

    // @Id @Column
    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = StringTools.stripUsername(username);
    }

    // @Column
    public String getSubjectDN() {
        return subjectDN;
    }

    public void setSubjectDN(String subjectDN) {
        this.subjectDN = subjectDN;
    }

    // @Column
    public int getCaId() {
        return caId;
    }

    public void setCaId(int caId) {
        this.caId = caId;
    }

    // @Column
    public String getSubjectAltName() {
        return subjectAltName;
    }

    public void setSubjectAltName(String subjectAltName) {
        this.subjectAltName = subjectAltName;
    }

    // @Column
    public String getCardNumber() {
        return cardNumber;
    }

    public void setCardNumber(String cardNumber) {
        this.cardNumber = cardNumber;
    }

    // @Column
    public String getSubjectEmail() {
        return subjectEmail;
    }

    public void setSubjectEmail(String subjectEmail) {
        this.subjectEmail = subjectEmail;
    }

    // @Column
    public int getStatus() {
        return this.status;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    // @Column
    public int getType() {
        return type;
    }

    public void setType(int type) {
        this.type = type;
    }

    /**
     * Returns clear text password or null.
     */
    // @Column
    public String getClearPassword() {
        return clearPassword;
    }

    /**
     * Sets clear text password, the preferred method is setOpenPassword().
     */
    public void setClearPassword(String clearPassword) {
        this.clearPassword = clearPassword;
    }

    /**
     * Returns hashed password or null.
     */
    // @Column
    public String getPasswordHash() {
        return passwordHash;
    }

    /**
     * Sets hash of password, this is the normal way to store passwords, but use the method setPassword() instead.
     */
    public void setPasswordHash(String passwordHash) {
        this.passwordHash = passwordHash;
    }

    /**
     * Returns the time when the user was created.
     */
    // @Column
    public long getTimeCreated() {
        return timeCreated;
    }

    /**
     * Sets the time when the user was created.
     */
    public void setTimeCreated(long timeCreated) {
        this.timeCreated = timeCreated;
    }

    /**
     * Returns the time when the user was last modified.
     */
    // @Column
    public long getTimeModified() {
        return timeModified;
    }

    /**
     * Sets the time when the user was last modified.
     */
    public void setTimeModified(long timeModified) {
        this.timeModified = timeModified;
    }

    /**
     * Returns the end entity profile id the user belongs to.
     */
    // @Column
    public int getEndEntityProfileId() {
        return endEntityProfileId;
    }

    /**
     * Sets the end entity profile id the user should belong to. 0 if profileid is not applicable.
     */
    public void setEndEntityProfileId(int endEntityProfileId) {
        this.endEntityProfileId = endEntityProfileId;
    }

    /**
     * Returns the certificate profile id that should be generated for the user.
     */
    // @Column
    public int getCertificateProfileId() {
        return certificateProfileId;
    }

    /**
     * Sets the certificate profile id that should be generated for the user. 0 if profileid is not applicable.
     */
    public void setCertificateProfileId(int certificateProfileId) {
        this.certificateProfileId = certificateProfileId;
    }

    /**
     * Returns the token type id that should be generated for the user.
     */
    // @Column
    public int getTokenType() {
        return tokenType;
    }

    /**
     * Sets the token type that should be generated for the user. Available token types can be found in SecConst.
     */
    public void setTokenType(int tokenType) {
        this.tokenType = tokenType;
    }

    /**
     * Returns the hard token issuer id that should genererate for the users hard token.
     */
    // @Column
    public int getHardTokenIssuerId() {
        return hardTokenIssuerId;
    }

    /**
     * Sets the hard token issuer id that should genererate for the users hard token. 0 if issuerid is not applicable.
     */
    public void setHardTokenIssuerId(int hardTokenIssuerId) {
        this.hardTokenIssuerId = hardTokenIssuerId;
    }

    /**
     * Non-searchable information about a user.
     */
    // @Column @Lob
    public String getExtendedInformationData() {
        return extendedInformationData;
    }

    /**
     * Non-searchable information about a user.
     */
    public void setExtendedInformationData(String extendedInformationData) {
        this.extendedInformationData = extendedInformationData;
    }

    @Deprecated
    // Can't find any references to this field. Please un-deprecate if an use is discovered! =)
    // @Column
    public String getKeyStorePassword() {
        return keyStorePassword;
    }

    @Deprecated
    // Can't find any references to this field. Please un-deprecate if an use is discovered! =)
    public void setKeyStorePassword(String keyStorePassword) {
        this.keyStorePassword = keyStorePassword;
    }

    // @Version @Column
    public int getRowVersion() {
        return rowVersion;
    }

    public void setRowVersion(int rowVersion) {
        this.rowVersion = rowVersion;
    }

    // @Column @Lob
    @Override
    public String getRowProtection() {
        return rowProtection;
    }

    @Override
    public void setRowProtection(String rowProtection) {
        this.rowProtection = rowProtection;
    }

    //
    // Public methods used to help us manage passwords
    //

    /**
     * Function that sets the BCDN representation of the string.
     */
    public void setDN(String dn) {
        setSubjectDN(CertTools.stringToBCDNString(dn));
    }

    /**
     * Sets password in hashed form in the database, this way it cannot be read in clear form
     */
    public void setPassword(String password) throws NoSuchAlgorithmException {
        String passwordHash = CryptoTools.makePasswordHash(password);
        setPasswordHash(passwordHash);
        setClearPassword(null);
    }

    /**
     * Sets the password in clear form in the database, needed for machine processing, also sets the hashed password to the same value
     */
    public void setOpenPassword(String password) {
        String passwordHash = CryptoTools.makePasswordHash(password);
        setPasswordHash(passwordHash);
        setClearPassword(password);
    }

    /**
     * 
     * @return which hashing algorithm was used for this UserData object
     */
    public SupportedPasswordHashAlgorithm findHashAlgorithm() {
        final String hash = getPasswordHash();
        if (StringUtils.startsWith(hash, "$2")) {
            return SupportedPasswordHashAlgorithm.SHA1_BCRYPT;
        } else {
            return SupportedPasswordHashAlgorithm.SHA1_OLD;
        }
    }

    /**
     * Verifies password by verifying against passwordhash
     */
    public boolean comparePassword(final String password) throws NoSuchAlgorithmException {
        if (log.isTraceEnabled()) {
            log.trace(">comparePassword()");
        }
        boolean ret = false;
        if (password != null) {
            final String hash = getPasswordHash();
            // Check if it is a new or old style hashing
            switch (findHashAlgorithm()) {
            case SHA1_BCRYPT:
                // new style with good salt
                ret = BCrypt.checkpw(password, hash);
                break;
            case SHA1_OLD:
            default:
                ret = CryptoTools.makeOldPasswordHash(password).equals(getPasswordHash());
                break;
            }
        }
        if (log.isTraceEnabled()) {
            log.trace("<comparePassword()");
        }
        return ret;
    }

    //
    // Helper functions
    //

    /**
     * Non-searchable information about a user.
     */
    @Transient
    public ExtendedInformation getExtendedInformation() {
        return EndEntityInformation.getExtendedInformation(getExtendedInformationData());
    }

    /**
     * Non-searchable information about a user.
     */
    public void setExtendedInformation(ExtendedInformation extendedinformation) {
        try {
            String eidata = EndEntityInformation.extendedInformationToStringData(extendedinformation);
            setExtendedInformationData(eidata);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("Problems storing extended information for user :" + getUsername(), e);
        }
    }

    /**
     * Non-searchable information about a user.
     */
    public EndEntityInformation toEndEntityInformation() {
        final EndEntityInformation data = new EndEntityInformation();
        data.setUsername(getUsername());
        data.setCAId(getCaId());
        data.setCertificateProfileId(getCertificateProfileId());
        data.setDN(getSubjectDN());
        data.setEmail(getSubjectEmail());
        data.setEndEntityProfileId(getEndEntityProfileId());
        data.setExtendedinformation(getExtendedInformation());
        data.setHardTokenIssuerId(getHardTokenIssuerId());
        data.setPassword(getClearPassword());
        data.setStatus(getStatus());
        data.setSubjectAltName(getSubjectAltName());
        data.setTimeCreated(new Date(getTimeCreated()));
        data.setTimeModified(new Date(getTimeModified()));
        data.setTokenType(getTokenType());
        data.setType(new EndEntityType(getType()));
        data.setCardNumber(getCardNumber());
        return data;
    }

    //
    // Start Database integrity protection methods
    //

    @Transient
    @Override
    protected String getProtectString(final int version) {
        final ProtectionStringBuilder build = new ProtectionStringBuilder();
        // rowVersion is automatically updated by JPA, so it's not important, it is only used for optimistic locking
        build.append(getUsername()).append(getSubjectDN()).append(getCardNumber()).append(getCaId())
                .append(getSubjectAltName()).append(getCardNumber());
        build.append(getSubjectEmail()).append(getStatus()).append(getType()).append(getClearPassword())
                .append(getPasswordHash()).append(getTimeCreated()).append(getTimeModified());
        build.append(getEndEntityProfileId()).append(getCertificateProfileId()).append(getTokenType())
                .append(getHardTokenIssuerId()).append(getExtendedInformationData());
        return build.toString();
    }

    @Transient
    @Override
    protected int getProtectVersion() {
        return 1;
    }

    @PrePersist
    @PreUpdate
    @Override
    protected void protectData() {
        super.protectData();
    }

    @PostLoad
    @Override
    protected void verifyData() {
        super.verifyData();
    }

    @Override
    @Transient
    protected String getRowId() {
        return getUsername();
    }

    //
    // End Database integrity protection methods
    //

    //
    // Search functions.
    //

    /** @return the found entity instance or null if the entity does not exist */
    public static UserData findByUsername(EntityManager entityManager, String username) {
        if (username == null) {
            return null;
        }
        return entityManager.find(UserData.class, username);
    }

    /**
     * 
     * @param entityManager an entity manager
     * @param subjectDN the subject DN to search for 
     * @param caId the CA ID to search for
     * @return a list if found UserData objects, or an empty list if none found.
     */
    @SuppressWarnings("unchecked")
    public static List<UserData> findBySubjectDNAndCAId(EntityManager entityManager, String subjectDN, int caId) {
        final Query query = entityManager
                .createQuery("SELECT a FROM UserData a WHERE a.subjectDN=:subjectDN AND a.caId=:caId");
        query.setParameter("subjectDN", subjectDN);
        query.setParameter("caId", caId);
        return query.getResultList();
    }

    /**
     * 
     * @param entityManager an entity manager
     * @param subjectDN the subject DN to check for.
     * @return a list of all users matching the given subject DN, or  an empty list if none are found.
     */
    @SuppressWarnings("unchecked")
    public static List<UserData> findBySubjectDN(EntityManager entityManager, String subjectDN) {
        final Query query = entityManager.createQuery("SELECT a FROM UserData a WHERE a.subjectDN=:subjectDN");
        query.setParameter("subjectDN", subjectDN);
        return query.getResultList();
    }

    /** @return return the query results as a List. */
    @SuppressWarnings("unchecked")
    public static List<UserData> findBySubjectEmail(EntityManager entityManager, String subjectEmail) {
        final Query query = entityManager
                .createQuery("SELECT a FROM UserData a WHERE a.subjectEmail=:subjectEmail");
        query.setParameter("subjectEmail", subjectEmail);
        return query.getResultList();
    }

    /** @return return the query results as a List. */
    @SuppressWarnings("unchecked")
    public static List<UserData> findByStatus(EntityManager entityManager, int status) {
        final Query query = entityManager.createQuery("SELECT a FROM UserData a WHERE a.status=:status");
        query.setParameter("status", status);
        return query.getResultList();
    }

    /** @return return the query results as a List. */
    @SuppressWarnings("unchecked")
    public static List<UserData> findAll(EntityManager entityManager) {
        final Query query = entityManager.createQuery("SELECT a FROM UserData a");
        return query.getResultList();
    }

    /** @return return the query results as a List. */
    @SuppressWarnings("unchecked")
    public static List<UserData> findAllBatchUsersByStatus(EntityManager entityManager, int status,
            int maximumQueryRowcount) {
        final Query query = entityManager
                .createQuery("SELECT a FROM UserData a WHERE a.status=:status AND (clearPassword IS NOT NULL)");
        query.setParameter("status", status);
        query.setMaxResults(maximumQueryRowcount);
        return query.getResultList();
    }

    /** @return return a List<UserData> with tokenType TOKEN_HARD_DEFAULT and status NEW or KEYRECOVERY. */
    @SuppressWarnings("unchecked")
    public static List<UserData> findNewOrKeyrecByHardTokenIssuerId(EntityManager entityManager,
            int hardTokenIssuerId, int maxResults) {
        final Query query = entityManager.createQuery(
                "SELECT a FROM UserData a WHERE a.hardTokenIssuerId=:hardTokenIssuerId AND a.tokenType>=:tokenType AND (a.status=:status1 OR a.status=:status2)");
        query.setParameter("hardTokenIssuerId", hardTokenIssuerId);
        query.setParameter("tokenType", SecConst.TOKEN_HARD_DEFAULT);
        query.setParameter("status1", EndEntityConstants.STATUS_NEW);
        query.setParameter("status2", EndEntityConstants.STATUS_KEYRECOVERY);
        if (maxResults > 0) {
            query.setMaxResults(maxResults);
        }
        return query.getResultList();
    }

    /** @return return a count of UserDatas with tokenType TOKEN_HARD_DEFAULT and status NEW or KEYRECOVERY. */
    public static long countNewOrKeyrecByHardTokenIssuerId(EntityManager entityManager, int hardTokenIssuerId) {
        final Query query = entityManager.createQuery(
                "SELECT COUNT(a) FROM UserData a WHERE a.hardTokenIssuerId=:hardTokenIssuerId AND a.tokenType>=:tokenType AND (a.status=:status1 OR a.status=:status2)");
        query.setParameter("hardTokenIssuerId", hardTokenIssuerId);
        query.setParameter("tokenType", SecConst.TOKEN_HARD_DEFAULT);
        query.setParameter("status1", EndEntityConstants.STATUS_NEW);
        query.setParameter("status2", EndEntityConstants.STATUS_KEYRECOVERY);
        return ((Long) query.getSingleResult()).longValue(); // Always returns a result
    }

    /** @return return a count of UserDatas with tokenType TOKEN_HARD_DEFAULT and status NEW or KEYRECOVERY. */
    public static long countByHardTokenIssuerId(EntityManager entityManager, int hardTokenIssuerId) {
        final Query query = entityManager
                .createQuery("SELECT COUNT(a) FROM UserData a WHERE a.hardTokenIssuerId=:hardTokenIssuerId");
        query.setParameter("hardTokenIssuerId", hardTokenIssuerId);
        return ((Long) query.getSingleResult()).longValue(); // Always returns a result
    }

    public static String findSubjectEmailByUsername(EntityManager entityManager, String username) {
        final Query query = entityManager
                .createQuery("SELECT a.subjectEmail FROM UserData a WHERE a.username=:username");
        query.setParameter("username", username);
        return (String) QueryResultWrapper.getSingleResult(query);
    }

    /** @return return a List<UserData> matching the custom query. */
    @SuppressWarnings("unchecked")
    public static List<UserData> findByCustomQuery(EntityManager entityManager, String customQuery,
            int maxResults) {
        final Query query = entityManager.createQuery("SELECT a FROM UserData a WHERE " + customQuery);
        if (maxResults > 0) {
            query.setMaxResults(maxResults);
        }
        return query.getResultList();
    }

    /** @return return a count of UserDatas with the specified End Entity Profile. */
    public static long countByEndEntityProfileId(EntityManager entityManager, int endEntityProfileId) {
        final Query query = entityManager
                .createQuery("SELECT COUNT(a) FROM UserData a WHERE a.endEntityProfileId=:endEntityProfileId");
        query.setParameter("endEntityProfileId", endEntityProfileId);
        return ((Long) query.getSingleResult()).longValue(); // Always returns a result
    }

    /** @return return a count of UserDatas with the specified Certificate Profile. */
    public static long countByCertificateProfileId(EntityManager entityManager, int certificateProfileId) {
        final Query query = entityManager
                .createQuery("SELECT COUNT(a) FROM UserData a WHERE a.certificateProfileId=:certificateProfileId");
        query.setParameter("certificateProfileId", certificateProfileId);
        return ((Long) query.getSingleResult()).longValue(); // Always returns a result
    }

    /** @return return a count of UserDatas with the specified CA. */
    public static long countByCaId(EntityManager entityManager, int caId) {
        final Query query = entityManager.createQuery("SELECT COUNT(a) FROM UserData a WHERE a.caId=:caId");
        query.setParameter("caId", caId);
        return ((Long) query.getSingleResult()).longValue(); // Always returns a result
    }

    /** @return return a count of UserDatas with the specified Hard Token Profile. */
    public static long countByHardTokenProfileId(EntityManager entityManager, int hardTokenProfileId) {
        final Query query = entityManager
                .createQuery("SELECT COUNT(a) FROM UserData a WHERE a.tokenType=:tokenType");
        query.setParameter("tokenType", hardTokenProfileId);
        return ((Long) query.getSingleResult()).longValue(); // Always returns a result
    }

    /** @return a list of subjectDNs that contain SN=serialnumber* for a CA and excludes a username. */
    @SuppressWarnings("unchecked")
    public static List<String> findSubjectDNsByCaIdAndNotUsername(final EntityManager entityManager, final int caId,
            final String username, final String serialnumber) {
        final Query query = entityManager.createQuery(
                "SELECT a.subjectDN FROM UserData a WHERE a.caId=:caId AND a.username!=:username AND a.subjectDN LIKE '%SN="
                        + serialnumber + "%'");
        query.setParameter("caId", caId);
        query.setParameter("username", username);
        return query.getResultList();
    }
}