package fr.openwide.core.basicapp.core.security.service; import static com.google.common.base.Preconditions.checkNotNull; import static fr.openwide.core.spring.property.SpringSecurityPropertyIds.PASSWORD_EXPIRATION_DAYS; import static fr.openwide.core.spring.property.SpringSecurityPropertyIds.PASSWORD_HISTORY_COUNT; import static fr.openwide.core.spring.property.SpringSecurityPropertyIds.PASSWORD_RECOVERY_REQUEST_EXPIRATION_MINUTES; import static fr.openwide.core.spring.property.SpringSecurityPropertyIds.PASSWORD_RECOVERY_REQUEST_TOKEN_RANDOM_COUNT; import java.util.Date; import java.util.Map; import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.time.DateUtils; import org.springframework.beans.factory.annotation.Autowired; import com.google.common.collect.EvictingQueue; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import fr.openwide.core.basicapp.core.business.history.model.atomic.HistoryEventType; import fr.openwide.core.basicapp.core.business.history.model.bean.HistoryLogAdditionalInformationBean; import fr.openwide.core.basicapp.core.business.history.service.IHistoryLogService; import fr.openwide.core.basicapp.core.business.notification.service.INotificationService; import fr.openwide.core.basicapp.core.business.user.model.User; import fr.openwide.core.basicapp.core.business.user.model.atomic.UserPasswordRecoveryRequestInitiator; import fr.openwide.core.basicapp.core.business.user.model.atomic.UserPasswordRecoveryRequestType; import fr.openwide.core.basicapp.core.business.user.service.IUserService; import fr.openwide.core.basicapp.core.security.model.SecurityOptions; import fr.openwide.core.jpa.exception.SecurityServiceException; import fr.openwide.core.jpa.exception.ServiceException; import fr.openwide.core.jpa.security.business.person.model.GenericUser; import fr.openwide.core.jpa.util.HibernateUtils; import fr.openwide.core.spring.property.service.IPropertyService; import fr.openwide.core.spring.util.StringUtils; public class SecurityManagementServiceImpl implements ISecurityManagementService { private final Map<Class<? extends GenericUser<?, ?>>, SecurityOptions> optionsByUser = Maps.newHashMap(); private SecurityOptions defaultOptions = SecurityOptions.DEFAULT; @Autowired private IUserService userService; @Autowired private INotificationService notificationService; @Autowired private IPropertyService propertyService; @Autowired private IHistoryLogService historyLogService; public SecurityManagementServiceImpl setOptions(Class<? extends User> clazz, SecurityOptions options) { checkNotNull(clazz); checkNotNull(options); optionsByUser.put(clazz, options); return this; } public SecurityManagementServiceImpl setDefaultOptions(SecurityOptions options) { checkNotNull(options); defaultOptions = options; return this; } @Override public SecurityOptions getOptions(Class<? extends User> clazz) { if (optionsByUser.containsKey(clazz)) { return optionsByUser.get(clazz); } return defaultOptions; } @Override public SecurityOptions getOptions(User user) { if (user == null) { return defaultOptions; } return getOptions(HibernateUtils.unwrap(user).getClass()); } @Override public void initiatePasswordRecoveryRequest(User user, UserPasswordRecoveryRequestType type, UserPasswordRecoveryRequestInitiator initiator) throws ServiceException, SecurityServiceException { initiatePasswordRecoveryRequest(user, type, initiator, user); } @Override public void initiatePasswordRecoveryRequest(User user, UserPasswordRecoveryRequestType type, UserPasswordRecoveryRequestInitiator initiator, User author) throws ServiceException, SecurityServiceException { Date now = new Date(); user.getPasswordRecoveryRequest().setToken(RandomStringUtils.randomAlphanumeric(propertyService.get(PASSWORD_RECOVERY_REQUEST_TOKEN_RANDOM_COUNT))); user.getPasswordRecoveryRequest().setCreationDate(now); user.getPasswordRecoveryRequest().setType(type); user.getPasswordRecoveryRequest().setInitiator(initiator); userService.update(user); notificationService.sendUserPasswordRecoveryRequest(user); switch (type) { case CREATION: historyLogService.log(HistoryEventType.PASSWORD_CREATION_REQUEST, user, HistoryLogAdditionalInformationBean.empty()); break; case RESET: historyLogService.log(HistoryEventType.PASSWORD_RESET_REQUEST, user, HistoryLogAdditionalInformationBean.empty()); break; default: break; } } @Override public boolean isPasswordExpired(User user) { if (user == null || user.getPasswordInformation().getLastUpdateDate() == null || !getOptions(user).isPasswordExpirationEnabled()) { return false; } Date expirationDate = DateUtils.addDays(user.getPasswordInformation().getLastUpdateDate(), propertyService.get(PASSWORD_EXPIRATION_DAYS)); Date now = new Date(); return now.after(expirationDate); } @Override public boolean isPasswordRecoveryRequestExpired(User user) { if (user == null || user.getPasswordRecoveryRequest().getToken() == null || user.getPasswordRecoveryRequest().getCreationDate() == null) { return true; } Date expirationDate = DateUtils.addMinutes(user.getPasswordRecoveryRequest().getCreationDate(), propertyService.get(PASSWORD_RECOVERY_REQUEST_EXPIRATION_MINUTES)); Date now = new Date(); return now.after(expirationDate); } @Override public void updatePassword(User user, String password) throws ServiceException, SecurityServiceException { updatePassword(user, password, user); } @Override public void updatePassword(User user, String password, User author) throws ServiceException, SecurityServiceException { if (user == null || !StringUtils.hasText(password)) { return; } userService.setPasswords(user, password); user.getPasswordInformation().setLastUpdateDate(new Date()); if (getOptions(user).isPasswordHistoryEnabled()) { EvictingQueue<String> historyQueue = EvictingQueue.create(propertyService.get(PASSWORD_HISTORY_COUNT)); for (String oldPassword : user.getPasswordInformation().getHistory()) { historyQueue.offer(oldPassword); } historyQueue.offer(user.getPasswordHash()); user.getPasswordInformation().setHistory(Lists.newArrayList(historyQueue)); } user.getPasswordRecoveryRequest().reset(); userService.update(user); historyLogService.log(HistoryEventType.PASSWORD_UPDATE, user, HistoryLogAdditionalInformationBean.empty()); } }