Skip to content

Commit

Permalink
Avoid making many selects and updates for user on each connection: ca…
Browse files Browse the repository at this point in the history
…che account tokens for 30 seconds.
  • Loading branch information
stuartcaunt committed Nov 27, 2024
1 parent aa81d53 commit d27bc07
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ AND COALESCE(cpc.visible, true) = true
@NamedQuery(name = "flavour.getAllForAdmin", query = """
SELECT f
FROM Flavour f
LEFT JOIN f.cloudProviderConfiguration cpc
LEFT JOIN FETCH f.cloudProviderConfiguration cpc
WHERE f.deleted = false
AND cpc.deletedAt IS NULL
ORDER BY f.cpu, f.memory, f.id
Expand Down
3 changes: 2 additions & 1 deletion visa-core/src/main/java/eu/ill/visa/core/entity/Image.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ AND COALESCE(cpc.visible, true) = true
@NamedQuery(name = "image.getAllForAdmin", query = """
SELECT i
FROM Image i
LEFT JOIN i.cloudProviderConfiguration cpc
LEFT JOIN FETCH i.cloudProviderConfiguration cpc
LEFT JOIN FETCH i.protocols p
WHERE i.deleted = false
AND cpc.deletedAt IS NULL
ORDER BY i.id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@ApplicationScoped
public class AccountTokenAuthenticator {
Expand All @@ -20,6 +23,8 @@ public class AccountTokenAuthenticator {

private final UserService userService;

private List<CachedAccountToken> cachedAccountTokens = new ArrayList<>();

@RestClient
AccountsServiceClient accountsServiceClient;

Expand All @@ -28,38 +33,62 @@ public AccountTokenAuthenticator(final UserService userService) {
this.userService = userService;
}

public Optional<AccountToken> authenticate(final String token) {
public Optional<AccountToken> authenticate(final String jwt) {
logger.debug("[Token] Authenticating token");

final AccountToken accountToken = this.getAccountToken(token);

if (accountToken != null) {
if (accountToken.getUser() != null) {
final User user = getOrCreateUser(accountToken.getUser());
accountToken.setUser(user);
return this.getCachedAccountToken(jwt)
.map(CachedAccountToken::token)
.or(() -> {
AccountToken accountToken = this.getAccountToken(jwt);

if (accountToken != null) {
if (accountToken.getUser() != null) {
final User user = getOrCreateUser(accountToken.getUser());
accountToken.setUser(user);

if ("0".equals(user.getId())) {
logger.warn("[Token] User {} with login {} has an invalid user id (0)", user.getFullName(), accountToken.getName());

} else {
logger.info("[Token] Successfully authenticated user: {} ({})", accountToken.getName(), user.getId());
}

if ("0".equals(user.getId())) {
logger.warn("[Token] User {} with login {} has an invalid user id (0)", user.getFullName(), accountToken.getName());
this.createCachedAccountToken(jwt, accountToken);

} else {
logger.info("[Token] Successfully authenticated user: {} ({})", accountToken.getName(), user.getId());
return Optional.of(accountToken);

} else {
logger.error("[Token] Did not obtain a valid user from the Account Service for username {}", accountToken.getName());
}
}

return Optional.of(accountToken);
return Optional.empty();
});
}

private synchronized Optional<CachedAccountToken> getCachedAccountToken(final String jwt) {
this.removeExpiredCachedUsers();
return this.cachedAccountTokens.stream().filter(cachedAccountToken -> cachedAccountToken.jwt.equals(jwt)).findAny();
}

} else {
logger.error("[Token] Did not obtain a valid user from the Account Service for username {}", accountToken.getName());
}
private synchronized void createCachedAccountToken(final String jwt, final AccountToken accountToken) {
if (this.getCachedAccountToken(jwt).isEmpty()) {
this.cachedAccountTokens.add(new CachedAccountToken(jwt, accountToken));
}
}

return Optional.empty();
private synchronized void removeExpiredCachedUsers() {
this.cachedAccountTokens = this.cachedAccountTokens.stream()
.filter(cachedAccountToken -> !cachedAccountToken.isExpired())
.collect(Collectors.toCollection(ArrayList::new));
}

private AccountToken getAccountToken(final String token) {
private AccountToken getAccountToken(final String jwt) {

try {
// Make REST API call to Accounts Service
return this.accountsServiceClient.getAccountToken(token);
return this.accountsServiceClient.getAccountToken(jwt);
} catch (UnauthorizedRuntimeException unauthorizedRuntimeException) {
logger.warn(unauthorizedRuntimeException.getMessage());

Expand Down Expand Up @@ -118,4 +147,18 @@ private User getOrCreateUser(User user) {
}


record CachedAccountToken(String jwt, AccountToken token, Date lastRefreshDate) {
// Identity is valid for one minute
static long VALID_DURATION_MS = 30000;

public CachedAccountToken(String jwt, AccountToken token) {
this(jwt, token, new Date());
}

boolean isExpired() {
Date now = new Date();
return (now.getTime() - lastRefreshDate.getTime()) > VALID_DURATION_MS;
}
}

}

0 comments on commit d27bc07

Please sign in to comment.