public class User {

	private final int id;

	private final String name;

	private final String email;

	private final String registrationControlCode;

	private final boolean registrationConfirmed;

	private final UserPassword password;

	private int rank;

	public int getId() {
		return id;
	}

	public String getName() {
		return name;
	}

	public String getEmail() {
		return email;
	}

	public String getRegistrationControlCode() {
		return registrationControlCode;
	}

	public boolean isRegistrationConfirmed() {
		return registrationConfirmed;
	}

	public UserPassword getPassword() {
		return password;
	}

	public int getRank() {
		return rank;
	}

	public void setRank(int rank) {
		this.rank = rank;
	}

	public User withId(int newId) {
		return new User(newId, name, email, registrationControlCode, registrationConfirmed, password, rank);
	}

	public User(int id, String name, String email, String registrationControlCode, boolean registrationConfirmed, UserPassword password, int rank) {
		this.id = id;
		this.name = name;
		this.email = email;
		this.registrationControlCode = registrationControlCode;
		this.registrationConfirmed = registrationConfirmed;
		this.password = password;
		this.rank = rank;
	}

}
 public interface UserDatabaseModel extends Serializable {

	/**
	 * Adds a new password reset token. This token should be later accessible by
	 * method {@link #getNewestPasswordResetToken(String)}, or could be canceled
	 * by {@link #cancelAllPasswordResetTokens(int)}.
	 * 
	 * @param token
	 *            token to be added
	 * @return True on success, otherwise false.
	 */
	boolean addPasswordResetToken(PasswordResetToken token);

	/**
	 * Adds new user to database.
	 * 
	 * @param newUser
	 *            user to be added
	 * @return Positive integer value representing the new user ID on success,
	 *         or negative integer value on failure.
	 */
	int addUser(User newUser);

	/**
	 * Cancels all password reset tokens for user with specified id. The
	 * canceled tokens should no longer be returned by method
	 * {@link #getNewestPasswordResetToken(String)}.
	 * 
	 * @param userId
	 *            id of user whose password reset tokens should be canceled
	 * @return True on success, otherwise false.
	 * @see #addPasswordResetToken(PasswordResetToken)
	 * @see #getNewestPasswordResetToken(String)
	 */
	boolean cancelAllPasswordResetTokens(int userId);

	/**
	 * Changes the status of user registration to confirmed.
	 * 
	 * @param email
	 *            email address of user whose registration should be confirmed
	 * @return True on success, otherwise false (no such user, database error).
	 * @see User#isRegistrationConfirmed()
	 */
	boolean confirmUserRegistration(String email);

	/**
	 * Deletes user with specified id from database.
	 * 
	 * @param userId
	 *            id of user to be deleted
	 * @return True on success, otherwise false (no such user, database error).
	 */
	boolean deleteUser(int userId);

	/**
	 * Returns number of users which have been added by method
	 * {@link #addUser(User)} since the specified date.
	 * 
	 * @param since
	 *            date since when to count registered users
	 * @return number of users registered since the specified date
	 */
	int getRegisteredUserCount(Date since);

	/**
	 * Returns password reset token for user with specified email, which has the
	 * latest {@link PasswordResetToken#getCreationTime() creation time}.
	 * 
	 * @param email
	 *            email address of user whose password reset token should be
	 *            returned
	 * @return Newest password reset token for user with specified email or
	 *         null, if no password reset token exists for that user.
	 * @see #addPasswordResetToken(PasswordResetToken)
	 * @see #cancelAllPasswordResetTokens(int)
	 */
	PasswordResetToken getNewestPasswordResetToken(String email);

	/**
	 * Returns user registered with the specified {@link User#getEmail() email
	 * address}.
	 * 
	 * @param email
	 *            email address of the user which should be returned
	 * @return User with the specified email address or null, if no such user
	 *         exists.
	 * @see #getUserByName(String)
	 * @see #getUserIdByEmail(String)
	 */
	User getUserByEmail(String email);

	/**
	 * Returns user registered with the specified {@link User#getName() name}.
	 * 
	 * @param name
	 *            name of the user which should be returned
	 * @return User with the specified name or null, if no such user exists.
	 * @see #getUserByEmail(String)
	 * @see #getUserIdByEmail(String)
	 */
	User getUserByName(String name);

	/**
	 * @since 0.2.0
	 * @see #saveUserWithSocialAccount(User, SocialAccountDetails)
	 */
	User getUserBySocialAccount(SocialAccountDetails details);

	/**
	 * Returns id of user registered with the specified {@link User#getEmail()
	 * email address}.
	 * 
	 * @param email
	 *            email address of the user whose id should be returned
	 * @return Id of user with the specified email address or -1, if no such
	 *         user exists.
	 * @see #getUserByEmail(String)
	 * @see #getUserByName(String)
	 */
	int getUserIdByEmail(String email);

	/**
	 * Returns password of user with the specified id.
	 * 
	 * @param userId
	 *            id of user whose password should be returned
	 * @return Password of user with the specified id or null, if no such user
	 *         exists.
	 * @see #setUserPassword(int, UserPassword)
	 */
	UserPassword getUserPassword(int userId);

	/**
	 * Check whether there is a user registered with the given email. The result
	 * should be true also for registered users whose registration was not
	 * confirmed yet.
	 * 
	 * @param email
	 *            email address which should be checked
	 * @return True if the email is already registered, otherwise false.
	 */
	boolean isEmailRegistered(String email);

	/**
	 * Check whether there is a user registered with the given name. The result
	 * should be true also for registered users whose registration was not
	 * confirmed yet.
	 * 
	 * @param name
	 *            user name which should be checked
	 * @return True if the user name is already registered, otherwise false.
	 */
	boolean isUserNameRegistered(String name);

	/**
	 * Records a log in attempt for the user with specified id.
	 * 
	 * @param userId
	 *            user who was trying to log in
	 * @param logInSuccessful
	 *            information if the log in was successful or not (for instance
	 *            wrong password)
	 * @param usersIp
	 *            users ip address
	 * @return True on success, otherwise false.
	 */
	boolean makeLogInRecord(int userId, boolean logInSuccessful, String usersIp);

	/**
	 * @return Positive integer value representing the new user ID on success,
	 *         or negative integer value on failure.
	 * @since 0.2.0
	 * @see #getUserBySocialAccount(SocialAccountDetails)
	 */
	int saveUserWithSocialAccount(User user, SocialAccountDetails details);

	/**
	 * Sets a new password for the user with the specified id.
	 * 
	 * @param userId
	 *            user whose password should be set
	 * @param password
	 *            password to be set
	 * @return True on success, otherwise false.
	 * @see #getUserPassword(int)
	 */
	boolean setUserPassword(int userId, UserPassword password);

}
 public class PasswordResetToken {

	private final int userId;
	private final Date creationTime;
	private final String key;

	public Date getCreationTime() {
		return creationTime;
	}

	public String getKey() {
		return key;
	}

	public int getUserId() {
		return userId;
	}

	public PasswordResetToken(int userId, String key, Date creationTime) {
		this.userId = userId;
		this.creationTime = creationTime;
		this.key = key;
	}
}
 public class SocialAccountDetails {

	private final String accountType;

	private final String userId;

	private final String userName;

	private final String email;

	public SocialAccountDetails(String accountType, String userId, String userName, String email) {
		this.accountType = accountType;
		this.userId = userId;
		this.userName = userName;
		this.email = email;
	}

	public String getAccountType() {
		return accountType;
	}

	public String getUserId() {
		return userId;
	}

	public String getUserName() {
		return userName;
	}

	public String getEmail() {
		return email;
	}

}
 public class UserPassword {

	private final String hash;
	private final String salt;

	public String getHash() {
		return hash;
	}

	public String getSalt() {
		return salt;
	}

	public UserPassword(String hash, String salt) {
		this.hash = hash;
		this.salt = salt;
	}

}
 public class LogInRecordEntityDao extends CommonDao<LogInRecordEntity> implements Serializable {

	private static final long serialVersionUID = 1L;

}
 public class PasswordResetTokenEntityDao extends CommonDao<PasswordResetTokenEntity> implements Serializable {

	private static final long serialVersionUID = 1L;

	public void cancelTokensForUser(int userId) {
		String queryStr = "UPDATE PasswordResetTokenEntity prt SET prt.valid = false WHERE prt.user.id = :userId";

		Query query = createQuery(queryStr);
		query.setParameter("userId", userId);
		query.executeUpdate();
	}

	public PasswordResetTokenEntity getNewestToken(String email) {
		String queryStr = "FROM PasswordResetTokenEntity prt WHERE prt.user.email = :email ORDER BY prt.time DESC";

		Query query = createQuery(queryStr);
		query.setParameter("email", email);
		query.setMaxResults(1);

		return getSingleResult(query);
	}

}
 public class SocialAccountDao  extends CommonDao<SocialAccountEntity> implements Serializable {

	private static final long serialVersionUID = 1L;

	public UserEntity getUserByAccount(String accountType, String originalAccountId) {

		Query query = createQuery("SELECT sa.user FROM SocialAccountEntity sa WHERE sa.accountType = :accountType AND sa.originalAccountId = :originalAccountId");

		query.setParameter("accountType", accountType);
		query.setParameter("originalAccountId", originalAccountId);

		return getSingleResult(query);
	}

}
 public class UserEntityDao extends CommonDao<UserEntity> implements Serializable {

	private static final long serialVersionUID = 1L;

	public boolean confirmRegistration(String email) {
		String queryStr = "UPDATE UserEntity u SET u.registrationConfirmed = true WHERE u.email = :email";
		Query query = createQuery(queryStr);
		query.setParameter("email", email);

		int updatedRows = query.executeUpdate();
		return updatedRows == 1;
	}

	public int getRegisteredUserCount(Date since) {
		if (since == null) {
			since = new Date(0);
		}

		String queryStr = "SELECT count(*) FROM UserEntity u WHERE u.registrationConfirmed = true AND u.deleted = false AND u.registrationDate >= :since";

		Query query = createQuery(queryStr);
		query.setParameter("since", since);

		Long result = getSingleResult(query);
		return result.intValue();
	}

	public int getUserIdByEmail(String email) {
		String queryStr = "SELECT u.id FROM UserEntity u WHERE u.email= :email AND u.deleted = false";

		Query query = createQuery(queryStr);
		query.setParameter("email", email);

		Integer result = getSingleResult(query);
		if (result == null) {
			return -1;
		}

		return result.intValue();
	}

	public UserPassword getUserPassword(int userId) {
		String queryStr = String.format("SELECT new %s(u.passwordHash, u.passwordSalt) FROM UserEntity u WHERE u.id= :id AND u.deleted = false", UserPassword.class.getName());

		Query query = createQuery(queryStr);
		query.setParameter("id", userId);

		return getSingleResult(query);
	}

	public boolean deleteUserEntity(int userId) {
		String queryStr = "UPDATE UserEntity u SET u.deleted = true WHERE u.id = :userId";

		Query query = createQuery(queryStr);
		query.setParameter("userId", userId);

		int updatedRows = query.executeUpdate();
		return updatedRows == 1;
	}

	public UserEntity findByEmail(String email) {
		String queryStr = "FROM UserEntity u WHERE u.email = :email AND u.deleted = false";

		Query query = createQuery(queryStr);
		query.setParameter("email", email);
		query.setMaxResults(1);

		return getSingleResult(query);
	}

	public UserEntity findByName(String name) {
		String queryStr = "FROM UserEntity u WHERE u.name = :name AND u.deleted = false";

		Query query = createQuery(queryStr);
		query.setParameter("name", name);
		query.setMaxResults(1);

		return getSingleResult(query);
	}

	public boolean isEmailRegistered(String email) {
		String queryStr = "SELECT count(*) FROM UserEntity u WHERE u.email = :email AND u.deleted = false";

		Query query = createQuery(queryStr);
		query.setParameter("email", email);

		Long result = getSingleResult(query);
		return result > 0;
	}

	public boolean isUserNameRegistered(String name) {
		String queryStr = "SELECT count(*) FROM UserEntity u WHERE u.name = :name AND u.deleted = false";

		Query query = createQuery(queryStr);
		query.setParameter("name", name);

		Long result = getSingleResult(query);
		return result > 0;
	}

	public boolean setUserPassword(int userId, UserPassword password) {
		String queryStr = "UPDATE UserEntity u SET u.passwordHash = :hash, u.passwordSalt = :salt WHERE u.id = :userId";

		Query query = createQuery(queryStr);
		query.setParameter("userId", userId);
		query.setParameter("hash", password.getHash());
		query.setParameter("salt", password.getSalt());

		int updatedRows = query.executeUpdate();
		return updatedRows == 1;
	}
}
 @Entity
