UserAuthorizationRepository.java
package fr.metabocloud.core.repositories;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Repository;
import fr.metabocloud.core.model.AppInfos;
import fr.metabocloud.core.model.UserDetailsCore;
import fr.metabohub.users_apis_authorizations.ApiClient;
import fr.metabohub.users_apis_authorizations.ApiException;
import fr.metabohub.users_apis_authorizations.client.AuthenticationApi;
import fr.metabohub.users_apis_authorizations.model.CheckUserApiAuthorizationsRequest;
@Repository
public class UserAuthorizationRepository {
/**
* Define the class' logger
*/
private static final Logger logger = LoggerFactory.getLogger(UserAuthorizationRepository.class);
/**
* Define the APP identifier
*/
private static final String APP_ARTIFACT_ID = new AppInfos().getArtifactId();
/**
* Define the Authentication API
*/
private final AuthenticationApi authenticationApi;
private static final HashMap<String, UserDetailsCore> sessionUsersDetailsCore = new HashMap<>();
private static final HashMap<String, Date> sessionUsersLastUsed = new HashMap<>();
private static final int SESSION_VALIDITY = (120 * 60 * 1000); // 2 hours
public UserAuthorizationRepository() {
final var client = new ApiClient();
client.setHost("unh-pfemlindev.ara.inrae.fr");
client.setBasePath("/mth-users-api/");
this.authenticationApi = new AuthenticationApi(client);
}
public UserAuthorizationRepository(final AuthenticationApi authenticationApi) {
this.authenticationApi = authenticationApi;
}
///////////////////////////////////////////////////////////////////////////
/**
* Get a user main information (ORCID, email, roles and authorization) for
* current API from user's JWT or API-KEY (token) using a local cache checking
* before a "MTH-USERS-API" call.
*
* @param userApiKey the user's JWT or API-KEY
* @return user core information for authentication or <code>NULL</code> if
* invalid / not found / ...
*/
public UserDetailsCore getUserDetails(final String userApiKey) {
// step 1 - try fetch from session cache
if (sessionUsersDetailsCore.containsKey(userApiKey)) {
// check if valid cache
if (sessionUsersDetailsCore.get(userApiKey) != null
&& (new Date()).getTime() - (sessionUsersLastUsed.get(userApiKey)).getTime() <= SESSION_VALIDITY) {
// update session cache last used (renew)
sessionUsersLastUsed.put(userApiKey, (new Date()));
// fetch from cache
logger.info("user {} rights fetched from session cache", userApiKey);
return sessionUsersDetailsCore.get(userApiKey);
} else {
// too old! clear cache
logger.info("user {} session cache was too old ⇒ clean/renew it", userApiKey);
sessionUsersDetailsCore.remove(userApiKey);
sessionUsersLastUsed.remove(userApiKey);
}
}
// step 2 - try fetch from database
final var user = this.getUserDetailsFromMthUsersApi(userApiKey);
if (user != null) {
// extract and set in session cache
sessionUsersDetailsCore.put(userApiKey, user);
sessionUsersLastUsed.put(userApiKey, (new Date()));
logger.info("user {} session cache loaded", userApiKey);
}
// step 3 - fetch from cache
return sessionUsersDetailsCore.get(userApiKey) != null ? //
sessionUsersDetailsCore.get(userApiKey) : //
null;
}
/**
* Get a user main information (ORCID, email, roles and authorization) for
* current API from user's JWT or API-KEY (token) using a direct
* "MTH-USERS-API" call.
*
* @param apiKey a {@link java.lang.String} object
* @return user core information for authentication or <code>NULL</code> if
* invalid / not found / ...
*/
public UserDetailsCore getUserDetailsFromMthUsersApi(final String apiKey) {
// try to authenticate user
try {
// init DTO
final var authDto = new CheckUserApiAuthorizationsRequest();
authDto.setAccessToken(apiKey);
authDto.setApi(APP_ARTIFACT_ID);
// get authentication data
final var userAuth = this.authenticationApi//
.checkUserApiAuthorization(authDto);
// extract data
final var userOrcid = userAuth.getUser().getOrcid();
final var userEmail = userAuth.getUser().getEmail();
final var userRoles = userAuth.getUserRoles();
final var userAuthorizations = userAuth.getTokenAuthorizations();
// init response
final var user = new UserDetailsCore()//
.setOrcid(userOrcid)//
.setEmail(userEmail)//
.setRoles(new HashSet<>(userRoles))//
.setAuthorizations(new HashSet<>(userAuthorizations));
// log
logger.info("user '{}' authenticated with @orcid={}", userEmail,
userOrcid);
// return
return user;
} catch (final ApiException e) {
logger.error("unable to authenticate user: '{}'", e.getMessage());
}
return null;
}
/**
* Clean old / deprecated sessions each 3 hours
*/
@Scheduled(fixedRate = 3 * 60 * 60 * 1000)
public void cronJobSch() {
this.cleanUsersSessions(SESSION_VALIDITY);
}
private void cleanUsersSessions(final int sessionValidity) {
// test each session
for (var sessionUserEntry : sessionUsersLastUsed.entrySet()) {
if (sessionUserEntry.getValue() != null
&& (new Date()).getTime() - (sessionUserEntry.getValue()).getTime() > sessionValidity) {
// session is deprecated - remove it!
logger.info("Session for user {} deprecated.", //
sessionUserEntry.getKey());
sessionUsersDetailsCore.remove(sessionUserEntry.getKey());
sessionUsersLastUsed.remove(sessionUserEntry.getKey());
}
}
}
}