package org.ros.chatto.service; import java.time.Duration; import java.time.Instant; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import org.ros.chatto.dto.ActiveUserDTO; import org.ros.chatto.dto.UserRegistrationDTO; import org.ros.chatto.logged.TokenCacheUtil; import org.ros.chatto.model.ChatUser; import org.ros.chatto.model.Role; import org.ros.chatto.model.UserRole; import org.ros.chatto.model.UserSession; import org.ros.chatto.repository.RoleRepository; import org.ros.chatto.repository.UserRepository; import org.ros.chatto.repository.UserRoleRepository; import org.ros.chatto.repository.UserSessionRepository; import org.springframework.security.authentication.InternalAuthenticationServiceException; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @Transactional @Service @RequiredArgsConstructor @Slf4j public class UserServiceImpl implements UserService { private final UserRepository userRepository; private final UserRoleRepository userRoleRepository; private final PasswordEncoder passwordEncoder; private final RoleRepository roleRepository; private final UserSessionRepository userSessionRepository; private final UserTokenService userTokenService; @Override public ChatUser createUser(final UserRegistrationDTO userRegistrationDTO) { final ChatUser user = new ChatUser(); user.setUserName(userRegistrationDTO.getUserName()); user.setPassword( passwordEncoder.encode(userRegistrationDTO.getPassword())); final ChatUser changedUser = userRepository.save(user); final UserRole userRole = new UserRole(); final Role role = roleRepository.findByName("USER"); userRole.setRole(role); userRole.setUser(changedUser); return userRoleRepository.save(userRole).getUser(); } @Override @Transactional(readOnly = true) public List findAllOtherUsers(final String userName) { return userRepository.findAllOtherUserNames(userName); } @Transactional(readOnly = true) @Override public List getAllRegularUsers() { return userRoleRepository.getAllRegularUser(); } @Transactional(readOnly = true) public List getOtherActiveUsers(final String userName) { final List userList = findAllOtherUsers(userName); final List userSessionsList = userSessionRepository .findAllUserSessions(); final Map lastActiveMap = convertToMap( userSessionsList); final List activeUserDTOs = new ArrayList(); userList.forEach(u -> { final ActiveUserDTO activeUserDTO = new ActiveUserDTO(); final UserSession us = lastActiveMap.get(u); activeUserDTO.setUserName(u); activeUserDTO.setOnline(false); activeUserDTO.setLastActive(null); if (us != null) { activeUserDTO.setOnline(us.isOnline()); activeUserDTO.setLastActive(us.getTimeStamp()); } activeUserDTOs.add(activeUserDTO); }); return activeUserDTOs; } @Transactional(readOnly = true) @Override public Optional getUser(final String userName) { return userRepository.findByUserName(userName); } private Map convertToMap( final List userSessionList) { final Map userMap = new HashMap<>(); userSessionList.forEach(us -> { userMap.put(us.getUser().getUserName(), us); }); return userMap; } private String toLastActiveString(final Instant lastActive) { if (lastActive == null) return null; final Duration duration = Duration.between(lastActive, Instant.now()); final long hours = duration.toHours(); final long minutes = duration.toMinutes(); final long days = duration.toDays(); if (minutes < 60) { return String.format("%d minutes ago", minutes); } else { if (hours < 24) { return String.format("%d hours ago", hours); } else { if (days < 30) { return String.format("%d days ago", days); } else if (days < 366) { return String.format("%d months ago", days / 30); } else { return String.format("%d years ago", days / 365); } } } } @Override @Transactional(readOnly = true) public ChatUser getUserWithRole(final String userName) { return userRepository.findByUserNameWithRole(userName); } @Override public void incrementUserSession(final String userName) { final Optional chatUser = getUser(userName); final Instant instant = Instant.now(); chatUser.ifPresent(cu -> { UserSession userSession = userSessionRepository .findByUserName(userName); if (userSession == null) { userSession = new UserSession(); } userSession.setUser(cu); userSession.setTimeStamp(instant); userSession.setOnline(true); userSession.setNumSessions(userSession.getNumSessions() + 1); userSessionRepository.save(userSession); }); } @Override public UserSession decrementUserSession(final String userName) { final UserSession userSession = userSessionRepository .findByUserName(userName); final Instant instant = Instant.now(); if (userSession == null) { log.error("User session is somehow null for user: " + userName); throw new InternalAuthenticationServiceException( "User session not found"); } final ChatUser chatUser = userSession.getUser(); int numSessions = userSession.getNumSessions(); if (--numSessions == 0) { log.info("Num sessions is 0 so setting user to offline"); log.info("Deleting token and evicting cache for user: " + chatUser.getUserName()); userSession.setOnline(false); userTokenService.deleteToken(chatUser.getUserName()); TokenCacheUtil.evictSingleTokenValue(chatUser.getUserName()); } userSession.setNumSessions(numSessions); userSession.setTimeStamp(instant); return userSessionRepository.save(userSession); } @Override public Set getRoles(final ChatUser user) { return user.getUserRoles().stream().map(ur -> ur.getRole()) .collect(Collectors.toSet()); } }