From 433ccd79ae7ea492962b8561514b9345b33166c2 Mon Sep 17 00:00:00 2001 From: Domenic Cassisi Date: Sat, 7 Feb 2026 13:00:38 +0100 Subject: [PATCH] Support TLS with default settings (CA) and without API key --- README.md | 41 +++++++++++++++---- .../io/umadb/client/UmaDbClientBuilder.java | 31 +++++++------- .../io/umadb/client/grpc/UmaDbClientImpl.java | 37 +++++++++++++---- .../umadb/client/UmaDbSecureClientTest.java | 4 +- 4 files changed, 80 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index c7aba3c..a027e98 100644 --- a/README.md +++ b/README.md @@ -130,25 +130,50 @@ public final class UmaDbExample { } ``` -### Using TLS and an API key +### Using TLS and API Key -For secured deployments (TLS + API key), the builder makes this explicit and safe: +To use a secured communication over TLS, simply enable TLS when building the UmaDbClient: ```java UmaDbClient client = UmaDbClient.builder() .withHost("localhost") .withPort(50051) - .withTlsAndApiKey( - "server.pem", // CA certificate - "umadb:example-api-key-123456789" // API key - ) + .withTlsEnabled() .build(); client.connect(); ``` -> ⚠️ Important: -> An API key requires TLS. The client will fail fast if an API key is provided without TLS. +You can also specify your own certificate authority like this (TLS will be automatically enabled): + +```java +UmaDbClient client = UmaDbClient.builder() + .withHost("localhost") + .withPort(50051) + .withCertificateAuthority("server.pem") + .build(); +``` + +For API key-protected servers, use the `withApiKey` when building the client: + +```java +UmaDbClient client = UmaDbClient.builder() + .withHost("localhost") + .withPort(50051) + .withApiKey("umadb:example-api-key-123456789") + .build(); +``` + +To specify both CA + API key, simply use the corresponding builder methods: + +```java +UmaDbClient client = UmaDbClient.builder() + .withHost("localhost") + .withPort(50051) + .withCertificateAuthority("server.pem") + .withApiKey("umadb:example-api-key-123456789") + .build(); +``` ### Conditional append (optimistic concurrency) diff --git a/src/main/java/io/umadb/client/UmaDbClientBuilder.java b/src/main/java/io/umadb/client/UmaDbClientBuilder.java index 9a88506..65f0a55 100644 --- a/src/main/java/io/umadb/client/UmaDbClientBuilder.java +++ b/src/main/java/io/umadb/client/UmaDbClientBuilder.java @@ -36,6 +36,7 @@ public final class UmaDbClientBuilder { private String host; private int port = -1; + private boolean tlsEnabled; private String caFilePath; private String apiKey; @@ -74,6 +75,16 @@ public UmaDbClientBuilder withPort(int port) { return this; } + /** + * Enables TLS with default settings. + * + * @return this builder instance + */ + public UmaDbClientBuilder withTlsEnabled() { + this.tlsEnabled = true; + return this; + } + /** * Enables TLS using a custom Certificate Authority (CA) certificate. *

@@ -84,7 +95,8 @@ public UmaDbClientBuilder withPort(int port) { * @param caFilePath path to the CA certificate file (PEM format) * @return this builder instance */ - public UmaDbClientBuilder withTls(String caFilePath) { + public UmaDbClientBuilder withCertificateAuthority(String caFilePath) { + this.tlsEnabled = true; this.caFilePath = caFilePath; return this; } @@ -97,26 +109,14 @@ public UmaDbClientBuilder withTls(String caFilePath) { *

* *

- * Note: TLS must be enabled when using an API key. + * Note: TLS is automatically enabled when using an API key. *

* * @param apiKey the API key to use for authentication * @return this builder instance */ public UmaDbClientBuilder withApiKey(String apiKey) { - this.apiKey = apiKey; - return this; - } - - /** - * Enables both TLS and API key authentication in a single call. - * - * @param caFilePath path to the CA certificate file (PEM format) - * @param apiKey the API key to use for authentication - * @return this builder instance - */ - public UmaDbClientBuilder withTlsAndApiKey(String caFilePath, String apiKey) { - this.caFilePath = caFilePath; + this.tlsEnabled = true; this.apiKey = apiKey; return this; } @@ -132,6 +132,7 @@ public UmaDbClient build() { return new UmaDbClientImpl( host, port, + tlsEnabled, caFilePath, apiKey ); diff --git a/src/main/java/io/umadb/client/grpc/UmaDbClientImpl.java b/src/main/java/io/umadb/client/grpc/UmaDbClientImpl.java index 2eaf2be..da0ecb3 100644 --- a/src/main/java/io/umadb/client/grpc/UmaDbClientImpl.java +++ b/src/main/java/io/umadb/client/grpc/UmaDbClientImpl.java @@ -45,6 +45,7 @@ public final class UmaDbClientImpl implements UmaDbClient { private final String host; private final int port; + private final boolean tlsEnabled; private final String optionalApiKey; private final Path optionalCaFilePath; @@ -59,11 +60,18 @@ public final class UmaDbClientImpl implements UmaDbClient { * * @param host UmaDB server host * @param port UmaDB server port + * @param tlsEnabled indicates whether an encrypted communication (TLS) is to be used * @param caFilePath optional path to a CA certificate for TLS * @param apiKey optional API key (requires TLS) * @throws IllegalArgumentException if arguments are invalid or insecure */ - public UmaDbClientImpl(String host, int port, String caFilePath, String apiKey) { + public UmaDbClientImpl( + String host, + int port, + boolean tlsEnabled, + String caFilePath, + String apiKey + ) { if (host == null) { throw new IllegalArgumentException("host must not be null"); } @@ -72,12 +80,17 @@ public UmaDbClientImpl(String host, int port, String caFilePath, String apiKey) } // Enforce security: API keys must never be sent over plaintext channels - if (apiKey != null && caFilePath == null) { - throw new IllegalArgumentException("TLS cert file must be defined when using API key"); + if (apiKey != null && !tlsEnabled) { + throw new IllegalArgumentException("TLS must be enabled when using API key"); + } + + if (!tlsEnabled && caFilePath != null) { + throw new IllegalArgumentException("TLS must be enabled when using custom CA"); } this.host = host; this.port = port; + this.tlsEnabled = tlsEnabled; this.optionalApiKey = apiKey; this.optionalCaFilePath = Optional.ofNullable(caFilePath).map(Path::of).orElse(null); } @@ -127,13 +140,21 @@ private List resolveClientInterceptors() { private ChannelCredentials resolveChannelCredentials() throws IOException { return isTlsEnabled() ? getTlsChannelCredentials() : - InsecureChannelCredentials.create(); + getInsecureChannelCredentials(); } private ChannelCredentials getTlsChannelCredentials() throws IOException { - return TlsChannelCredentials.newBuilder() - .trustManager(optionalCaFilePath.toFile()) - .build(); + if (optionalCaFilePath != null) { + return TlsChannelCredentials.newBuilder() + .trustManager(optionalCaFilePath.toFile()) + .build(); + } else { + return TlsChannelCredentials.create(); + } + } + + private ChannelCredentials getInsecureChannelCredentials() { + return InsecureChannelCredentials.create(); } @Override @@ -197,7 +218,7 @@ private static Optional extractErrorResponseFromMetadata(Me } private boolean isTlsEnabled() { - return this.optionalCaFilePath != null; + return this.tlsEnabled; } @Override diff --git a/src/test/java/io/umadb/client/UmaDbSecureClientTest.java b/src/test/java/io/umadb/client/UmaDbSecureClientTest.java index 2793e38..ef183e4 100644 --- a/src/test/java/io/umadb/client/UmaDbSecureClientTest.java +++ b/src/test/java/io/umadb/client/UmaDbSecureClientTest.java @@ -30,7 +30,7 @@ void testSecureConnectionWithProperlyConfiguredClient() { var client = UmaDbClient.builder() .withHost(SECURED_UMA_DB_CONTAINER.getHost()) .withPort(SECURED_UMA_DB_CONTAINER.getExposedGrpcPort()) - .withTls(CLASS_PATH_TLS_CERT) + .withCertificateAuthority(CLASS_PATH_TLS_CERT) .withApiKey(TEST_API_KEY) .build(); @@ -66,7 +66,7 @@ void testConnectWithSecureClientButNoApiKey() { var client = UmaDbClient.builder() .withHost(SECURED_UMA_DB_CONTAINER.getHost()) .withPort(SECURED_UMA_DB_CONTAINER.getExposedGrpcPort()) - .withTls(CLASS_PATH_TLS_CERT) + .withCertificateAuthority(CLASS_PATH_TLS_CERT) .build(); client.connect();