package ru.ulstu.user.service; import com.google.common.collect.ImmutableMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Lazy; import org.springframework.data.domain.Page; import org.springframework.data.domain.Sort; import org.springframework.mail.MailException; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; import ru.ulstu.conference.service.ConferenceService; import ru.ulstu.configuration.ApplicationProperties; import ru.ulstu.core.error.EntityIdIsNullException; import ru.ulstu.core.jpa.OffsetablePageRequest; import ru.ulstu.core.model.BaseEntity; import ru.ulstu.core.model.response.PageableItems; import ru.ulstu.user.error.UserActivationError; import ru.ulstu.user.error.UserEmailExistsException; import ru.ulstu.user.error.UserIdExistsException; import ru.ulstu.user.error.UserIsUndeadException; import ru.ulstu.user.error.UserLoginExistsException; import ru.ulstu.user.error.UserNotActivatedException; import ru.ulstu.user.error.UserNotFoundException; import ru.ulstu.user.error.UserPasswordsNotValidOrNotMatchException; import ru.ulstu.user.error.UserResetKeyError; import ru.ulstu.user.error.UserSendingMailException; import ru.ulstu.user.model.User; import ru.ulstu.user.model.UserDto; import ru.ulstu.user.model.UserInfoNow; import ru.ulstu.user.model.UserListDto; import ru.ulstu.user.model.UserResetPasswordDto; import ru.ulstu.user.model.UserRole; import ru.ulstu.user.model.UserRoleConstants; import ru.ulstu.user.model.UserRoleDto; import ru.ulstu.user.repository.UserRepository; import ru.ulstu.user.repository.UserRoleRepository; import ru.ulstu.user.util.UserUtils; import ru.ulstu.utils.timetable.TimetableService; import ru.ulstu.utils.timetable.errors.TimetableClientException; import ru.ulstu.utils.timetable.model.Lesson; import javax.mail.MessagingException; import java.text.ParseException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @Service @Transactional public class UserService implements UserDetailsService { private static final String INVITE_USER_EXCEPTION = "Во время отправки приглашения произошла ошибка"; private final Logger log = LoggerFactory.getLogger(UserService.class); private final UserRepository userRepository; private final PasswordEncoder passwordEncoder; private final UserRoleRepository userRoleRepository; private final UserMapper userMapper; private final MailService mailService; private final ApplicationProperties applicationProperties; private final TimetableService timetableService; private final ConferenceService conferenceService; private final UserSessionService userSessionService; public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder, UserRoleRepository userRoleRepository, UserMapper userMapper, MailService mailService, ApplicationProperties applicationProperties, @Lazy ConferenceService conferenceRepository, @Lazy UserSessionService userSessionService) throws ParseException { this.userRepository = userRepository; this.passwordEncoder = passwordEncoder; this.userRoleRepository = userRoleRepository; this.userMapper = userMapper; this.mailService = mailService; this.applicationProperties = applicationProperties; this.conferenceService = conferenceRepository; this.timetableService = new TimetableService(); this.userSessionService = userSessionService; } private User getUserByEmail(String email) { return userRepository.findOneByEmailIgnoreCase(email); } private User getUserByActivationKey(String activationKey) { return userRepository.findOneByActivationKey(activationKey); } public User getUserByLogin(String login) { return userRepository.findOneByLoginIgnoreCase(login); } @Transactional(readOnly = true) public UserDto getUserWithRolesById(Integer userId) { final User userEntity = userRepository.findOneWithRolesById(userId); if (userEntity == null) { throw new UserNotFoundException(userId.toString()); } return userMapper.userEntityToUserDto(userEntity); } @Transactional(readOnly = true) public PageableItems getAllUsers(int offset, int count) { final Page page = userRepository.findAll(new OffsetablePageRequest(offset, count, new Sort("id"))); return new PageableItems<>(page.getTotalElements(), userMapper.userEntitiesToUserListDtos(page.getContent())); } // TODO: read only active users public List findAll() { return userRepository.findAll(); } @Transactional(readOnly = true) public PageableItems getUserRoles() { final List roles = userRoleRepository.findAll().stream() .map(UserRoleDto::new) .sorted(Comparator.comparing(UserRoleDto::getViewValue)) .collect(Collectors.toList()); return new PageableItems<>(roles.size(), roles); } public UserDto createUser(UserDto userDto) { if (userDto.getId() != null) { throw new UserIdExistsException(); } if (getUserByLogin(userDto.getLogin()) != null) { throw new UserLoginExistsException(userDto.getLogin()); } if (getUserByEmail(userDto.getEmail()) != null) { throw new UserEmailExistsException(userDto.getEmail()); } if (!userDto.isPasswordsValid()) { throw new UserPasswordsNotValidOrNotMatchException(""); } User user = userMapper.userDtoToUserEntity(userDto); user.setActivated(false); user.setActivationKey(UserUtils.generateActivationKey()); user.setRoles(Collections.singleton(new UserRole(UserRoleConstants.USER))); user.setPassword(passwordEncoder.encode(userDto.getPassword())); user = userRepository.save(user); mailService.sendActivationEmail(user); log.debug("Created Information for User: {}", user.getLogin()); return userMapper.userEntityToUserDto(user); } public UserDto activateUser(String activationKey) { final User user = getUserByActivationKey(activationKey); if (user == null) { throw new UserActivationError(activationKey); } user.setActivated(true); user.setActivationKey(null); user.setActivationDate(null); log.debug("Activated user: {}", user.getLogin()); return userMapper.userEntityToUserDto(userRepository.save(user)); } public UserDto updateUser(UserDto userDto) { if (userDto.getId() == null) { throw new EntityIdIsNullException(); } if (!Objects.equals( Optional.ofNullable(getUserByEmail(userDto.getEmail())) .map(BaseEntity::getId).orElse(userDto.getId()), userDto.getId())) { throw new UserEmailExistsException(userDto.getEmail()); } if (!Objects.equals( Optional.ofNullable(getUserByLogin(userDto.getLogin())) .map(BaseEntity::getId).orElse(userDto.getId()), userDto.getId())) { throw new UserLoginExistsException(userDto.getLogin()); } User user = userRepository.findOne(userDto.getId()); if (user == null) { throw new UserNotFoundException(userDto.getId().toString()); } if (applicationProperties.getUndeadUserLogin().equalsIgnoreCase(user.getLogin())) { userDto.setLogin(applicationProperties.getUndeadUserLogin()); userDto.setActivated(true); userDto.setRoles(Collections.singletonList(new UserRoleDto(UserRoleConstants.ADMIN))); } user.setLogin(userDto.getLogin()); user.setFirstName(userDto.getFirstName()); user.setLastName(userDto.getLastName()); user.setEmail(userDto.getEmail()); if (userDto.isActivated() != user.getActivated()) { if (userDto.isActivated()) { user.setActivationKey(null); user.setActivationDate(null); } else { user.setActivationKey(UserUtils.generateActivationKey()); user.setActivationDate(new Date()); } } user.setActivated(userDto.isActivated()); final Set roles = userMapper.rolesFromDto(userDto.getRoles()); user.setRoles(roles.isEmpty() ? Collections.singleton(new UserRole(UserRoleConstants.USER)) : roles); if (!StringUtils.isEmpty(userDto.getOldPassword())) { if (!userDto.isPasswordsValid() || !userDto.isOldPasswordValid()) { throw new UserPasswordsNotValidOrNotMatchException(""); } if (!passwordEncoder.matches(userDto.getOldPassword(), user.getPassword())) { throw new UserPasswordsNotValidOrNotMatchException(""); } user.setPassword(passwordEncoder.encode(userDto.getPassword())); log.debug("Changed password for User: {}", user.getLogin()); } user = userRepository.save(user); log.debug("Changed Information for User: {}", user.getLogin()); return userMapper.userEntityToUserDto(user); } public UserDto updateUserInformation(User user, UserDto updateUser) { user.setFirstName(updateUser.getFirstName()); user.setLastName(updateUser.getLastName()); user.setEmail(updateUser.getEmail()); user.setLogin(updateUser.getLogin()); user = userRepository.save(user); log.debug("Updated Information for User: {}", user.getLogin()); return userMapper.userEntityToUserDto(user); } public void changeUserPassword(User user, Map payload) { if (!payload.get("password").equals(payload.get("confirmPassword"))) { throw new UserPasswordsNotValidOrNotMatchException(""); } if (!passwordEncoder.matches(payload.get("oldPassword"), user.getPassword())) { throw new UserPasswordsNotValidOrNotMatchException("Старый пароль введен неправильно"); } user.setPassword(passwordEncoder.encode(payload.get("password"))); log.debug("Changed password for User: {}", user.getLogin()); userRepository.save(user); mailService.sendChangePasswordMail(user); } public boolean requestUserPasswordReset(String email) { User user = userRepository.findOneByEmailIgnoreCase(email); if (user == null) { throw new UserNotFoundException(email); } if (!user.getActivated()) { throw new UserNotActivatedException(); } user.setResetKey(UserUtils.generateResetKey()); user.setResetDate(new Date()); user = userRepository.save(user); try { mailService.sendPasswordResetMail(user); } catch (MessagingException | MailException e) { throw new UserSendingMailException(email); } log.debug("Created Reset Password Request for User: {}", user.getLogin()); return true; } public boolean completeUserPasswordReset(UserResetPasswordDto userResetPasswordDto) { if (!userResetPasswordDto.isPasswordsValid()) { throw new UserPasswordsNotValidOrNotMatchException("Пароли не совпадают"); } User user = userRepository.findOneByResetKey(userResetPasswordDto.getResetKey()); if (user == null) { throw new UserResetKeyError(userResetPasswordDto.getResetKey()); } user.setPassword(passwordEncoder.encode(userResetPasswordDto.getPassword())); user.setResetKey(null); user.setResetDate(null); user = userRepository.save(user); mailService.sendChangePasswordMail(user); log.debug("Reset Password for User: {}", user.getLogin()); return true; } public UserDto deleteUser(Integer userId) { final User user = userRepository.findOne(userId); if (user == null) { throw new UserNotFoundException(userId.toString()); } if (applicationProperties.getUndeadUserLogin().equalsIgnoreCase(user.getLogin())) { throw new UserIsUndeadException(user.getLogin()); } userRepository.delete(user); log.debug("Deleted User: {}", user.getLogin()); return userMapper.userEntityToUserDto(user); } @Override public UserDetails loadUserByUsername(String username) { final User user = userRepository.findOneByLoginIgnoreCase(username); if (user == null) { throw new UserNotFoundException(username); } if (!user.getActivated()) { throw new UserNotActivatedException(); } return new org.springframework.security.core.userdetails.User(user.getLogin(), user.getPassword(), Optional.ofNullable(user.getRoles()).orElse(Collections.emptySet()).stream() .map(role -> new SimpleGrantedAuthority(role.getName())) .collect(Collectors.toList())); } public List findByIds(List ids) { return userRepository.findAll(ids); } public User findById(Integer id) { return userRepository.findOne(id); } public User getCurrentUser() { String login = UserUtils.getCurrentUserLogin(); User user = userRepository.findOneByLoginIgnoreCase(login); if (user == null) { throw new UserNotFoundException(login); } return user; } public List filterByAgeAndDegree(boolean hasDegree, boolean hasAge) { return userRepository.filterByAgeAndDegree(hasDegree, hasAge); } public void inviteUser(String email) throws UserSendingMailException { if (userRepository.findOneByEmailIgnoreCase(email) != null) { throw new UserEmailExistsException(email); } String password = UserUtils.generatePassword(); User user = new User(); user.setPassword(passwordEncoder.encode(password)); user.setLogin(email); user.setEmail(email); user.setFirstName("user"); user.setLastName("user"); user.setActivated(true); userRepository.save(user); Map variables = ImmutableMap.of("password", password, "email", email); try { mailService.sendInviteMail(variables, email); } catch (MessagingException | MailException e) { throw new UserSendingMailException(email); } } public User findOneByLoginIgnoreCase(String login) { return userRepository.findOneByLoginIgnoreCase(login); } public Map getUsersInfo() { List usersInfoNow = new ArrayList<>(); String err = ""; for (User user : userRepository.findAll()) { Lesson lesson = null; try { lesson = timetableService.getCurrentLesson(user.getUserAbbreviate()); } catch (TimetableClientException e) { err = "Не удалось загрузить расписание"; } usersInfoNow.add(new UserInfoNow( lesson, conferenceService.getActiveConferenceByUser(user), user, userSessionService.isOnline(user)) ); } return ImmutableMap.of("users", usersInfoNow, "error", err); } }