Skip to content

Commit

Permalink
retrieve session credentials in graphql fetchers/mutators
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewazores committed Mar 3, 2023
1 parent f5d2913 commit b44b7dd
Show file tree
Hide file tree
Showing 18 changed files with 248 additions and 105 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,26 @@
*/
package io.cryostat.net.web.http.api.v2.graph;

import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Matcher;

import io.cryostat.configuration.CredentialsManager;
import io.cryostat.core.net.Credentials;
import io.cryostat.net.AuthManager;
import io.cryostat.net.AuthorizationErrorException;
import io.cryostat.net.security.PermissionedAction;
import io.cryostat.net.web.http.AbstractAuthenticatedRequestHandler;
import io.cryostat.net.web.http.api.v2.ApiException;

import graphql.GraphQLContext;
import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;
import io.vertx.core.http.HttpHeaders;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.graphql.GraphQLHandler;

abstract class AbstractPermissionedDataFetcher<T> implements DataFetcher<T>, PermissionedAction {

Expand Down Expand Up @@ -81,4 +90,74 @@ public final T get(DataFetchingEnvironment environment) throws Exception {
}

abstract T getAuthenticated(DataFetchingEnvironment environment) throws Exception;

// FIXME targetId should not be supplied, this method should either figure it out from context,
// or the X-JMX-Authorization header should actually have a value that encodes a map from
// targetId to credentials
protected Optional<Credentials> getSessionCredentials(
DataFetchingEnvironment environment, String targetId) {
RoutingContext ctx = GraphQLHandler.getRoutingContext(environment.getGraphQlContext());
if (!ctx.request()
.headers()
.contains(AbstractAuthenticatedRequestHandler.JMX_AUTHORIZATION_HEADER)) {
return Optional.empty();
}
String proxyAuth =
ctx.request()
.getHeader(AbstractAuthenticatedRequestHandler.JMX_AUTHORIZATION_HEADER);
Matcher m = AbstractAuthenticatedRequestHandler.AUTH_HEADER_PATTERN.matcher(proxyAuth);
if (!m.find()) {
ctx.response()
.putHeader(
AbstractAuthenticatedRequestHandler.JMX_AUTHENTICATE_HEADER, "Basic");
throw new ApiException(
427,
"Invalid "
+ AbstractAuthenticatedRequestHandler.JMX_AUTHORIZATION_HEADER
+ " format");
}
String t = m.group("type");
if (!"basic".equals(t.toLowerCase())) {
ctx.response()
.putHeader(
AbstractAuthenticatedRequestHandler.JMX_AUTHENTICATE_HEADER, "Basic");
throw new ApiException(
427,
"Unacceptable "
+ AbstractAuthenticatedRequestHandler.JMX_AUTHORIZATION_HEADER
+ " type");
}
String c;
try {
c =
new String(
Base64.getUrlDecoder().decode(m.group("credentials")),
StandardCharsets.UTF_8);
} catch (IllegalArgumentException iae) {
ctx.response()
.putHeader(
AbstractAuthenticatedRequestHandler.JMX_AUTHENTICATE_HEADER, "Basic");
throw new ApiException(
427,
AbstractAuthenticatedRequestHandler.JMX_AUTHORIZATION_HEADER
+ " credentials do not appear to be Base64-encoded",
iae);
}
String[] parts = c.split(":");
if (parts.length != 2) {
ctx.response()
.putHeader(
AbstractAuthenticatedRequestHandler.JMX_AUTHENTICATE_HEADER, "Basic");
throw new ApiException(
427,
"Unrecognized "
+ AbstractAuthenticatedRequestHandler.JMX_AUTHORIZATION_HEADER
+ " credential format");
}
Credentials credentials = new Credentials(parts[0], parts[1]);
CredentialsManager.SESSION_JMX_CREDENTIALS.get().put(targetId, credentials);
ctx.addEndHandler(
unused -> CredentialsManager.SESSION_JMX_CREDENTIALS.get().remove(targetId));
return Optional.of(credentials);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import javax.inject.Inject;

import io.cryostat.configuration.CredentialsManager;
import io.cryostat.core.net.Credentials;
import io.cryostat.net.AuthManager;
import io.cryostat.net.ConnectionDescriptor;
import io.cryostat.net.security.ResourceAction;
Expand Down Expand Up @@ -92,8 +93,11 @@ public ArchivedRecordingInfo getAuthenticated(DataFetchingEnvironment environmen
GraphRecordingDescriptor source = environment.getSource();
ServiceRef target = source.target;
String uri = target.getServiceUri().toString();
ConnectionDescriptor cd =
new ConnectionDescriptor(uri, credentialsManager.getCredentials(target));

Credentials credentials =
getSessionCredentials(environment, uri.toString())
.orElse(credentialsManager.getCredentials(target));
ConnectionDescriptor cd = new ConnectionDescriptor(uri, credentials);

return recordingArchiveHelper.saveRecording(cd, source.getName()).get();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import javax.inject.Inject;

import io.cryostat.configuration.CredentialsManager;
import io.cryostat.core.net.Credentials;
import io.cryostat.net.AuthManager;
import io.cryostat.net.ConnectionDescriptor;
import io.cryostat.net.security.ResourceAction;
Expand Down Expand Up @@ -92,8 +93,11 @@ public GraphRecordingDescriptor getAuthenticated(DataFetchingEnvironment environ
GraphRecordingDescriptor source = environment.getSource();
ServiceRef target = source.target;
String uri = target.getServiceUri().toString();
ConnectionDescriptor cd =
new ConnectionDescriptor(uri, credentialsManager.getCredentials(target));

Credentials credentials =
getSessionCredentials(environment, uri.toString())
.orElse(credentialsManager.getCredentials(target));
ConnectionDescriptor cd = new ConnectionDescriptor(uri, credentials);

recordingTargetHelper.deleteRecording(cd, source.getName()).get();
return source;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,14 @@
*/
package io.cryostat.net.web.http.api.v2.graph;

import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.regex.Matcher;

import javax.inject.Inject;

import io.cryostat.configuration.CredentialsManager;
import io.cryostat.core.log.Logger;
import io.cryostat.core.net.Credentials;
import io.cryostat.net.AuthManager;
import io.cryostat.net.security.ResourceAction;
import io.cryostat.net.web.http.AbstractAuthenticatedRequestHandler;
import io.cryostat.net.web.http.RequestHandler;
import io.cryostat.net.web.http.api.ApiVersion;
import io.cryostat.net.web.http.api.v2.ApiException;
Expand All @@ -60,7 +54,6 @@
import io.vertx.core.http.HttpMethod;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.HttpException;
import io.vertx.ext.web.handler.graphql.GraphQLHandler;

class GraphQLPostHandler implements RequestHandler {
Expand Down Expand Up @@ -114,78 +107,6 @@ public void handle(RoutingContext ctx) {
.get()) {
throw new ApiException(401);
}

String targetId = ctx.pathParam("targetId");
Credentials credentials;
if (ctx.request()
.headers()
.contains(AbstractAuthenticatedRequestHandler.JMX_AUTHORIZATION_HEADER)) {
String proxyAuth =
ctx.request()
.getHeader(
AbstractAuthenticatedRequestHandler
.JMX_AUTHORIZATION_HEADER);
Matcher m =
AbstractAuthenticatedRequestHandler.AUTH_HEADER_PATTERN.matcher(proxyAuth);
if (!m.find()) {
ctx.response()
.putHeader(
AbstractAuthenticatedRequestHandler.JMX_AUTHENTICATE_HEADER,
"Basic");
throw new HttpException(
427,
"Invalid "
+ AbstractAuthenticatedRequestHandler.JMX_AUTHORIZATION_HEADER
+ " format");
}
String t = m.group("type");
if (!"basic".equals(t.toLowerCase())) {
ctx.response()
.putHeader(
AbstractAuthenticatedRequestHandler.JMX_AUTHENTICATE_HEADER,
"Basic");
throw new HttpException(
427,
"Unacceptable "
+ AbstractAuthenticatedRequestHandler.JMX_AUTHORIZATION_HEADER
+ " type");
}
String c;
try {
c =
new String(
Base64.getUrlDecoder().decode(m.group("credentials")),
StandardCharsets.UTF_8);
} catch (IllegalArgumentException iae) {
ctx.response()
.putHeader(
AbstractAuthenticatedRequestHandler.JMX_AUTHENTICATE_HEADER,
"Basic");
throw new HttpException(
427,
AbstractAuthenticatedRequestHandler.JMX_AUTHORIZATION_HEADER
+ " credentials do not appear to be Base64-encoded",
iae);
}
String[] parts = c.split(":");
if (parts.length != 2) {
ctx.response()
.putHeader(
AbstractAuthenticatedRequestHandler.JMX_AUTHENTICATE_HEADER,
"Basic");
throw new HttpException(
427,
"Unrecognized "
+ AbstractAuthenticatedRequestHandler.JMX_AUTHORIZATION_HEADER
+ " credential format");
}
credentials = new Credentials(parts[0], parts[1]);
CredentialsManager.SESSION_JMX_CREDENTIALS.get().put(targetId, credentials);
ctx.addEndHandler(
unused ->
CredentialsManager.SESSION_JMX_CREDENTIALS.get().remove(targetId));
}

} catch (InterruptedException | ExecutionException e) {
throw new ApiException(500, e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@

import io.cryostat.configuration.CredentialsManager;
import io.cryostat.core.log.Logger;
import io.cryostat.core.net.Credentials;
import io.cryostat.core.net.MBeanMetrics;
import io.cryostat.net.AuthManager;
import io.cryostat.net.ConnectionDescriptor;
Expand Down Expand Up @@ -92,13 +93,11 @@ public MBeanMetrics getAuthenticated(DataFetchingEnvironment environment) throws
TargetNode source = (TargetNode) environment.getSource();
ServiceRef target = source.getTarget();
String targetId = target.getServiceUri().toString();
ConnectionDescriptor cd =
new ConnectionDescriptor(targetId, credentialsManager.getCredentials(target));
try {
return tcm.executeConnectedTask(cd, conn -> conn.getMBeanMetrics());
} catch (Exception e) {
logger.warn(e);
return null;
}

Credentials credentials =
getSessionCredentials(environment, targetId)
.orElse(credentialsManager.getCredentials(target));
ConnectionDescriptor cd = new ConnectionDescriptor(targetId, credentials);
return tcm.executeConnectedTask(cd, conn -> conn.getMBeanMetrics());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import org.openjdk.jmc.rjmx.services.jfr.IRecordingDescriptor;

import io.cryostat.configuration.CredentialsManager;
import io.cryostat.core.net.Credentials;
import io.cryostat.jmc.serialization.HyperlinkedSerializableRecordingDescriptor;
import io.cryostat.net.AuthManager;
import io.cryostat.net.ConnectionDescriptor;
Expand Down Expand Up @@ -129,8 +130,10 @@ public HyperlinkedSerializableRecordingDescriptor getAuthenticated(
}
}

ConnectionDescriptor cd =
new ConnectionDescriptor(uri, credentialsManager.getCredentials(target));
Credentials credentials =
getSessionCredentials(environment, uri.toString())
.orElse(credentialsManager.getCredentials(target));
ConnectionDescriptor cd = new ConnectionDescriptor(uri, credentials);

return targetConnectionManager.executeConnectedTask(
cd,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@

import io.cryostat.configuration.CredentialsManager;
import io.cryostat.core.log.Logger;
import io.cryostat.core.net.Credentials;
import io.cryostat.net.AuthManager;
import io.cryostat.net.ConnectionDescriptor;
import io.cryostat.net.TargetConnectionManager;
Expand Down Expand Up @@ -132,8 +133,11 @@ public Recordings getAuthenticated(DataFetchingEnvironment environment) throws E
.collect(Collectors.toList());

if (requestedFields.contains("active")) {
ConnectionDescriptor cd =
new ConnectionDescriptor(targetId, credentialsManager.getCredentials(target));

Credentials credentials =
getSessionCredentials(environment, targetId)
.orElse(credentialsManager.getCredentials(target));
ConnectionDescriptor cd = new ConnectionDescriptor(targetId, credentials);
// FIXME populating these two struct members are each async tasks. we should do them in
// parallel
recordings.active =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,11 @@
import javax.inject.Inject;

import io.cryostat.configuration.CredentialsManager;
import io.cryostat.core.net.Credentials;
import io.cryostat.net.AuthManager;
import io.cryostat.net.ConnectionDescriptor;
import io.cryostat.net.security.ResourceAction;
import io.cryostat.platform.ServiceRef;
import io.cryostat.platform.discovery.TargetNode;
import io.cryostat.recordings.RecordingTargetHelper;

Expand Down Expand Up @@ -91,11 +93,13 @@ public Set<ResourceAction> resourceActions() {
public GraphRecordingDescriptor getAuthenticated(DataFetchingEnvironment environment)
throws Exception {
TargetNode node = environment.getSource();
ServiceRef target = node.getTarget();
String uri = target.getServiceUri().toString();

String uri = node.getTarget().getServiceUri().toString();
ConnectionDescriptor cd =
new ConnectionDescriptor(uri, credentialsManager.getCredentials(node.getTarget()));
return new GraphRecordingDescriptor(
node.getTarget(), recordingTargetHelper.createSnapshot(cd).get());
Credentials credentials =
getSessionCredentials(environment, uri.toString())
.orElse(credentialsManager.getCredentials(target));
ConnectionDescriptor cd = new ConnectionDescriptor(uri, credentials);
return new GraphRecordingDescriptor(target, recordingTargetHelper.createSnapshot(cd).get());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,15 @@
import org.openjdk.jmc.rjmx.services.jfr.IRecordingDescriptor;

import io.cryostat.configuration.CredentialsManager;
import io.cryostat.core.net.Credentials;
import io.cryostat.core.templates.TemplateType;
import io.cryostat.jmc.serialization.HyperlinkedSerializableRecordingDescriptor;
import io.cryostat.net.AuthManager;
import io.cryostat.net.ConnectionDescriptor;
import io.cryostat.net.TargetConnectionManager;
import io.cryostat.net.security.ResourceAction;
import io.cryostat.net.web.WebServer;
import io.cryostat.platform.ServiceRef;
import io.cryostat.platform.discovery.TargetNode;
import io.cryostat.recordings.RecordingMetadataManager;
import io.cryostat.recordings.RecordingMetadataManager.Metadata;
Expand Down Expand Up @@ -121,11 +123,14 @@ public Set<ResourceAction> resourceActions() {
public HyperlinkedSerializableRecordingDescriptor getAuthenticated(
DataFetchingEnvironment environment) throws Exception {
TargetNode node = environment.getSource();
ServiceRef target = node.getTarget();
Map<String, Object> settings = environment.getArgument("recording");

String uri = node.getTarget().getServiceUri().toString();
ConnectionDescriptor cd =
new ConnectionDescriptor(uri, credentialsManager.getCredentials(node.getTarget()));
String uri = target.getServiceUri().toString();
Credentials credentials =
getSessionCredentials(environment, uri.toString())
.orElse(credentialsManager.getCredentials(target));
ConnectionDescriptor cd = new ConnectionDescriptor(uri, credentials);
return targetConnectionManager.executeConnectedTask(
cd,
conn -> {
Expand Down
Loading

0 comments on commit b44b7dd

Please sign in to comment.