argos/dmp-backend/core/src/main/java/eu/eudat/service/user/UserServiceImpl.java

644 lines
35 KiB
Java

package eu.eudat.service.user;
import com.fasterxml.jackson.core.JsonProcessingException;
import eu.eudat.authorization.AuthorizationFlags;
import eu.eudat.authorization.OwnedResource;
import eu.eudat.authorization.Permission;
import eu.eudat.commons.JsonHandlingService;
import eu.eudat.commons.XmlHandlingService;
import eu.eudat.commons.enums.ActionConfirmationStatus;
import eu.eudat.commons.enums.ActionConfirmationType;
import eu.eudat.commons.enums.ContactInfoType;
import eu.eudat.commons.enums.IsActive;
import eu.eudat.commons.enums.notification.NotificationContactType;
import eu.eudat.commons.scope.user.UserScope;
import eu.eudat.commons.types.actionconfirmation.MergeAccountConfirmationEntity;
import eu.eudat.commons.types.actionconfirmation.RemoveCredentialRequestEntity;
import eu.eudat.commons.types.notification.*;
import eu.eudat.commons.types.user.AdditionalInfoEntity;
import eu.eudat.commons.types.usercredential.UserCredentialDataEntity;
import eu.eudat.commons.notification.NotificationProperties;
import eu.eudat.convention.ConventionService;
import eu.eudat.data.*;
import eu.eudat.errorcode.ErrorThesaurusProperties;
import eu.eudat.event.UserTouchedEvent;
import eu.eudat.event.EventBroker;
import eu.eudat.integrationevent.outbox.notification.NotifyIntegrationEvent;
import eu.eudat.integrationevent.outbox.notification.NotifyIntegrationEventHandler;
import eu.eudat.integrationevent.outbox.userremoval.UserRemovalIntegrationEventHandler;
import eu.eudat.integrationevent.outbox.usertouched.UserTouchedIntegrationEventHandler;
import eu.eudat.model.User;
import eu.eudat.model.UserContactInfo;
import eu.eudat.model.UserCredential;
import eu.eudat.model.builder.UserBuilder;
import eu.eudat.model.deleter.*;
import eu.eudat.model.persist.*;
import eu.eudat.model.persist.actionconfirmation.MergeAccountConfirmationPersist;
import eu.eudat.model.persist.actionconfirmation.RemoveCredentialRequestPersist;
import eu.eudat.query.*;
import eu.eudat.service.actionconfirmation.ActionConfirmationService;
import eu.eudat.service.elastic.ElasticService;
import eu.eudat.service.keycloak.KeycloakRole;
import eu.eudat.service.keycloak.KeycloakService;
import gr.cite.commons.web.authz.service.AuthorizationService;
import gr.cite.tools.data.builder.BuilderFactory;
import gr.cite.tools.data.deleter.DeleterFactory;
import gr.cite.tools.data.query.Ordering;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.exception.MyApplicationException;
import gr.cite.tools.exception.MyForbiddenException;
import gr.cite.tools.exception.MyNotFoundException;
import gr.cite.tools.exception.MyValidationException;
import gr.cite.tools.fieldset.BaseFieldSet;
import gr.cite.tools.fieldset.FieldSet;
import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.logging.MapLogEntry;
import gr.cite.tools.validation.ValidatorFactory;
import jakarta.persistence.EntityManager;
import jakarta.xml.bind.JAXBException;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVPrinter;
import org.apache.commons.csv.QuoteMode;
import org.jetbrains.annotations.NotNull;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Service;
import javax.management.InvalidApplicationException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.time.Instant;
import java.util.*;
import java.util.stream.Collectors;
@Service
public class UserServiceImpl implements UserService {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserServiceImpl.class));
private final EntityManager entityManager;
private final AuthorizationService authorizationService;
private final DeleterFactory deleterFactory;
private final BuilderFactory builderFactory;
private final ConventionService conventionService;
private final ErrorThesaurusProperties errors;
private final MessageSource messageSource;
private final EventBroker eventBroker;
private final JsonHandlingService jsonHandlingService;
private final XmlHandlingService xmlHandlingService;
private final QueryFactory queryFactory;
private final UserScope userScope;
private final KeycloakService keycloakService;
private final ActionConfirmationService actionConfirmationService;
private final NotificationProperties notificationProperties;
private final NotifyIntegrationEventHandler eventHandler;
private final ValidatorFactory validatorFactory;
private final ElasticService elasticService;
private final UserTouchedIntegrationEventHandler userTouchedIntegrationEventHandler;
private final UserRemovalIntegrationEventHandler userRemovalIntegrationEventHandler;
@Autowired
public UserServiceImpl(
EntityManager entityManager,
AuthorizationService authorizationService,
DeleterFactory deleterFactory,
BuilderFactory builderFactory,
ConventionService conventionService,
ErrorThesaurusProperties errors,
MessageSource messageSource,
EventBroker eventBroker,
JsonHandlingService jsonHandlingService,
XmlHandlingService xmlHandlingService, QueryFactory queryFactory,
UserScope userScope, KeycloakService keycloakService, ActionConfirmationService actionConfirmationService, NotificationProperties notificationProperties, NotifyIntegrationEventHandler eventHandler, ValidatorFactory validatorFactory, ElasticService elasticService, UserTouchedIntegrationEventHandler userTouchedIntegrationEventHandler, UserRemovalIntegrationEventHandler userRemovalIntegrationEventHandler) {
this.entityManager = entityManager;
this.authorizationService = authorizationService;
this.deleterFactory = deleterFactory;
this.builderFactory = builderFactory;
this.conventionService = conventionService;
this.errors = errors;
this.messageSource = messageSource;
this.eventBroker = eventBroker;
this.jsonHandlingService = jsonHandlingService;
this.xmlHandlingService = xmlHandlingService;
this.queryFactory = queryFactory;
this.userScope = userScope;
this.keycloakService = keycloakService;
this.actionConfirmationService = actionConfirmationService;
this.notificationProperties = notificationProperties;
this.eventHandler = eventHandler;
this.validatorFactory = validatorFactory;
this.elasticService = elasticService;
this.userTouchedIntegrationEventHandler = userTouchedIntegrationEventHandler;
this.userRemovalIntegrationEventHandler = userRemovalIntegrationEventHandler;
}
//region persist
@Override
public User persist(UserPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, JsonProcessingException {
logger.debug(new MapLogEntry("persisting data User").And("model", model).And("fields", fields));
this.authorizationService.authorizeAtLeastOneForce(model.getId() != null ? List.of(new OwnedResource(model.getId())) : null, Permission.EditUser);
Boolean isUpdate = this.conventionService.isValidGuid(model.getId());
UserEntity data;
if (isUpdate) {
data = this.entityManager.find(UserEntity.class, model.getId());
if (data == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), User.class.getSimpleName()}, LocaleContextHolder.getLocale()));
if (!this.conventionService.hashValue(data.getUpdatedAt()).equals(model.getHash())) throw new MyValidationException(this.errors.getHashConflict().getCode(), this.errors.getHashConflict().getMessage());
} else {
data = new UserEntity();
data.setId(UUID.randomUUID());
data.setIsActive(IsActive.Active);
data.setCreatedAt(Instant.now());
}
data.setAdditionalInfo(this.jsonHandlingService.toJson(this.buildAdditionalInfoEntity(model.getAdditionalInfo())));
data.setName(model.getName());
data.setUpdatedAt(Instant.now());
if (isUpdate) this.entityManager.merge(data);
else this.entityManager.persist(data);
this.entityManager.flush();
this.eventBroker.emit(new UserTouchedEvent(data.getId()));
this.userTouchedIntegrationEventHandler.handle(data.getId());
return this.builderFactory.builder(UserBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(BaseFieldSet.build(fields, User._id), data);
}
private @NotNull AdditionalInfoEntity buildAdditionalInfoEntity(UserAdditionalInfoPersist persist){
AdditionalInfoEntity data = new AdditionalInfoEntity();
if (persist == null) return data;
data.setOrganizationId(persist.getOrganizationId());
data.setRoleOrganization(persist.getRoleOrganization());
data.setCulture(persist.getCulture());
data.setTimezone(persist.getTimezone());
data.setLanguage(persist.getLanguage());
data.setAvatarUrl(persist.getAvatarUrl());
return data;
}
//endregion
//region delete
@Override
public void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException {
logger.debug("deleting User: {}", id);
this.authorizationService.authorizeForce(Permission.DeleteUser);
this.deleterFactory.deleter(UserDeleter.class).deleteAndSaveByIds(List.of(id));
this.userRemovalIntegrationEventHandler.handle(id);
}
//endregion
//region export
@Override
public byte[] exportCsv() throws IOException {
this.authorizationService.authorizeForce(Permission.ExportUsers);
FieldSet fieldSet = new BaseFieldSet().ensure(User._id).ensure(User._name).ensure(User._contacts + "." + UserContactInfo._value).ensure(User._contacts + "." + UserContactInfo._type);
List<User> users = this.builderFactory.builder(UserBuilder.class).build(fieldSet, this.queryFactory.query(UserQuery.class).collectAs(fieldSet));
final ByteArrayOutputStream out = new ByteArrayOutputStream();
final CSVFormat format = CSVFormat.DEFAULT.withHeader("User Id", "User Name", "User Email").withQuoteMode(QuoteMode.NON_NUMERIC);
final CSVPrinter csvPrinter = new CSVPrinter(new PrintWriter(out), format);
for (User user : users) {
csvPrinter.printRecord(user.getId(), user.getName(), (user.getContacts() != null ? String.join(" ", user.getContacts().stream().filter(x-> ContactInfoType.Email.equals(x.getType())).map(UserContactInfo::getValue).toList()) : ""));
}
csvPrinter.flush();
return out.toByteArray();
}
//endregion
@Override
public User patchRoles(UserRolePatchPersist model, FieldSet fields) throws InvalidApplicationException {
logger.debug(new MapLogEntry("persisting data UserRole").And("model", model).And("fields", fields));
this.authorizationService.authorizeForce(Permission.EditUser);
UserEntity data = this.entityManager.find(UserEntity.class, model.getId());
if (data == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), User.class.getSimpleName()}, LocaleContextHolder.getLocale()));
if (!this.conventionService.hashValue(data.getUpdatedAt()).equals(model.getHash())) throw new MyValidationException(this.errors.getHashConflict().getCode(), this.errors.getHashConflict().getMessage());
List<UserCredentialEntity> userCredentials = this.queryFactory.query(UserCredentialQuery.class).userIds(data.getId()).collect();
if (userCredentials.isEmpty())
throw new MyApplicationException("Currently cannot update roles for this user");
if (userCredentials.getFirst().getExternalId() == null)
throw new MyApplicationException("Currently cannot update roles for this user");
UUID subjectId = UUID.fromString(userCredentials.getFirst().getExternalId());
List<UserRoleEntity> existingItems = this.queryFactory.query(UserRoleQuery.class).userIds(data.getId()).collect();
List<UUID> foundIds = new ArrayList<>();
for (String roleName : model.getRoles().stream().filter(x-> x != null && !x.isBlank()).distinct().toList()) {
UserRoleEntity item = existingItems.stream().filter(x-> x.getRole().equals(roleName)).findFirst().orElse(null);
if (item == null) {
item = new UserRoleEntity();
item.setId(UUID.randomUUID());
item.setUserId(data.getId());
item.setRole(roleName);
item.setCreatedAt(Instant.now());
this.entityManager.persist(item);
this.keycloakService.addUserToGroup(subjectId, KeycloakRole.valueOf(roleName));
}
foundIds.add(item.getId());
}
this.entityManager.flush();
List<UserRoleEntity> toDelete = existingItems.stream().filter(x-> foundIds.stream().noneMatch(y-> y.equals(x.getId()))).collect(Collectors.toList());
toDelete.forEach(x -> this.keycloakService.removeUserFromGroup(subjectId, KeycloakRole.valueOf(x.getRole())));
this.deleterFactory.deleter(UserRoleDeleter.class).deleteAndSave(toDelete);
this.entityManager.flush();
this.eventBroker.emit(new UserTouchedEvent(data.getId()));
this.userTouchedIntegrationEventHandler.handle(data.getId());
return this.builderFactory.builder(UserBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(BaseFieldSet.build(fields, User._id), data);
}
//region mine
@Override
public void updateLanguageMine(String language) throws JsonProcessingException {
logger.debug(new MapLogEntry("persisting User language").And("language", language));
UUID userId = this.userScope.getUserIdSafe();
if (userId == null) throw new MyForbiddenException(this.errors.getForbidden().getCode(), this.errors.getForbidden().getMessage());
UserEntity data = this.entityManager.find(UserEntity.class, userId);
if (data == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{userId, User.class.getSimpleName()}, LocaleContextHolder.getLocale()));
AdditionalInfoEntity additionalInfoEntity = this.jsonHandlingService.fromJsonSafe(AdditionalInfoEntity.class, data.getAdditionalInfo());
if (additionalInfoEntity == null) additionalInfoEntity = new AdditionalInfoEntity();
additionalInfoEntity.setLanguage(language);
data.setAdditionalInfo(this.jsonHandlingService.toJson(additionalInfoEntity));
data.setUpdatedAt(Instant.now());
this.entityManager.merge(data);
this.entityManager.flush();
this.userTouchedIntegrationEventHandler.handle(data.getId());
this.eventBroker.emit(new UserTouchedEvent(data.getId()));
}
@Override
public void updateTimezoneMine(String timezone) throws JsonProcessingException {
logger.debug(new MapLogEntry("persisting User timezone").And("timezone", timezone));
UUID userId = this.userScope.getUserIdSafe();
if (userId == null) throw new MyForbiddenException(this.errors.getForbidden().getCode(), this.errors.getForbidden().getMessage());
UserEntity data = this.entityManager.find(UserEntity.class, userId);
if (data == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{userId, User.class.getSimpleName()}, LocaleContextHolder.getLocale()));
AdditionalInfoEntity additionalInfoEntity = this.jsonHandlingService.fromJsonSafe(AdditionalInfoEntity.class, data.getAdditionalInfo());
if (additionalInfoEntity == null) additionalInfoEntity = new AdditionalInfoEntity();
additionalInfoEntity.setTimezone(timezone);
data.setAdditionalInfo(this.jsonHandlingService.toJson(additionalInfoEntity));
data.setUpdatedAt(Instant.now());
this.entityManager.merge(data);
this.entityManager.flush();
this.userTouchedIntegrationEventHandler.handle(data.getId());
this.eventBroker.emit(new UserTouchedEvent(data.getId()));
}
@Override
public void updateCultureMine(String culture) throws JsonProcessingException {
logger.debug(new MapLogEntry("persisting User culture").And("culture", culture));
UUID userId = this.userScope.getUserIdSafe();
if (userId == null) throw new MyForbiddenException(this.errors.getForbidden().getCode(), this.errors.getForbidden().getMessage());
UserEntity data = this.entityManager.find(UserEntity.class, userId);
if (data == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{userId, User.class.getSimpleName()}, LocaleContextHolder.getLocale()));
AdditionalInfoEntity additionalInfoEntity = this.jsonHandlingService.fromJsonSafe(AdditionalInfoEntity.class, data.getAdditionalInfo());
if (additionalInfoEntity == null) additionalInfoEntity = new AdditionalInfoEntity();
additionalInfoEntity.setCulture(culture);
data.setAdditionalInfo(this.jsonHandlingService.toJson(additionalInfoEntity));
data.setUpdatedAt(Instant.now());
this.entityManager.merge(data);
this.entityManager.flush();
this.userTouchedIntegrationEventHandler.handle(data.getId());
this.eventBroker.emit(new UserTouchedEvent(data.getId()));
}
//endregion
//notifications
public void sendMergeAccountConfirmation(UserMergeRequestPersist model) throws InvalidApplicationException, JAXBException {
UserContactInfoEntity userContactInfoEntity = this.queryFactory.query(UserContactInfoQuery.class).values(model.getEmail()).types(ContactInfoType.Email).first();
if (userContactInfoEntity == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getEmail(), User.class.getSimpleName()}, LocaleContextHolder.getLocale()));
UserEntity user = this.queryFactory.query(UserQuery.class).ids(userContactInfoEntity.getUserId()).isActive(IsActive.Active).first();
if (user == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{userContactInfoEntity.getUserId(), User.class.getSimpleName()}, LocaleContextHolder.getLocale()));
String token = this.createMergeAccountConfirmation(model.getEmail());
createMergeNotificationEvent(token, user, model.getEmail(), NotificationContactType.EMAIL);
createMergeNotificationEvent(token, user, model.getEmail(), NotificationContactType.IN_APP);
}
private void createMergeNotificationEvent(String token, UserEntity user, String email, NotificationContactType type) throws InvalidApplicationException {
NotifyIntegrationEvent event = new NotifyIntegrationEvent();
event.setUserId(user.getId());
List<ContactPair> contactPairs = new ArrayList<>();
contactPairs.add(new ContactPair(ContactInfoType.Email, email));
NotificationContactData contactData = new NotificationContactData(contactPairs, null, null);
event.setContactHint(jsonHandlingService.toJsonSafe(contactData));
event.setContactTypeHint(type);
event.setNotificationType(notificationProperties.getMergeAccountConfirmationType());
NotificationFieldData data = new NotificationFieldData();
List<FieldInfo> fieldInfoList = new ArrayList<>();
fieldInfoList.add(new FieldInfo("{userName}", DataType.String, user.getName()));
fieldInfoList.add(new FieldInfo("{confirmationToken}", DataType.String, token));
fieldInfoList.add(new FieldInfo("{expiration_time}", DataType.String, this.secondsToTime(this.notificationProperties.getEmailExpirationTimeSeconds())));
data.setFields(fieldInfoList);
event.setData(jsonHandlingService.toJsonSafe(data));
eventHandler.handle(event);
}
public void sendRemoveCredentialConfirmation(RemoveCredentialRequestPersist model) throws InvalidApplicationException, JAXBException {
UserCredentialEntity data = this.entityManager.find(UserCredentialEntity.class, model.getCredentialId());
if (data == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getCredentialId(), UserCredentialEntity.class.getSimpleName()}, LocaleContextHolder.getLocale()));
if (!data.getUserId().equals(this.userScope.getUserId())) throw new MyForbiddenException(this.errors.getForbidden().getCode(), this.errors.getForbidden().getMessage());
String token = this.createRemoveConfirmation(data.getId());
this.createRemoveCredentialNotificationEvent(token, data.getUserId(), NotificationContactType.EMAIL);
this.createRemoveCredentialNotificationEvent(token, data.getUserId(), NotificationContactType.IN_APP);
}
private void createRemoveCredentialNotificationEvent(String token, UUID userId, NotificationContactType type) throws InvalidApplicationException {
NotifyIntegrationEvent event = new NotifyIntegrationEvent();
event.setUserId(userId);
event.setContactTypeHint(type);
event.setNotificationType(notificationProperties.getRemoveCredentialConfirmationType());
NotificationFieldData data = new NotificationFieldData();
List<FieldInfo> fieldInfoList = new ArrayList<>();
fieldInfoList.add(new FieldInfo("{confirmationToken}", DataType.String, token));
fieldInfoList.add(new FieldInfo("{expiration_time}", DataType.String, this.secondsToTime(this.notificationProperties.getEmailExpirationTimeSeconds())));
data.setFields(fieldInfoList);
event.setData(jsonHandlingService.toJsonSafe(data));
eventHandler.handle(event);
}
private String createMergeAccountConfirmation(String email) throws JAXBException, InvalidApplicationException {
ActionConfirmationPersist persist = new ActionConfirmationPersist();
persist.setType(ActionConfirmationType.MergeAccount);
persist.setStatus(ActionConfirmationStatus.Requested);
persist.setToken(UUID.randomUUID().toString());
persist.setMergeAccountConfirmation(new MergeAccountConfirmationPersist());
persist.getMergeAccountConfirmation().setEmail(email);
persist.setExpiresAt(Instant.now().plusSeconds(this.notificationProperties.getEmailExpirationTimeSeconds()));
validatorFactory.validator(ActionConfirmationPersist.ActionConfirmationPersistValidator.class).validateForce(persist);
this.actionConfirmationService.persist(persist, null);
return persist.getToken();
}
private String createRemoveConfirmation(UUID credentialId) throws JAXBException, InvalidApplicationException {
ActionConfirmationPersist persist = new ActionConfirmationPersist();
persist.setType(ActionConfirmationType.RemoveCredential);
persist.setStatus(ActionConfirmationStatus.Requested);
persist.setToken(UUID.randomUUID().toString());
persist.setRemoveCredentialRequest(new RemoveCredentialRequestPersist());
persist.getRemoveCredentialRequest().setCredentialId(credentialId);
persist.setExpiresAt(Instant.now().plusSeconds(this.notificationProperties.getEmailExpirationTimeSeconds()));
validatorFactory.validator(ActionConfirmationPersist.ActionConfirmationPersistValidator.class).validateForce(persist);
this.actionConfirmationService.persist(persist, null);
return persist.getToken();
}
private String secondsToTime(int seconds) {
int sec = seconds % 60;
int hour = seconds / 60;
int min = hour % 60;
hour = hour / 60;
return (hour + ":" + min + ":" + sec);
}
public void confirmMergeAccount(String token) throws IOException, InvalidApplicationException {
ActionConfirmationEntity action = this.queryFactory.query(ActionConfirmationQuery.class).tokens(token).types(ActionConfirmationType.MergeAccount).isActive(IsActive.Active).first();
if (action == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{token, ActionConfirmationEntity.class.getSimpleName()}, LocaleContextHolder.getLocale()));
this.checkActionState(action);
MergeAccountConfirmationEntity mergeAccountConfirmationEntity = this.xmlHandlingService.fromXmlSafe(MergeAccountConfirmationEntity.class, action.getData());
if (mergeAccountConfirmationEntity == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{mergeAccountConfirmationEntity, MergeAccountConfirmationEntity.class.getSimpleName()}, LocaleContextHolder.getLocale()));
UserContactInfoEntity userContactInfoEntity = this.queryFactory.query(UserContactInfoQuery.class).values(mergeAccountConfirmationEntity.getEmail()).types(ContactInfoType.Email).first();
if (userContactInfoEntity == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{mergeAccountConfirmationEntity.getEmail(), User.class.getSimpleName()}, LocaleContextHolder.getLocale()));
UserEntity userToBeMerge = this.queryFactory.query(UserQuery.class).ids(userContactInfoEntity.getUserId()).isActive(IsActive.Active).first();
if (userToBeMerge == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{userContactInfoEntity.getUserId(), User.class.getSimpleName()}, LocaleContextHolder.getLocale()));
if (!this.userScope.getUserIdSafe().equals(userToBeMerge.getId())) throw new MyForbiddenException("Only requested user can approve");
UserEntity newUser = this.queryFactory.query(UserQuery.class).ids(action.getCreatedById()).isActive(IsActive.Active).first();
if (newUser == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{action.getCreatedById(), User.class.getSimpleName()}, LocaleContextHolder.getLocale()));
if (!newUser.getId().equals(userToBeMerge.getId())){
this.mergeNewUserToOld(newUser, userToBeMerge);
}
action.setUpdatedAt(Instant.now());
action.setStatus(ActionConfirmationStatus.Accepted);
this.entityManager.merge(action);
this.entityManager.flush();
this.userTouchedIntegrationEventHandler.handle(newUser.getId());
this.userRemovalIntegrationEventHandler.handle(userToBeMerge.getId());
}
private void mergeNewUserToOld(UserEntity newUser, UserEntity oldUser) throws IOException, InvalidApplicationException {
List<UserCredentialEntity> userCredentials = this.queryFactory.query(UserCredentialQuery.class).userIds(oldUser.getId()).collect();
for (UserCredentialEntity userCredential : userCredentials){
userCredential.setUserId(newUser.getId());
this.entityManager.merge(userCredential);
}
List<UserContactInfoEntity> userContactInfos = this.queryFactory.query(UserContactInfoQuery.class).userIds(oldUser.getId()).collect();
UserContactInfoQuery newUserContactInfoQuery = this.queryFactory.query(UserContactInfoQuery.class).userIds(newUser.getId());
newUserContactInfoQuery.setOrder(new Ordering().addDescending(UserContactInfo._ordinal));
UserContactInfoEntity newUserContactInfo = newUserContactInfoQuery.first();
int ordinal = newUserContactInfo == null ? 0 : newUserContactInfo.getOrdinal() + 1;
for (UserContactInfoEntity userContactInfo : userContactInfos){
userContactInfo.setUserId(newUser.getId());
userContactInfo.setOrdinal(ordinal);
this.entityManager.merge(userContactInfo);
ordinal++;
}
List<UserRoleEntity> userRoles = this.queryFactory.query(UserRoleQuery.class).userIds(oldUser.getId()).collect();
List<UserRoleEntity> newUserRoles = this.queryFactory.query(UserRoleQuery.class).userIds(newUser.getId()).collect();
List<UserRoleEntity> rolesToDelete = new ArrayList<>();
for (UserRoleEntity userRole : userRoles){
if (newUserRoles.stream().anyMatch(x-> x.getRole().equals(userRole.getRole()))) {
rolesToDelete.add(userRole);
} else {
userRole.setUserId(newUser.getId());
this.entityManager.merge(userRole);
}
}
this.deleterFactory.deleter(UserRoleDeleter.class).delete(rolesToDelete);
List<UserSettingsEntity> userSettings = this.queryFactory.query(UserSettingsQuery.class).entityIds(oldUser.getId()).collect();
List<UserSettingsEntity> newUserSettings = this.queryFactory.query(UserSettingsQuery.class).entityIds(newUser.getId()).collect();
List<UserSettingsEntity> userSettingsToDelete = new ArrayList<>();
for (UserSettingsEntity userSetting : userSettings){
if (newUserSettings.stream().anyMatch(x-> x.getKey().equals(userSetting.getKey()))) {
userSettingsToDelete.add(userSetting);
} else {
userSetting.setEntityId(newUser.getId());
this.entityManager.merge(userSetting);
}
}
this.deleterFactory.deleter(UserSettingsSettingsDeleter.class).delete(userSettingsToDelete);
List<TagEntity> tags = this.queryFactory.query(TagQuery.class).createdByIds(oldUser.getId()).collect();
for (TagEntity tag : tags){
tag.setCreatedById(newUser.getId());
this.entityManager.merge(tag);
}
List<StorageFileEntity> storageFiles = this.queryFactory.query(StorageFileQuery.class).ownerIds(oldUser.getId()).collect();
for (StorageFileEntity storageFile : storageFiles){
storageFile.setOwnerId(newUser.getId());
this.entityManager.merge(storageFile);
}
List<LockEntity> locks = this.queryFactory.query(LockQuery.class).lockedByIds(oldUser.getId()).collect();
for (LockEntity lock : locks){
lock.setLockedBy(newUser.getId());
this.entityManager.merge(lock);
}
List<DmpUserEntity> dmpUsers = this.queryFactory.query(DmpUserQuery.class).userIds(oldUser.getId()).collect();
for (DmpUserEntity dmpUser : dmpUsers){
dmpUser.setUserId(newUser.getId());
this.entityManager.merge(dmpUser);
}
List<UserDescriptionTemplateEntity> userDescriptionTemplates = this.queryFactory.query(UserDescriptionTemplateQuery.class).userIds(oldUser.getId()).collect();
for (UserDescriptionTemplateEntity userDescriptionTemplate : userDescriptionTemplates){
userDescriptionTemplate.setUserId(newUser.getId());
this.entityManager.merge(userDescriptionTemplate);
}
List<DmpEntity> dmps = this.queryFactory.query(DmpQuery.class).creatorIds(oldUser.getId()).collect();
for (DmpEntity dmp : dmps){
dmp.setCreatorId(newUser.getId());
this.entityManager.merge(dmp);
}
List<DescriptionEntity> descriptions = this.queryFactory.query(DescriptionQuery.class).createdByIds(oldUser.getId()).collect();
for (DescriptionEntity description : descriptions){
description.setCreatedById(newUser.getId());
this.entityManager.merge(description);
}
oldUser.setIsActive(IsActive.Inactive);
this.entityManager.merge(oldUser);
this.entityManager.flush();
for (DmpEntity dmp : dmps){
this.elasticService.persistDmp(dmp);
}
for (DescriptionEntity description : descriptions){
this.elasticService.persistDescription(description);
}
}
public void confirmRemoveCredential(String token) throws InvalidApplicationException {
ActionConfirmationEntity action = this.queryFactory.query(ActionConfirmationQuery.class).tokens(token).types(ActionConfirmationType.RemoveCredential).isActive(IsActive.Active).first();
if (action == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{token, ActionConfirmationEntity.class.getSimpleName()}, LocaleContextHolder.getLocale()));
this.checkActionState(action);
RemoveCredentialRequestEntity removeCredentialRequestEntity = this.xmlHandlingService.fromXmlSafe(RemoveCredentialRequestEntity.class, action.getData());
if (removeCredentialRequestEntity == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{removeCredentialRequestEntity, RemoveCredentialRequestEntity.class.getSimpleName()}, LocaleContextHolder.getLocale()));
UserCredentialEntity userCredentialEntity = this.queryFactory.query(UserCredentialQuery.class).ids(removeCredentialRequestEntity.getCredentialId()).first();
if (userCredentialEntity == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{removeCredentialRequestEntity.getCredentialId(), UserCredential.class.getSimpleName()}, LocaleContextHolder.getLocale()));
if (!this.userScope.getUserIdSafe().equals(userCredentialEntity.getId())) throw new MyForbiddenException("Only requested user can approve");
if (userCredentialEntity.getData() != null){
UserCredentialDataEntity userCredentialDataEntity = this.jsonHandlingService.fromJsonSafe(UserCredentialDataEntity.class, userCredentialEntity.getData());
if (userCredentialDataEntity != null && !this.conventionService.isNullOrEmpty(userCredentialDataEntity.getEmail())) {
List<UserContactInfoEntity> userContactInfos = this.queryFactory.query(UserContactInfoQuery.class).values(userCredentialDataEntity.getEmail()).userIds(userCredentialEntity.getUserId()).collect();
if (!this.conventionService.isListNullOrEmpty(userContactInfos))
this.deleterFactory.deleter(UserContactInfoDeleter.class).delete(userContactInfos);
}
}
this.deleterFactory.deleter(UserCredentialDeleter.class).delete(List.of(userCredentialEntity));
action.setUpdatedAt(Instant.now());
action.setStatus(ActionConfirmationStatus.Accepted);
this.entityManager.merge(action);
this.entityManager.flush();
this.userTouchedIntegrationEventHandler.handle(userCredentialEntity.getUserId());
}
private void checkActionState(ActionConfirmationEntity action) throws MyApplicationException {
if (action.getStatus().equals(ActionConfirmationStatus.Accepted)){
throw new MyApplicationException("Account is already confirmed!");
}
if (action.getExpiresAt().compareTo(Instant.now()) < 0){
throw new MyApplicationException("Token has expired!");
}
}
}