/*******************************************************************************
* 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.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.staff.Staff;
import gov.samhsa.consent2share.domain.staff.StaffRepository;
import gov.samhsa.consent2share.infrastructure.security.EmailAddressNotExistException;
import gov.samhsa.consent2share.infrastructure.security.TokenExpiredException;
import gov.samhsa.consent2share.infrastructure.security.TokenNotExistException;
import gov.samhsa.consent2share.infrastructure.security.UsernameNotExistException;
import gov.samhsa.consent2share.service.dto.PasswordChangeDto;
import gov.samhsa.consent2share.service.dto.PasswordResetDto;
import java.util.Date;
import javax.mail.MessagingException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.util.StringUtils;
/**
* The Class PasswordResetServiceImpl.
*/
public class PasswordResetServiceImpl implements PasswordResetService {
/** The logger. */
private final Logger logger = LoggerFactory.getLogger(this.getClass());
/** The users repository. */
private UsersRepository usersRepository;
/** The patient repository. */
private PatientRepository patientRepository;
/** The staff repository. */
private StaffRepository staffRepository;
/** The token generator. */
private TokenGenerator tokenGenerator;
/** The password reset token expire in hours. */
private Integer passwordResetTokenExpireInHours;
/** The password reset token repository. */
private EmailTokenRepository passwordResetTokenRepository;
/** The email sender. */
private EmailSender emailSender;
/** The password encoder. */
private PasswordEncoder passwordEncoder;
/**
* Instantiates a new password reset service impl.
*
* @param passwordResetTokenExpireInHours
* the password reset token expire in hours
* @param usersRepository
* the users repository
* @param patientRepository
* the patient repository
* @param staffRepository
* the staff repository
* @param tokenGenerator
* the token generator
* @param passwordResetTokenRepository
* the password reset token repository
* @param emailSender
* the email sender
* @param passwordEncoder
* the password encoder
*/
public PasswordResetServiceImpl(Integer passwordResetTokenExpireInHours,
UsersRepository usersRepository,
PatientRepository patientRepository,
StaffRepository staffRepository, TokenGenerator tokenGenerator,
EmailTokenRepository passwordResetTokenRepository,
EmailSender emailSender, PasswordEncoder passwordEncoder) {
this.usersRepository = usersRepository;
this.patientRepository = patientRepository;
this.staffRepository = staffRepository;
this.tokenGenerator = tokenGenerator;
this.passwordResetTokenExpireInHours = passwordResetTokenExpireInHours;
this.passwordResetTokenRepository = passwordResetTokenRepository;
this.emailSender = emailSender;
this.passwordEncoder = passwordEncoder;
}
/*
* (non-Javadoc)
*
* @see gov.samhsa.consent2share.service.account.PasswordResetService#
* createPasswordResetToken(java.lang.String, java.lang.String,
* java.lang.String)
*/
@Override
public void createPasswordResetToken(String username, String emailAddress,
String linkUrl) throws UsernameNotExistException,
EmailAddressNotExistException, MessagingException {
Staff staff = null;
if (!StringUtils.hasText(username)) {
throw new IllegalArgumentException("Username is required.");
}
if (!StringUtils.hasText(emailAddress)) {
throw new IllegalArgumentException("Email Address is required.");
}
if (!StringUtils.hasText(linkUrl)) {
throw new IllegalArgumentException("Email link is required.");
}
Patient patient = patientRepository.findByUsername(username);
if (patient == null) {
staff = staffRepository.findByUsername(username);
if (staff == null)
throw new UsernameNotExistException(
"The username is not found.");
}
String patientEmailAddress = (patient != null) ? patient.getEmail()
: staff.getEmail();
String firstName = (patient != null) ? patient.getFirstName() : staff
.getFirstName();
String lastName = (patient != null) ? patient.getLastName() : staff
.getLastName();
if (!patientEmailAddress.equalsIgnoreCase(emailAddress)) {
String message = String.format(
"Email address %s doesn't exist for username %s.",
emailAddress, username);
logger.warn(message);
throw new EmailAddressNotExistException(message);
}
EmailToken passwordResetToken = new EmailToken();
passwordResetToken.setExpireInHours(passwordResetTokenExpireInHours);
passwordResetToken.setRequestDateTime(new Date());
String token = tokenGenerator.generateToken();
passwordResetToken.setUsername(username);
passwordResetToken.setToken(token);
passwordResetToken.setIsTokenUsed(false);
passwordResetToken.setTokenType(TokenType.PASSWORD_RESET);
passwordResetTokenRepository.save(passwordResetToken);
emailSender.sendMessage(firstName + " " + lastName, emailAddress,
EmailType.PASSWORD_RESET_REQUEST, linkUrl, token);
}
/*
* (non-Javadoc)
*
* @see gov.samhsa.consent2share.service.account.PasswordResetService#
* isPasswordResetTokenExpired(java.lang.String)
*/
@Override
public Boolean isPasswordResetTokenExpired(String token)
throws TokenNotExistException {
if (!StringUtils.hasText(token)) {
throw new IllegalArgumentException(
"Password reset token is required.");
}
EmailToken passwordResetToken = findPasswordResetToken(token);
Boolean isExpired = passwordResetToken.isTokenExpired();
return isExpired;
}
/*
* (non-Javadoc)
*
* @see
* gov.samhsa.consent2share.service.account.PasswordResetService#resetPassword
* (gov.samhsa.consent2share.service.dto.PasswordResetDto, java.lang.String)
*/
@Override
public void resetPassword(PasswordResetDto passwordResetDto, String linkUrl)
throws TokenNotExistException, TokenExpiredException,
UsernameNotExistException, MessagingException {
if (passwordResetDto == null) {
throw new IllegalArgumentException(
"Password reset dto is required.");
}
String token = passwordResetDto.getToken();
EmailToken passwordResetToken = findPasswordResetToken(token);
Boolean isExpired = passwordResetToken.isTokenExpired();
if (isExpired) {
throw new TokenExpiredException("Password reset token is expired.");
}
passwordResetToken.setIsTokenUsed(true);
passwordResetTokenRepository.save(passwordResetToken);
String username = passwordResetToken.getUsername();
Users user = null;
try {
user = usersRepository.loadUserByUsername(username);
} catch (UsernameNotFoundException e) {
// TODO: Log here
throw new UsernameNotExistException(e.getMessage());
}
String encodedPassword = passwordEncoder.encode(passwordResetDto
.getPassword());
Users updatedUser = null;
updatedUser = new Users(user.getFailedLoginAttempts(), username,
encodedPassword, user.isEnabled(),
user.isAccountNonExpired(), user.isCredentialsNonExpired(),
user.getAuthorities());
// if (user.isEnabled()) {
//
// updatedUser = new User(username, encodedPassword,
// user.getAuthorities());
// } else {
// updatedUser = new User(username, encodedPassword, false,
// true, true, true, user.getAuthorities());
// }
usersRepository.updateUser(updatedUser);
Patient patient = patientRepository.findByUsername(username);
emailSender.sendMessage(
patient.getFirstName() + " " + patient.getLastName(),
patient.getEmail(), EmailType.PASSWORD_CONFIRMATION, linkUrl,
null);
}
/*
* (non-Javadoc)
*
* @see
* gov.samhsa.consent2share.service.account.PasswordResetService#changePassword
* (gov.samhsa.consent2share.service.dto.PasswordChangeDto)
*/
@Override
public boolean changePassword(PasswordChangeDto passwordChangeDto)
throws UsernameNotExistException, MessagingException {
if (passwordChangeDto == null) {
throw new IllegalArgumentException(
"Password change dto is required.");
}
String username = passwordChangeDto.getUsername();
Users user = null;
try {
user = usersRepository.loadUserByUsername(username);
} catch (UsernameNotFoundException e) {
// TODO: Log here
throw new UsernameNotExistException(e.getMessage());
}
String encodedOldPassword = passwordEncoder.encode(passwordChangeDto
.getOldPassword());
String encodedNewPassword = passwordEncoder.encode(passwordChangeDto
.getNewPassword());
if (passwordEncoder.matches(passwordChangeDto.getOldPassword(),
user.getPassword()) == true) {
usersRepository.changePassword(encodedOldPassword,
encodedNewPassword);
return true;
} else {
return false;
}
}
/**
* Find password reset token.
*
* @param token
* the token
* @return the email token
* @throws TokenNotExistException
* the token not exist exception
*/
private EmailToken findPasswordResetToken(String token)
throws TokenNotExistException {
EmailToken passwordResetToken = passwordResetTokenRepository
.findByToken(token);
if (passwordResetToken == null) {
throw new TokenNotExistException(
"Password reset token doesn't exist.");
}
return passwordResetToken;
}
}