@Table(name = "um_login_record")
public class LogInRecordEntity {

	@Id
	@GeneratedValue
	private long id;

	@ManyToOne
	@JoinColumn(name = "user_id")
	UserEntity user;

	@Column(name = "date_time")
	Date time;

	@Column(name = "ip", length = 45)
	private String ip;

	@Column(name = "successful")
	private boolean successful;

	public long getId() {
		return id;
	}

	public void setId(long id) {
		this.id = id;
	}

	public UserEntity getUser() {
		return user;
	}

	public void setUser(UserEntity user) {
		this.user = user;
	}

	public Date getTime() {
		return time;
	}

	public void setTime(Date time) {
		this.time = time;
	}

	public String getIp() {
		return ip;
	}

	public void setIp(String ip) {
		this.ip = ip;
	}

	public boolean isSuccessful() {
		return successful;
	}

	public void setSuccessful(boolean successful) {
		this.successful = successful;
	}

}
 @Entity
@Table(name = "um_password_reset_token")
public class PasswordResetTokenEntity {

	@Id
	@GeneratedValue
	long id;

	@ManyToOne
	@JoinColumn(name = "user_id")
	UserEntity user;

	@Column(name = "date_time")
	Date time;

	@Column(name = "token_key")
	String key;

	@Column(name = "valid")
	boolean valid;

