UserRepositoryImpl.java

package com.tradecloud.repository.user;

import com.tradecloud.authentication.Role;
import com.tradecloud.authentication.User;
import com.tradecloud.authentication.UserInfo;
import com.tradecloud.domain.party.Employee;
import com.tradecloud.domain.party.base.Contact;
import com.tradecloud.domain.place.Region;
import com.tradecloud.repository.base.impl.RepositoryBaseImpl;
import org.apache.log4j.Logger;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.query.NativeQuery;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.Query;
import javax.persistence.criteria.*;
import java.util.*;

/**
 * User Repository.
 * Note the SessionFactory used here is hard coded for the 'tradecloud'
 * database.
 * This is wired directly into the Spring security classes to be used for login.
 * ALso used by our UserService for other user actions.
 */
@Repository(value = "userRepository")
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_UNCOMMITTED)
public class UserRepositoryImpl extends RepositoryBaseImpl<User, Object> implements UserRepository, UserDetailsService {

    private static final long serialVersionUID = 1L;
    private static final Logger logger = Logger.getLogger(UserRepositoryImpl.class);

    /**
     * Method need to satisfy Spring security interface.
     */
    @Override
    public UserDetails loadUserByUsername(String username) {
        return findByUsername(username);
    }

    /**
     * Scan through the users list and pick out any of the users with a role
     * matching the one we're interested in.
     *
     * @param currentRole
     * @return
     */
    @Override
    public List<User> findUsersInRole(String currentRole) {
        Role searchRole = new Role(currentRole);
        List<User> allUsers = super.findAll();
        List<User> matchedUsers = new ArrayList<User>();

        for (User user : allUsers) {
            if (user.getRoles().contains(searchRole)) {
                matchedUsers.add(user);
            }
        }
        return matchedUsers;
    }

    @Override
    public User findByUsername(String username) {
        CriteriaBuilder criteriaBuilder = getSession().getCriteriaBuilder();
        CriteriaQuery<User> criteriaQuery = criteriaBuilder.createQuery(User.class);
        Root<User> c = criteriaQuery.from(User.class);
        criteriaQuery = criteriaQuery.select(c).where(criteriaBuilder.equal(c.get("username"), username));
        org.hibernate.query.Query<User> q = getSession().createQuery(criteriaQuery);
        return q.uniqueResult();
    }

    @Override
    public User findByEmail(String email) {
        CriteriaBuilder criteriaBuilder = getSession().getCriteriaBuilder();
        CriteriaQuery<User> criteriaQuery = criteriaBuilder.createQuery(User.class);
        Root<User> c = criteriaQuery.from(User.class);
        criteriaQuery = criteriaQuery.select(c).where(criteriaBuilder.equal(c.get("userInfo").get("email"), email));
        org.hibernate.query.Query<User> q = getSession().createQuery(criteriaQuery);
        return q.uniqueResult();
    }

    @Override
    public User findByContact(Contact contact) {
        CriteriaBuilder criteriaBuilder = getSession().getCriteriaBuilder();
        CriteriaQuery<User> criteriaQuery = criteriaBuilder.createQuery(User.class);
        Root<User> c = criteriaQuery.from(User.class);
        criteriaQuery = criteriaQuery.select(c).where(criteriaBuilder.equal(c.get("contact"), contact));
        org.hibernate.query.Query<User> q = getSession().createQuery(criteriaQuery);
        return q.uniqueResult();
    }

    @Override
    public User findByEmployee(Employee employee) {
        CriteriaBuilder criteriaBuilder = getSession().getCriteriaBuilder();
        CriteriaQuery<User> criteriaQuery = criteriaBuilder.createQuery(User.class);
        Root<User> c = criteriaQuery.from(User.class);
        criteriaQuery = criteriaQuery.select(c).where(criteriaBuilder.equal(c.get("employee"), employee));
        org.hibernate.query.Query<User> q = getSession().createQuery(criteriaQuery);
        return q.uniqueResult();
    }

    @Override
    public List<User> findUserByEmailAddress(String emailAddress, boolean caseSensitive) {
        CriteriaBuilder criteriaBuilder = getSession().getCriteriaBuilder();
        CriteriaQuery<User> criteriaQuery = criteriaBuilder.createQuery(User.class);
        Root<User> root = criteriaQuery.from(User.class);
        Join<User, UserInfo> childJoin = root.join("userInfo");

        Path<String> emailPath = childJoin.get("email");
        Predicate predicate = caseSensitive
                ? criteriaBuilder.equal(emailPath, emailAddress)
                : criteriaBuilder.equal(criteriaBuilder.lower(emailPath), emailAddress.toLowerCase());

        criteriaQuery.where(predicate);

        org.hibernate.query.Query<User> query = getSession().createQuery(criteriaQuery);
        return query.list();
    }

