Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #126: Allow API Key usage for Temporal Cloud #128

Merged
merged 1 commit into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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