A self hosted chat application with end-to-end encrypted messaging.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

203 lines
6.1 KiB

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<String> findAllOtherUsers(final String userName) {
return userRepository.findAllOtherUserNames(userName);
}
@Transactional(readOnly = true)
@Override
public List<String> getAllRegularUsers() {
return userRoleRepository.getAllRegularUser();
}
@Transactional(readOnly = true)
public List<ActiveUserDTO> getOtherActiveUsers(final String userName) {
final List<String> userList = findAllOtherUsers(userName);
final List<UserSession> userSessionsList = userSessionRepository
.findAllUserSessions();
final Map<String, UserSession> lastActiveMap = convertToMap(
userSessionsList);
final List<ActiveUserDTO> activeUserDTOs = new ArrayList<ActiveUserDTO>();
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<ChatUser> getUser(final String userName) {
return userRepository.findByUserName(userName);
}
private Map<String, UserSession> convertToMap(
final List<UserSession> userSessionList) {
final Map<String, UserSession> 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> 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<Role> getRoles(final ChatUser user) {
return user.getUserRoles().stream().map(ur -> ur.getRole())
.collect(Collectors.toSet());
}
}