change notification interceptor
This commit is contained in:
parent
d5292f5cec
commit
c30f499f30
|
@ -4,21 +4,11 @@ package gr.cite.notification.web.interceptors;
|
||||||
import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver;
|
import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver;
|
||||||
import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractor;
|
import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractor;
|
||||||
import gr.cite.notification.common.JsonHandlingService;
|
import gr.cite.notification.common.JsonHandlingService;
|
||||||
import gr.cite.notification.common.enums.ContactInfoType;
|
|
||||||
import gr.cite.notification.common.enums.IsActive;
|
|
||||||
import gr.cite.notification.common.lock.LockByKeyManager;
|
import gr.cite.notification.common.lock.LockByKeyManager;
|
||||||
import gr.cite.notification.common.scope.user.UserScope;
|
import gr.cite.notification.common.scope.user.UserScope;
|
||||||
import gr.cite.notification.common.types.user.AdditionalInfoEntity;
|
|
||||||
import gr.cite.notification.data.UserContactInfoEntity;
|
|
||||||
import gr.cite.notification.data.UserCredentialEntity;
|
import gr.cite.notification.data.UserCredentialEntity;
|
||||||
import gr.cite.notification.data.UserEntity;
|
|
||||||
import gr.cite.notification.data.UserRoleEntity;
|
|
||||||
import gr.cite.notification.model.UserContactInfo;
|
|
||||||
import gr.cite.notification.model.UserCredential;
|
import gr.cite.notification.model.UserCredential;
|
||||||
import gr.cite.notification.model.UserRole;
|
|
||||||
import gr.cite.notification.query.UserContactInfoQuery;
|
|
||||||
import gr.cite.notification.query.UserCredentialQuery;
|
import gr.cite.notification.query.UserCredentialQuery;
|
||||||
import gr.cite.notification.query.UserRoleQuery;
|
|
||||||
import gr.cite.tools.data.query.QueryFactory;
|
import gr.cite.tools.data.query.QueryFactory;
|
||||||
import gr.cite.tools.exception.MyForbiddenException;
|
import gr.cite.tools.exception.MyForbiddenException;
|
||||||
import gr.cite.tools.fieldset.BaseFieldSet;
|
import gr.cite.tools.fieldset.BaseFieldSet;
|
||||||
|
@ -28,21 +18,14 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.lang.NonNull;
|
import org.springframework.lang.NonNull;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.transaction.PlatformTransactionManager;
|
import org.springframework.transaction.PlatformTransactionManager;
|
||||||
import org.springframework.transaction.TransactionDefinition;
|
|
||||||
import org.springframework.transaction.TransactionStatus;
|
|
||||||
import org.springframework.transaction.support.DefaultTransactionDefinition;
|
|
||||||
import org.springframework.ui.ModelMap;
|
import org.springframework.ui.ModelMap;
|
||||||
import org.springframework.web.context.request.WebRequest;
|
import org.springframework.web.context.request.WebRequest;
|
||||||
import org.springframework.web.context.request.WebRequestInterceptor;
|
import org.springframework.web.context.request.WebRequestInterceptor;
|
||||||
|
|
||||||
import jakarta.persistence.EntityManager;
|
import jakarta.persistence.EntityManager;
|
||||||
import jakarta.persistence.PersistenceContext;
|
import jakarta.persistence.PersistenceContext;
|
||||||
import java.time.Instant;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class UserInterceptor implements WebRequestInterceptor {
|
public class UserInterceptor implements WebRequestInterceptor {
|
||||||
|
@ -79,212 +62,33 @@ public class UserInterceptor implements WebRequestInterceptor {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void preHandle(WebRequest request) throws InterruptedException {
|
public void preHandle(WebRequest request) {
|
||||||
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()) throw new MyForbiddenException("Empty subjects not allowed");
|
if (subjectId == null || subjectId.isBlank()) 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 && emailExistsToUser(cacheValue.getEmails()) && userRolesSynced(cacheValue.getRoles())) {
|
if (cacheValue != null) {
|
||||||
userId = cacheValue.getUserId();
|
userId = cacheValue.getUserId();
|
||||||
} else {
|
} else {
|
||||||
boolean usedResource = false;
|
userId = this.findExistingUserFromDbForce(subjectId);
|
||||||
try {
|
|
||||||
usedResource = this.lockByKeyManager.tryLock(subjectId, 5000, TimeUnit.MILLISECONDS);
|
|
||||||
String email = this.claimExtractor.email(this.currentPrincipalResolver.currentPrincipal());
|
|
||||||
|
|
||||||
DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
|
|
||||||
definition.setName(UUID.randomUUID().toString());
|
|
||||||
definition.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
|
|
||||||
definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
|
|
||||||
TransactionStatus status = null;
|
|
||||||
try {
|
|
||||||
status = transactionManager.getTransaction(definition);
|
|
||||||
userId = this.findExistingUserFromDb(subjectId);
|
|
||||||
boolean isNewUser = userId == null;
|
|
||||||
if (isNewUser) {
|
|
||||||
UserEntity user = this.addNewUser(subjectId, email);
|
|
||||||
userId = user.getId();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isNewUser) this.syncUserWithClaims(userId);
|
|
||||||
|
|
||||||
this.entityManager.flush();
|
|
||||||
transactionManager.commit(status);
|
|
||||||
} catch (Exception ex) {
|
|
||||||
if (status != null) transactionManager.rollback(status);
|
|
||||||
throw ex;
|
|
||||||
}
|
|
||||||
|
|
||||||
cacheValue = new UserInterceptorCacheService.UserInterceptorCacheValue(subjectId, userId);
|
cacheValue = new UserInterceptorCacheService.UserInterceptorCacheValue(subjectId, userId);
|
||||||
cacheValue.setEmails(new ArrayList<>());
|
|
||||||
if (email != null && !email.isBlank()) cacheValue.getEmails().add(email);
|
|
||||||
cacheValue.setRoles(claimExtractor.roles(currentPrincipalResolver.currentPrincipal()));
|
|
||||||
|
|
||||||
this.userInterceptorCacheService.put(cacheValue);
|
this.userInterceptorCacheService.put(cacheValue);
|
||||||
} finally {
|
|
||||||
if (usedResource) this.lockByKeyManager.unlock(subjectId);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.userScope.setUserId(userId);
|
this.userScope.setUserId(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void syncUserWithClaims(UUID userId){
|
private UUID findExistingUserFromDbForce(String subjectId){
|
||||||
List<String> existingUserEmails = this.collectUserEmails(userId);
|
|
||||||
List<String> existingUserRoles = this.collectUserRoles(userId);
|
|
||||||
if (!this.emailExistsToUser(existingUserEmails)){
|
|
||||||
String email = this.claimExtractor.email(this.currentPrincipalResolver.currentPrincipal());
|
|
||||||
long contactUsedByOthersCount = this.queryFactory.query(UserContactInfoQuery.class).excludedUserIds(userId).type(ContactInfoType.Email).values(email).count();
|
|
||||||
if (contactUsedByOthersCount > 0) {
|
|
||||||
logger.warn("user contact exists to other user" + email);
|
|
||||||
} else {
|
|
||||||
Long emailContactsCount = this.queryFactory.query(UserContactInfoQuery.class).userIds(userId).type(ContactInfoType.Email).count();
|
|
||||||
UserContactInfoEntity contactInfo = this.buildEmailContact(userId, email);
|
|
||||||
contactInfo.setOrdinal(emailContactsCount.intValue());
|
|
||||||
this.entityManager.persist(contactInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.userRolesSynced(existingUserRoles)){
|
|
||||||
this.syncRoles(userId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private UUID findExistingUserFromDb(String subjectId){
|
|
||||||
UserCredentialEntity userCredential = this.queryFactory.query(UserCredentialQuery.class).externalIds(subjectId).firstAs(new BaseFieldSet().ensure(UserCredential._user));
|
UserCredentialEntity userCredential = this.queryFactory.query(UserCredentialQuery.class).externalIds(subjectId).firstAs(new BaseFieldSet().ensure(UserCredential._user));
|
||||||
if (userCredential != null) {
|
if (userCredential != null) {
|
||||||
return userCredential.getUserId();
|
return userCredential.getUserId();
|
||||||
} else {
|
} else {
|
||||||
String email = this.claimExtractor.email(this.currentPrincipalResolver.currentPrincipal());
|
throw new MyForbiddenException("User not created try again.");
|
||||||
if (email != null && !email.isBlank()) {
|
|
||||||
UserContactInfoEntity userContactInfo = this.queryFactory.query(UserContactInfoQuery.class).type(ContactInfoType.Email).values(email).firstAs(new BaseFieldSet().ensure(UserContactInfo._user));
|
|
||||||
if (userContactInfo != null) {
|
|
||||||
UserCredentialEntity credential = this.buildCredential(userContactInfo.getUserId(), subjectId);
|
|
||||||
this.entityManager.persist(credential);
|
|
||||||
|
|
||||||
return credential.getUserId();
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
throw new MyForbiddenException("Email is required");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void syncRoles(UUID userId){
|
|
||||||
List<String> claimsRoles = claimExtractor.roles(currentPrincipalResolver.currentPrincipal());
|
|
||||||
if (claimsRoles == null) claimsRoles = new ArrayList<>();
|
|
||||||
claimsRoles = claimsRoles.stream().filter(x-> x != null && !x.isBlank()).distinct().collect(Collectors.toList());
|
|
||||||
|
|
||||||
List<UserRoleEntity> existingUserRoles = this.queryFactory.query(UserRoleQuery.class).userIds(userId).collect();
|
|
||||||
List<UUID> foundRoles = new ArrayList<>();
|
|
||||||
for (String claimRole : claimsRoles) {
|
|
||||||
UserRoleEntity roleEntity = existingUserRoles.stream().filter(x-> x.getRole().equals(claimRole)).findFirst().orElse(null);
|
|
||||||
if (roleEntity == null) {
|
|
||||||
roleEntity = this.buildRole(userId, claimRole);
|
|
||||||
this.entityManager.persist(roleEntity);
|
|
||||||
}
|
|
||||||
foundRoles.add(roleEntity.getId());
|
|
||||||
}
|
|
||||||
for (UserRoleEntity existing: existingUserRoles) {
|
|
||||||
if (!foundRoles.contains(existing.getId())){
|
|
||||||
this.entityManager.remove(existing);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> collectUserRoles(UUID userId){
|
|
||||||
List<UserRoleEntity> items = this.queryFactory.query(UserRoleQuery.class).userIds(userId).collectAs(new BaseFieldSet().ensure(UserRole._role));
|
|
||||||
return items == null ? new ArrayList<>() : items.stream().map(UserRoleEntity::getRole).collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> collectUserEmails(UUID userId){
|
|
||||||
List<UserContactInfoEntity> items = this.queryFactory.query(UserContactInfoQuery.class).userIds(userId).type(ContactInfoType.Email).collectAs(new BaseFieldSet().ensure(UserContactInfo._value));
|
|
||||||
return items == null ? new ArrayList<>() : items.stream().map(UserContactInfoEntity::getValue).collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean emailExistsToUser(List<String> existingUserEmails){
|
|
||||||
String email = this.claimExtractor.email(this.currentPrincipalResolver.currentPrincipal());
|
|
||||||
return email == null || email.isBlank() ||
|
|
||||||
(existingUserEmails != null && existingUserEmails.stream().anyMatch(email::equals));
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean userRolesSynced(List<String> existingUserRoles){
|
|
||||||
List<String> claimsRoles = claimExtractor.roles(currentPrincipalResolver.currentPrincipal());
|
|
||||||
if (claimsRoles == null) claimsRoles = new ArrayList<>();
|
|
||||||
if (existingUserRoles == null) existingUserRoles = new ArrayList<>();
|
|
||||||
claimsRoles = claimsRoles.stream().filter(x-> x != null && !x.isBlank()).distinct().collect(Collectors.toList());
|
|
||||||
existingUserRoles = existingUserRoles.stream().filter(x-> x != null && !x.isBlank()).distinct().collect(Collectors.toList());
|
|
||||||
if (claimsRoles.size() != existingUserRoles.size()) return false;
|
|
||||||
|
|
||||||
for (String claim : claimsRoles ) {
|
|
||||||
if (existingUserRoles.stream().noneMatch(claim::equalsIgnoreCase)) return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private UserCredentialEntity buildCredential(UUID userId, String subjectId){
|
|
||||||
UserCredentialEntity data = new UserCredentialEntity();
|
|
||||||
data.setId(UUID.randomUUID());
|
|
||||||
data.setUserId(userId);
|
|
||||||
data.setCreatedAt(Instant.now());
|
|
||||||
data.setExternalId(subjectId);
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
private UserRoleEntity buildRole(UUID userId, String role){
|
|
||||||
UserRoleEntity data = new UserRoleEntity();
|
|
||||||
data.setId(UUID.randomUUID());
|
|
||||||
data.setUserId(userId);
|
|
||||||
data.setRole(role);
|
|
||||||
data.setCreatedAt(Instant.now());
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
private UserContactInfoEntity buildEmailContact(UUID userId, String email){
|
|
||||||
UserContactInfoEntity data = new UserContactInfoEntity();
|
|
||||||
data.setId(UUID.randomUUID());
|
|
||||||
data.setUserId(userId);
|
|
||||||
data.setValue(email);
|
|
||||||
data.setType(ContactInfoType.Email);
|
|
||||||
data.setOrdinal(0);
|
|
||||||
data.setCreatedAt(Instant.now());
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private UserEntity addNewUser(String subjectId, String email){
|
|
||||||
List<String> roles = claimExtractor.roles(currentPrincipalResolver.currentPrincipal());
|
|
||||||
String name = this.claimExtractor.name(this.currentPrincipalResolver.currentPrincipal());
|
|
||||||
|
|
||||||
UserEntity user = new UserEntity();
|
|
||||||
user.setId(UUID.randomUUID());
|
|
||||||
user.setName(name);
|
|
||||||
user.setCreatedAt(Instant.now());
|
|
||||||
user.setUpdatedAt(Instant.now());
|
|
||||||
user.setIsActive(IsActive.Active);
|
|
||||||
user.setAdditionalInfo(this.jsonHandlingService.toJsonSafe(new AdditionalInfoEntity()));
|
|
||||||
this.entityManager.persist(user);
|
|
||||||
|
|
||||||
UserCredentialEntity credential = this.buildCredential(user.getId(), subjectId);
|
|
||||||
this.entityManager.persist(credential);
|
|
||||||
|
|
||||||
if (email != null && !email.isBlank()) {
|
|
||||||
UserContactInfoEntity contactInfo = this.buildEmailContact(user.getId(), email);
|
|
||||||
this.entityManager.persist(contactInfo);
|
|
||||||
}
|
|
||||||
if (roles != null) {
|
|
||||||
for (String role: roles) {
|
|
||||||
UserRoleEntity roleEntity = this.buildRole(user.getId(), role);
|
|
||||||
this.entityManager.persist(roleEntity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return user;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -33,9 +33,6 @@ public class UserInterceptorCacheService extends CacheService<UserInterceptorCac
|
||||||
}
|
}
|
||||||
|
|
||||||
private UUID userId;
|
private UUID userId;
|
||||||
private List<String> roles;
|
|
||||||
private List<String> emails;
|
|
||||||
|
|
||||||
public UUID getUserId() {
|
public UUID getUserId() {
|
||||||
return userId;
|
return userId;
|
||||||
}
|
}
|
||||||
|
@ -44,26 +41,11 @@ public class UserInterceptorCacheService extends CacheService<UserInterceptorCac
|
||||||
this.userId = userId;
|
this.userId = userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getRoles() {
|
|
||||||
return roles;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRoles(List<String> roles) {
|
|
||||||
this.roles = roles;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> getEmails() {
|
|
||||||
return emails;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEmails(List<String> emails) {
|
|
||||||
this.emails = emails;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public UserInterceptorCacheService(UserInterceptorCacheOptions options, ConventionService conventionService) {
|
public UserInterceptorCacheService(UserInterceptorCacheOptions options) {
|
||||||
super(options);
|
super(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue