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.context.SecurityContextHolder; 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.UserActivity; import ru.ulstu.core.model.response.PageableItems; import ru.ulstu.ping.model.Ping; import ru.ulstu.ping.service.PingService; import ru.ulstu.user.error.UserActivationError; import ru.ulstu.user.error.UserBlockedException; 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.HashMap; 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; private final PingService pingService; public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder, UserRoleRepository userRoleRepository, UserMapper userMapper, MailService mailService, ApplicationProperties applicationProperties, @Lazy PingService pingService, @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; this.pingService = pingService; } 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, Sort.by("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.getOne(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.getOne(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(); } if (user.getBlocker() != null) { throw new UserBlockedException(String.format("Вы заблокированы пользователем %s", user.getBlocker().getUserAbbreviate())); } 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.findAllById(ids); } public User findById(Integer id) { return userRepository.getOne(id); } public User getCurrentUser() { String login = UserUtils.getCurrentUserLogin(SecurityContextHolder.getContext()); 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); } public Map getActivitiesPings(Integer userId, String activityName) { User user = null; if (userId != null) { user = findById(userId); } Map activitiesPings = new HashMap<>(); for (Ping ping : pingService.getPings(activityName)) { UserActivity activity = ping.getActivity(); if (user != null && !activity.getActivityUsers().contains(user)) { continue; } if (activitiesPings.containsKey(activity.getTitle())) { activitiesPings.put(activity.getTitle(), activitiesPings.get(activity.getTitle()) + 1); } else { activitiesPings.put(activity.getTitle(), 1); } } return activitiesPings; } public void blockUser(int userId) { User userToBlock = findById(userId); userToBlock.setBlocker(getCurrentUser()); userRepository.save(userToBlock); } }