/******************************************************************************* * Open Behavioral Health Information Technology Architecture (OBHITA.org) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the <organization> nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ package gov.samhsa.consent2share.service.account; import gov.samhsa.consent2share.common.UniqueValueGeneratorException; import gov.samhsa.consent2share.common.UserContext; import gov.samhsa.consent2share.domain.account.EmailToken; import gov.samhsa.consent2share.domain.account.EmailTokenRepository; import gov.samhsa.consent2share.domain.account.TokenGenerator; import gov.samhsa.consent2share.domain.account.TokenType; import gov.samhsa.consent2share.domain.account.Users; import gov.samhsa.consent2share.domain.account.UsersRepository; import gov.samhsa.consent2share.domain.commondomainservices.EmailSender; import gov.samhsa.consent2share.domain.commondomainservices.EmailType; import gov.samhsa.consent2share.domain.patient.Patient; import gov.samhsa.consent2share.domain.patient.PatientRepository; import gov.samhsa.consent2share.domain.reference.AdministrativeGenderCodeRepository; import gov.samhsa.consent2share.infrastructure.PixService; import gov.samhsa.consent2share.infrastructure.security.EmailAddressNotExistException; import gov.samhsa.consent2share.infrastructure.security.UsernameNotExistException; import gov.samhsa.consent2share.infrastructure.security.UsersAuthorityUtils; import gov.samhsa.consent2share.pixclient.util.PixManagerBean; import gov.samhsa.consent2share.service.dto.SignupDto; import gov.samhsa.consent2share.service.util.TypeConverter; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Set; import javax.mail.MessagingException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** * The Class AccountServiceImpl. */ public class AccountServiceImpl implements AccountService { /** The logger. */ private final Logger logger = LoggerFactory.getLogger(this.getClass()); /** The patient repository. */ protected PatientRepository patientRepository; /** The administrative gender code repository. */ protected AdministrativeGenderCodeRepository administrativeGenderCodeRepository; /** The password encoder. */ protected PasswordEncoder passwordEncoder; /** The user context. */ protected UserContext userContext; /** The email sender. */ protected EmailSender emailSender; /** The token generator. */ protected TokenGenerator tokenGenerator; /** The account verification token expire in hours. */ protected Integer accountVerificationTokenExpireInHours; /** The email token repository. */ protected EmailTokenRepository emailTokenRepository; /** The users repository. */ protected UsersRepository usersRepository; /** The mrn service. */ private MrnService mrnService; /** The pix service. */ private PixService pixService; /** * Instantiates a new account service impl. * * @param accountVerificationTokenExpireInHours * the account verification token expire in hours * @param patientRepository * the patient repository * @param administrativeGenderCodeRepository * the administrative gender code repository * @param passwordEncoder * the password encoder * @param userContext * the user context * @param emailSender * the email sender * @param tokenGenerator * the token generator * @param emailTokenRepository * the email token repository * @param usersRepository * the users repository * @param mrnService * the mrn service * @param pixService * the pix service */ public AccountServiceImpl( Integer accountVerificationTokenExpireInHours, PatientRepository patientRepository, AdministrativeGenderCodeRepository administrativeGenderCodeRepository, PasswordEncoder passwordEncoder, UserContext userContext, EmailSender emailSender, TokenGenerator tokenGenerator, EmailTokenRepository emailTokenRepository, UsersRepository usersRepository, MrnService mrnService, PixService pixService) { this.patientRepository = patientRepository; this.administrativeGenderCodeRepository = administrativeGenderCodeRepository; this.passwordEncoder = passwordEncoder; this.userContext = userContext; this.emailSender = emailSender; this.emailTokenRepository = emailTokenRepository; this.usersRepository = usersRepository; this.tokenGenerator = tokenGenerator; this.accountVerificationTokenExpireInHours = accountVerificationTokenExpireInHours; this.mrnService = mrnService; this.pixService = pixService; } /* * (non-Javadoc) * * @see * gov.samhsa.consent2share.service.account.AccountService#signup(gov.samhsa * .consent2share.service.dto.SignupDto, java.lang.String) */ @Override @Transactional public void signup(SignupDto signupDto, String linkUrl) throws MessagingException, UsernameNotExistException, EmailAddressNotExistException { String eid = null; final String mrn = generateMrn(); try { PixManagerBean pixManagerBean = pixService.addPatient(TypeConverter .signupDtoToPixPatientDto(signupDto, mrn)); Assert.isTrue( pixManagerBean.isSuccess(), "Patient cannot be added to MPI! Error: " + pixManagerBean.getAddMessage()); eid = pixService.getEid(mrn); Assert.hasText(eid, "EID cannot be retrieved from MPI!"); } catch (IllegalArgumentException e) { logger.error(e.getMessage(), e); throw e; } if (!StringUtils.hasText(linkUrl)) { throw new IllegalArgumentException("Email link is required."); } String encodedPassword = passwordEncoder .encode(signupDto.getPassword()); Patient patient = new Patient(); patient.setMedicalRecordNumber(mrn); patient.setEnterpriseIdentifier(eid); patient.setFirstName(signupDto.getFirstName()); patient.setLastName(signupDto.getLastName()); patient.setUsername(signupDto.getUsername()); patient.setEmail(signupDto.getEmail()); patient.setBirthDay(signupDto.getBirthDate()); if (StringUtils.hasText(signupDto.getGenderCode())) { patient.setAdministrativeGenderCode(administrativeGenderCodeRepository .findByCode(signupDto.getGenderCode())); } patientRepository.save(patient); Set<GrantedAuthority> authorities = UsersAuthorityUtils .createAuthoritySet("ROLE_USER"); String username = signupDto.getUsername(); Users user = new Users(username, encodedPassword, false, true, true, authorities); usersRepository.createUser(user); userContext.setCurrentUser(signupDto.getUsername()); String token = createEmailToken(signupDto.getUsername(), signupDto.getEmail()); emailSender.sendMessage( signupDto.getFirstName() + " " + signupDto.getLastName(), signupDto.getEmail(), EmailType.SIGNUP_VERIFICATION, linkUrl, token); } /* * (non-Javadoc) * * @see * gov.samhsa.consent2share.service.account.AccountService#createEmailToken * (java.lang.String, java.lang.String) */ @Override public String createEmailToken(String username, String emailAddress) throws UsernameNotExistException, EmailAddressNotExistException, MessagingException { String emailLinkPlaceHolder = "?token=%s"; if (!StringUtils.hasText(username)) { throw new IllegalArgumentException("Username is required."); } if (!StringUtils.hasText(emailAddress)) { throw new IllegalArgumentException("Email Address is required."); } Patient patient = patientRepository.findByUsername(username); String patientEmailAddress = patient.getEmail(); if (!patientEmailAddress.equalsIgnoreCase(emailAddress)) { String message = String.format( "Email address %s doesn't exist for username %s.", emailAddress, username); logger.info("message"); throw new EmailAddressNotExistException(message); } EmailToken accountVerificationToken = new EmailToken(); accountVerificationToken .setExpireInHours(accountVerificationTokenExpireInHours); accountVerificationToken.setRequestDateTime(new Date()); String token = tokenGenerator.generateToken(); accountVerificationToken.setToken(token); accountVerificationToken.setUsername(username); accountVerificationToken.setIsTokenUsed(false); accountVerificationToken.setTokenType(TokenType.ACCOUNT_VERIFICATION); emailTokenRepository.save(accountVerificationToken); String link = String.format(emailLinkPlaceHolder, token); return link; } /* * (non-Javadoc) * * @see * gov.samhsa.consent2share.service.account.AccountService#findUserByUsername * (java.lang.String) */ @Override public Users findUserByUsername(String username) { Users user = null; try { user = usersRepository.loadUserByUsername(username); } catch (UsernameNotFoundException e) { String message = String.format("Username %s is not found.", username); logger.info(message); } return user; } /* * (non-Javadoc) * * @see * gov.samhsa.consent2share.service.account.AccountService#recoverUsername * (java.lang.String, java.lang.String, java.lang.String, java.lang.String) */ @Override public String recoverUsername(String firstName, String lastName, String birthDay, String email) throws ParseException { DateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy"); Date date = dateFormat.parse(birthDay); Patient user = patientRepository .findByFirstNameAndLastNameAndBirthDayAndEmail(firstName, lastName, date, email); if (user == null) { return null; } return user.getUsername(); } /** * Generate mrn. * * @return the string */ protected String generateMrn() { String mrn = null; StringBuilder errorBuilder = new StringBuilder(); try { mrn = mrnService.generateMrn(); } catch (UniqueValueGeneratorException e) { errorBuilder.append(". "); errorBuilder.append(e.getMessage()); logger.error(e.getMessage(), e); } finally { Assert.hasText(mrn, errorBuilder.insert(0, "MRN cannot be generated by PCM!") .toString()); } return mrn; } }