org.patientview.radar.service.impl.UserManagerImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.patientview.radar.service.impl.UserManagerImpl.java

Source

/*
 * PatientView
 *
 * Copyright (c) Worth Solutions Limited 2004-2013
 *
 * This file is part of PatientView.
 *
 * PatientView is free software: you can redistribute it and/or modify it under the terms of the
 * GNU General Public License as published by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 * PatientView 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 General Public License for more details.
 * You should have received a copy of the GNU General Public License along with PatientView in a file
 * titled COPYING. If not, see <http://www.gnu.org/licenses/>.
 *
 * @package PatientView
 * @link http://www.patientview.org
 * @author PatientView <info@patientview.org>
 * @copyright Copyright (c) 2004-2013, Worth Solutions Limited
 * @license http://www.gnu.org/licenses/gpl-3.0.html The GNU General Public License V3.0
 */

package org.patientview.radar.service.impl;

import org.apache.commons.lang.RandomStringUtils;
import org.patientview.model.Patient;
import org.patientview.radar.dao.JoinRequestDao;
import org.patientview.radar.dao.UserDao;
import org.patientview.radar.exception.JoinCreationException;
import org.patientview.radar.exception.PatientLinkException;
import org.patientview.radar.exception.RegisterException;
import org.patientview.radar.exception.UserCreationException;
import org.patientview.radar.exception.UserMappingException;
import org.patientview.radar.exception.UserRoleException;
import org.patientview.radar.model.JoinRequest;
import org.patientview.radar.model.LogEntry;
import org.patientview.radar.model.exception.DaoException;
import org.patientview.radar.model.exception.DecryptionException;
import org.patientview.radar.model.exception.EmailAddressNotFoundException;
import org.patientview.radar.model.exception.InvalidSecurityQuestionAnswer;
import org.patientview.radar.model.exception.RegistrationException;
import org.patientview.radar.model.exception.UserEmailAlreadyExists;
import org.patientview.radar.model.filter.PatientUserFilter;
import org.patientview.radar.model.filter.ProfessionalUserFilter;
import org.patientview.radar.model.user.AdminUser;
import org.patientview.radar.model.user.PatientUser;
import org.patientview.radar.model.user.ProfessionalUser;
import org.patientview.radar.model.user.User;
import org.patientview.radar.service.EmailManager;
import org.patientview.radar.service.LogEntryManager;
import org.patientview.radar.service.PatientManager;
import org.patientview.radar.service.UserManager;
import org.patientview.radar.web.RadarSecuredSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.util.StringUtils;

import java.util.Date;
import java.util.List;

public class UserManagerImpl implements UserManager, UserDetailsService {

    private static final Logger LOGGER = LoggerFactory.getLogger(UserManagerImpl.class);

    private static final String PATIENT_GROUP = "PATIENT"; // Radar patient mapping
    private static final String PATIENT_VIEW_GROUP = "patient"; // Patient view mapping

    private EmailManager emailManager;
    private ProviderManager authenticationManager;

    private UserDao userDao;
    private JoinRequestDao joinRequestDao;
    private PatientManager patientManager;

    private LogEntryManager logEntryManager;

    public AdminUser getAdminUser(String email) {
        return userDao.getAdminUser(email);
    }

    public AdminUser getAdminUserWithUsername(String username) {
        return userDao.getAdminUserWithUsername(username);
    }

    public PatientUser getPatientUser(Long id) {
        return userDao.getPatientUser(id);
    }

    public PatientUser getPatientUser(String email) {
        return userDao.getPatientUser(email);
    }

    public PatientUser getPatientUserWithUsername(String username) {
        return userDao.getPatientUserWithUsername(username);
    }

    public PatientUser getPatientUser(String email, Date dateOfBirth) {
        PatientUser user = userDao.getPatientUser(email);
        if (user != null) {
            return user.getDateOfBirth().equals(dateOfBirth) ? user : null;
        }
        return null;
    }

    public PatientUser getPatientUserWithUsername(String username, Date dateOfBirth) {
        PatientUser user = userDao.getPatientUserWithUsername(username);
        if (user != null && user.getDateOfBirth() != null) {
            return user.getDateOfBirth().equals(dateOfBirth) ? user : null;
        }
        return null;
    }

    public List<PatientUser> getPatientUsers() {
        return getPatientUsers(new PatientUserFilter(), -1, -1);
    }

    public List<PatientUser> getPatientUsers(PatientUserFilter filter) {
        return getPatientUsers(filter, -1, -1);
    }

    public List<PatientUser> getPatientUsers(PatientUserFilter filter, int page, int numberPerPage) {
        return userDao.getPatientUsers(filter, page, numberPerPage);
    }

