Coverage Summary for Class: UserService (org.kitodo.production.services.data)
Class |
Class, %
|
Method, %
|
Line, %
|
UserService |
100%
(1/1)
|
57,1%
(20/35)
|
46,3%
(63/136)
|
/*
* (c) Kitodo. Key to digital objects e. V. <contact@kitodo.org>
*
* This file is part of the Kitodo project.
*
* It is licensed under GNU General Public License version 3 or later.
*
* For the full copyright and license information, please read the
* GPL3-License.txt file that was distributed with this source code.
*/
package org.kitodo.production.services.data;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.kitodo.config.ConfigCore;
import org.kitodo.config.enums.KitodoConfigFile;
import org.kitodo.config.enums.ParameterCore;
import org.kitodo.data.database.beans.Client;
import org.kitodo.data.database.beans.Filter;
import org.kitodo.data.database.beans.Task;
import org.kitodo.data.database.beans.User;
import org.kitodo.data.database.enums.TaskStatus;
import org.kitodo.data.database.exceptions.DAOException;
import org.kitodo.data.database.persistence.UserDAO;
import org.kitodo.data.exceptions.DataException;
import org.kitodo.exceptions.FilterException;
import org.kitodo.production.dto.UserDTO;
import org.kitodo.production.helper.Helper;
import org.kitodo.production.security.SecurityUserDetails;
import org.kitodo.production.security.password.SecurityPasswordEncoder;
import org.kitodo.production.services.ServiceManager;
import org.kitodo.production.services.data.base.ClientSearchDatabaseService;
import org.primefaces.model.SortOrder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
public class UserService extends ClientSearchDatabaseService<User, UserDAO> implements UserDetailsService {
private static final Logger logger = LogManager.getLogger(UserService.class);
private static volatile UserService instance = null;
private static final String CLIENT_ID = "clientId";
private final SecurityPasswordEncoder passwordEncoder = new SecurityPasswordEncoder();
private static final int DEFAULT_CLIENT_ID =
ConfigCore.getIntParameterOrDefaultValue(ParameterCore.DEFAULT_CLIENT_ID);
/**
* Constructor.
*/
private UserService() {
super(new UserDAO());
}
/**
* Return singleton variable of type UserService.
*
* @return unique instance of UserService
*/
public static UserService getInstance() {
UserService localReference = instance;
if (Objects.isNull(localReference)) {
synchronized (UserService.class) {
localReference = instance;
if (Objects.isNull(localReference)) {
localReference = new UserService();
instance = localReference;
}
}
}
return localReference;
}
@Override
public Long countDatabaseRows() throws DAOException {
return countDatabaseRows("SELECT COUNT(*) FROM User WHERE deleted = 0");
}
@Override
public Long countResults(Map filters) throws DAOException {
HashMap<String, Object> filterMap;
try {
filterMap = ServiceManager.getFilterService().getSQLFilterMap(filters, User.class);
} catch (NoSuchFieldException | NumberFormatException e) {
throw new FilterException(e.getClass().getSimpleName() + ": " + e.getMessage());
}
String sqlFilterString = ServiceManager.getFilterService().mapToSQLFilterString(filterMap.keySet());
if (ServiceManager.getSecurityAccessService().hasAuthorityGlobalToViewUserList()) {
return countDatabaseRows("SELECT COUNT(*) FROM User WHERE deleted = 0" + sqlFilterString, filterMap);
}
if (ServiceManager.getSecurityAccessService().hasAuthorityToViewUserList()) {
filterMap.put(CLIENT_ID, getSessionClientId());
return countDatabaseRows(
"SELECT COUNT(*) FROM User u INNER JOIN u.clients AS c WITH c.id = :clientId WHERE deleted = 0"
+ sqlFilterString, filterMap);
}
return 0L;
}
@Override
public List<User> getAllForSelectedClient() {
return getByQuery(
"SELECT u FROM User AS u INNER JOIN u.clients AS c WITH c.id = :clientId WHERE deleted = 0",
Collections.singletonMap(CLIENT_ID, getSessionClientId()));
}
@Override
public UserDetails loadUserByUsername(String username) {
return new SecurityUserDetails(getByLdapLoginOrLogin(username));
}
@Override
public List<User> loadData(int first, int pageSize, String sortField, SortOrder sortOrder, Map filters) {
HashMap<String, Object> filterMap;
try {
filterMap = ServiceManager.getFilterService().getSQLFilterMap(filters, User.class);
} catch (NoSuchFieldException | NumberFormatException e) {
throw new FilterException(e.getClass().getSimpleName() + ": " + e.getMessage());
}
String sqlFilterString = ServiceManager.getFilterService().mapToSQLFilterString(filterMap.keySet());
if (ServiceManager.getSecurityAccessService().hasAuthorityGlobalToViewUserList()) {
return dao.getByQuery("FROM User WHERE deleted = 0" + sqlFilterString + getSort(sortField, sortOrder),
filterMap, first, pageSize);
}
if (ServiceManager.getSecurityAccessService().hasAuthorityToViewUserList()) {
filterMap.put(CLIENT_ID, getSessionClientId());
return dao.getByQuery(
"SELECT u FROM User AS u INNER JOIN u.clients AS c WITH c.id = :clientId WHERE deleted = 0"
+ sqlFilterString + getSort(sortField, sortOrder),
filterMap, first, pageSize);
}
return new ArrayList<>();
}
/**
* Returns a user by his or her LDAP login or common login.
*
* @param login
* the login of the user to look for
* @return the user, if one is found
* @throws UsernameNotFoundException
* if no unique user can be found
*/
public User getByLdapLoginOrLogin(String login) {
List<User> users = getByLoginQuery(login, "from User where ldapLogin = :login");
users.addAll(getByLoginQuery(login, "from User where login = :login"));
return uniqueResult(users, login);
}
/**
* Gets user by login.
*
* @param login
* The login.
* @return The user.
*/
public User getByLogin(String login) {
List<User> users = getByLoginQuery(login, "from User where login = :login");
return uniqueResult(users, login);
}
private List<User> getByLoginQuery(String login, String query) {
return getByQuery(query, Collections.singletonMap("login", login));
}
private User uniqueResult(List<User> users, String login) {
if (users.size() == 1) {
return users.get(0);
} else if (users.isEmpty()) {
throw new UsernameNotFoundException("Login '" + login + "' not found!");
} else {
logger.error("Login '{}' was found more than once! Affected IDs: {}", login,
users.stream().map(user -> Objects.toString(user.getId())).collect(Collectors.joining(", ")));
throw new UsernameNotFoundException("Login '" + login + "' was found more than once!");
}
}
/**
* Gets the current authenticated user of current threads security context.
*
* @return The SecurityUserDetails object or null if no user is authenticated.
*/
public SecurityUserDetails getAuthenticatedUser() {
return ServiceManager.getSecurityAccessService().getAuthenticatedSecurityUserDetails();
}
/**
* Get the current authenticated user as User bean.
*
* @return the User object
*/
public User getCurrentUser() {
return new User(getAuthenticatedUser());
}
/**
* Gets the session client of the current authenticated user.
*
* @return The client object or null if no session client is set or no user is
* authenticated.
*/
public Client getSessionClientOfAuthenticatedUser() {
if (Objects.nonNull(getAuthenticatedUser())) {
return getAuthenticatedUser().getSessionClient();
} else {
return null;
}
}
/**
* Gets the selected session client id of the current authenticated user.
*
* @return session client id
*/
public int getSessionClientId() {
if (Objects.nonNull(getSessionClientOfAuthenticatedUser())) {
return getSessionClientOfAuthenticatedUser().getId();
}
return DEFAULT_CLIENT_ID;
}
/**
* Get amount of users with exactly the same login like given but different id.
*
* @param id
* of user
* @param login
* of user
* @return amount of users with exactly the same login like given but different
* id
*/
public Long getAmountOfUsersWithExactlyTheSameLogin(Integer id, String login) throws DAOException {
return dao.countUsersWithExactlyTheSameLogin(id, login);
}
/**
* Get all active users sorted by surname and name.
*
* @return sorted list of all active users as User objects
*/
public List<User> getAllActiveUsersSortedByNameAndSurname() {
return dao.getAllActiveUsersSortedByNameAndSurname();
}
/**
* Check validity of given login.
*
* @param login
* to validation
* @return true or false
*/
public boolean isLoginValid(String login) {
String patternString = "[A-Za-z0-9@_\\-.]*";
Pattern pattern = Pattern.compile(patternString);
Matcher matcher = pattern.matcher(login);
if (!matcher.matches()) {
return false;
}
return isLoginAllowed(login);
}
private boolean isLoginAllowed(String login) {
KitodoConfigFile blacklist = KitodoConfigFile.LOGIN_BLACKLIST;
// If user defined blacklist doesn't exists, use default one
try (InputStream inputStream = blacklist.exists() ? Files.newInputStream(blacklist.getFile().toPath())
: Thread.currentThread().getContextClassLoader().getResourceAsStream(blacklist.getName());
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
BufferedReader reader = new BufferedReader(inputStreamReader)) {
if (isLoginFoundOnBlackList(reader, login)) {
return false;
}
} catch (IOException e) {
Helper.setErrorMessage(e.getLocalizedMessage(), logger, e);
return false;
}
return true;
}
/**
* Go through the user defined blacklist file line by line and compare with
* login.
*/
private boolean isLoginFoundOnBlackList(BufferedReader reader, String login) throws IOException {
String notAllowedLogin;
while ((notAllowedLogin = reader.readLine()) != null) {
if (notAllowedLogin.length() > 0 && login.equalsIgnoreCase(notAllowedLogin)) {
return true;
}
}
return false;
}
public String getFullName(User user) {
return user.getSurname() + ", " + user.getName();
}
/**
* At that moment only add this method.
*
* @param user
* object as UserDTo
* @return full name of the user as String
*/
public String getFullName(UserDTO user) {
return user.getSurname() + ", " + user.getName();
}
/**
* Get user home directory (either from the LDAP or directly from the
* configuration). If LDAP is used, find home directory there, otherwise in
* configuration.
*
* @return path as String
* @throws IOException
* add description
*/
public URI getHomeDirectory(User user) throws IOException {
URI homeDirectory;
if (Objects.nonNull(user)) {
if (ConfigCore.getBooleanParameterOrDefaultValue(ParameterCore.LDAP_USE)) {
homeDirectory = Paths.get(ServiceManager.getLdapServerService().getUserHomeDirectory(user)).toUri();
} else {
homeDirectory = Paths.get(ConfigCore.getParameter(ParameterCore.DIR_USERS), user.getLogin()).toUri();
}
if (!new File(homeDirectory).exists()) {
ServiceManager.getFileService().createDirectoryForUser(homeDirectory, user.getLogin());
}
} else {
throw new IOException("No user for home directory!");
}
return homeDirectory;
}
/**
* Adds a new filter to list.
*
* @param user
* object
* @param filter
* the filter to add
*/
public void addFilter(User user, String filter) {
if (getFilters(user).contains(filter)) {
return;
}
try {
addFilterToUser(user, filter);
} catch (DataException e) {
logger.error("Cannot not add filter to user with id {}", user.getId(), e);
}
}
/**
* Removes filter from list.
*
* @param user
* object
* @param filter
* the filter to remove
*/
public void removeFilter(User user, String filter) {
if (!getFilters(user).contains(filter)) {
return;
}
try {
removeFilterFromUser(user, filter);
} catch (DataException e) {
logger.error("Cannot not remove filter from user with id {}", user.getId(), e);
}
}
/**
* Get list of filters.
*
* @param user
* object
* @return List of filters as strings
*/
public List<String> getFilters(User user) {
return getFiltersForUser(user);
}
/**
* Add filter to user.
*
* @param user
* object
* @param userFilter
* String
*/
private void addFilterToUser(User user, String userFilter) throws DataException {
Filter filter = new Filter();
filter.setValue(userFilter);
filter.setCreationDate(new Date());
filter.setUser(user);
ServiceManager.getFilterService().save(filter);
}
/**
* Get filters for user.
*
* @param user
* object
* @return list of filters
*/
private List<String> getFiltersForUser(User user) {
List<String> userFilters = new ArrayList<>();
List<Filter> filters = user.getFilters();
for (Filter filter : filters) {
userFilters.add(filter.getValue());
}
return userFilters;
}
/**
* Remove filter from user.
*
* @param user
* object
* @param userFilter
* String
*/
private void removeFilterFromUser(User user, String userFilter) throws DataException {
List<Filter> filters = user.getFilters();
for (Filter filter : filters) {
if (filter.getValue().equals(userFilter)) {
ServiceManager.getFilterService().remove(filter);
}
}
}
/**
* Retrieve and return the list of tasks that are assigned to the user and
* that are "INWORK" and belong to process, not template.
*
* @return list of tasks that are currently assigned to the user and that
* are "INWORK" and belong to process, not template
*/
public List<Task> getTasksInProgress(User user) {
return user.getProcessingTasks().stream()
.filter(
task -> task.getProcessingStatus().equals(TaskStatus.INWORK) && Objects.nonNull(task.getProcess()))
.collect(Collectors.toList());
}
/**
* Changes the password for given User object.
*
* @param user
* The User object.
* @param newPassword
* The new password.
*/
public void changeUserPassword(User user, String newPassword) throws DAOException {
User userWithNewPassword;
if (user instanceof SecurityUserDetails) {
userWithNewPassword = new User(user);
} else {
userWithNewPassword = user;
}
userWithNewPassword.setPassword(passwordEncoder.encrypt(newPassword));
saveToDatabase(userWithNewPassword);
}
/**
* Gets the configured metadatalanguage from the User.
* @return a List of LanguageRange
*/
public List<Locale.LanguageRange> getCurrentMetadataLanguage() {
String metadataLanguage = getCurrentUser().getMetadataLanguage();
return Locale.LanguageRange.parse(metadataLanguage.isEmpty() ? "en" : metadataLanguage);
}
/**
* Return the keyboard shortcuts for the specified user.
*
* @param userId specifying the user
* @return JSON object containing the user's shortcuts as java.lang.String
* @throws DAOException when user object could not be loaded from database
*/
public String getShortcuts(int userId) throws DAOException {
return getById(userId).getShortcuts();
}
}