// Copyright © 2015 HSL <https://www.hsl.fi>
// This program is dual-licensed under the EUPL v1.2 and AGPLv3 licenses.
package fi.hsl.parkandride.core.service;
import static fi.hsl.parkandride.core.domain.Permission.ALL_OPERATORS;
import static fi.hsl.parkandride.core.domain.Permission.USER_CREATE;
import static fi.hsl.parkandride.core.domain.Permission.USER_UPDATE;
import static fi.hsl.parkandride.core.domain.Permission.USER_VIEW;
import static fi.hsl.parkandride.core.service.AuthenticationService.authorize;
import java.util.ArrayList;
import java.util.Collection;
import fi.hsl.parkandride.core.back.UserRepository;
import fi.hsl.parkandride.core.domain.*;
public class UserService {
private final AuthenticationService authenticationService;
private final UserRepository userRepository;
private final ValidationService validationService;
public UserService(UserRepository userRepository, AuthenticationService authenticationService, ValidationService validationService) {
this.userRepository = userRepository;
this.authenticationService = authenticationService;
this.validationService = validationService;
}
@TransactionalRead
public SearchResults<User> findUsers(UserSearch search, User currentUser) {
authorize(currentUser, search, USER_VIEW);
return userRepository.findUsers(search);
}
@TransactionalWrite
public User createUser(NewUser newUser, User actor) {
authorize(actor, newUser, USER_CREATE);
validate(newUser, actor);
return createUserNoValidate(newUser);
}
@TransactionalWrite
public User createUserNoValidate(NewUser newUser) {
UserSecret userSecret = new UserSecret();
if (!newUser.role.perpetualToken) {
userSecret.password = authenticationService.encryptPassword(newUser.password);
}
userSecret.user = new User(newUser);
userSecret.user.id = userRepository.insertUser(userSecret);
return userSecret.user;
}
@TransactionalWrite
public String resetToken(Long userId, User actor) {
authorize(actor, userRepository.getUser(userId).user, USER_UPDATE);
return authenticationService.resetToken(userId);
}
@TransactionalWrite
public void updatePassword(Long userId, String newPassword, User actor) {
User user = userRepository.getUser(userId).user;
authorize(actor, user, USER_UPDATE);
if (user.role == Role.OPERATOR_API) {
throw new ValidationException(new Violation("PasswordUpdateNotApplicable", "", "Password update is not applicable for api user"));
}
PasswordValidator.validate(newPassword);
userRepository.updatePassword(userId, authenticationService.encryptPassword(newPassword));
}
@TransactionalWrite
public void deleteUser(long userId, User actor) {
User user = userRepository.getUser(userId).user;
authorize(actor, user, USER_UPDATE);
// don't allow suicide
if (userId == actor.id) {
throw new AccessDeniedException();
}
userRepository.deleteUser(userId);
}
private void validate(NewUser newUser, User actor) {
Collection<Violation> violations = new ArrayList<>();
if (newUser.hasPermission(ALL_OPERATORS) && !actor.hasPermission(ALL_OPERATORS)) {
violations.add(new Violation("IllegalRole", "role", "Expected an operator role, got " + newUser.role));
}
validationService.validate(newUser, violations);
// cannot continue if e.g. role is not provided
if (violations.isEmpty()) {
if (!newUser.role.perpetualToken) {
PasswordValidator.validate(newUser.password, violations);
}
validateOperator(newUser, violations);
}
if (!violations.isEmpty()) {
throw new ValidationException(violations);
}
}
private void validateOperator(NewUser newUser, Collection<Violation> violations) {
if (newUser.hasPermission(ALL_OPERATORS)) {
if (newUser.operatorId != null) {
violations.add(new Violation("OperatorNotAllowed", "operator", "Operator is not allowed for admin user"));
}
} else if (newUser.operatorId == null) {
violations.add(new Violation("OperatorRequired", "operator", "Operator is required for operator user"));
}
}
}