    public void savePatientUser(PatientUser patientUser) throws Exception {

        userDao.savePatientUser(patientUser);
    }

    public void deletePatientUser(PatientUser patientUser) throws Exception {
        userDao.deletePatientUser(patientUser);
    }

    /**
     * Create a PV user for this patient.
     * Create Radar user access for this patient
     * Create PV join request for this patient
     * @param patient patient that is being added in Radar
     * @return the combined patient - user object
     * @throws UserCreationException
     * @throws UserMappingException
     * @throws UserRoleException
     * @throws PatientLinkException
     * @throws JoinCreationException
     */
    private PatientUser createUserInPatientViewAndRadar(Patient patient) throws UserCreationException,
            UserMappingException, UserRoleException, PatientLinkException, JoinCreationException {

        //-------- Patient View Tables ------//
        // Create the user record
        PatientUser patientUser = createPatientViewUser(patient);

        // Create the patient mapping in patient view so patient view knows the user is a patient
        userDao.createRoleInPatientView(patientUser.getId(), PATIENT_VIEW_GROUP);

        // Switch from patient view to Radar
        patientUser.setUserId(patientUser.getId());

        //---------- Radar Tables -----------//
        patientUser = createRadarUser(patientUser, patient);
        userDao.saveUserMapping(patientUser);

        //logPatientCreation(patient, patientUser);

        // We've created a new user so we need to create a join request
        createJoinRequest(patient);

        return patientUser;

    }

    /**
     * Ensure that this newly added Radar patient/patientUser has the correct permissions.
     * This means, a usermapping to the selected renal unit on the demographic panel,
     * and a usermapping for the disease group they have been added to
     * @param patient the patient record for the patient added
     * @param patientUser the patientUser for the patient added (yes this is confusing)
     */
    private void createPermissionsForNewPatientUser(Patient patient, PatientUser patientUser)
            throws UserMappingException {
        createUserMappingInPatientView(patientUser.getUsername(), patient.getNhsno(), getUnitCode(patient));
        // Map the Disease Group
        if (patient.getDiseaseGroup() != null) {
            createUserMappingInPatientView(patientUser.getUsername(), patient.getNhsno(),
                    patient.getDiseaseGroup().getId());
        }
        // Map the Patient Group
        createUserMappingInPatientView(patientUser.getUsername(), patient.getNhsno(), PATIENT_GROUP);
    }

    private PatientUser createRadarUser(PatientUser patientUser, Patient patient) throws UserCreationException {

        // Invalidate the id which relates to patient view
        patientUser.setId(0L);
        // now fill in the radar patient stuff
        // and invalidate the id and this will create a record in tbl_patient_users
        patientUser.setRadarNumber(patient.getRadarNo());
        patientUser.setDateOfBirth(patient.getDob());
        userDao.savePatientUser(patientUser);

        return patientUser;
    }

    /**
     * Entry point to update or add a patient from the Demographics screen.
     *
     * This is responsible for:
     *  - creating the patient record and linking if necessary.
     *  - create user records and user mappings in radar and patientview
     *  - send a join request for new patients
     *
     * @param patient
     * @return
     * @throws RegisterException
     * @throws Exception
     */
    public PatientUser addPatientUserOrUpdatePatient(Patient patient) throws Exception {

        try {
            final boolean isNewPatient = !patient.hasValidId();

            patientManager.save(patient);

            if (isNewPatient) {
                PatientUser patientUser;

                if (!userExistsInPatientView(patient.getNhsno())) {
                    patientUser = createUserInPatientViewAndRadar(patient);
                } else {
                    patientUser = userDao.getPatientViewUser(patient.getNhsno());
                }

                createPermissionsForNewPatientUser(patient, patientUser);

            }

        } catch (UserCreationException uce) {
            throw new RegisterException("Could not create user", uce);
        } catch (UserMappingException ume) {
            throw new RegisterException("Could not create user mappings", ume);
        } catch (UserRoleException ure) {
            throw new RegisterException("Could not create role", ure);
        } catch (JoinCreationException jce) {
            throw new RegisterException("User created but could not create join request", jce);
        } catch (PatientLinkException ple) {
            throw new RegisterException("Could not create role", ple);
        }

        return null;
    }

    public void createUserMappingInPatientView(String username, String nhsNo, String unitCode)
            throws UserMappingException {
        try {
            if (!userDao.userExistsInPatientView(nhsNo, unitCode)) {
                userDao.createUserMappingInPatientView(username, nhsNo, unitCode);
            }
        } catch (Exception e) {
            LOGGER.error("Error mapping user {}, {}", username, e.getMessage());
            throw new UserMappingException("Error creating mapping", e);
        }

    }