    private void updateIncorrectLoginAttemps(String username, int val) {
        String update = "";
        if (val == 0) {
            update = "UPDATE User  SET lastlogindate = now(), incorrectLoginAttempts = :incorrectLoginAttempts WHERE username=:username";
        } else {
            update = "UPDATE User u SET u.incorrectLoginAttempts = :incorrectLoginAttempts WHERE u.username=:username";
        }
        javax.persistence.Query query = getSessionCustom().createQuery(update);
        query.setParameter("incorrectLoginAttempts", val);
        query.setParameter("username", username);

        query.executeUpdate();
    }

    @Override
    public void clearIncorrectLoginAttempts(String username) {
        updateIncorrectLoginAttemps(username, 0);
    }

    @Override
    public void incrementIncorrectLoginAttempts(String username) {
        String update = "SELECT u.incorrectLoginAttempts from User u WHERE u.username=:username";
        Query query = getSessionCustom().createQuery(update);
        query.setParameter("username", username);
        List list = query.getResultList();

        // If the user didn't exist
        if (list != null && !list.isEmpty()) {
            updateIncorrectLoginAttemps(username, ((Integer) list.get(0)) + 1);
        }
    }

    @Override
    public Date findUserPasswordChangeDate(Long userId) {
        String update = "SELECT u.passwordChangedDate from User u WHERE u.id=:id";
        Query query = getSessionCustom().createQuery(update);
        query.setParameter("id", userId);
        List resultList = query.getResultList();
        if (!resultList.isEmpty())
            return (Date) resultList.get(0);
        return null;
    }

    @Override
    public User findByUsernameAndClientCode(String username, String client, boolean fetchHistory) {
        Session session = null;
        try {
            session = getSessionFactory().withOptions().tenantIdentifier("tradecloud_" + client).openSession();
            String queryString = "select u from User u " + (fetchHistory ? "LEFT JOIN FETCH u.passwordHistory" : "") +
                    " where u.username =:userName ";
            org.hibernate.query.Query query = session.createQuery(queryString);
            query.setParameter("userName", username);

            User user = (User) query.uniqueResult();
            user.getRolesList();
            return user;
        } finally {
            if (session != null) {
                session.close();
            }
        }
    }

    @Override
    public void updateUserForPasswordReset(User user) {
        Session session = null;
        try {
            session = getSessionFactory().withOptions().tenantIdentifier("tradecloud_" + user.getActiveClient()).openSession();
            Transaction transaction = session.beginTransaction();

            //session.isReadOnly(user);

            NativeQuery nativeQuery = session.createNativeQuery("update users set password =:password, lastlogindate=:lastlogindate, " +
                    " passwordchangeddate=:passwordchangeddate, accountNonExpired='t'" +
                    " where username=:userName");
            nativeQuery.setParameter("lastlogindate", user.getLastLoginDate());
            nativeQuery.setParameter("password", user.getPassword());
            nativeQuery.setParameter("passwordchangeddate", user.getPasswordChangedDate());
            nativeQuery.setParameter("userName", user.getUsername());
            int i = nativeQuery.executeUpdate();
            logger.debug("" + i);
            session.flush();
            transaction.commit();
        } finally {
            if (session != null) {
                session.close();
            }
        }
    }

    @Override
    public Set<User> findByUsernames(List<String> usersNames) {
        if (usersNames != null && !usersNames.isEmpty()) {
            org.hibernate.query.Query query = getSession().createQuery("from User where username in (:usersNames)");
            query.setParameterList("usersNames", usersNames);
            return new HashSet<>(query.list());
        } else {
            return Collections.EMPTY_SET;
        }
    }

    @Override
    public List<User> findAllUsersWithRegionsLinked(String role) {
        //can only link regions with FD
        String q = "select distinct u from User u join u.roles r where r.authority = :role and u.regions is not empty";
        return getSession().createQuery(q, User.class).setParameter("role", role).getResultList();
    }

    @Override
    public List<User> findUsersByRoleAndRegions(String role, Set<Region> regions) {
        if (regions == null || regions.isEmpty()) {
            return Collections.emptyList();
        }
        String q = "select distinct u from User u join u.roles r join u.regions reg where r.authority = :role and reg in (:regions)";
        return getSession().createQuery(q, User.class)
                .setParameter("role", role)
                .setParameterList("regions", regions)
                .getResultList();
    }

    public List<User> findEmployees() {
        String hql = """
        select u
        from User u
        join u.employee e
        join e.employeeRoles r
        where r.code = :roleCode
    """;
        return getSession()
                .createQuery(hql, User.class)
                .setParameter("roleCode", "SHIPPING_CONTROLLER")
                .list();
    }
}