Skip to content

Commit

Permalink
Fix #126: Allow API Key usage for Temporal Cloud
Browse files Browse the repository at this point in the history
  • Loading branch information
melloware committed Nov 19, 2024
1 parent a121d36 commit 3634eec
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 19 deletions.
30 changes: 26 additions & 4 deletions docs/modules/ROOT/pages/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -477,14 +477,18 @@ public class TestWorkflowClientInterceptor implements WorkflowClientInterceptor

== Temporal Cloud

You may be using the Temporal Cloud offering instead of self-hosting. Each Temporal Cloud service Client has four prerequisites.
You may be using the Temporal Cloud offering instead of self-hosting. Temporal Cloud supports both TLS and API key authentication.

* The full Namespace ID from the https://cloud.temporal.io/namespaces[Cloud Namespace] details page
* The gRPC endpoint from the https://cloud.temporal.io/namespaces[Cloud Namespace] details page
=== TLS Authentication

You must provide your own CA certificates. These certificates are needed to create a Namespace, which are in turn used to grant Temporal Clients and Workers access to it. You will need:

* The full Namespace ID from the https://cloud.temporal.io/namespaces[Cloud Namespace] details page such as `<namespace>.<account>`
* The gRPC endpoint from the https://cloud.temporal.io/namespaces[Cloud Namespace] details page such as `<namespace>.<account>.tmprl.cloud:7233`
* Your mTLS private key
* Your mTLS x509 Certificate

To configure with Quarkus Temporal:
To configure with Quarkus Temporal TLS:

[source,properties]
----
Expand All @@ -495,6 +499,24 @@ quarkus.temporal.connection.mtls.client-key-path=/your-temporal.key
quarkus.temporal.connection.mtls.password=Passw0rd
----

=== API Key Authentication

Each Temporal Cloud API key is a unique identity linked to role-based access control (RBAC) settings to ensure secure and appropriate access.

You will need:

* The full Namespace ID from the https://cloud.temporal.io/namespaces[Cloud Namespace] details page such as `<namespace>.<account>`
* The gRPC endpoint from the https://cloud.temporal.io/namespaces[Cloud Namespace] details page such as `<region>.<cloud_provider>.api.temporal.io:7233.`
* Your API key

To configure with Quarkus Temporal API key:

[source,properties]
----
quarkus.temporal.namespace=<namespace>.<account>
quarkus.temporal.connection=<region>.<cloud_provider>.api.temporal.io:7233
quarkus.temporal.connection.api-key=<api-key>
----

[[extension-configuration-reference]]
== Extension Configuration Reference
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

import io.grpc.Channel;
import io.grpc.ManagedChannel;
import io.grpc.Metadata;
import io.grpc.stub.MetadataUtils;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Metrics;
import io.quarkiverse.temporal.config.ConnectionRuntimeConfig;
Expand All @@ -26,6 +28,7 @@
import io.quarkus.runtime.annotations.Recorder;
import io.quarkus.runtime.configuration.ConfigurationException;
import io.quarkus.runtime.util.ClassPathUtils;
import io.temporal.authorization.AuthorizationGrpcMetadataProvider;
import io.temporal.common.reporter.MicrometerClientStatsReporter;
import io.temporal.serviceclient.RpcRetryOptions;
import io.temporal.serviceclient.SimpleSslContextBuilder;
Expand Down Expand Up @@ -69,21 +72,35 @@ WorkflowServiceStubsOptions createWorkflowServiceStubsOptions(
.setTarget(connection.target())
.setEnableHttps(connection.enableHttps());

MTLSRuntimeConfig mtls = connection.mtls();

if (mtls.clientCertPath().isPresent() != mtls.clientKeyPath().isPresent()) {
throw new ConfigurationException("Both client cert and key must be provided");
}
// API KEY
if (connection.apiKey().isPresent()) {
// Create a Metadata object with the Temporal namespace header key.
Metadata.Key<String> key = Metadata.Key.of("temporal-namespace", Metadata.ASCII_STRING_MARSHALLER);
Metadata metadata = new Metadata();
metadata.put(key, runtimeConfig.namespace());
builder.setChannelInitializer(
(channel) -> {
channel.intercept(MetadataUtils.newAttachHeadersInterceptor(metadata));
});
builder.addGrpcMetadataProvider(new AuthorizationGrpcMetadataProvider(() -> "Bearer " + connection.apiKey()));
} else {
// Mutual Transport Layer Security
MTLSRuntimeConfig mtls = connection.mtls();

if (mtls.clientCertPath().isPresent() != mtls.clientKeyPath().isPresent()) {
throw new ConfigurationException("Both client cert and key must be provided");
}

if (mtls.clientCertPath().isPresent() && mtls.clientKeyPath().isPresent()) {
try {
SimpleSslContextBuilder sslContextBuilder = SimpleSslContextBuilder.forPKCS8(
read(mtls.clientCertPath().get()),
read(mtls.clientKeyPath().get()));
mtls.password().ifPresent(sslContextBuilder::setKeyPassword);
builder.setSslContext(sslContextBuilder.build());
} catch (SSLException e) {
throw new ConfigurationException("Failed to create SSL context", e);
if (mtls.clientCertPath().isPresent() && mtls.clientKeyPath().isPresent()) {
try {
SimpleSslContextBuilder sslContextBuilder = SimpleSslContextBuilder.forPKCS8(
read(mtls.clientCertPath().get()),
read(mtls.clientKeyPath().get()));
mtls.password().ifPresent(sslContextBuilder::setKeyPassword);
builder.setSslContext(sslContextBuilder.build());
} catch (SSLException e) {
throw new ConfigurationException("Failed to create SSL context", e);
}
}
}

Expand Down Expand Up @@ -166,4 +183,4 @@ static InputStream read(Path path) {
throw new ConfigurationException("Client cert or key file not found", e);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package io.quarkiverse.temporal.config;

import java.util.Optional;

import io.grpc.ManagedChannelBuilder;
import io.grpc.NameResolver;
import io.quarkus.runtime.annotations.ConfigGroup;
Expand All @@ -22,6 +24,12 @@ public interface ConnectionRuntimeConfig {
@WithDefault("false")
Boolean enableHttps();

/**
* Temporal Cloud API key is a unique identity linked to role-based access control (RBAC) settings to ensure secure and
* appropriate access.
*/
Optional<String> apiKey();

/**
* Rpc Retry Options.
*/
Expand Down

0 comments on commit 3634eec

Please sign in to comment.