	public PasswordResetTokenEntity() {
		super();
	}

	public PasswordResetTokenEntity(PasswordResetToken passwordResetToken) {
		this.user = new UserEntity();
		this.user.setId(passwordResetToken.getUserId());
		this.time = passwordResetToken.getCreationTime();
		this.key = passwordResetToken.getKey();
	}

	public long getId() {
		return id;
	}

	public void setId(long id) {
		this.id = id;
	}

	public UserEntity getUser() {
		return user;
	}

	public void setUser(UserEntity user) {
		this.user = user;
	}

	public Date getTime() {
		return time;
	}

	public void setTime(Date time) {
		this.time = time;
	}

	public String getKey() {
		return key;
	}

	public void setKey(String key) {
		this.key = key;
	}

	public boolean isValid() {
		return valid;
	}

	public void setValid(boolean valid) {
		this.valid = valid;
	}
}
 @Entity
@Table(name="um_social_account", uniqueConstraints = @UniqueConstraint(columnNames = {"account_type", "original_account_id"}))
public class SocialAccountEntity {

	public static final int ACCOUNT_TYPE_MAX_LENGTH = 10;
	public static final int ORIGINAL_ACCOUNT_ID_MAX_LENGTH = 50;

	@Id
	@GeneratedValue
	private long id;

