From dccf4cbe36d66140f9e35a6ee755c3c440d745f9 Mon Sep 17 00:00:00 2001 From: Sean Michael Wykes Date: Wed, 25 Aug 2021 09:25:12 -0300 Subject: feat(fetch): mTLS client certificates for fetch() (#11721) This commit adds support for specifying client certificates when using fetch, by means of `Deno.createHttpClient`. --- cli/dts/lib.deno.unstable.d.ts | 2 ++ cli/file_fetcher.rs | 1 + cli/http_util.rs | 17 +++++++-- cli/tests/unit/fetch_test.ts | 80 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 98 insertions(+), 2 deletions(-) (limited to 'cli') diff --git a/cli/dts/lib.deno.unstable.d.ts b/cli/dts/lib.deno.unstable.d.ts index 78f771ce1..ead4609c6 100644 --- a/cli/dts/lib.deno.unstable.d.ts +++ b/cli/dts/lib.deno.unstable.d.ts @@ -859,6 +859,8 @@ declare namespace Deno { */ caData?: string; proxy?: Proxy; + certChain?: string; + privateKey?: string; } export interface Proxy { diff --git a/cli/file_fetcher.rs b/cli/file_fetcher.rs index 8fda66382..a1729825f 100644 --- a/cli/file_fetcher.rs +++ b/cli/file_fetcher.rs @@ -237,6 +237,7 @@ impl FileFetcher { None, None, unsafely_ignore_certificate_errors, + None, )?, blob_store, }) diff --git a/cli/http_util.rs b/cli/http_util.rs index 46ec73cc7..61b1abcbe 100644 --- a/cli/http_util.rs +++ b/cli/http_util.rs @@ -144,8 +144,15 @@ mod tests { use std::fs::read; fn create_test_client(ca_data: Option>) -> Client { - create_http_client("test_client".to_string(), None, ca_data, None, None) - .unwrap() + create_http_client( + "test_client".to_string(), + None, + ca_data, + None, + None, + None, + ) + .unwrap() } #[tokio::test] @@ -340,6 +347,7 @@ mod tests { ), None, None, + None, ) .unwrap(); let result = fetch_once(FetchOnceArgs { @@ -370,6 +378,7 @@ mod tests { None, None, None, + None, ) .unwrap(); @@ -402,6 +411,7 @@ mod tests { None, None, None, + None, ) .unwrap(); @@ -440,6 +450,7 @@ mod tests { ), None, None, + None, ) .unwrap(); let result = fetch_once(FetchOnceArgs { @@ -480,6 +491,7 @@ mod tests { ), None, None, + None, ) .unwrap(); let result = fetch_once(FetchOnceArgs { @@ -533,6 +545,7 @@ mod tests { ), None, None, + None, ) .unwrap(); let result = fetch_once(FetchOnceArgs { diff --git a/cli/tests/unit/fetch_test.ts b/cli/tests/unit/fetch_test.ts index 6e2b1a5d6..ed384dd4f 100644 --- a/cli/tests/unit/fetch_test.ts +++ b/cli/tests/unit/fetch_test.ts @@ -1211,3 +1211,83 @@ unitTest( assertEquals(res.body, null); }, ); + +unitTest( + { perms: { read: true, net: true } }, + async function fetchClientCertWrongPrivateKey(): Promise { + await assertThrowsAsync(async () => { + const client = Deno.createHttpClient({ + certChain: "bad data", + privateKey: await Deno.readTextFile( + "cli/tests/testdata/tls/localhost.key", + ), + }); + await fetch("https://localhost:5552/fixture.json", { + client, + }); + }, Deno.errors.InvalidData); + }, +); + +unitTest( + { perms: { read: true, net: true } }, + async function fetchClientCertBadPrivateKey(): Promise { + await assertThrowsAsync(async () => { + const client = Deno.createHttpClient({ + certChain: await Deno.readTextFile( + "cli/tests/testdata/tls/localhost.crt", + ), + privateKey: "bad data", + }); + await fetch("https://localhost:5552/fixture.json", { + client, + }); + }, Deno.errors.InvalidData); + }, +); + +unitTest( + { perms: { read: true, net: true } }, + async function fetchClientCertNotPrivateKey(): Promise { + await assertThrowsAsync(async () => { + const client = Deno.createHttpClient({ + certChain: await Deno.readTextFile( + "cli/tests/testdata/tls/localhost.crt", + ), + privateKey: "", + }); + await fetch("https://localhost:5552/fixture.json", { + client, + }); + }, Deno.errors.InvalidData); + }, +); + +unitTest( + { perms: { read: true, net: true } }, + async function fetchCustomClientPrivateKey(): Promise< + void + > { + const data = "Hello World"; + const client = Deno.createHttpClient({ + certChain: await Deno.readTextFile( + "cli/tests/testdata/tls/localhost.crt", + ), + privateKey: await Deno.readTextFile( + "cli/tests/testdata/tls/localhost.key", + ), + caData: await Deno.readTextFile("cli/tests/testdata/tls/RootCA.crt"), + }); + const response = await fetch("https://localhost:5552/echo_server", { + client, + method: "POST", + body: new TextEncoder().encode(data), + }); + assertEquals( + response.headers.get("user-agent"), + `Deno/${Deno.version.deno}`, + ); + await response.text(); + client.close(); + }, +); -- cgit v1.2.3