    private void createJoinRequest(Patient patient) throws JoinCreationException {

        try {
            // Now create a join request for the new user
            JoinRequest joinRequest = new JoinRequest();
            joinRequest.setNhsNo(patient.getNhsno());
            joinRequest.setDateOfBirth(patient.getDob());
            joinRequest.setEmail(patient.getEmailAddress());
            joinRequest.setFirstName(patient.getForename());
            joinRequest.setLastName(patient.getSurname());
            String unitCode = patient.getUnitcode();

            if (!StringUtils.hasText(unitCode) && patient.getRenalUnit() != null) {
                unitCode = patient.getRenalUnit().getUnitCode();
            }

            if (!StringUtils.hasText(unitCode) && patient.getDiseaseGroup() != null) {
                unitCode = patient.getDiseaseGroup().getId();
            }

            joinRequest.setUnitcode(unitCode);
            joinRequest.setDateOfRequest(new Date());

            joinRequestDao.saveJoinRequest(joinRequest);
        } catch (Exception e) {
            LOGGER.error("Error creating join request", e);
            throw new JoinCreationException("Error creating join request", e);
        }

    }

    private PatientUser createPatientViewUser(Patient patient) throws UserCreationException {

        PatientUser patientUser = null;

        try {

            patientUser = userDao.getPatientViewUser(patient.getNhsno());

            // Not registered on the system so create a username for them and a mapping to the patients unit
            if (patientUser == null) {

                patientUser = new PatientUser();

                patientUser.setUsername(generateUsername(patient));
                patientUser.setFirstName(patient.getForename());
                patientUser.setLastName(patient.getSurname());
                patientUser.setPassword(User.getPasswordHash(generateRandomPassword()));
                patientUser.setEmail(patient.getEmailAddress());

                patientUser = (PatientUser) userDao.createUser(patientUser);

            }
        } catch (Exception e) {
            LOGGER.error("Error creating user");
            throw new UserCreationException("Error creating user in database", e);
        }

        return patientUser;
    }

    public void registerProfessional(ProfessionalUser professionalUser)
            throws UserEmailAlreadyExists, InvalidSecurityQuestionAnswer, RegistrationException {
        User user = userDao.getProfessionalUser(professionalUser.getEmail());
        if (user != null) {
            throw new UserEmailAlreadyExists("Email address already exists");
        }

        if (!professionalUser.getSecurityQuestion().equals(professionalUser.getSecurityQuestionAnsw())) {
            throw new InvalidSecurityQuestionAnswer("Security question answer is incorrect");
        }

        try {
            saveProfessionalUser(professionalUser);
        } catch (Exception e) {
            LOGGER.error("Could not register professional", e);
            throw new RegistrationException("Could not register professional", e);
        }
        emailManager.sendProfessionalRegistrationAdminNotificationEmail(professionalUser);
    }

    /** Log the creation of a new user into the log **/
    private void logPatientCreation(Patient patient, PatientUser patientUser) {

        LogEntry logEntry = new LogEntry();
        logEntry.setAction(LogEntry.PATIENT_ADD);
        logEntry.setActor(RadarSecuredSession.get().getUser().getUsername());
        logEntry.setUser(patientUser.getUsername());
        logEntry.setDate(new Date());
        logEntry.setUnitcode(patient.getUnitcode());
        logEntry.setNhsno(patient.getNhsno());
        logEntryManager.save(logEntry);

    }

    public ProfessionalUser getProfessionalUser(Long id) {
        return userDao.getProfessionalUser(id);
    }

    public ProfessionalUser getProfessionalUser(String email) {
        return userDao.getProfessionalUser(email);
    }

    public ProfessionalUser getProfessionalUserWithUsername(String username) {
        return userDao.getProfessionalUserWithUsername(username);
    }

    public User getSuperUserWithUsername(String username) {
        return userDao.getSuperUserWithUsername(username);
    }

    public void saveProfessionalUser(ProfessionalUser professionalUser) throws Exception {
        // if its a new user generate a password
        if (!professionalUser.hasValidId()) {
            String password = generateRandomPassword();
            professionalUser.setPassword(ProfessionalUser.getPasswordHash(password));
            professionalUser.setUsername(professionalUser.getEmail());
        }

        userDao.saveProfessionalUser(professionalUser);
    }

    public void deleteProfessionalUser(ProfessionalUser professionalUser) throws Exception {
        userDao.deleteProfessionalUser(professionalUser);
    }

    public List<ProfessionalUser> getProfessionalUsers() {
        return getProfessionalUsers(new ProfessionalUserFilter(), -1, -1);
    }

    public List<ProfessionalUser> getProfessionalUsers(ProfessionalUserFilter filter) {
        return getProfessionalUsers(filter, -1, -1);
    }

