UserRemovalIntegrationEvent changes

This commit is contained in:
Efstratios Giannopoulos 2024-01-29 18:10:55 +02:00
parent 80a3aa5616
commit aebc6056f9
10 changed files with 172 additions and 198 deletions

View File

@ -1,30 +0,0 @@
package eu.eudat.commons.enums.notification;
import com.fasterxml.jackson.annotation.JsonValue;
import eu.eudat.commons.enums.EnumUtils;
import eu.eudat.data.converters.enums.DatabaseEnum;
import java.util.Map;
public enum UserType implements DatabaseEnum<Short> {
Person((short) 0), Service((short) 1);
private final Short value;
UserType(Short value) {
this.value = value;
}
@JsonValue
public Short getValue() {
return value;
}
private static final Map<Short, UserType> map = EnumUtils.getEnumValueMap(UserType.class);
public static UserType of(Short i) {
return map.get(i);
}
}

View File

@ -1,7 +1,9 @@
package eu.eudat.integrationevent.outbox.userremoval; package eu.eudat.integrationevent.outbox.userremoval;
import java.util.UUID;
public interface UserRemovalIntegrationEventHandler { public interface UserRemovalIntegrationEventHandler {
void handle(UserRemovalIntegrationEvent event); void handle(UUID userId);
} }

View File

@ -1,7 +1,12 @@
package eu.eudat.integrationevent.outbox.userremoval; package eu.eudat.integrationevent.outbox.userremoval;
import eu.eudat.data.UserEntity;
import eu.eudat.integrationevent.outbox.OutboxIntegrationEvent; import eu.eudat.integrationevent.outbox.OutboxIntegrationEvent;
import eu.eudat.integrationevent.outbox.OutboxService; import eu.eudat.integrationevent.outbox.OutboxService;
import eu.eudat.integrationevent.outbox.usertouched.UserTouchedIntegrationEvent;
import eu.eudat.model.User;
import eu.eudat.query.UserQuery;
import gr.cite.tools.fieldset.BaseFieldSet;
import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.LoggerService;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory;
@ -27,14 +32,19 @@ public class UserRemovalIntegrationEventHandlerImpl implements UserRemovalIntegr
} }
@Override @Override
public void handle(UserRemovalIntegrationEvent event) { public void handle(UUID userId) {
UserRemovalConsistencyHandler userRemovalConsistencyHandler = this.applicationContext.getBean(UserRemovalConsistencyHandler.class); UserRemovalConsistencyHandler userRemovalConsistencyHandler = this.applicationContext.getBean(UserRemovalConsistencyHandler.class);
if (!userRemovalConsistencyHandler.isConsistent(new UserRemovalConsistencyPredicates(event.getUserId()))) if (!userRemovalConsistencyHandler.isConsistent(new UserRemovalConsistencyPredicates(userId)))
return; return;
OutboxIntegrationEvent message = new OutboxIntegrationEvent(); OutboxIntegrationEvent message = new OutboxIntegrationEvent();
message.setMessageId(UUID.randomUUID()); message.setMessageId(UUID.randomUUID());
message.setType(OutboxIntegrationEvent.USER_REMOVE); message.setType(OutboxIntegrationEvent.USER_REMOVE);
UserRemovalIntegrationEvent event = new UserRemovalIntegrationEvent();
event.setUserId(userId);
event.setTenant(null); //TODO
message.setEvent(event); message.setEvent(event);
this.outboxService.publish(message); this.outboxService.publish(message);
} }
} }

View File