	@ManyToOne
	@JoinColumn(name = "user_id")
	private UserEntity user;

	@Column(name = "account_type", nullable = false, length = ACCOUNT_TYPE_MAX_LENGTH)
	private String accountType;

	@Column(name = "original_account_id", nullable = false, length = ORIGINAL_ACCOUNT_ID_MAX_LENGTH)
	private String originalAccountId;

	public long getId() {
		return id;
	}

	public void setId(long id) {
		this.id = id;
	}

	public UserEntity getUser() {
		return user;
	}

	public void setUser(UserEntity user) {
		this.user = user;
	}

	public String getAccountType() {
		return accountType;
	}

	public void setAccountType(String accountType) {
		this.accountType = accountType;
	}

	public String getOriginalAccountId() {
		return originalAccountId;
	}

	public void setOriginalAccountId(String originalAccountId) {
		this.originalAccountId = originalAccountId;
	}

}
 @Entity
@Table(name = "um_user")
public class UserEntity {

	@Id
	@Column(name = "id")
	@SequenceGenerator(name = "um_user_id_seq", sequenceName = "um_user_id_seq")
	@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "um_user_id_seq")
	private int id;

	@Column(name = "name")
	private String name;

	@Column(name = "email")
	private String email;

	@Column(name = "reg_date")
	private Date registrationDate;

	@Column(name = "reg_control_code")
	private String registrationControlCode;

	@Column(name = "confirmed")
	private boolean registrationConfirmed;

	@Column(name = "deleted")
	private boolean deleted;

	@Column(name = "rank")
	private int rank;

	@Column(name = "password")
	private String passwordHash;

	@Column(name = "salt")
	private String passwordSalt;

	public UserEntity() {
		super();
	}

	public void copyUser(User user) {
		id = user.getId();
		name = user.getName();
		email = user.getEmail();
		registrationControlCode = user.getRegistrationControlCode();
		registrationConfirmed = user.isRegistrationConfirmed();
		rank = user.getRank();

		UserPassword password = user.getPassword();
		if (password != null) {
			passwordHash = password.getHash();
			passwordSalt = password.getSalt();
		} else {
			passwordHash = null;
			passwordSalt = null;
		}
	}

	public int getId() {
		return id;
	}

	public String getName() {
		return name;
	}

	public String getEmail() {
		return email;
	}

	public Date getRegistrationDate() {
		return registrationDate;
	}

	public String getRegistrationControlCode() {
		return registrationControlCode;
	}

	public boolean isRegistrationConfirmed() {
		return registrationConfirmed;
	}

	public boolean isDeleted() {
		return deleted;
	}

	public int getRank() {
		return rank;
	}

	public String getPasswordHash() {
		return passwordHash;
	}

	public String getPasswordSalt() {
		return passwordSalt;
	}

	public void setId(int id) {
		this.id = id;
	}

	public void setName(String name) {
		this.name = name;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public void setRegistrationDate(Date registrationDate) {
		this.registrationDate = registrationDate;
	}

	public void setRegistrationControlCode(String registrationControlCode) {
		this.registrationControlCode = registrationControlCode;
	}

	public void setRegistrationConfirmed(boolean registrationConfirmed) {
		this.registrationConfirmed = registrationConfirmed;
	}

	public void setDeleted(boolean deleted) {
		this.deleted = deleted;
	}

	public void setRank(int rank) {
		this.rank = rank;
	}

	public void setPasswordHash(String passwordHash) {
		this.passwordHash = passwordHash;
	}

	public void setPasswordSalt(String passwordSalt) {
		this.passwordSalt = passwordSalt;
	}

	public User toUser() {
		UserPassword password = new UserPassword(passwordHash, passwordSalt);
		User result = new User(id, name, email, registrationControlCode, registrationConfirmed, password, rank);
		return result;
	}

}

