From e9706c7bedb8ca8167a241f9ba2344e66c1f1454 Mon Sep 17 00:00:00 2001 From: "vpatil16@ext.uber.com" Date: Mon, 20 Oct 2025 14:45:52 -0700 Subject: [PATCH 1/5] docs: Convert slack links to CNCF, add link to contributing guide --- CONTRIBUTING.md | 2 ++ README.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c66a0c6e4..3c8a93c07 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,6 +2,8 @@ This doc is intended for contributors to `cadence-java-client` (hopefully that's you!) +For general information about contributing to Cadence projects, please see our [shared contributing guide](https://github.com/cadence-workflow/.github/blob/main/CONTRIBUTING.md). + **Note:** All contributors also need to fill out the [Uber Contributor License Agreement](http://t.uber.com/cla) before we can merge in any of your changes ## Development Environment diff --git a/README.md b/README.md index bc6c33bae..83e761553 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,8 @@ The documentation on how to use the Cadence Java client is [here](https://cadenc Javadocs for the client API are located [here](https://www.javadoc.io/doc/com.uber.cadence/cadence-client). +[![Slack Status](https://img.shields.io/badge/slack-join_chat-white.svg?logo=slack&style=social)](https://communityinviter.com/apps/cloud-native/cncf) + ## Contributing We'd love your help in making the Cadence Java client great. Please review our [contribution guidelines](CONTRIBUTING.md). From 7e1d9d84ebb40dc92ba1e49946544cf49693af1c Mon Sep 17 00:00:00 2001 From: "vpatil16@ext.uber.com" Date: Mon, 20 Oct 2025 15:34:41 -0700 Subject: [PATCH 2/5] docs: Convert slack links to CNCF, add link to contributing guide --- CONTRIBUTING.md | 6 ++++-- README.md | 6 ++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3c8a93c07..c9152c8c5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,9 +2,11 @@ This doc is intended for contributors to `cadence-java-client` (hopefully that's you!) -For general information about contributing to Cadence projects, please see our [shared contributing guide](https://github.com/cadence-workflow/.github/blob/main/CONTRIBUTING.md). +📚 **New to contributing to Cadence?** Check out our [Contributing Guide](https://cadenceworkflow.io/community/how-to-contribute/getting-started) for an overview of the contribution process across all Cadence repositories. This document contains cadence-java-client specific setup and development instructions. -**Note:** All contributors also need to fill out the [Uber Contributor License Agreement](http://t.uber.com/cla) before we can merge in any of your changes +**Note:** All contributors also need to fill out the [Uber Contributor License Agreement](http://t.uber.com/cla) before we can merge in any of your changes. + +Join our community on the CNCF Slack workspace at [cloud-native.slack.com](https://communityinviter.com/apps/cloud-native/cncf) in the **#cadence-users** channel to reach out and discuss issues with the team. ## Development Environment diff --git a/README.md b/README.md index 83e761553..9d5256805 100644 --- a/README.md +++ b/README.md @@ -44,10 +44,16 @@ The documentation on how to use the Cadence Java client is [here](https://cadenc Javadocs for the client API are located [here](https://www.javadoc.io/doc/com.uber.cadence/cadence-client). +## Community + +Join our community on the CNCF Slack workspace at [cloud-native.slack.com](https://communityinviter.com/apps/cloud-native/cncf) in the **#cadence-users** channel for discussions, questions, and support. + [![Slack Status](https://img.shields.io/badge/slack-join_chat-white.svg?logo=slack&style=social)](https://communityinviter.com/apps/cloud-native/cncf) ## Contributing We'd love your help in making the Cadence Java client great. Please review our [contribution guidelines](CONTRIBUTING.md). +If you'd like to propose a new feature, first join the [CNCF Slack workspace](https://communityinviter.com/apps/cloud-native/cncf) in the **#cadence-users** channel to start a discussion. + ## License Apache 2.0 License, please see [LICENSE](LICENSE) for details. From aed6bd6948de7463b394b904ee305affb1ff6ca2 Mon Sep 17 00:00:00 2001 From: "vpatil16@ext.uber.com" Date: Mon, 20 Oct 2025 15:51:12 -0700 Subject: [PATCH 3/5] accommodating review comments --- CONTRIBUTING.md | 2 - .../cadence/serviceclient/TLSOptions.java | 280 ++++++++++++++++++ .../cadence/serviceclient/TLSOptionsTest.java | 156 ++++++++++ 3 files changed, 436 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/uber/cadence/serviceclient/TLSOptions.java create mode 100644 src/test/java/com/uber/cadence/serviceclient/TLSOptionsTest.java diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c9152c8c5..ac440b16c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,8 +4,6 @@ This doc is intended for contributors to `cadence-java-client` (hopefully that's 📚 **New to contributing to Cadence?** Check out our [Contributing Guide](https://cadenceworkflow.io/community/how-to-contribute/getting-started) for an overview of the contribution process across all Cadence repositories. This document contains cadence-java-client specific setup and development instructions. -**Note:** All contributors also need to fill out the [Uber Contributor License Agreement](http://t.uber.com/cla) before we can merge in any of your changes. - Join our community on the CNCF Slack workspace at [cloud-native.slack.com](https://communityinviter.com/apps/cloud-native/cncf) in the **#cadence-users** channel to reach out and discuss issues with the team. ## Development Environment diff --git a/src/main/java/com/uber/cadence/serviceclient/TLSOptions.java b/src/main/java/com/uber/cadence/serviceclient/TLSOptions.java new file mode 100644 index 000000000..e4070c937 --- /dev/null +++ b/src/main/java/com/uber/cadence/serviceclient/TLSOptions.java @@ -0,0 +1,280 @@ +/* + * Modifications Copyright (c) 2017-2020 Uber Technologies Inc. + * Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. + * Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not + * use this file except in compliance with the License. A copy of the License is + * located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.uber.cadence.serviceclient; + +import io.grpc.netty.shaded.io.netty.handler.ssl.SslContext; +import java.io.File; +import java.io.InputStream; + +/** + * TLSOptions encapsulates TLS/SSL configuration for gRPC connections. + * + *

This class provides options for configuring secure connections to Cadence servers, including + * support for custom CA certificates, mutual TLS (mTLS) with client certificates, and custom SSL + * contexts. + * + *

Example usage: + * + *

{@code
+ * // Simple TLS with custom CA certificate
+ * TLSOptions tlsOptions = TLSOptions.newBuilder()
+ *     .setTrustManagerCertFile(new File("path/to/ca.crt"))
+ *     .build();
+ *
+ * // Mutual TLS with client certificate
+ * TLSOptions tlsOptions = TLSOptions.newBuilder()
+ *     .setTrustManagerCertFile(new File("path/to/ca.crt"))
+ *     .setClientCertFile(new File("path/to/client.crt"))
+ *     .setClientKeyFile(new File("path/to/client.key"))
+ *     .build();
+ *
+ * // Use with ClientOptions
+ * ClientOptions options = ClientOptions.newBuilder()
+ *     .setHost("cadence.example.com")
+ *     .setPort(443)
+ *     .setTLSOptions(tlsOptions)
+ *     .build();
+ * }
+ */ +public class TLSOptions { + + private final File trustManagerCertFile; + private final InputStream trustManagerCertInputStream; + private final File clientCertFile; + private final InputStream clientCertInputStream; + private final File clientKeyFile; + private final InputStream clientKeyInputStream; + private final String clientKeyPassword; + private final SslContext customSslContext; + private final boolean disableHostVerification; + + private TLSOptions(Builder builder) { + this.trustManagerCertFile = builder.trustManagerCertFile; + this.trustManagerCertInputStream = builder.trustManagerCertInputStream; + this.clientCertFile = builder.clientCertFile; + this.clientCertInputStream = builder.clientCertInputStream; + this.clientKeyFile = builder.clientKeyFile; + this.clientKeyInputStream = builder.clientKeyInputStream; + this.clientKeyPassword = builder.clientKeyPassword; + this.customSslContext = builder.customSslContext; + this.disableHostVerification = builder.disableHostVerification; + } + + public static Builder newBuilder() { + return new Builder(); + } + + /** @return The trust manager certificate file (CA certificate) */ + public File getTrustManagerCertFile() { + return trustManagerCertFile; + } + + /** @return The trust manager certificate input stream (CA certificate) */ + public InputStream getTrustManagerCertInputStream() { + return trustManagerCertInputStream; + } + + /** @return The client certificate file for mutual TLS */ + public File getClientCertFile() { + return clientCertFile; + } + + /** @return The client certificate input stream for mutual TLS */ + public InputStream getClientCertInputStream() { + return clientCertInputStream; + } + + /** @return The client private key file for mutual TLS */ + public File getClientKeyFile() { + return clientKeyFile; + } + + /** @return The client private key input stream for mutual TLS */ + public InputStream getClientKeyInputStream() { + return clientKeyInputStream; + } + + /** @return The password for the client private key, if encrypted */ + public String getClientKeyPassword() { + return clientKeyPassword; + } + + /** @return A custom SSL context, if provided */ + public SslContext getCustomSslContext() { + return customSslContext; + } + + /** @return Whether to disable host verification */ + public boolean isDisableHostVerification() { + return disableHostVerification; + } + + /** Builder for TLSOptions */ + public static class Builder { + + private File trustManagerCertFile; + private InputStream trustManagerCertInputStream; + private File clientCertFile; + private InputStream clientCertInputStream; + private File clientKeyFile; + private InputStream clientKeyInputStream; + private String clientKeyPassword; + private SslContext customSslContext; + private boolean disableHostVerification; + + private Builder() {} + + /** + * Sets the trust manager certificate file (CA certificate) to verify the server's certificate. + * + * @param trustManagerCertFile File containing the CA certificate in PEM format + * @return this Builder + */ + public Builder setTrustManagerCertFile(File trustManagerCertFile) { + this.trustManagerCertFile = trustManagerCertFile; + return this; + } + + /** + * Sets the trust manager certificate input stream (CA certificate) to verify the server's + * certificate. + * + * @param trustManagerCertInputStream InputStream containing the CA certificate in PEM format + * @return this Builder + */ + public Builder setTrustManagerCertInputStream(InputStream trustManagerCertInputStream) { + this.trustManagerCertInputStream = trustManagerCertInputStream; + return this; + } + + /** + * Sets the client certificate file for mutual TLS authentication. + * + * @param clientCertFile File containing the client certificate in PEM format + * @return this Builder + */ + public Builder setClientCertFile(File clientCertFile) { + this.clientCertFile = clientCertFile; + return this; + } + + /** + * Sets the client certificate input stream for mutual TLS authentication. + * + * @param clientCertInputStream InputStream containing the client certificate in PEM format + * @return this Builder + */ + public Builder setClientCertInputStream(InputStream clientCertInputStream) { + this.clientCertInputStream = clientCertInputStream; + return this; + } + + /** + * Sets the client private key file for mutual TLS authentication. + * + * @param clientKeyFile File containing the client private key in PEM format + * @return this Builder + */ + public Builder setClientKeyFile(File clientKeyFile) { + this.clientKeyFile = clientKeyFile; + return this; + } + + /** + * Sets the client private key input stream for mutual TLS authentication. + * + * @param clientKeyInputStream InputStream containing the client private key in PEM format + * @return this Builder + */ + public Builder setClientKeyInputStream(InputStream clientKeyInputStream) { + this.clientKeyInputStream = clientKeyInputStream; + return this; + } + + /** + * Sets the password for the client private key if it is encrypted. + * + * @param clientKeyPassword Password for the encrypted private key + * @return this Builder + */ + public Builder setClientKeyPassword(String clientKeyPassword) { + this.clientKeyPassword = clientKeyPassword; + return this; + } + + /** + * Sets a custom SSL context. If provided, this will be used instead of building one from + * certificate files. + * + * @param customSslContext Custom SSL context + * @return this Builder + */ + public Builder setCustomSslContext(SslContext customSslContext) { + this.customSslContext = customSslContext; + return this; + } + + /** + * Disables host verification. Use with caution - this reduces security by not verifying the + * server's hostname matches its certificate. + * + * @param disableHostVerification true to disable host verification + * @return this Builder + */ + public Builder setDisableHostVerification(boolean disableHostVerification) { + this.disableHostVerification = disableHostVerification; + return this; + } + + /** + * Builds the TLSOptions. + * + * @return TLSOptions instance + * @throws IllegalStateException if the configuration is invalid + */ + public TLSOptions build() { + // Validate configuration + if (customSslContext == null) { + if (trustManagerCertFile == null && trustManagerCertInputStream == null) { + throw new IllegalStateException( + "Either trustManagerCertFile, trustManagerCertInputStream, or customSslContext must be set"); + } + if (trustManagerCertFile != null && trustManagerCertInputStream != null) { + throw new IllegalStateException( + "Cannot set both trustManagerCertFile and trustManagerCertInputStream"); + } + // Validate mutual TLS configuration + boolean hasClientCert = clientCertFile != null || clientCertInputStream != null; + boolean hasClientKey = clientKeyFile != null || clientKeyInputStream != null; + if (hasClientCert != hasClientKey) { + throw new IllegalStateException( + "Both client certificate and client key must be provided for mutual TLS"); + } + if (clientCertFile != null && clientCertInputStream != null) { + throw new IllegalStateException( + "Cannot set both clientCertFile and clientCertInputStream"); + } + if (clientKeyFile != null && clientKeyInputStream != null) { + throw new IllegalStateException("Cannot set both clientKeyFile and clientKeyInputStream"); + } + } + + return new TLSOptions(this); + } + } +} diff --git a/src/test/java/com/uber/cadence/serviceclient/TLSOptionsTest.java b/src/test/java/com/uber/cadence/serviceclient/TLSOptionsTest.java new file mode 100644 index 000000000..a916d5ec1 --- /dev/null +++ b/src/test/java/com/uber/cadence/serviceclient/TLSOptionsTest.java @@ -0,0 +1,156 @@ +/* + * Modifications Copyright (c) 2017-2020 Uber Technologies Inc. + * Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not + * use this file except in compliance with the License. A copy of the License is + * located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.uber.cadence.serviceclient; + +import static org.junit.Assert.*; + +import io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts; +import io.grpc.netty.shaded.io.netty.handler.ssl.SslContext; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.InputStream; +import javax.net.ssl.SSLException; +import org.junit.Test; + +public class TLSOptionsTest { + + @Test + public void testBuilderWithTrustManagerFile() { + File caCert = new File("path/to/ca.crt"); + TLSOptions tlsOptions = TLSOptions.newBuilder().setTrustManagerCertFile(caCert).build(); + + assertEquals(caCert, tlsOptions.getTrustManagerCertFile()); + assertNull(tlsOptions.getTrustManagerCertInputStream()); + assertNull(tlsOptions.getClientCertFile()); + assertNull(tlsOptions.getClientKeyFile()); + assertFalse(tlsOptions.isDisableHostVerification()); + } + + @Test + public void testBuilderWithTrustManagerInputStream() { + InputStream caCertStream = new ByteArrayInputStream("test".getBytes()); + TLSOptions tlsOptions = + TLSOptions.newBuilder().setTrustManagerCertInputStream(caCertStream).build(); + + assertNull(tlsOptions.getTrustManagerCertFile()); + assertEquals(caCertStream, tlsOptions.getTrustManagerCertInputStream()); + } + + @Test + public void testBuilderWithMutualTLS() { + File caCert = new File("path/to/ca.crt"); + File clientCert = new File("path/to/client.crt"); + File clientKey = new File("path/to/client.key"); + + TLSOptions tlsOptions = + TLSOptions.newBuilder() + .setTrustManagerCertFile(caCert) + .setClientCertFile(clientCert) + .setClientKeyFile(clientKey) + .build(); + + assertEquals(caCert, tlsOptions.getTrustManagerCertFile()); + assertEquals(clientCert, tlsOptions.getClientCertFile()); + assertEquals(clientKey, tlsOptions.getClientKeyFile()); + assertNull(tlsOptions.getClientKeyPassword()); + } + + @Test + public void testBuilderWithEncryptedClientKey() { + File caCert = new File("path/to/ca.crt"); + File clientCert = new File("path/to/client.crt"); + File clientKey = new File("path/to/client.key"); + String password = "secret"; + + TLSOptions tlsOptions = + TLSOptions.newBuilder() + .setTrustManagerCertFile(caCert) + .setClientCertFile(clientCert) + .setClientKeyFile(clientKey) + .setClientKeyPassword(password) + .build(); + + assertEquals(password, tlsOptions.getClientKeyPassword()); + } + + @Test + public void testBuilderWithCustomSslContext() throws SSLException { + SslContext customContext = GrpcSslContexts.forClient().build(); + TLSOptions tlsOptions = TLSOptions.newBuilder().setCustomSslContext(customContext).build(); + + assertEquals(customContext, tlsOptions.getCustomSslContext()); + } + + @Test + public void testBuilderWithDisableHostVerification() { + File caCert = new File("path/to/ca.crt"); + TLSOptions tlsOptions = + TLSOptions.newBuilder() + .setTrustManagerCertFile(caCert) + .setDisableHostVerification(true) + .build(); + + assertTrue(tlsOptions.isDisableHostVerification()); + } + + @Test(expected = IllegalStateException.class) + public void testBuilderFailsWithoutTrustManager() { + TLSOptions.newBuilder().build(); + } + + @Test(expected = IllegalStateException.class) + public void testBuilderFailsWithBothTrustManagerFileAndStream() { + File caCert = new File("path/to/ca.crt"); + InputStream caCertStream = new ByteArrayInputStream("test".getBytes()); + + TLSOptions.newBuilder() + .setTrustManagerCertFile(caCert) + .setTrustManagerCertInputStream(caCertStream) + .build(); + } + + @Test(expected = IllegalStateException.class) + public void testBuilderFailsWithClientCertButNoKey() { + File caCert = new File("path/to/ca.crt"); + File clientCert = new File("path/to/client.crt"); + + TLSOptions.newBuilder().setTrustManagerCertFile(caCert).setClientCertFile(clientCert).build(); + } + + @Test(expected = IllegalStateException.class) + public void testBuilderFailsWithClientKeyButNoCert() { + File caCert = new File("path/to/ca.crt"); + File clientKey = new File("path/to/client.key"); + + TLSOptions.newBuilder().setTrustManagerCertFile(caCert).setClientKeyFile(clientKey).build(); + } + + @Test(expected = IllegalStateException.class) + public void testBuilderFailsWithBothClientCertFileAndStream() { + File caCert = new File("path/to/ca.crt"); + File clientCert = new File("path/to/client.crt"); + InputStream clientCertStream = new ByteArrayInputStream("test".getBytes()); + File clientKey = new File("path/to/client.key"); + + TLSOptions.newBuilder() + .setTrustManagerCertFile(caCert) + .setClientCertFile(clientCert) + .setClientCertInputStream(clientCertStream) + .setClientKeyFile(clientKey) + .build(); + } +} From 0acc9555f90936cd947bbdfb7a3e1390a4721675 Mon Sep 17 00:00:00 2001 From: "vpatil16@ext.uber.com" Date: Mon, 20 Oct 2025 15:52:13 -0700 Subject: [PATCH 4/5] Revert "accommodating review comments" This reverts commit aed6bd6948de7463b394b904ee305affb1ff6ca2. --- CONTRIBUTING.md | 2 + .../cadence/serviceclient/TLSOptions.java | 280 ------------------ .../cadence/serviceclient/TLSOptionsTest.java | 156 ---------- 3 files changed, 2 insertions(+), 436 deletions(-) delete mode 100644 src/main/java/com/uber/cadence/serviceclient/TLSOptions.java delete mode 100644 src/test/java/com/uber/cadence/serviceclient/TLSOptionsTest.java diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ac440b16c..c9152c8c5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,6 +4,8 @@ This doc is intended for contributors to `cadence-java-client` (hopefully that's 📚 **New to contributing to Cadence?** Check out our [Contributing Guide](https://cadenceworkflow.io/community/how-to-contribute/getting-started) for an overview of the contribution process across all Cadence repositories. This document contains cadence-java-client specific setup and development instructions. +**Note:** All contributors also need to fill out the [Uber Contributor License Agreement](http://t.uber.com/cla) before we can merge in any of your changes. + Join our community on the CNCF Slack workspace at [cloud-native.slack.com](https://communityinviter.com/apps/cloud-native/cncf) in the **#cadence-users** channel to reach out and discuss issues with the team. ## Development Environment diff --git a/src/main/java/com/uber/cadence/serviceclient/TLSOptions.java b/src/main/java/com/uber/cadence/serviceclient/TLSOptions.java deleted file mode 100644 index e4070c937..000000000 --- a/src/main/java/com/uber/cadence/serviceclient/TLSOptions.java +++ /dev/null @@ -1,280 +0,0 @@ -/* - * Modifications Copyright (c) 2017-2020 Uber Technologies Inc. - * Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. - * Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). You may not - * use this file except in compliance with the License. A copy of the License is - * located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package com.uber.cadence.serviceclient; - -import io.grpc.netty.shaded.io.netty.handler.ssl.SslContext; -import java.io.File; -import java.io.InputStream; - -/** - * TLSOptions encapsulates TLS/SSL configuration for gRPC connections. - * - *

This class provides options for configuring secure connections to Cadence servers, including - * support for custom CA certificates, mutual TLS (mTLS) with client certificates, and custom SSL - * contexts. - * - *

Example usage: - * - *

{@code
- * // Simple TLS with custom CA certificate
- * TLSOptions tlsOptions = TLSOptions.newBuilder()
- *     .setTrustManagerCertFile(new File("path/to/ca.crt"))
- *     .build();
- *
- * // Mutual TLS with client certificate
- * TLSOptions tlsOptions = TLSOptions.newBuilder()
- *     .setTrustManagerCertFile(new File("path/to/ca.crt"))
- *     .setClientCertFile(new File("path/to/client.crt"))
- *     .setClientKeyFile(new File("path/to/client.key"))
- *     .build();
- *
- * // Use with ClientOptions
- * ClientOptions options = ClientOptions.newBuilder()
- *     .setHost("cadence.example.com")
- *     .setPort(443)
- *     .setTLSOptions(tlsOptions)
- *     .build();
- * }
- */ -public class TLSOptions { - - private final File trustManagerCertFile; - private final InputStream trustManagerCertInputStream; - private final File clientCertFile; - private final InputStream clientCertInputStream; - private final File clientKeyFile; - private final InputStream clientKeyInputStream; - private final String clientKeyPassword; - private final SslContext customSslContext; - private final boolean disableHostVerification; - - private TLSOptions(Builder builder) { - this.trustManagerCertFile = builder.trustManagerCertFile; - this.trustManagerCertInputStream = builder.trustManagerCertInputStream; - this.clientCertFile = builder.clientCertFile; - this.clientCertInputStream = builder.clientCertInputStream; - this.clientKeyFile = builder.clientKeyFile; - this.clientKeyInputStream = builder.clientKeyInputStream; - this.clientKeyPassword = builder.clientKeyPassword; - this.customSslContext = builder.customSslContext; - this.disableHostVerification = builder.disableHostVerification; - } - - public static Builder newBuilder() { - return new Builder(); - } - - /** @return The trust manager certificate file (CA certificate) */ - public File getTrustManagerCertFile() { - return trustManagerCertFile; - } - - /** @return The trust manager certificate input stream (CA certificate) */ - public InputStream getTrustManagerCertInputStream() { - return trustManagerCertInputStream; - } - - /** @return The client certificate file for mutual TLS */ - public File getClientCertFile() { - return clientCertFile; - } - - /** @return The client certificate input stream for mutual TLS */ - public InputStream getClientCertInputStream() { - return clientCertInputStream; - } - - /** @return The client private key file for mutual TLS */ - public File getClientKeyFile() { - return clientKeyFile; - } - - /** @return The client private key input stream for mutual TLS */ - public InputStream getClientKeyInputStream() { - return clientKeyInputStream; - } - - /** @return The password for the client private key, if encrypted */ - public String getClientKeyPassword() { - return clientKeyPassword; - } - - /** @return A custom SSL context, if provided */ - public SslContext getCustomSslContext() { - return customSslContext; - } - - /** @return Whether to disable host verification */ - public boolean isDisableHostVerification() { - return disableHostVerification; - } - - /** Builder for TLSOptions */ - public static class Builder { - - private File trustManagerCertFile; - private InputStream trustManagerCertInputStream; - private File clientCertFile; - private InputStream clientCertInputStream; - private File clientKeyFile; - private InputStream clientKeyInputStream; - private String clientKeyPassword; - private SslContext customSslContext; - private boolean disableHostVerification; - - private Builder() {} - - /** - * Sets the trust manager certificate file (CA certificate) to verify the server's certificate. - * - * @param trustManagerCertFile File containing the CA certificate in PEM format - * @return this Builder - */ - public Builder setTrustManagerCertFile(File trustManagerCertFile) { - this.trustManagerCertFile = trustManagerCertFile; - return this; - } - - /** - * Sets the trust manager certificate input stream (CA certificate) to verify the server's - * certificate. - * - * @param trustManagerCertInputStream InputStream containing the CA certificate in PEM format - * @return this Builder - */ - public Builder setTrustManagerCertInputStream(InputStream trustManagerCertInputStream) { - this.trustManagerCertInputStream = trustManagerCertInputStream; - return this; - } - - /** - * Sets the client certificate file for mutual TLS authentication. - * - * @param clientCertFile File containing the client certificate in PEM format - * @return this Builder - */ - public Builder setClientCertFile(File clientCertFile) { - this.clientCertFile = clientCertFile; - return this; - } - - /** - * Sets the client certificate input stream for mutual TLS authentication. - * - * @param clientCertInputStream InputStream containing the client certificate in PEM format - * @return this Builder - */ - public Builder setClientCertInputStream(InputStream clientCertInputStream) { - this.clientCertInputStream = clientCertInputStream; - return this; - } - - /** - * Sets the client private key file for mutual TLS authentication. - * - * @param clientKeyFile File containing the client private key in PEM format - * @return this Builder - */ - public Builder setClientKeyFile(File clientKeyFile) { - this.clientKeyFile = clientKeyFile; - return this; - } - - /** - * Sets the client private key input stream for mutual TLS authentication. - * - * @param clientKeyInputStream InputStream containing the client private key in PEM format - * @return this Builder - */ - public Builder setClientKeyInputStream(InputStream clientKeyInputStream) { - this.clientKeyInputStream = clientKeyInputStream; - return this; - } - - /** - * Sets the password for the client private key if it is encrypted. - * - * @param clientKeyPassword Password for the encrypted private key - * @return this Builder - */ - public Builder setClientKeyPassword(String clientKeyPassword) { - this.clientKeyPassword = clientKeyPassword; - return this; - } - - /** - * Sets a custom SSL context. If provided, this will be used instead of building one from - * certificate files. - * - * @param customSslContext Custom SSL context - * @return this Builder - */ - public Builder setCustomSslContext(SslContext customSslContext) { - this.customSslContext = customSslContext; - return this; - } - - /** - * Disables host verification. Use with caution - this reduces security by not verifying the - * server's hostname matches its certificate. - * - * @param disableHostVerification true to disable host verification - * @return this Builder - */ - public Builder setDisableHostVerification(boolean disableHostVerification) { - this.disableHostVerification = disableHostVerification; - return this; - } - - /** - * Builds the TLSOptions. - * - * @return TLSOptions instance - * @throws IllegalStateException if the configuration is invalid - */ - public TLSOptions build() { - // Validate configuration - if (customSslContext == null) { - if (trustManagerCertFile == null && trustManagerCertInputStream == null) { - throw new IllegalStateException( - "Either trustManagerCertFile, trustManagerCertInputStream, or customSslContext must be set"); - } - if (trustManagerCertFile != null && trustManagerCertInputStream != null) { - throw new IllegalStateException( - "Cannot set both trustManagerCertFile and trustManagerCertInputStream"); - } - // Validate mutual TLS configuration - boolean hasClientCert = clientCertFile != null || clientCertInputStream != null; - boolean hasClientKey = clientKeyFile != null || clientKeyInputStream != null; - if (hasClientCert != hasClientKey) { - throw new IllegalStateException( - "Both client certificate and client key must be provided for mutual TLS"); - } - if (clientCertFile != null && clientCertInputStream != null) { - throw new IllegalStateException( - "Cannot set both clientCertFile and clientCertInputStream"); - } - if (clientKeyFile != null && clientKeyInputStream != null) { - throw new IllegalStateException("Cannot set both clientKeyFile and clientKeyInputStream"); - } - } - - return new TLSOptions(this); - } - } -} diff --git a/src/test/java/com/uber/cadence/serviceclient/TLSOptionsTest.java b/src/test/java/com/uber/cadence/serviceclient/TLSOptionsTest.java deleted file mode 100644 index a916d5ec1..000000000 --- a/src/test/java/com/uber/cadence/serviceclient/TLSOptionsTest.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Modifications Copyright (c) 2017-2020 Uber Technologies Inc. - * Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). You may not - * use this file except in compliance with the License. A copy of the License is - * located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package com.uber.cadence.serviceclient; - -import static org.junit.Assert.*; - -import io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts; -import io.grpc.netty.shaded.io.netty.handler.ssl.SslContext; -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.InputStream; -import javax.net.ssl.SSLException; -import org.junit.Test; - -public class TLSOptionsTest { - - @Test - public void testBuilderWithTrustManagerFile() { - File caCert = new File("path/to/ca.crt"); - TLSOptions tlsOptions = TLSOptions.newBuilder().setTrustManagerCertFile(caCert).build(); - - assertEquals(caCert, tlsOptions.getTrustManagerCertFile()); - assertNull(tlsOptions.getTrustManagerCertInputStream()); - assertNull(tlsOptions.getClientCertFile()); - assertNull(tlsOptions.getClientKeyFile()); - assertFalse(tlsOptions.isDisableHostVerification()); - } - - @Test - public void testBuilderWithTrustManagerInputStream() { - InputStream caCertStream = new ByteArrayInputStream("test".getBytes()); - TLSOptions tlsOptions = - TLSOptions.newBuilder().setTrustManagerCertInputStream(caCertStream).build(); - - assertNull(tlsOptions.getTrustManagerCertFile()); - assertEquals(caCertStream, tlsOptions.getTrustManagerCertInputStream()); - } - - @Test - public void testBuilderWithMutualTLS() { - File caCert = new File("path/to/ca.crt"); - File clientCert = new File("path/to/client.crt"); - File clientKey = new File("path/to/client.key"); - - TLSOptions tlsOptions = - TLSOptions.newBuilder() - .setTrustManagerCertFile(caCert) - .setClientCertFile(clientCert) - .setClientKeyFile(clientKey) - .build(); - - assertEquals(caCert, tlsOptions.getTrustManagerCertFile()); - assertEquals(clientCert, tlsOptions.getClientCertFile()); - assertEquals(clientKey, tlsOptions.getClientKeyFile()); - assertNull(tlsOptions.getClientKeyPassword()); - } - - @Test - public void testBuilderWithEncryptedClientKey() { - File caCert = new File("path/to/ca.crt"); - File clientCert = new File("path/to/client.crt"); - File clientKey = new File("path/to/client.key"); - String password = "secret"; - - TLSOptions tlsOptions = - TLSOptions.newBuilder() - .setTrustManagerCertFile(caCert) - .setClientCertFile(clientCert) - .setClientKeyFile(clientKey) - .setClientKeyPassword(password) - .build(); - - assertEquals(password, tlsOptions.getClientKeyPassword()); - } - - @Test - public void testBuilderWithCustomSslContext() throws SSLException { - SslContext customContext = GrpcSslContexts.forClient().build(); - TLSOptions tlsOptions = TLSOptions.newBuilder().setCustomSslContext(customContext).build(); - - assertEquals(customContext, tlsOptions.getCustomSslContext()); - } - - @Test - public void testBuilderWithDisableHostVerification() { - File caCert = new File("path/to/ca.crt"); - TLSOptions tlsOptions = - TLSOptions.newBuilder() - .setTrustManagerCertFile(caCert) - .setDisableHostVerification(true) - .build(); - - assertTrue(tlsOptions.isDisableHostVerification()); - } - - @Test(expected = IllegalStateException.class) - public void testBuilderFailsWithoutTrustManager() { - TLSOptions.newBuilder().build(); - } - - @Test(expected = IllegalStateException.class) - public void testBuilderFailsWithBothTrustManagerFileAndStream() { - File caCert = new File("path/to/ca.crt"); - InputStream caCertStream = new ByteArrayInputStream("test".getBytes()); - - TLSOptions.newBuilder() - .setTrustManagerCertFile(caCert) - .setTrustManagerCertInputStream(caCertStream) - .build(); - } - - @Test(expected = IllegalStateException.class) - public void testBuilderFailsWithClientCertButNoKey() { - File caCert = new File("path/to/ca.crt"); - File clientCert = new File("path/to/client.crt"); - - TLSOptions.newBuilder().setTrustManagerCertFile(caCert).setClientCertFile(clientCert).build(); - } - - @Test(expected = IllegalStateException.class) - public void testBuilderFailsWithClientKeyButNoCert() { - File caCert = new File("path/to/ca.crt"); - File clientKey = new File("path/to/client.key"); - - TLSOptions.newBuilder().setTrustManagerCertFile(caCert).setClientKeyFile(clientKey).build(); - } - - @Test(expected = IllegalStateException.class) - public void testBuilderFailsWithBothClientCertFileAndStream() { - File caCert = new File("path/to/ca.crt"); - File clientCert = new File("path/to/client.crt"); - InputStream clientCertStream = new ByteArrayInputStream("test".getBytes()); - File clientKey = new File("path/to/client.key"); - - TLSOptions.newBuilder() - .setTrustManagerCertFile(caCert) - .setClientCertFile(clientCert) - .setClientCertInputStream(clientCertStream) - .setClientKeyFile(clientKey) - .build(); - } -} From 7c5b6e3a6206b822930239294adb8a1eb173c5c5 Mon Sep 17 00:00:00 2001 From: "vpatil16@ext.uber.com" Date: Mon, 20 Oct 2025 15:53:58 -0700 Subject: [PATCH 5/5] accommodating review comments --- CONTRIBUTING.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c9152c8c5..ac440b16c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,8 +4,6 @@ This doc is intended for contributors to `cadence-java-client` (hopefully that's 📚 **New to contributing to Cadence?** Check out our [Contributing Guide](https://cadenceworkflow.io/community/how-to-contribute/getting-started) for an overview of the contribution process across all Cadence repositories. This document contains cadence-java-client specific setup and development instructions. -**Note:** All contributors also need to fill out the [Uber Contributor License Agreement](http://t.uber.com/cla) before we can merge in any of your changes. - Join our community on the CNCF Slack workspace at [cloud-native.slack.com](https://communityinviter.com/apps/cloud-native/cncf) in the **#cadence-users** channel to reach out and discuss issues with the team. ## Development Environment