Java tutorial
/* * 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; } }