import com.jardoapps.usermodule.jpa.dao.PasswordResetTokenEntityDao;
import com.jardoapps.usermodule.jpa.dao.SocialAccountDao;
import com.jardoapps.usermodule.jpa.dao.UserEntityDao;
import com.jardoapps.usermodule.jpa.entities.LogInRecordEntity;
import com.jardoapps.usermodule.jpa.entities.PasswordResetTokenEntity;
import com.jardoapps.usermodule.jpa.entities.SocialAccountEntity;
import com.jardoapps.usermodule.jpa.entities.UserEntity;
import java.io.Serializable;
import java.util.Date;
import javax.inject.Inject;
import javax.transaction.Transactional;
import com.jardoapps.usermodule.User;
import com.jardoapps.usermodule.UserDatabaseModel;
import com.jardoapps.usermodule.containers.PasswordResetToken;
import com.jardoapps.usermodule.containers.SocialAccountDetails;
import com.jardoapps.usermodule.containers.UserPassword;
import com.jardoapps.usermodule.jpa.dao.LogInRecordEntityDao;

/*
 * This file is part of the User Module library.
 * Copyright (C) 2014 Jaroslav Brtiš
 *
 * User Module library 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 3 of the License, or
 * (at your option) any later version.
 *
 * User Module library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with User Module library. If not, see <http://www.gnu.org/licenses/>.
 */

