package stirling.software.SPDF.config.security; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.UUID; import java.util.stream.Collectors; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.MessageSource; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import stirling.software.SPDF.controller.api.pipeline.UserServiceInterface; import stirling.software.SPDF.model.AuthenticationType; import stirling.software.SPDF.model.Authority; import stirling.software.SPDF.model.Role; import stirling.software.SPDF.model.User; import stirling.software.SPDF.repository.AuthorityRepository; import stirling.software.SPDF.repository.UserRepository; @Service public class UserService implements UserServiceInterface { @Autowired private UserRepository userRepository; @Autowired private AuthorityRepository authorityRepository; @Autowired private PasswordEncoder passwordEncoder; @Autowired private MessageSource messageSource; // Handle OAUTH2 login and user auto creation. public boolean processOAuth2PostLogin(String username, boolean autoCreateUser) { if (!isUsernameValidWithReturn(username).equals(username)) { return false; } Optional existUser = userRepository.findByUsernameIgnoreCase(username); if (existUser.isPresent()) { return true; } if (autoCreateUser) { saveUser(username, AuthenticationType.OAUTH2); return true; } return false; } public Authentication getAuthentication(String apiKey) { User user = getUserByApiKey(apiKey); if (user == null) { throw new UsernameNotFoundException("API key is not valid"); } // Convert the user into an Authentication object return new UsernamePasswordAuthenticationToken( user, // principal (typically the user) null, // credentials (we don't expose the password or API key here) getAuthorities(user) // user's authorities (roles/permissions) ); } private Collection getAuthorities(User user) { // Convert each Authority object into a SimpleGrantedAuthority object. return user.getAuthorities().stream() .map((Authority authority) -> new SimpleGrantedAuthority(authority.getAuthority())) .collect(Collectors.toList()); } private String generateApiKey() { String apiKey; do { apiKey = UUID.randomUUID().toString(); } while (userRepository.findByApiKey(apiKey) != null); // Ensure uniqueness return apiKey; } public User addApiKeyToUser(String username) { User user = userRepository .findByUsernameIgnoreCase(username) .orElseThrow(() -> new UsernameNotFoundException("User not found")); user.setApiKey(generateApiKey()); return userRepository.save(user); } public User refreshApiKeyForUser(String username) { return addApiKeyToUser(username); // reuse the add API key method for refreshing } public String getApiKeyForUser(String username) { User user = userRepository .findByUsernameIgnoreCase(username) .orElseThrow(() -> new UsernameNotFoundException("User not found")); return user.getApiKey(); } public boolean isValidApiKey(String apiKey) { return userRepository.findByApiKey(apiKey) != null; } public User getUserByApiKey(String apiKey) { return userRepository.findByApiKey(apiKey); } public UserDetails loadUserByApiKey(String apiKey) { User userOptional = userRepository.findByApiKey(apiKey); if (userOptional != null) { User user = userOptional; // Convert your User entity to a UserDetails object with authorities return new org.springframework.security.core.userdetails.User( user.getUsername(), user.getPassword(), // you might not need this for API key auth getAuthorities(user)); } return null; // or throw an exception } public boolean validateApiKeyForUser(String username, String apiKey) { Optional userOpt = userRepository.findByUsernameIgnoreCase(username); return userOpt.isPresent() && userOpt.get().getApiKey().equals(apiKey); } public void saveUser(String username, AuthenticationType authenticationType) throws IllegalArgumentException { User user = new User(); user.setUsername(isUsernameValidWithReturn(username)); user.setEnabled(true); user.setFirstLogin(false); user.addAuthority(new Authority(Role.USER.getRoleId(), user)); user.setAuthenticationType(authenticationType); userRepository.save(user); } public void saveUser(String username, String password) throws IllegalArgumentException { User user = new User(); user.setUsername(isUsernameValidWithReturn(username)); user.setPassword(passwordEncoder.encode(password)); user.setEnabled(true); user.setAuthenticationType(AuthenticationType.WEB); userRepository.save(user); } public void saveUser(String username, String password, String role, boolean firstLogin) throws IllegalArgumentException { User user = new User(); user.setUsername(isUsernameValidWithReturn(username)); user.setPassword(passwordEncoder.encode(password)); user.addAuthority(new Authority(role, user)); user.setEnabled(true); user.setAuthenticationType(AuthenticationType.WEB); user.setFirstLogin(firstLogin); userRepository.save(user); } public void saveUser(String username, String password, String role) throws IllegalArgumentException { User user = new User(); user.setUsername(isUsernameValidWithReturn(username)); user.setPassword(passwordEncoder.encode(password)); user.addAuthority(new Authority(role, user)); user.setEnabled(true); user.setAuthenticationType(AuthenticationType.WEB); user.setFirstLogin(false); userRepository.save(user); } public void deleteUser(String username) { Optional userOpt = userRepository.findByUsernameIgnoreCase(username); if (userOpt.isPresent()) { for (Authority authority : userOpt.get().getAuthorities()) { if (authority.getAuthority().equals(Role.INTERNAL_API_USER.getRoleId())) { return; } } userRepository.delete(userOpt.get()); } } public boolean usernameExists(String username) { return userRepository.findByUsername(username).isPresent(); } public boolean usernameExistsIgnoreCase(String username) { return userRepository.findByUsernameIgnoreCase(username).isPresent(); } public boolean hasUsers() { return userRepository.count() > 0; } public void updateUserSettings(String username, Map updates) { Optional userOpt = userRepository.findByUsernameIgnoreCase(username); if (userOpt.isPresent()) { User user = userOpt.get(); Map settingsMap = user.getSettings(); if (settingsMap == null) { settingsMap = new HashMap(); } settingsMap.clear(); settingsMap.putAll(updates); user.setSettings(settingsMap); userRepository.save(user); } } public Optional findByUsername(String username) { return userRepository.findByUsername(username); } public Optional findByUsernameIgnoreCase(String username) { return userRepository.findByUsernameIgnoreCase(username); } public Authority findRole(User user) { return authorityRepository.findByUserId(user.getId()); } public void changeUsername(User user, String newUsername) throws IllegalArgumentException { user.setUsername(isUsernameValidWithReturn(newUsername)); userRepository.save(user); } public void changePassword(User user, String newPassword) { user.setPassword(passwordEncoder.encode(newPassword)); userRepository.save(user); } public void changeFirstUse(User user, boolean firstUse) { user.setFirstLogin(firstUse); userRepository.save(user); } public void changeRole(User user, String newRole) { Authority userAuthority = this.findRole(user); userAuthority.setAuthority(newRole); authorityRepository.save(userAuthority); } public boolean isPasswordCorrect(User user, String currentPassword) { return passwordEncoder.matches(currentPassword, user.getPassword()); } public boolean isUsernameValid(String username) { // Checks whether the simple username is formatted correctly boolean isValidSimpleUsername = username.matches("^[a-zA-Z0-9][a-zA-Z0-9@._+-]*[a-zA-Z0-9]$"); // Checks whether the email address is formatted correctly boolean isValidEmail = username.matches( "^(?=.{1,64}@)[A-Za-z0-9]+(\\.[A-Za-z0-9_+.-]+)*@[^-][A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(\\.[A-Za-z]{2,})$"); return isValidSimpleUsername || isValidEmail; } public String isUsernameValidWithReturn(String username) throws IllegalArgumentException { if (!isUsernameValid(username)) { String message = messageSource.getMessage( "invalidUsernameMessage", null, LocaleContextHolder.getLocale()); throw new IllegalArgumentException(message); } return username; } public boolean hasPassword(String username) { Optional user = userRepository.findByUsernameIgnoreCase(username); if (user.isPresent() && user.get().hasPassword()) { return true; } return false; } public boolean isAuthenticationTypeByUsername( String username, AuthenticationType authenticationType) { Optional user = userRepository.findByUsernameIgnoreCase(username); if (user.isPresent() && user.get().getAuthenticationType() != null) { return user.get().getAuthenticationType().equalsIgnoreCase(authenticationType.name()); } return false; } }