Skip to content

Commit

Permalink
QueryJobManager to use IdentitytManager from AuthenticationUtil & upg…
Browse files Browse the repository at this point in the history
…rade to uws-server lib (1.2.21)
  • Loading branch information
stvoutsin committed Jul 26, 2024
1 parent 96c22a2 commit bc3fa19
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 2 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ Find changes for the upcoming release in the project's [changelog.d](https://git

<!-- scriv-insert-here -->

<a id='changelog-2.4.5'></a>
## 2.4.5 (2024-07-24)

### Changed

- 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

<a id='changelog-2.4.4'></a>
## 2.4.4 (2024-07-23)

Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ dependencies {
implementation 'org.opencadc:cadc-tap-server-oracle:1.2.11'
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.
Expand Down
152 changes: 152 additions & 0 deletions src/main/java/org/opencadc/tap/impl/AuthenticatorImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package org.opencadc.tap.impl;

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.UUID;
import java.util.concurrent.ConcurrentHashMap;

import javax.security.auth.Subject;
import javax.security.auth.x500.X500Principal;

import ca.nrc.cadc.auth.Authenticator;
import ca.nrc.cadc.auth.AuthMethod;
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;

/**
* @deprecated This class is deprecated and will be removed in future releases.
* The TAP Service now uses IdentityManager for authentication, available in the opencadc library
*
* @author cbanek
*/
@Deprecated
public class AuthenticatorImpl implements Authenticator
{
private static final Logger log = Logger.getLogger(AuthenticatorImpl.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<String,TokenInfo> 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;
}
}

public AuthenticatorImpl()
{
}

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<Principal> addedPrincipals = new ArrayList<Principal>();
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<String> 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);
}
}
11 changes: 10 additions & 1 deletion src/main/java/org/opencadc/tap/ws/QueryJobManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@
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;
import ca.nrc.cadc.uws.server.RequestPathJobManager;
import org.apache.log4j.Logger;


/**
* @author pdowler
Expand All @@ -22,7 +29,9 @@ public class QueryJobManager extends SimpleJobManager {
public QueryJobManager() {
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
Expand Down

0 comments on commit bc3fa19

Please sign in to comment.