Skip to content

Commit

Permalink
Fix #126: Allow API Key usage for Temporal Cloud (#128)
Browse files Browse the repository at this point in the history
  • Loading branch information
melloware authored Nov 19, 2024
1 parent a121d36 commit 121c846
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 20 deletions.
17 changes: 17 additions & 0 deletions docs/modules/ROOT/pages/includes/quarkus-temporal.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,23 @@ endif::add-copy-button-to-env-var[]
|boolean
|`false`

a| [[quarkus-temporal_quarkus-temporal-connection-api-key]] [.property-path]##link:#quarkus-temporal_quarkus-temporal-connection-api-key[`quarkus.temporal.connection.api-key`]##

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


ifdef::add-copy-button-to-env-var[]
Environment variable: env_var_with_copy_button:+++QUARKUS_TEMPORAL_CONNECTION_API_KEY+++[]
endif::add-copy-button-to-env-var[]
ifndef::add-copy-button-to-env-var[]
Environment variable: `+++QUARKUS_TEMPORAL_CONNECTION_API_KEY+++`
endif::add-copy-button-to-env-var[]
--
|string
|

a| [[quarkus-temporal_quarkus-temporal-connection-rpc-retry-initial-interval]] [.property-path]##link:#quarkus-temporal_quarkus-temporal-connection-rpc-retry-initial-interval[`quarkus.temporal.connection.rpc-retry.initial-interval`]##

[.description]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,23 @@ endif::add-copy-button-to-env-var[]
|boolean
|`false`

a| [[quarkus-temporal_quarkus-temporal-connection-api-key]] [.property-path]##link:#quarkus-temporal_quarkus-temporal-connection-api-key[`quarkus.temporal.connection.api-key`]##

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


ifdef::add-copy-button-to-env-var[]
Environment variable: env_var_with_copy_button:+++QUARKUS_TEMPORAL_CONNECTION_API_KEY+++[]
endif::add-copy-button-to-env-var[]
ifndef::add-copy-button-to-env-var[]
Environment variable: `+++QUARKUS_TEMPORAL_CONNECTION_API_KEY+++`
endif::add-copy-button-to-env-var[]
--
|string
|

a| [[quarkus-temporal_quarkus-temporal-connection-rpc-retry-initial-interval]] [.property-path]##link:#quarkus-temporal_quarkus-temporal-connection-rpc-retry-initial-interval[`quarkus.temporal.connection.rpc-retry.initial-interval`]##

[.description]
Expand Down
32 changes: 27 additions & 5 deletions docs/modules/ROOT/pages/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -477,24 +477,46 @@ 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]
----
quarkus.temporal.namespace=your-namespace.123def
quarkus.temporal.connection=your-namespace.123def.tmprl.cloud:7233
quarkus.temporal.connection.target=your-namespace.123def.tmprl.cloud:7233
quarkus.temporal.connection.mtls.client-cert-path=/your-temporal-x509.cert
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.target=<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 121c846

Please sign in to comment.