package com.jardoapps.usermodule.jpa;




/**
 * A Hibernate implementation of the user database model. It uses internal entity 
 * classes, which are a part of the user module library. In order to use this implementation,
 * you have to tell Hibernate where are the entity classes.
 * This can be done in the hibernate.cfg.xml file:
 * <p>
 * <code>
 * {@literal <mapping class="com.jardoapps.usermodule.jpa.entities.UserEntity" />}<br>
 * {@literal <mapping class="com.jardoapps.usermodule.jpa.entities.PasswordResetTokenEntity" />}<br>
 * {@literal <mapping class="com.jardoapps.usermodule.jpa.entities.LogInRecordEntity" />}
 * </code>
 * <p>
 * If you're using Spring, you can set the SessionFactory's packagesToScan property to
 * <code>com.jardoapps.usermodule.jpa.entities</code>.
 * <p>
 * This class is a part of this library's public API.
 * 
 * @author Jaroslav Brtiš
 * 
 */
public class UserDatabaseModelJpa implements UserDatabaseModel, Serializable {

	private static final long serialVersionUID = 1L;

	@Inject
	private UserEntityDao userEntityDao;

	@Inject
	private PasswordResetTokenEntityDao passwordResetTokenEntityDao;

	@Inject
	private LogInRecordEntityDao logInRecordEntityDao;

	@Inject
	private SocialAccountDao socialAccountDao;

	@Transactional
	public boolean addPasswordResetToken(PasswordResetToken token) {
		PasswordResetTokenEntity tokenEntity = new PasswordResetTokenEntity(token);
		tokenEntity.setValid(true);

		passwordResetTokenEntityDao.add(tokenEntity);

		return true;
	}

	@Transactional
	public int addUser(User newUser) {
		UserEntity userEntity = new UserEntity();
		userEntity.copyUser(newUser);
		userEntity.setRegistrationDate(new Date());

		userEntityDao.add(userEntity);

		return userEntity.getId();
	}

	@Transactional
	public boolean cancelAllPasswordResetTokens(int userId) {
		passwordResetTokenEntityDao.cancelTokensForUser(userId);
		return true;
	}

	@Transactional
	public boolean confirmUserRegistration(String email) {
		return userEntityDao.confirmRegistration(email);
	}

	@Transactional
	public boolean deleteUser(int userId) {
		return userEntityDao.deleteUserEntity(userId);
	}

	@Transactional
	public int getRegisteredUserCount(Date since) {
		return userEntityDao.getRegisteredUserCount(since);
	}

	public PasswordResetToken getNewestPasswordResetToken(String email) {
		PasswordResetTokenEntity tokenEntity = passwordResetTokenEntityDao.getNewestToken(email);

		if (tokenEntity == null) {
			return null;
		}

		return new PasswordResetToken(tokenEntity.getUser().getId(), tokenEntity.getKey(), tokenEntity.getTime());
	}

	public User getUserByEmail(String email) {
		UserEntity userEntity = userEntityDao.findByEmail(email);

		if (userEntity == null) {
			return null;
		}

		return userEntity.toUser();
	}

	public User getUserByName(String name) {
		UserEntity userEntity = userEntityDao.findByName(name);

		if (userEntity == null) {
			return null;
		}

		return userEntity.toUser();
	}


	public User getUserBySocialAccount(SocialAccountDetails details) {
