argos/dmp-backend/web/src/main/java/eu/eudat/interceptors/UserInterceptor.java

248 lines
10 KiB
Java

package eu.eudat.interceptors;
import eu.eudat.commons.enums.ProviderType;
import eu.eudat.commons.enums.IsActive;
import eu.eudat.commons.scope.user.UserScope;
import eu.eudat.data.CredentialEntity;
import eu.eudat.data.entities.UserInfo;
import eu.eudat.data.entities.UserRole;
import eu.eudat.exceptions.security.NullEmailException;
import eu.eudat.types.Authorities;
import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver;
import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractor;
import gr.cite.tools.exception.MyApplicationException;
import gr.cite.tools.logging.LoggerService;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.persistence.Tuple;
import jakarta.persistence.TypedQuery;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Root;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
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.web.context.request.ServletWebRequest;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.context.request.WebRequestInterceptor;
import javax.management.InvalidApplicationException;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.UUID;
@Component
public class UserInterceptor implements WebRequestInterceptor {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserInterceptor.class));
private final UserScope userScope;
private final ClaimExtractor claimExtractor;
private final CurrentPrincipalResolver currentPrincipalResolver;
private final PlatformTransactionManager transactionManager;
private final UserInterceptorCacheService userInterceptorCacheService;
@PersistenceContext
public EntityManager entityManager;
@Autowired
public UserInterceptor(
UserScope userScope,
ClaimExtractor claimExtractor,
CurrentPrincipalResolver currentPrincipalResolver,
PlatformTransactionManager transactionManager,
UserInterceptorCacheService userInterceptorCacheService
) {
this.userScope = userScope;
this.currentPrincipalResolver = currentPrincipalResolver;
this.claimExtractor = claimExtractor;
this.transactionManager = transactionManager;
this.userInterceptorCacheService = userInterceptorCacheService;
}
@Override
public void preHandle(WebRequest request) throws InvalidApplicationException {
UUID userId = null;
if (this.currentPrincipalResolver.currentPrincipal().isAuthenticated()) {
String subjectId = this.claimExtractor.subjectString(this.currentPrincipalResolver.currentPrincipal());
var aa = this.claimExtractor.roles(this.currentPrincipalResolver.currentPrincipal());
UserInterceptorCacheService.UserInterceptorCacheValue cacheValue = this.userInterceptorCacheService.lookup(this.userInterceptorCacheService.buildKey(subjectId));
if (cacheValue != null) {
userId = cacheValue.getUserId();
} else {
userId = this.getUserIdFromDatabaseBySubject(subjectId);
if (userId == null ) {
String email = this.claimExtractor.email(this.currentPrincipalResolver.currentPrincipal());
if (email != null && !email.isBlank()) {
userId = this.getUserIdFromDatabaseByEmail(email);
userId = this.createOrUpdateUser(subjectId, userId);
} else {
boolean checkMailNull = ((ServletWebRequest) request).getRequest().getServletPath().toLowerCase(Locale.ROOT).startsWith("/api/emailConfirmation".toLowerCase(Locale.ROOT));
if (!checkMailNull) throw new NullEmailException();
}
}
if (userId != null) this.userInterceptorCacheService.put(new UserInterceptorCacheService.UserInterceptorCacheValue(subjectId, userId));
}
}
this.userScope.setUserId(userId);
}
private UUID getUserIdFromDatabaseBySubject(String subjectId) {
CriteriaBuilder credentialCriteriaBuilder = this.entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> credentialQuery = credentialCriteriaBuilder.createQuery(Tuple.class);
Root<CredentialEntity> credentialRoot = credentialQuery.from(CredentialEntity.class);
credentialQuery.where(
credentialCriteriaBuilder.and(
credentialCriteriaBuilder.equal(credentialRoot.get(CredentialEntity._externalId), subjectId),
credentialCriteriaBuilder.equal(credentialRoot.get(CredentialEntity._isActive), IsActive.Active),
credentialCriteriaBuilder.equal(credentialRoot.get(CredentialEntity._provider), ProviderType.Keycloack)
));
credentialQuery.multiselect(credentialRoot.get(CredentialEntity._userId).alias(UserInfo._id));
List<Tuple> results = this.entityManager.createQuery(credentialQuery).getResultList();
return this.getUUIDFromTuple(results, UserInfo._id);
}
private UUID getUserIdFromDatabaseByEmail(String email) {
CriteriaBuilder emailCriteriaBuilder = this.entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> emailQuery = emailCriteriaBuilder.createQuery(Tuple.class);
Root<UserInfo> emailRoot = emailQuery.from(UserInfo.class);
emailQuery.where(
emailCriteriaBuilder.and(
emailCriteriaBuilder.equal(emailRoot.get(UserInfo._email), email),
emailCriteriaBuilder.equal(emailRoot.get(UserInfo._userStatus), 0) //TODO: Authn
));
emailQuery.multiselect(emailRoot.get(UserInfo._id).alias(UserInfo._id));
List<Tuple> results = this.entityManager.createQuery(emailQuery).getResultList();
return this.getUUIDFromTuple(results, UserInfo._id);
}
private UUID getUUIDFromTuple(List<Tuple> results, String field){
if (results.size() > 0) {//TODO: Authn
Object o;
try {
o = results.get(0).get(field);
} catch (IllegalArgumentException e) {
return null;
}
if (o == null) return null;
try {
return UUID.class.cast(o);
} catch (ClassCastException e) {
return null;
}
}
return null;
}
private UUID createOrUpdateUser(String subjectId, UUID userId) {
String name = this.claimExtractor.name(this.currentPrincipalResolver.currentPrincipal());
String emailVerified = this.claimExtractor.emailVerified(this.currentPrincipalResolver.currentPrincipal());
String email = this.claimExtractor.email(this.currentPrincipalResolver.currentPrincipal());
if (name == null) name = subjectId;
UserInfo user = null;
UserRole userRole = null;
boolean isUpdateUser = userId != null;
if (!isUpdateUser) {
user = new UserInfo();
user.setId(UUID.randomUUID());
user.setVerified_email("true".equals(emailVerified));//TODO: Authn
user.setName(name);
user.setEmail(email);
user.setCreated(new Date());
user.setLastloggedin(new Date());
user.setLastloggedin(new Date());
user.setAuthorization_level((short) 1);//TODO: Authn
user.setUsertype((short) 1);
user.setUserStatus((short) 0);
// user.setAdditionalinfo("{\"data\":{\"avatar\":{\"url\":\"" + profile.getAvatarUrl()
// + "\"},\"zenodoToken\":\"" + profile.getZenodoId()
// + "\", \"expirationDate\": \"" + Instant.now().plusSeconds((profile.getZenodoExpire() != null ? profile.getZenodoExpire(): 0)).toEpochMilli()
// + "\", \"zenodoRefresh\": \"" + profile.getZenodoRefresh()
// + (profile.getProvider() == TokenValidatorFactoryImpl.LoginProvider.ZENODO ? "\", \"zenodoEmail\": \"" + profile.getEmail() : "") +"\"}}");
user.setAdditionalinfo("{\"data\":{\"avatar\":{\"url\":\"\"},\"zenodoToken\":\"\", \"expirationDate\": \"\", \"zenodoRefresh\": \"\", \"zenodoEmail\": \"\"}}"); //TODO: Authn
userRole = new UserRole();
userRole.setId(UUID.randomUUID());
userRole.setUserInfo(user);
userRole.setRole(Authorities.USER.getValue());
} else {
CriteriaBuilder emailCriteriaBuilder = this.entityManager.getCriteriaBuilder();
CriteriaQuery<UserInfo> userQuery = emailCriteriaBuilder.createQuery(UserInfo.class);
Root<UserInfo> userRoot = userQuery.from(UserInfo.class);
userQuery.where(
emailCriteriaBuilder.and(
emailCriteriaBuilder.equal(userRoot.get(UserInfo._id), userId),
emailCriteriaBuilder.equal(userRoot.get(UserInfo._userStatus), 0) //TODO: Authn
));
userQuery.select(userRoot);
TypedQuery<UserInfo> q = this.entityManager.createQuery(userQuery);
List<UserInfo> userInfos = q.getResultList();
if (userInfos == null || userInfos.size() < 1) {
throw new MyApplicationException("Can not found user " + userId);
}
user = userInfos.get(0);
user.setAdditionalinfo("{\"data\":{\"avatar\":{\"url\":\"\"},\"zenodoToken\":\"\", \"expirationDate\": \"\", \"zenodoRefresh\": \"\", \"zenodoEmail\": \"\"}}"); //TODO: Authn
}
CredentialEntity credentialEntity = new CredentialEntity();
credentialEntity.setId(UUID.randomUUID());
credentialEntity.setUserId(user.getId());
credentialEntity.setSecret(subjectId);
credentialEntity.setCreationTime(new Date());
credentialEntity.setLastUpdateTime(new Date());
credentialEntity.setIsActive(IsActive.Active);
credentialEntity.setProvider(ProviderType.Keycloack);//TODO: Authn
credentialEntity.setExternalId(subjectId);
credentialEntity.setEmail(email);
credentialEntity.setPublicValue(email);
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);
if (!isUpdateUser) {
user = this.entityManager.merge(user);
this.entityManager.flush();
userRole.setUserInfo(user);
credentialEntity.setUserId(user.getId());
this.entityManager.merge(userRole);
this.entityManager.merge(credentialEntity);
} else {
this.entityManager.persist(user);
this.entityManager.persist(credentialEntity);
}
this.entityManager.flush();
transactionManager.commit(status);
} catch (Exception ex) {
if (status != null) transactionManager.rollback(status);
throw ex;
}
return user.getId();
}
@Override
public void postHandle(@NonNull WebRequest request, ModelMap model) {
this.userScope.setUserId(null);
}
@Override
public void afterCompletion(@NonNull WebRequest request, Exception ex) {
}
}