From e3ecffe68c0f207f942450ca3f6e4e34e9513de4 Mon Sep 17 00:00:00 2001 From: uartnet <140632163+uartnet@users.noreply.github.com> Date: Fri, 9 Oct 2026 13:20:38 +0200 Subject: [PATCH] test(config): cover unsupported external mTLS signers --- docs/001-configuration.md | 8 +++- .../java/io/rstream/ConfigResolverTest.java | 45 +++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/docs/001-configuration.md b/docs/001-configuration.md index 0cb1c23..7766fb0 100644 --- a/docs/001-configuration.md +++ b/docs/001-configuration.md @@ -121,4 +121,10 @@ or credential storage settings instead of ignoring them: - Datagram tunnels. - SOCKS or custom proxy transport. - Custom DNS transport settings. -- Keychain, PKCS#11, or other external credential stores. +- Keychain, PKCS#11, external mTLS signers (`auth.mtls.storage.kind: exec`), + or other external credential stores. + +The `exec` backend is supported by the Go SDK and CLI. Selecting a context or +environment that uses it raises `ERR_RSTREAM_UNSUPPORTED_MTLS_STORAGE` before +connecting; this SDK never executes the configured helper. Other contexts in the +same configuration file remain usable. diff --git a/src/test/java/io/rstream/ConfigResolverTest.java b/src/test/java/io/rstream/ConfigResolverTest.java index 6a02b28..747507b 100644 --- a/src/test/java/io/rstream/ConfigResolverTest.java +++ b/src/test/java/io/rstream/ConfigResolverTest.java @@ -637,6 +637,51 @@ void invalidControlPlaneHeadersAreRejected() { } } + @Test + void externalMtlsSignerIsRejectedForTheSelectedContextOrEnvironment() throws Exception { + for (var environment : List.of(false, true)) { + var configPath = + config( + (environment + ? "environments:\n - apiUrl: https://rstream.io\n" + : "contexts:\n - name: external\n engine: engine.example:443\n") + + """ + auth: + mtls: + storage: + kind: exec + certificateSHA256: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef + exec: + command: /nonexistent/identity-helper + args: [--slot, device] + """ + + (environment + ? "contexts:\n - name: external\n apiUrl: https://rstream.io\n engine: engine.example:443\n" + : "") + + " - name: software\n apiUrl: https://other.example\n engine: software.example:443\n"); + assertThatThrownBy( + () -> + ConfigResolver.resolve( + ClientOptions.builder() + .configPath(configPath.toString()) + .context("external") + .build(), + Map.of())) + .isInstanceOf(UnsupportedFeatureException.class) + .hasMessage("mTLS storage provider 'exec' is not supported by rstream-java.") + .hasFieldOrPropertyWithValue("code", "ERR_RSTREAM_UNSUPPORTED_MTLS_STORAGE"); + var resolved = + ConfigResolver.resolve( + ClientOptions.builder() + .configPath(configPath.toString()) + .context("software") + .noToken(true) + .build(), + Map.of()); + assertThat(resolved.engine()).isEqualTo("software.example:443"); + } + } + private Path config(String content) throws Exception { var path = temp.resolve("config.yaml"); Files.writeString(path, content);