diff --git a/CHANGELOG.md b/CHANGELOG.md
index 75018d2..dedfde8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,7 +11,9 @@ Find changes for the upcoming release in the project's [changelog.d](https://git
### Fixed
-- Introduce RubinIdentityManagerImpl, extends IdentityManager which replaces the deprecated Authenticator
+- Changed QueryJobManager to use the IdentityManager available via the AuthenticationUtil class (OpenID in our case)
+- Added deprecated AuthenticatorImpl, this is only useful in case this version of TAP is used with the old Auth params/implementations (Unlikely)
+- Upgrade version of uws-server to 1.2.21
## 1.18.4 (2024-07-19)
diff --git a/tap/build.gradle b/tap/build.gradle
index 7a13089..69575f8 100644
--- a/tap/build.gradle
+++ b/tap/build.gradle
@@ -31,7 +31,7 @@ dependencies {
implementation 'org.opencadc:cadc-tap-server-pg:[1.0.0,)'
implementation 'org.opencadc:cadc-util:1.11.2'
implementation 'org.opencadc:cadc-uws:1.0.5'
- implementation 'org.opencadc:cadc-uws-server:1.2.20'
+ implementation 'org.opencadc:cadc-uws-server:1.2.21'
implementation 'org.opencadc:cadc-vosi:1.4.6'
// Switch out this to use any supported database instead of PostgreSQL.
diff --git a/tap/src/main/java/ca/nrc/cadc/sample/JobManager.java b/tap/src/main/java/ca/nrc/cadc/sample/JobManager.java
index f85a881..5dcea5f 100644
--- a/tap/src/main/java/ca/nrc/cadc/sample/JobManager.java
+++ b/tap/src/main/java/ca/nrc/cadc/sample/JobManager.java
@@ -7,6 +7,10 @@
import ca.nrc.cadc.uws.server.impl.PostgresJobPersistence;
import ca.nrc.cadc.uws.server.SimpleJobManager;
import ca.nrc.cadc.uws.server.ThreadPoolExecutor;
+import ca.nrc.cadc.auth.AuthenticationUtil;
+import ca.nrc.cadc.auth.IdentityManager;
+import ca.nrc.cadc.uws.server.JobPersistence;
+import ca.nrc.cadc.uws.server.RandomStringGenerator;
/**
@@ -21,7 +25,9 @@ public class JobManager extends SimpleJobManager {
public JobManager() {
super();
- PostgresJobPersistence jobPersist = new PostgresJobPersistence(new X500IdentityManager());
+ IdentityManager im = AuthenticationUtil.getIdentityManager();
+ // persist UWS jobs to PostgreSQL using default jdbc/uws connection pool
+ JobPersistence jobPersist = new PostgresJobPersistence(new RandomStringGenerator(16), im, true);
// max threads: 6 == number of simultaneously running async queries (per
// web server), plus sync queries, plus VOSI-tables queries
diff --git a/tap/src/main/java/ca/nrc/cadc/sample/RubinIdentityManagerImpl.java b/tap/src/main/java/ca/nrc/cadc/sample/RubinIdentityManagerImpl.java
deleted file mode 100644
index 3d66d74..0000000
--- a/tap/src/main/java/ca/nrc/cadc/sample/RubinIdentityManagerImpl.java
+++ /dev/null
@@ -1,237 +0,0 @@
-package ca.nrc.cadc.sample;
-import java.io.IOException;
-import java.net.http.HttpClient;
-import java.net.http.HttpResponse;
-import java.net.http.HttpResponse.BodyHandlers;
-import java.net.http.HttpRequest;
-import java.net.URI;
-import java.security.AccessControlException;
-import java.security.Principal;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
-import java.util.UUID;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.Collections;
-
-import javax.security.auth.Subject;
-import javax.security.auth.x500.X500Principal;
-
-import ca.nrc.cadc.auth.AuthMethod;
-import ca.nrc.cadc.auth.AuthenticationUtil;
-import ca.nrc.cadc.auth.Authenticator;
-import ca.nrc.cadc.auth.AuthMethod;
-import ca.nrc.cadc.auth.IdentityManager;
-import ca.nrc.cadc.reg.Standards;
-
-import ca.nrc.cadc.auth.AuthorizationTokenPrincipal;
-import ca.nrc.cadc.auth.HttpPrincipal;
-import ca.nrc.cadc.auth.NumericPrincipal;
-
-import com.google.gson.Gson;
-import com.google.gson.JsonObject;
-import org.apache.log4j.Logger;
-
-/**
- * Implementes the Authenticator for processing Gafaelfawr auth,
- * and using it to authenticate against the TAP service.
- *
- * The token in the authorization header is used to make a call
- * to Gafaelfawr to retrieve details such as the uid and uidNumber.
- *
- * @author cbanek
- * @author stvoutsin
- */
-public class RubinIdentityManagerImpl implements IdentityManager
-{
- private static final Logger log = Logger.getLogger(RubinIdentityManagerImpl.class);
-
- // Size of the token cache is read from the maxTokenCache property, with
- // a default of 1000 tokens cached.
- private static final int maxTokenCache = Integer.getInteger("maxTokenCache", 1000);
-
- private static final String gafaelfawr_url = System.getProperty("gafaelfawr_url");
-
- private static final HttpClient client = HttpClient.newHttpClient();
-
- private static final ConcurrentHashMap tokenCache = new ConcurrentHashMap<>();
-
- private final class TokenInfo
- {
- public final String username;
- public final int uid;
-
- public TokenInfo(String username, int uid)
- {
- this.username = username;
- this.uid = uid;
- }
- }
- @Override
- public Set getSecurityMethods() {
- return SEC_METHODS;
- }
- private static final Set SEC_METHODS;
-
- static {
- Set tmp = new TreeSet<>();
- tmp.add(Standards.SECURITY_METHOD_ANON);
- tmp.add(Standards.SECURITY_METHOD_TOKEN);
- SEC_METHODS = Collections.unmodifiableSet(tmp);
- }
-
- public RubinIdentityManagerImpl()
- {
- }
-
- public Subject validate(Subject subject) throws AccessControlException {
- log.debug("Subject to augment starts as: " + subject);
-
- // Check if the cache is too big, and if so, clear it out.
- if (tokenCache.size() > maxTokenCache) {
- tokenCache.clear();
- }
-
- List addedPrincipals = new ArrayList();
- AuthorizationTokenPrincipal tokenPrincipal = null;
-
- for (Principal principal : subject.getPrincipals()) {
- if (principal instanceof AuthorizationTokenPrincipal) {
- tokenPrincipal = (AuthorizationTokenPrincipal) principal;
- TokenInfo tokenInfo = null;
-
- for (int i = 1; i < 5 && tokenInfo == null; i++) {
- try {
- tokenInfo = getTokenInfo(tokenPrincipal.getHeaderValue());
- } catch (IOException|InterruptedException e) {
- log.warn("Exception thrown while getting info from Gafaelfawr");
- log.warn(e);
- }
- }
-
- if (tokenInfo != null) {
- X500Principal xp = new X500Principal("CN=" + tokenInfo.username);
- addedPrincipals.add(xp);
-
- HttpPrincipal hp = new HttpPrincipal(tokenInfo.username);
- addedPrincipals.add(hp);
-
- UUID uuid = new UUID(0L, (long) tokenInfo.uid);
- NumericPrincipal np = new NumericPrincipal(uuid);
- addedPrincipals.add(np);
- }
- else {
- log.error("Gave up retrying user-info requests to Gafaelfawr");
- }
- }
- }
-
- if (tokenPrincipal != null) {
- subject.getPrincipals().remove(tokenPrincipal);
- }
-
- subject.getPrincipals().addAll(addedPrincipals);
- subject.getPublicCredentials().add(AuthMethod.TOKEN);
-
- log.debug("Augmented subject is " + subject);
- return subject;
- }
-
- // Here we could check the token again, but gafaelfawr should be
- // doing that for us already by the time it gets to us. So for
- // this layer, we just let this go through.
- public Subject augment(Subject subject) {
- return subject;
- }
-
- private TokenInfo getTokenInfo(String token) throws IOException, InterruptedException {
- // If the request has gotten this far, the token has already
- // been checked upstream, so we know it's valid, we just need
- // to determine the uid and the username.
- if (!tokenCache.containsKey(token)) {
- HttpRequest request = HttpRequest.newBuilder(URI.create(gafaelfawr_url))
- .header("Accept", "application/json")
- .header("Authorization", token)
- .build();
-
- HttpResponse response = client.send(request, BodyHandlers.ofString());
- String body = response.body();
-
- Gson gson = new Gson();
- JsonObject authData = gson.fromJson(body, JsonObject.class);
- String username = authData.getAsJsonPrimitive("username").getAsString();
- int uid = authData.getAsJsonPrimitive("uid").getAsInt();
-
- // Insert the info into the cache here since we retrieved it.
- tokenCache.put(token, new TokenInfo(username, uid));
- }
-
- return tokenCache.get(token);
- }
-
-
- @Override
- public Subject toSubject(Object owner) {
- Subject ret = new Subject();
- if (owner != null) {
- UUID uuid = null;
- if (owner instanceof UUID) {
- uuid = (UUID) owner;
- } else if (owner instanceof String) {
- String sub = (String) owner;
- uuid = UUID.fromString(sub);
- } else {
- throw new RuntimeException("unexpected owner type: " + owner.getClass().getName() + " value: " + owner);
- }
- NumericPrincipal p = new NumericPrincipal(uuid);
-
- // effectively augment by using the current subject as a "cache" of known identities
- Subject s = AuthenticationUtil.getCurrentSubject();
- if (s != null) {
- for (Principal cp : s.getPrincipals()) {
- if (AuthenticationUtil.equals(p, cp)) {
- log.debug("[cache hit] caller Subject matches " + p + ": " + s);
- ret.getPrincipals().addAll(s.getPrincipals());
- return ret;
- }
- }
- }
-
- ret.getPrincipals().add(p);
- // this is sufficient for some purposes, but not for output using toDisplayString (eg vospace node owner)
- // TODO: use PosixMapperClient.augment() to try to add a PosixPrincipal and infer an HttpPrincipal?
- }
- return ret;
- }
-
- @Override
- public Object toOwner(Subject subject) {
- // use NumericPrincipal aka OIDC sub for persistence
- Set ps = subject.getPrincipals(NumericPrincipal.class);
- if (ps.isEmpty()) {
- return null;
- }
- return ps.iterator().next().getUUID().toString();
- }
-
- @Override
- public String toDisplayString(Subject subject) {
- if (subject != null) {
- // prefer HttpPrincipal aka OIDC preferred_username for string output, eg logging
- Set ps = subject.getPrincipals(HttpPrincipal.class);
- if (!ps.isEmpty()) {
- return ps.iterator().next().getName(); // kind of ugh
- }
-
- // default
- Set ps2 = subject.getPrincipals();
- if (!ps2.isEmpty()) {
- return ps2.iterator().next().getName();
- }
- }
-
- return null;
- }
-}