@ -1,7 +1,6 @@
package eu.eudat.integrationevent.outbox.usertouched; package eu.eudat.integrationevent.outbox.usertouched;
import eu.eudat.commons.enums.ContactInfoType; import eu.eudat.commons.enums.ContactInfoType;
import eu.eudat.commons.enums.notification.UserType;
import eu.eudat.integrationevent.TrackedEvent; import eu.eudat.integrationevent.TrackedEvent;
import java.util.List; import java.util.List;
@ -13,11 +12,7 @@ public class UserTouchedIntegrationEvent extends TrackedEvent {
private UUID tenant; private UUID tenant;
private String firstName; private String name;
private String lastName;
private UserType type;
private UserProfile profile; private UserProfile profile;
@ -39,28 +34,12 @@ public class UserTouchedIntegrationEvent extends TrackedEvent {
this.tenant = tenant; this.tenant = tenant;
} }
public String getFirstName() { public String getName() {
return firstName; return name;
} }
public void setFirstName(String firstName) { public void setName(String name) {
this.firstName = firstName; this.name = name;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public UserType getType() {
return type;
}
public void setType(UserType type) {
this.type = type;
} }
public UserProfile getProfile() { public UserProfile getProfile() {
@ -117,6 +96,7 @@ public class UserTouchedIntegrationEvent extends TrackedEvent {
private ContactInfoType type; private ContactInfoType type;
private String value; private String value;
private int ordinal;
public ContactInfoType getType() { public ContactInfoType getType() {
return type; return type;
@ -133,6 +113,14 @@ public class UserTouchedIntegrationEvent extends TrackedEvent {
public void setValue(String value) { public void setValue(String value) {
this.value = value; this.value = value;
} }
public int getOrdinal() {
return ordinal;
}
public void setOrdinal(int ordinal) {
this.ordinal = ordinal;
}
} }
} }

View File

@ -1,7 +1,9 @@
package eu.eudat.integrationevent.outbox.usertouched; package eu.eudat.integrationevent.outbox.usertouched;
import java.util.UUID;
public interface UserTouchedIntegrationEventHandler { public interface UserTouchedIntegrationEventHandler {
void handle(UserTouchedIntegrationEvent event); void handle(UUID userId);
} }

View File

@ -1,13 +1,33 @@
package eu.eudat.integrationevent.outbox.usertouched; package eu.eudat.integrationevent.outbox.usertouched;
import eu.eudat.commons.JsonHandlingService;
import eu.eudat.commons.types.user.AdditionalInfoEntity;
import eu.eudat.data.UserContactInfoEntity;
import eu.eudat.data.UserCredentialEntity;
import eu.eudat.data.UserEntity;
import eu.eudat.integrationevent.outbox.OutboxIntegrationEvent; import eu.eudat.integrationevent.outbox.OutboxIntegrationEvent;
import eu.eudat.integrationevent.outbox.OutboxService; import eu.eudat.integrationevent.outbox.OutboxService;
import eu.eudat.model.Reference;
import eu.eudat.model.User;
import eu.eudat.model.UserContactInfo;
import eu.eudat.model.UserCredential;
import eu.eudat.model.builder.UserAdditionalInfoBuilder;
import eu.eudat.query.UserContactInfoQuery;
import eu.eudat.query.UserCredentialQuery;
import eu.eudat.query.UserQuery;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.exception.MyNotFoundException;
import gr.cite.tools.fieldset.BaseFieldSet;
import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.LoggerService;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID; import java.util.UUID;
@Component("outboxusertouchedintegrationeventhandler") @Component("outboxusertouchedintegrationeventhandler")
@ -17,17 +37,54 @@ public class UserTouchedIntegrationEventHandlerImpl implements UserTouchedIntegr
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserTouchedIntegrationEventHandlerImpl.class)); private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserTouchedIntegrationEventHandlerImpl.class));
private final OutboxService outboxService; private final OutboxService outboxService;
private final JsonHandlingService jsonHandlingService;
private final MessageSource messageSource;
private final QueryFactory queryFactory;
public UserTouchedIntegrationEventHandlerImpl( public UserTouchedIntegrationEventHandlerImpl(
OutboxService outboxService) { OutboxService outboxService, JsonHandlingService jsonHandlingService, MessageSource messageSource, QueryFactory queryFactory) {
this.outboxService = outboxService; this.outboxService = outboxService;
this.jsonHandlingService = jsonHandlingService;
this.messageSource = messageSource;
this.queryFactory = queryFactory;
} }
@Override @Override
public void handle(UserTouchedIntegrationEvent event) { public void handle(UUID userId) {
OutboxIntegrationEvent message = new OutboxIntegrationEvent(); OutboxIntegrationEvent message = new OutboxIntegrationEvent();
message.setMessageId(UUID.randomUUID()); message.setMessageId(UUID.randomUUID());
message.setType(OutboxIntegrationEvent.USER_TOUCH); message.setType(OutboxIntegrationEvent.USER_TOUCH);
UserEntity user = this.queryFactory.query(UserQuery.class).ids(userId)
.firstAs(new BaseFieldSet().ensure(User._name).ensure(User._additionalInfo));
if (user == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{userId, User.class.getSimpleName()}, LocaleContextHolder.getLocale()));
List<UserContactInfoEntity> userContactInfoEntities = this.queryFactory.query(UserContactInfoQuery.class).userIds(userId)
.collectAs(new BaseFieldSet().ensure(UserContactInfo._type).ensure(UserContactInfo._ordinal).ensure(UserContactInfo._value));
UserTouchedIntegrationEvent event = new UserTouchedIntegrationEvent();
event.setId(userId);
event.setTenant(null); //TODO
event.setName(user.getName());
event.setProfile(new UserTouchedIntegrationEvent.UserProfile());
AdditionalInfoEntity definition = this.jsonHandlingService.fromJsonSafe(AdditionalInfoEntity.class, user.getAdditionalInfo());
if (definition != null) {
event.getProfile().setCulture(definition.getCulture());
event.getProfile().setLanguage(definition.getLanguage());
event.getProfile().setTimezone(definition.getTimezone());
}
event.setUserContactInfo(new ArrayList<>());
if (userContactInfoEntities != null){
for (UserContactInfoEntity contactInfoEntity : userContactInfoEntities){
UserTouchedIntegrationEvent.UserContactInfo contactInfo = new UserTouchedIntegrationEvent.UserContactInfo();
contactInfo.setType(contactInfoEntity.getType());
contactInfo.setValue(contactInfoEntity.getValue());
contactInfo.setOrdinal(contactInfoEntity.getOrdinal());
event.getUserContactInfo().add(contactInfo);
}
}
message.setEvent(event); message.setEvent(event);
this.outboxService.publish(message); this.outboxService.publish(message);
} }

View File

