docs/docs-core/src/main/java/com/sismics/docs/core/dao/UserDao.java

398 lines
14 KiB
Java

package com.sismics.docs.core.dao;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import at.favre.lib.crypto.bcrypt.BCrypt;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.sismics.docs.core.constant.AuditLogType;
import com.sismics.docs.core.constant.Constants;
import com.sismics.docs.core.dao.criteria.UserCriteria;
import com.sismics.docs.core.dao.dto.UserDto;
import com.sismics.docs.core.model.jpa.User;
import com.sismics.docs.core.util.AuditLogUtil;
import com.sismics.docs.core.util.EncryptionUtil;
import com.sismics.docs.core.util.jpa.QueryParam;
import com.sismics.docs.core.util.jpa.QueryUtil;
import com.sismics.docs.core.util.jpa.SortCriteria;
import com.sismics.util.context.ThreadLocalContext;
import jakarta.persistence.EntityManager;
import jakarta.persistence.NoResultException;
import jakarta.persistence.Query;
import java.sql.Timestamp;
import java.util.*;
/**
* User DAO.
*
* @author jtremeaux
*/
public class UserDao {
/**
* Logger.
*/
private static final Logger log = LoggerFactory.getLogger(UserDao.class);
/**
* Authenticates an user.
*
* @param username User login
* @param password User password
* @return The authenticated user or null
*/
public User authenticate(String username, String password) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
Query q = em.createQuery("select u from User u where u.username = :username and u.deleteDate is null");
q.setParameter("username", username);
try {
User user = (User) q.getSingleResult();
BCrypt.Result result = BCrypt.verifyer().verify(password.toCharArray(), user.getPassword());
if (!result.verified || user.getDisableDate() != null) {
return null;
}
return user;
} catch (NoResultException e) {
return null;
}
}
/**
* Creates a new user.
*
* @param user User to create
* @param userId User ID
* @return User ID
* @throws Exception e
*/
public String create(User user, String userId) throws Exception {
// Create the user UUID
user.setId(UUID.randomUUID().toString());
// Checks for user unicity
EntityManager em = ThreadLocalContext.get().getEntityManager();
Query q = em.createQuery("select u from User u where u.username = :username and u.deleteDate is null");
q.setParameter("username", user.getUsername());
List<?> l = q.getResultList();
if (l.size() > 0) {
throw new Exception("AlreadyExistingUsername");
}
// Create the user
user.setCreateDate(new Date());
user.setPassword(hashPassword(user.getPassword()));
user.setPrivateKey(EncryptionUtil.generatePrivateKey());
user.setStorageCurrent(0L);
em.persist(user);
// Create audit log
AuditLogUtil.create(user, AuditLogType.CREATE, userId);
return user.getId();
}
/**
* Updates a user.
*
* @param user User to update
* @param userId User ID
* @return Updated user
*/
public User update(User user, String userId) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
// Get the user
Query q = em.createQuery("select u from User u where u.id = :id and u.deleteDate is null");
q.setParameter("id", user.getId());
User userDb = (User) q.getSingleResult();
// Update the user (except password)
userDb.setEmail(user.getEmail());
userDb.setStorageQuota(user.getStorageQuota());
userDb.setStorageCurrent(user.getStorageCurrent());
userDb.setTotpKey(user.getTotpKey());
userDb.setDisableDate(user.getDisableDate());
// Create audit log
AuditLogUtil.create(userDb, AuditLogType.UPDATE, userId);
return user;
}
/**
* Updates a user's quota.
*
* @param user User to update
*/
public void updateQuota(User user) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
// Get the user
Query q = em.createQuery("select u from User u where u.id = :id and u.deleteDate is null");
q.setParameter("id", user.getId());
User userDb = (User) q.getSingleResult();
// Update the user
userDb.setStorageCurrent(user.getStorageCurrent());
}
/**
* Update the user password.
*
* @param user User to update
* @param userId User ID
* @return Updated user
*/
public User updatePassword(User user, String userId) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
// Get the user
Query q = em.createQuery("select u from User u where u.id = :id and u.deleteDate is null");
q.setParameter("id", user.getId());
User userDb = (User) q.getSingleResult();
// Update the user
userDb.setPassword(hashPassword(user.getPassword()));
// Create audit log
AuditLogUtil.create(userDb, AuditLogType.UPDATE, userId);
return user;
}
/**
* Update the hashed password silently.
*
* @param user User to update
* @return Updated user
*/
public User updateHashedPassword(User user) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
// Get the user
Query q = em.createQuery("select u from User u where u.id = :id and u.deleteDate is null");
q.setParameter("id", user.getId());
User userDb = (User) q.getSingleResult();
// Update the user
userDb.setPassword(user.getPassword());
return user;
}
/**
* Update the onboarding status.
*
* @param user User to update
* @return Updated user
*/
public User updateOnboarding(User user) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
// Get the user
Query q = em.createQuery("select u from User u where u.id = :id and u.deleteDate is null");
q.setParameter("id", user.getId());
User userDb = (User) q.getSingleResult();
// Update the user
userDb.setOnboarding(user.isOnboarding());
return user;
}
/**
* Gets a user by its ID.
*
* @param id User ID
* @return User
*/
public User getById(String id) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
try {
return em.find(User.class, id);
} catch (NoResultException e) {
return null;
}
}
/**
* Gets an active user by its username.
*
* @param username User's username
* @return User
*/
public User getActiveByUsername(String username) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
try {
Query q = em.createQuery("select u from User u where u.username = :username and u.deleteDate is null");
q.setParameter("username", username);
return (User) q.getSingleResult();
} catch (NoResultException e) {
return null;
}
}
/**
* Deletes a user.
*
* @param username User's username
* @param userId User ID
*/
public void delete(String username, String userId) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
// Get the user
Query q = em.createQuery("select u from User u where u.username = :username and u.deleteDate is null");
q.setParameter("username", username);
User userDb = (User) q.getSingleResult();
// Delete the user
Date dateNow = new Date();
userDb.setDeleteDate(dateNow);
// Delete linked data
q = em.createQuery("delete from AuthenticationToken at where at.userId = :userId");
q.setParameter("userId", userDb.getId());
q.executeUpdate();
q = em.createQuery("update Document d set d.deleteDate = :dateNow where d.userId = :userId and d.deleteDate is null");
q.setParameter("userId", userDb.getId());
q.setParameter("dateNow", dateNow);
q.executeUpdate();
q = em.createQuery("update File f set f.deleteDate = :dateNow where f.userId = :userId and f.deleteDate is null");
q.setParameter("userId", userDb.getId());
q.setParameter("dateNow", dateNow);
q.executeUpdate();
q = em.createQuery("update Acl a set a.deleteDate = :dateNow where a.targetId = :userId and a.deleteDate is null");
q.setParameter("userId", userDb.getId());
q.setParameter("dateNow", dateNow);
q.executeUpdate();
q = em.createQuery("update Comment c set c.deleteDate = :dateNow where c.userId = :userId and c.deleteDate is null");
q.setParameter("userId", userDb.getId());
q.setParameter("dateNow", dateNow);
q.executeUpdate();
// Create audit log
AuditLogUtil.create(userDb, AuditLogType.DELETE, userId);
}
/**
* Hash the user's password.
*
* @param password Clear password
* @return Hashed password
*/
private String hashPassword(String password) {
int bcryptWork = Constants.DEFAULT_BCRYPT_WORK;
String envBcryptWork = System.getenv(Constants.BCRYPT_WORK_ENV);
if (!Strings.isNullOrEmpty(envBcryptWork)) {
try {
int envBcryptWorkInt = Integer.parseInt(envBcryptWork);
if (envBcryptWorkInt >= 4 && envBcryptWorkInt <= 31) {
bcryptWork = envBcryptWorkInt;
} else {
log.warn(Constants.BCRYPT_WORK_ENV + " needs to be in range 4...31. Falling back to " + Constants.DEFAULT_BCRYPT_WORK + ".");
}
} catch (NumberFormatException e) {
log.warn(Constants.BCRYPT_WORK_ENV + " needs to be a number in range 4...31. Falling back to " + Constants.DEFAULT_BCRYPT_WORK + ".");
}
}
return BCrypt.withDefaults().hashToString(bcryptWork, password.toCharArray());
}
/**
* Returns the list of all users.
*
* @param criteria Search criteria
* @param sortCriteria Sort criteria
* @return List of users
*/
public List<UserDto> findByCriteria(UserCriteria criteria, SortCriteria sortCriteria) {
Map<String, Object> parameterMap = new HashMap<>();
List<String> criteriaList = new ArrayList<>();
StringBuilder sb = new StringBuilder("select u.USE_ID_C as c0, u.USE_USERNAME_C as c1, u.USE_EMAIL_C as c2, u.USE_CREATEDATE_D as c3, u.USE_STORAGECURRENT_N as c4, u.USE_STORAGEQUOTA_N as c5, u.USE_TOTPKEY_C as c6, u.USE_DISABLEDATE_D as c7");
sb.append(" from T_USER u ");
// Add search criterias
if (criteria.getSearch() != null) {
criteriaList.add("lower(u.USE_USERNAME_C) like lower(:search)");
parameterMap.put("search", "%" + criteria.getSearch() + "%");
}
if (criteria.getUserId() != null) {
criteriaList.add("u.USE_ID_C = :userId");
parameterMap.put("userId", criteria.getUserId());
}
if (criteria.getUserName() != null) {
criteriaList.add("u.USE_USERNAME_C = :userName");
parameterMap.put("userName", criteria.getUserName());
}
if (criteria.getGroupId() != null) {
sb.append(" join T_USER_GROUP ug on ug.UGP_IDUSER_C = u.USE_ID_C and ug.UGP_IDGROUP_C = :groupId and ug.UGP_DELETEDATE_D is null ");
parameterMap.put("groupId", criteria.getGroupId());
}
criteriaList.add("u.USE_DELETEDATE_D is null");
if (!criteriaList.isEmpty()) {
sb.append(" where ");
sb.append(Joiner.on(" and ").join(criteriaList));
}
// Perform the search
QueryParam queryParam = QueryUtil.getSortedQueryParam(new QueryParam(sb.toString(), parameterMap), sortCriteria);
@SuppressWarnings("unchecked")
List<Object[]> l = QueryUtil.getNativeQuery(queryParam).getResultList();
// Assemble results
List<UserDto> userDtoList = new ArrayList<>();
for (Object[] o : l) {
int i = 0;
UserDto userDto = new UserDto();
userDto.setId((String) o[i++]);
userDto.setUsername((String) o[i++]);
userDto.setEmail((String) o[i++]);
userDto.setCreateTimestamp(((Timestamp) o[i++]).getTime());
userDto.setStorageCurrent(((Number) o[i++]).longValue());
userDto.setStorageQuota(((Number) o[i++]).longValue());
userDto.setTotpKey((String) o[i++]);
if (o[i] != null) {
userDto.setDisableTimestamp(((Timestamp) o[i]).getTime());
}
userDtoList.add(userDto);
}
return userDtoList;
}
/**
* Returns the global storage used by all users.
*
* @return Current global storage
*/
public long getGlobalStorageCurrent() {
EntityManager em = ThreadLocalContext.get().getEntityManager();
Query query = em.createNativeQuery("select sum(u.USE_STORAGECURRENT_N) from T_USER u where u.USE_DELETEDATE_D is null");
return ((Number) query.getSingleResult()).longValue();
}
/**
* Returns the number of active users.
*
* @return Number of active users
*/
public long getActiveUserCount() {
EntityManager em = ThreadLocalContext.get().getEntityManager();
Query query = em.createNativeQuery("select count(u.USE_ID_C) from T_USER u where u.USE_DELETEDATE_D is null and (u.USE_DISABLEDATE_D is null or u.USE_DISABLEDATE_D >= :fromDate and u.USE_DISABLEDATE_D < :toDate)");
DateTime fromDate = DateTime.now().minusMonths(1).dayOfMonth().withMinimumValue().withTimeAtStartOfDay();
DateTime toDate = fromDate.plusMonths(1);
query.setParameter("fromDate", fromDate.toDate());
query.setParameter("toDate", toDate.toDate());
return ((Number) query.getSingleResult()).longValue();
}
}