    public List<ProfessionalUser> getProfessionalUsers(ProfessionalUserFilter filter, int page, int numberPerPage) {
        return userDao.getProfessionalUsers(filter, page, numberPerPage);
    }

    public boolean authenticateProfessionalUser(String username, String password) throws AuthenticationException {
        ProfessionalUser professionalUser = userDao.getProfessionalUserByUsername(username);
        if (professionalUser != null) {
            try {
                Authentication authentication = authenticationManager
                        .authenticate(new UsernamePasswordAuthenticationToken(username, password));
                return authentication.isAuthenticated();
            } catch (AuthenticationException e) {
                LOGGER.warn("Authentication failed for user {} and password {}", username, e.getMessage());
                throw e;
            }
        }
        return false;
    }

    public void changeUserPassword(String username, String password) throws DecryptionException, DaoException {
        ProfessionalUser professionalUser = getProfessionalUser(username);

        try {
            userDao.saveProfessionalUser(professionalUser);
        } catch (Exception e) {
            LOGGER.error("could not save professional user", e);
            throw new DaoException("Could not save professional user");
        }
    }

    public boolean userExistsInPatientView(String nhsno) {
        return userDao.userExistsInPatientView(nhsno);
    }

    public void sendForgottenPasswordToPatient(String username)
            throws EmailAddressNotFoundException, DecryptionException {
        // In theory this could just go in the email manager but we need to query for user first
        PatientUser patientUser = userDao.getPatientUser(username);
        if (patientUser != null) {
            try {
                String password = generateRandomPassword();
                patientUser.setPassword(ProfessionalUser.getPasswordHash(password));

                userDao.savePatientUser(patientUser);

                emailManager.sendForgottenPassword(patientUser, password);
            } catch (Exception e) {
                LOGGER.error("Could not decrypt password for forgotten password email for {}", username, e);
                throw new DecryptionException("Could not decrypt password for forgotten password email", e);
            }
        } else {
            LOGGER.error("Could not find user with email {}", username);
            throw new EmailAddressNotFoundException("Email Address not found");
        }
    }

    public void sendForgottenPasswordToProfessional(String username)
            throws EmailAddressNotFoundException, DecryptionException {
        // In theory this could just go in the email manager but we need to query for user first
        ProfessionalUser professionalUser = userDao.getProfessionalUser(username);
        if (professionalUser != null) {
            try {
                String password = generateRandomPassword();
                professionalUser.setPassword(ProfessionalUser.getPasswordHash(password));

                userDao.saveProfessionalUser(professionalUser);

                emailManager.sendForgottenPassword(professionalUser, password);
            } catch (Exception e) {
                LOGGER.error("Could not decrypt");
                throw new DecryptionException("Could not decrypt", e);
            }
        } else {
            LOGGER.error("Could not find user with email {}", username);
            throw new EmailAddressNotFoundException("Email Address not found");
        }
    }

    private String generateRandomPassword() {
        // I love you Apache commons
        return RandomStringUtils.randomAlphanumeric(8);
    }

    public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException, DataAccessException {
        // Pull the user from the DAO
        User user = userDao.getProfessionalUser(email);
        if (user != null) {
            return user;
        }
        throw new UsernameNotFoundException("User not found with email address " + email);
    }

    public User getExternallyCreatedUser(String nshNo) {
        return userDao.getPatientViewUser(nshNo);
    }

    public void setEmailManager(EmailManager emailManager) {
        this.emailManager = emailManager;
    }

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void setAuthenticationManager(ProviderManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }

    public String generateUsername(Patient patient) {

        //Strip non alpha numeric characters
        String username = patient.getForename().replaceAll("\\P{Alnum}", "") + "."
                + patient.getSurname().replaceAll("\\P{Alnum}", "");

        username = username.toLowerCase();

        if (userDao.usernameExistsInPatientView(username)) {
            int i = 1;
            while (userDao.usernameExistsInPatientView(username + i)
                    || userDao.getPatientUserWithUsername(username + i) != null) {
                ++i;
            }
            username += i;
        }

        return username;
    }

    private String getUnitCode(Patient patient) {
        String unitCode = null;
        if (patient.getRenalUnit() != null) {
            unitCode = patient.getRenalUnit().getUnitCode();
        } else {
            unitCode = patient.getUnitcode();
        }

        return unitCode;
    }

    public List<String> getPatientRadarMappings(String nhsNo) {
        return userDao.getPatientRadarMappings(nhsNo);
    }

    public void setJoinRequestDao(JoinRequestDao joinRequestDao) {
        this.joinRequestDao = joinRequestDao;
    }

    public void setPatientManager(PatientManager patientManager) {
        this.patientManager = patientManager;
    }

    public void setLogEntryManager(LogEntryManager logEntryManager) {
        this.logEntryManager = logEntryManager;
    }
}