@ -216,7 +216,7 @@ public class UserContactInfoQuery extends QueryBase<UserContactInfoEntity> {
else if (item.match(UserContactInfo._ordinal)) return UserContactInfoEntity._ordinal; else if (item.match(UserContactInfo._ordinal)) return UserContactInfoEntity._ordinal;
else if (item.prefix(UserContactInfo._user)) return UserContactInfoEntity._userId; else if (item.prefix(UserContactInfo._user)) return UserContactInfoEntity._userId;
else if (item.match(UserContactInfo._user)) return UserContactInfoEntity._userId; else if (item.match(UserContactInfo._user)) return UserContactInfoEntity._userId;
else if (item.prefix(UserContactInfo._type)) return UserContactInfoEntity._type; else if (item.match(UserContactInfo._type)) return UserContactInfoEntity._type;
else if (item.match(UserContactInfo._createdAt) ) return UserContactInfoEntity._createdAt; else if (item.match(UserContactInfo._createdAt) ) return UserContactInfoEntity._createdAt;
else return null; else return null;
} }

View File

@ -245,6 +245,7 @@ public class UserQuery extends QueryBase<UserEntity> {
if (item.match(User._id) || item.match(PublicUser._id)) return UserEntity._id; if (item.match(User._id) || item.match(PublicUser._id)) return UserEntity._id;
else if (item.match(User._name) || item.match(PublicUser._name)) return UserEntity._name; else if (item.match(User._name) || item.match(PublicUser._name)) return UserEntity._name;
else if (item.prefix(User._additionalInfo)) return UserEntity._additionalInfo; else if (item.prefix(User._additionalInfo)) return UserEntity._additionalInfo;
else if (item.match(User._additionalInfo)) return UserEntity._additionalInfo;
else if (item.match(User._createdAt) ) return UserEntity._createdAt; else if (item.match(User._createdAt) ) return UserEntity._createdAt;
else if (item.match(User._updatedAt)) return UserEntity._updatedAt; else if (item.match(User._updatedAt)) return UserEntity._updatedAt;
else if (item.match(User._hash)) return UserEntity._updatedAt; else if (item.match(User._hash)) return UserEntity._updatedAt;

View File

@ -25,6 +25,8 @@ import eu.eudat.event.UserTouchedEvent;
import eu.eudat.event.EventBroker; import eu.eudat.event.EventBroker;
import eu.eudat.integrationevent.outbox.notification.NotifyIntegrationEvent; import eu.eudat.integrationevent.outbox.notification.NotifyIntegrationEvent;
import eu.eudat.integrationevent.outbox.notification.NotifyIntegrationEventHandler; 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.User;
import eu.eudat.model.UserContactInfo; import eu.eudat.model.UserContactInfo;
import eu.eudat.model.UserCredential; import eu.eudat.model.UserCredential;
@ -104,20 +106,22 @@ public class UserServiceImpl implements UserService {
private final ValidatorFactory validatorFactory; private final ValidatorFactory validatorFactory;
private final ElasticService elasticService; private final ElasticService elasticService;
private final UserTouchedIntegrationEventHandler userTouchedIntegrationEventHandler;
private final UserRemovalIntegrationEventHandler userRemovalIntegrationEventHandler;
@Autowired @Autowired
public UserServiceImpl( public UserServiceImpl(
EntityManager entityManager, EntityManager entityManager,
AuthorizationService authorizationService, AuthorizationService authorizationService,
DeleterFactory deleterFactory, DeleterFactory deleterFactory,
BuilderFactory builderFactory, BuilderFactory builderFactory,
ConventionService conventionService, ConventionService conventionService,
ErrorThesaurusProperties errors, ErrorThesaurusProperties errors,
MessageSource messageSource, MessageSource messageSource,
EventBroker eventBroker, EventBroker eventBroker,
JsonHandlingService jsonHandlingService, JsonHandlingService jsonHandlingService,
XmlHandlingService xmlHandlingService, QueryFactory queryFactory, XmlHandlingService xmlHandlingService, QueryFactory queryFactory,
UserScope userScope, KeycloakService keycloakService, ActionConfirmationService actionConfirmationService, NotificationProperties notificationProperties, NotifyIntegrationEventHandler eventHandler, ValidatorFactory validatorFactory, ElasticService elasticService) { UserScope userScope, KeycloakService keycloakService, ActionConfirmationService actionConfirmationService, NotificationProperties notificationProperties, NotifyIntegrationEventHandler eventHandler, ValidatorFactory validatorFactory, ElasticService elasticService, UserTouchedIntegrationEventHandler userTouchedIntegrationEventHandler, UserRemovalIntegrationEventHandler userRemovalIntegrationEventHandler) {
this.entityManager = entityManager; this.entityManager = entityManager;
this.authorizationService = authorizationService; this.authorizationService = authorizationService;
this.deleterFactory = deleterFactory; this.deleterFactory = deleterFactory;
@ -136,6 +140,8 @@ public class UserServiceImpl implements UserService {
this.eventHandler = eventHandler; this.eventHandler = eventHandler;
this.validatorFactory = validatorFactory; this.validatorFactory = validatorFactory;
this.elasticService = elasticService; this.elasticService = elasticService;
this.userTouchedIntegrationEventHandler = userTouchedIntegrationEventHandler;
this.userRemovalIntegrationEventHandler = userRemovalIntegrationEventHandler;
} }
//region persist //region persist
@ -170,6 +176,8 @@ public class UserServiceImpl implements UserService {
this.entityManager.flush(); this.entityManager.flush();
this.eventBroker.emit(new UserTouchedEvent(data.getId())); this.eventBroker.emit(new UserTouchedEvent(data.getId()));
this.userTouchedIntegrationEventHandler.handle(data.getId());
return this.builderFactory.builder(UserBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).build(BaseFieldSet.build(fields, User._id), data); return this.builderFactory.builder(UserBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).build(BaseFieldSet.build(fields, User._id), data);
} }
@ -196,6 +204,7 @@ public class UserServiceImpl implements UserService {
this.authorizationService.authorizeForce(Permission.DeleteUser); this.authorizationService.authorizeForce(Permission.DeleteUser);
this.deleterFactory.deleter(UserDeleter.class).deleteAndSaveByIds(List.of(id)); this.deleterFactory.deleter(UserDeleter.class).deleteAndSaveByIds(List.of(id));
this.userRemovalIntegrationEventHandler.handle(id);
} }
//endregion //endregion
@ -265,6 +274,8 @@ public class UserServiceImpl implements UserService {
this.entityManager.flush(); this.entityManager.flush();
this.eventBroker.emit(new UserTouchedEvent(data.getId())); this.eventBroker.emit(new UserTouchedEvent(data.getId()));
this.userTouchedIntegrationEventHandler.handle(data.getId());
return this.builderFactory.builder(UserBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).build(BaseFieldSet.build(fields, User._id), data); return this.builderFactory.builder(UserBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).build(BaseFieldSet.build(fields, User._id), data);
} }
@ -293,6 +304,8 @@ public class UserServiceImpl implements UserService {
this.entityManager.flush(); this.entityManager.flush();
this.userTouchedIntegrationEventHandler.handle(data.getId());
this.eventBroker.emit(new UserTouchedEvent(data.getId())); this.eventBroker.emit(new UserTouchedEvent(data.getId()));
} }
@ -319,6 +332,8 @@ public class UserServiceImpl implements UserService {
this.entityManager.flush(); this.entityManager.flush();
this.userTouchedIntegrationEventHandler.handle(data.getId());
this.eventBroker.emit(new UserTouchedEvent(data.getId())); this.eventBroker.emit(new UserTouchedEvent(data.getId()));
} }
@ -344,6 +359,8 @@ public class UserServiceImpl implements UserService {
this.entityManager.flush(); this.entityManager.flush();
this.userTouchedIntegrationEventHandler.handle(data.getId());
this.eventBroker.emit(new UserTouchedEvent(data.getId())); this.eventBroker.emit(new UserTouchedEvent(data.getId()));
} }
@ -473,6 +490,9 @@ public class UserServiceImpl implements UserService {
this.entityManager.merge(action); this.entityManager.merge(action);
this.entityManager.flush(); this.entityManager.flush();
this.userTouchedIntegrationEventHandler.handle(newUser.getId());
this.userRemovalIntegrationEventHandler.handle(userToBeMerge.getId());
} }
private void mergeNewUserToOld(UserEntity newUser, UserEntity oldUser) throws IOException, InvalidApplicationException { private void mergeNewUserToOld(UserEntity newUser, UserEntity oldUser) throws IOException, InvalidApplicationException {
@ -608,6 +628,8 @@ public class UserServiceImpl implements UserService {
this.entityManager.merge(action); this.entityManager.merge(action);
this.entityManager.flush(); this.entityManager.flush();
this.userTouchedIntegrationEventHandler.handle(userCredentialEntity.getUserId());
} }
private void checkActionState(ActionConfirmationEntity action) throws MyApplicationException { private void checkActionState(ActionConfirmationEntity action) throws MyApplicationException {

View File

@ -1,10 +1,10 @@
package eu.eudat.interceptors; package eu.eudat.interceptors;
import eu.eudat.authorization.ClaimNames; import eu.eudat.authorization.ClaimNames;
import eu.eudat.commons.JsonHandlingService; import eu.eudat.commons.JsonHandlingService;
import eu.eudat.commons.enums.ContactInfoType; import eu.eudat.commons.enums.ContactInfoType;
import eu.eudat.commons.enums.IsActive; import eu.eudat.commons.enums.IsActive;
import eu.eudat.commons.enums.notification.UserType;
import eu.eudat.commons.lock.LockByKeyManager; import eu.eudat.commons.lock.LockByKeyManager;
import eu.eudat.commons.scope.user.UserScope; import eu.eudat.commons.scope.user.UserScope;
import eu.eudat.commons.types.user.AdditionalInfoEntity; import eu.eudat.commons.types.user.AdditionalInfoEntity;
@ -14,9 +14,7 @@ import eu.eudat.data.UserContactInfoEntity;
import eu.eudat.data.UserCredentialEntity; import eu.eudat.data.UserCredentialEntity;
import eu.eudat.data.UserEntity; import eu.eudat.data.UserEntity;
import eu.eudat.data.UserRoleEntity; import eu.eudat.data.UserRoleEntity;
import eu.eudat.integrationevent.outbox.OutboxIntegrationEvent; import eu.eudat.integrationevent.outbox.usertouched.UserTouchedIntegrationEventHandler;
import eu.eudat.integrationevent.outbox.OutboxService;
import eu.eudat.integrationevent.outbox.usertouched.UserTouchedIntegrationEvent;
import eu.eudat.model.UserContactInfo; import eu.eudat.model.UserContactInfo;
import eu.eudat.model.UserCredential; import eu.eudat.model.UserCredential;
import eu.eudat.model.UserRole; import eu.eudat.model.UserRole;
@ -32,7 +30,6 @@ import gr.cite.tools.logging.LoggerService;
import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext; import jakarta.persistence.PersistenceContext;
import org.apache.commons.validator.routines.EmailValidator; import org.apache.commons.validator.routines.EmailValidator;
import org.jetbrains.annotations.NotNull;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.NonNull; import org.springframework.lang.NonNull;
@ -53,43 +50,31 @@ import java.util.concurrent.TimeUnit;
@Component @Component
public class UserInterceptor implements WebRequestInterceptor { public class UserInterceptor implements WebRequestInterceptor {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserInterceptor.class)); private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserInterceptor.class));
private final UserScope userScope; private final UserScope userScope;
private final ClaimExtractor claimExtractor; private final ClaimExtractor claimExtractor;
private final CurrentPrincipalResolver currentPrincipalResolver; private final CurrentPrincipalResolver currentPrincipalResolver;
private final PlatformTransactionManager transactionManager; private final PlatformTransactionManager transactionManager;
private final UserInterceptorCacheService userInterceptorCacheService; private final UserInterceptorCacheService userInterceptorCacheService;
private final JsonHandlingService jsonHandlingService; private final JsonHandlingService jsonHandlingService;
private final QueryFactory queryFactory; private final QueryFactory queryFactory;
private final LockByKeyManager lockByKeyManager; private final LockByKeyManager lockByKeyManager;
private final LocaleProperties localeProperties; private final LocaleProperties localeProperties;
private final UserTouchedIntegrationEventHandler userTouchedIntegrationEventHandler;
private final OutboxService outboxService;
@PersistenceContext @PersistenceContext
public EntityManager entityManager; public EntityManager entityManager;
@Autowired @Autowired
public UserInterceptor( public UserInterceptor(
UserScope userScope, UserScope userScope,
ClaimExtractor claimExtractor, ClaimExtractor claimExtractor,
CurrentPrincipalResolver currentPrincipalResolver, CurrentPrincipalResolver currentPrincipalResolver,
PlatformTransactionManager transactionManager, PlatformTransactionManager transactionManager,
UserInterceptorCacheService userInterceptorCacheService, UserInterceptorCacheService userInterceptorCacheService,
JsonHandlingService jsonHandlingService, JsonHandlingService jsonHandlingService,
QueryFactory queryFactory, QueryFactory queryFactory,
LockByKeyManager lockByKeyManager, LockByKeyManager lockByKeyManager,
LocaleProperties localeProperties, OutboxService outboxService) { LocaleProperties localeProperties, UserTouchedIntegrationEventHandler userTouchedIntegrationEventHandler) {
this.userScope = userScope; this.userScope = userScope;
this.currentPrincipalResolver = currentPrincipalResolver; this.currentPrincipalResolver = currentPrincipalResolver;
this.claimExtractor = claimExtractor; this.claimExtractor = claimExtractor;
@ -99,22 +84,23 @@ public class UserInterceptor implements WebRequestInterceptor {
this.queryFactory = queryFactory; this.queryFactory = queryFactory;
this.lockByKeyManager = lockByKeyManager; this.lockByKeyManager = lockByKeyManager;
this.localeProperties = localeProperties; this.localeProperties = localeProperties;
this.outboxService = outboxService; this.userTouchedIntegrationEventHandler = userTouchedIntegrationEventHandler;
} }
@Override @Override
public void preHandle(@NotNull WebRequest request) throws InterruptedException { public void preHandle(WebRequest request) throws InterruptedException {
UUID userId = null; UUID userId = null;
if (this.currentPrincipalResolver.currentPrincipal().isAuthenticated()) { if (this.currentPrincipalResolver.currentPrincipal().isAuthenticated()) {
String subjectId = this.claimExtractor.subjectString(this.currentPrincipalResolver.currentPrincipal()); String subjectId = this.claimExtractor.subjectString(this.currentPrincipalResolver.currentPrincipal());
if (subjectId == null || subjectId.isBlank()) if (subjectId == null || subjectId.isBlank()) throw new MyForbiddenException("Empty subjects not allowed");
throw new MyForbiddenException("Empty subjects not allowed");
UserInterceptorCacheService.UserInterceptorCacheValue cacheValue = this.userInterceptorCacheService.lookup(this.userInterceptorCacheService.buildKey(subjectId)); UserInterceptorCacheService.UserInterceptorCacheValue cacheValue = this.userInterceptorCacheService.lookup(this.userInterceptorCacheService.buildKey(subjectId));
if (cacheValue != null && emailExistsToPrincipal(cacheValue.getProviderEmail()) && userRolesSynced(cacheValue.getRoles()) && providerExistsToPrincipal(cacheValue.getExternalProviderNames())) { if (cacheValue != null && emailExistsToPrincipal(cacheValue.getProviderEmail()) && userRolesSynced(cacheValue.getRoles()) && providerExistsToPrincipal(cacheValue.getExternalProviderNames())) {
userId = cacheValue.getUserId(); userId = cacheValue.getUserId();
} else { } else {
boolean usedResource = false; boolean usedResource = false;
boolean shouldSendUserTouchedIntegrationEvent = false;
try { try {
usedResource = this.lockByKeyManager.tryLock(subjectId, 5000, TimeUnit.MILLISECONDS); usedResource = this.lockByKeyManager.tryLock(subjectId, 5000, TimeUnit.MILLISECONDS);
String email = this.getEmailFromClaims(); String email = this.getEmailFromClaims();
@ -132,36 +118,37 @@ public class UserInterceptor implements WebRequestInterceptor {
if (isNewUser) { if (isNewUser) {
UserEntity user = this.addNewUser(subjectId, email); UserEntity user = this.addNewUser(subjectId, email);
userId = user.getId(); userId = user.getId();
shouldSendUserTouchedIntegrationEvent = true;
} }
this.entityManager.flush(); this.entityManager.flush();
if (!isNewUser) { if (!isNewUser) {
this.syncUserWithClaims(userId, subjectId); boolean hasChanges = this.syncUserWithClaims(userId, subjectId);
if (hasChanges) shouldSendUserTouchedIntegrationEvent = true;
} }
this.entityManager.flush(); this.entityManager.flush();
transactionManager.commit(status); transactionManager.commit(status);
} catch (Exception ex) { } catch (Exception ex) {
if (status != null) if (status != null) transactionManager.rollback(status);
transactionManager.rollback(status);
throw ex; throw ex;
} }
cacheValue = new UserInterceptorCacheService.UserInterceptorCacheValue(subjectId, userId); cacheValue = new UserInterceptorCacheService.UserInterceptorCacheValue(subjectId, userId);
cacheValue.setRoles(this.getRolesFromClaims()); cacheValue.setRoles(this.getRolesFromClaims());
if (email != null && !email.isBlank()) if (email != null && !email.isBlank()) cacheValue.setProviderEmail(email);
cacheValue.setProviderEmail(email);
UserCredentialEntity userCredential = this.queryFactory.query(UserCredentialQuery.class).externalIds(subjectId).firstAs(new BaseFieldSet().ensure(UserCredential._data)); UserCredentialEntity userCredential = this.queryFactory.query(UserCredentialQuery.class).externalIds(subjectId).firstAs(new BaseFieldSet().ensure(UserCredential._data));
if (userCredential != null && userCredential.getData() != null) { if (userCredential != null && userCredential.getData() != null) {
UserCredentialDataEntity userCredentialDataEntity = this.jsonHandlingService.fromJsonSafe(UserCredentialDataEntity.class, userCredential.getData()); UserCredentialDataEntity userCredentialDataEntity = this.jsonHandlingService.fromJsonSafe(UserCredentialDataEntity.class, userCredential.getData());
if (userCredentialDataEntity != null) if (userCredentialDataEntity != null) cacheValue.setExternalProviderNames(userCredentialDataEntity.getExternalProviderNames());
cacheValue.setExternalProviderNames(userCredentialDataEntity.getExternalProviderNames());
} }
this.userInterceptorCacheService.put(cacheValue); this.userInterceptorCacheService.put(cacheValue);
} finally { } finally {
if (usedResource) if (usedResource) this.lockByKeyManager.unlock(subjectId);
this.lockByKeyManager.unlock(subjectId); }
if (shouldSendUserTouchedIntegrationEvent){
this.userTouchedIntegrationEventHandler.handle(userId);
} }
} }
@ -169,10 +156,9 @@ public class UserInterceptor implements WebRequestInterceptor {
this.userScope.setUserId(userId); this.userScope.setUserId(userId);
} }
private void syncUserWithClaims(UUID userId, String subjectId) { private boolean syncUserWithClaims(UUID userId, String subjectId) {
UserTouchedIntegrationEvent event = new UserTouchedIntegrationEvent();
List<String> existingUserEmails = this.collectUserEmails(userId); List<String> existingUserEmails = this.collectUserEmails(userId);
boolean hasChanges = false;
if (!this.containsPrincipalEmail(existingUserEmails)) { if (!this.containsPrincipalEmail(existingUserEmails)) {
String email = this.getEmailFromClaims(); String email = this.getEmailFromClaims();
long contactUsedByOthersCount = this.queryFactory.query(UserContactInfoQuery.class).excludedUserIds(userId).types(ContactInfoType.Email).values(email).count(); long contactUsedByOthersCount = this.queryFactory.query(UserContactInfoQuery.class).excludedUserIds(userId).types(ContactInfoType.Email).values(email).count();
@ -182,19 +168,15 @@ public class UserInterceptor implements WebRequestInterceptor {
Long emailContactsCount = this.queryFactory.query(UserContactInfoQuery.class).userIds(userId).types(ContactInfoType.Email).count(); Long emailContactsCount = this.queryFactory.query(UserContactInfoQuery.class).userIds(userId).types(ContactInfoType.Email).count();
UserContactInfoEntity contactInfo = this.buildEmailContact(userId, email); UserContactInfoEntity contactInfo = this.buildEmailContact(userId, email);
contactInfo.setOrdinal(emailContactsCount.intValue()); contactInfo.setOrdinal(emailContactsCount.intValue());
hasChanges = true;
this.entityManager.persist(contactInfo); this.entityManager.persist(contactInfo);
this.initUserTouchedEvent(event, userId);
UserTouchedIntegrationEvent.UserContactInfo eventContactInfo = new UserTouchedIntegrationEvent.UserContactInfo();
eventContactInfo.setType(ContactInfoType.Email);
eventContactInfo.setValue(email);
event.getUserContactInfo().add(eventContactInfo);
} }
} }
List<String> existingUserRoles = this.collectUserRoles(userId); List<String> existingUserRoles = this.collectUserRoles(userId);
if (!this.userRolesSynced(existingUserRoles)) { if (!this.userRolesSynced(existingUserRoles)) {
this.syncRoles(userId); this.syncRoles(userId);
hasChanges = true;
} }
UserCredentialEntity userCredential = this.queryFactory.query(UserCredentialQuery.class).externalIds(subjectId).first(); UserCredentialEntity userCredential = this.queryFactory.query(UserCredentialQuery.class).externalIds(subjectId).first();
@ -203,10 +185,8 @@ public class UserInterceptor implements WebRequestInterceptor {
} else { } else {
boolean updatedUserCredential = false; boolean updatedUserCredential = false;
UserCredentialDataEntity userCredentialDataEntity = this.jsonHandlingService.fromJsonSafe(UserCredentialDataEntity.class, userCredential.getData()); UserCredentialDataEntity userCredentialDataEntity = this.jsonHandlingService.fromJsonSafe(UserCredentialDataEntity.class, userCredential.getData());
if (userCredentialDataEntity == null) if (userCredentialDataEntity == null) userCredentialDataEntity = new UserCredentialDataEntity();
userCredentialDataEntity = new UserCredentialDataEntity(); if (userCredentialDataEntity.getExternalProviderNames() == null) userCredentialDataEntity.setExternalProviderNames(new ArrayList<>());
if (userCredentialDataEntity.getExternalProviderNames() == null)
userCredentialDataEntity.setExternalProviderNames(new ArrayList<>());
String email = this.getEmailFromClaims(); String email = this.getEmailFromClaims();
String provider = this.getProviderFromClaims(); String provider = this.getProviderFromClaims();
@ -221,15 +201,11 @@ public class UserInterceptor implements WebRequestInterceptor {
} }
if (updatedUserCredential) { if (updatedUserCredential) {
userCredential.setData(this.jsonHandlingService.toJsonSafe(userCredentialDataEntity)); userCredential.setData(this.jsonHandlingService.toJsonSafe(userCredentialDataEntity));
hasChanges = true;
this.entityManager.persist(userCredential); this.entityManager.persist(userCredential);
} }
} }
return hasChanges;
OutboxIntegrationEvent message = new OutboxIntegrationEvent();
message.setMessageId(UUID.randomUUID());
message.setType(OutboxIntegrationEvent.USER_TOUCH);
message.setEvent(event);
this.outboxService.publish(message);
} }
private UUID findExistingUserFromDb(String subjectId) { private UUID findExistingUserFromDb(String subjectId) {
@ -255,8 +231,7 @@ public class UserInterceptor implements WebRequestInterceptor {
private List<String> getRolesFromClaims() { private List<String> getRolesFromClaims() {
List<String> claimsRoles = claimExtractor.roles(currentPrincipalResolver.currentPrincipal()); List<String> claimsRoles = claimExtractor.roles(currentPrincipalResolver.currentPrincipal());
if (claimsRoles == null) if (claimsRoles == null) claimsRoles = new ArrayList<>();
claimsRoles = new ArrayList<>();
claimsRoles = claimsRoles.stream().filter(x -> x != null && !x.isBlank()).distinct().toList(); claimsRoles = claimsRoles.stream().filter(x -> x != null && !x.isBlank()).distinct().toList();
return claimsRoles; return claimsRoles;
} }
@ -306,32 +281,28 @@ public class UserInterceptor implements WebRequestInterceptor {
(principalCredentialProviders != null && principalCredentialProviders.stream().anyMatch(provider::equalsIgnoreCase)); (principalCredentialProviders != null && principalCredentialProviders.stream().anyMatch(provider::equalsIgnoreCase));
} }
private boolean userRolesSynced(List<String> existingUserRoles) { private boolean userRolesSynced(List<String> existingUserRoles) {
List<String> claimsRoles = this.getRolesFromClaims(); List<String> claimsRoles = this.getRolesFromClaims();
if (existingUserRoles == null) if (existingUserRoles == null) existingUserRoles = new ArrayList<>();
existingUserRoles = new ArrayList<>();
existingUserRoles = existingUserRoles.stream().filter(x -> x != null && !x.isBlank()).distinct().toList(); existingUserRoles = existingUserRoles.stream().filter(x -> x != null && !x.isBlank()).distinct().toList();
if (claimsRoles.size() != existingUserRoles.size()) if (claimsRoles.size() != existingUserRoles.size()) return false;
return false;
for (String claim : claimsRoles) { for (String claim : claimsRoles) {
if (existingUserRoles.stream().noneMatch(claim::equalsIgnoreCase)) if (existingUserRoles.stream().noneMatch(claim::equalsIgnoreCase)) return false;
return false;
} }
return true; return true;
} }
private String getEmailFromClaims() { private String getEmailFromClaims() {
String email = this.claimExtractor.email(this.currentPrincipalResolver.currentPrincipal()); String email = this.claimExtractor.email(this.currentPrincipalResolver.currentPrincipal());
if (email == null || email.isBlank() || !EmailValidator.getInstance().isValid(email)) if (email == null || email.isBlank() || !EmailValidator.getInstance().isValid(email)) return null;
return null;
return email.trim(); return email.trim();
} }
private String getProviderFromClaims() { private String getProviderFromClaims() {
String provider = this.claimExtractor.asString(this.currentPrincipalResolver.currentPrincipal(), ClaimNames.ExternalProviderName); String provider = this.claimExtractor.asString(this.currentPrincipalResolver.currentPrincipal(), ClaimNames.ExternalProviderName);
if (provider == null || provider.isBlank()) if (provider == null || provider.isBlank()) return null;
return null;
return provider.trim(); return provider.trim();
} }
@ -341,10 +312,8 @@ public class UserInterceptor implements WebRequestInterceptor {
String email = this.getEmailFromClaims(); String email = this.getEmailFromClaims();
String provider = this.getProviderFromClaims(); String provider = this.getProviderFromClaims();
if (email != null && !email.isBlank()) if (email != null && !email.isBlank()) userCredentialDataEntity.setEmail(email);
userCredentialDataEntity.setEmail(email); if (provider != null && !provider.isBlank()) userCredentialDataEntity.setExternalProviderNames(List.of(provider));
if (provider != null && !provider.isBlank())
userCredentialDataEntity.setExternalProviderNames(List.of(provider));
data.setData(this.jsonHandlingService.toJsonSafe(userCredentialDataEntity)); data.setData(this.jsonHandlingService.toJsonSafe(userCredentialDataEntity));
data.setId(UUID.randomUUID()); data.setId(UUID.randomUUID());
@ -375,8 +344,6 @@ public class UserInterceptor implements WebRequestInterceptor {
} }
private UserEntity addNewUser(String subjectId, String email) { private UserEntity addNewUser(String subjectId, String email) {
UserTouchedIntegrationEvent event = new UserTouchedIntegrationEvent();
List<String> roles = this.getRolesFromClaims(); List<String> roles = this.getRolesFromClaims();
String name = this.claimExtractor.name(this.currentPrincipalResolver.currentPrincipal()); String name = this.claimExtractor.name(this.currentPrincipalResolver.currentPrincipal());
@ -393,28 +360,12 @@ public class UserInterceptor implements WebRequestInterceptor {
user.setAdditionalInfo(this.jsonHandlingService.toJsonSafe(additionalInfoEntity)); user.setAdditionalInfo(this.jsonHandlingService.toJsonSafe(additionalInfoEntity));
this.entityManager.persist(user); this.entityManager.persist(user);
this.initUserTouchedEvent(event, user.getId());
event.setType(UserType.Person);
event.setFirstName(user.getName().split(" ")[0]);
event.setLastName(user.getName().split(" ")[1]);
UserTouchedIntegrationEvent.UserProfile eventUserProfile = new UserTouchedIntegrationEvent.UserProfile();
eventUserProfile.setCulture(additionalInfoEntity.getCulture());
eventUserProfile.setTimezone(additionalInfoEntity.getTimezone());
eventUserProfile.setLanguage(additionalInfoEntity.getLanguage());
event.setProfile(eventUserProfile);
UserCredentialEntity credential = this.buildCredential(user.getId(), subjectId); UserCredentialEntity credential = this.buildCredential(user.getId(), subjectId);
this.entityManager.persist(credential); this.entityManager.persist(credential);
if (email != null && !email.isBlank()) { if (email != null && !email.isBlank()) {
UserContactInfoEntity contactInfo = this.buildEmailContact(user.getId(), email); UserContactInfoEntity contactInfo = this.buildEmailContact(user.getId(), email);
this.entityManager.persist(contactInfo); this.entityManager.persist(contactInfo);
UserTouchedIntegrationEvent.UserContactInfo userContactInfo = new UserTouchedIntegrationEvent.UserContactInfo();
userContactInfo.setType(ContactInfoType.Email);
userContactInfo.setValue(email);
event.getUserContactInfo().add(userContactInfo);
} }
if (roles != null) { if (roles != null) {
for (String role : roles) { for (String role : roles) {
@ -423,38 +374,9 @@ public class UserInterceptor implements WebRequestInterceptor {
} }
} }
OutboxIntegrationEvent message = new OutboxIntegrationEvent();
message.setMessageId(UUID.randomUUID());
message.setType(OutboxIntegrationEvent.USER_TOUCH);
message.setEvent(event);
this.outboxService.publish(message);
return user; return user;
} }
private void initUserTouchedEvent(UserTouchedIntegrationEvent event, UUID userId) {
event.setId(userId);
UserEntity userEntity = this.entityManager.find(UserEntity.class, userId);
if (userEntity != null) {
event.setFirstName(userEntity.getName().split(" ")[0]);
event.setLastName(userEntity.getName().split(" ")[1]);
event.setType(UserType.Person);
}
List<UserTouchedIntegrationEvent.UserContactInfo> userContactInfoList = new ArrayList<>();
if (userEntity != null) {
List<UserContactInfoEntity> contactInfoEntities = this.queryFactory.query(UserContactInfoQuery.class).userIds(userId).collect();
for (UserContactInfoEntity entity : contactInfoEntities) {
UserTouchedIntegrationEvent.UserContactInfo userContactInfo = new UserTouchedIntegrationEvent.UserContactInfo();
userContactInfo.setType(entity.getType());
userContactInfo.setValue(entity.getValue());
userContactInfoList.add(userContactInfo);
}
}
event.setUserContactInfo(userContactInfoList);
}
@Override @Override
public void postHandle(@NonNull WebRequest request, ModelMap model) { public void postHandle(@NonNull WebRequest request, ModelMap model) {
this.userScope.setUserId(null); this.userScope.setUserId(null);