Skip to content

Commit 28d6acd

Browse files
author
Adrian Gracia
committed
SQC-480 implement sslmode field for hyperdrive
1 parent 639c136 commit 28d6acd

File tree

3 files changed

+126
-12
lines changed

3 files changed

+126
-12
lines changed

packages/wrangler/src/__tests__/hyperdrive.test.ts

Lines changed: 95 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -628,13 +628,14 @@ describe("hyperdrive commands", () => {
628628
it("should create a hyperdrive with mtls config", async () => {
629629
const reqProm = mockHyperdriveCreate();
630630
await runWrangler(
631-
"hyperdrive create test123 --host=example.com --database=neondb --user=test --password=password --port=1234 --ca-certificate-id=12345 --mtls-certificate-id=1234"
631+
"hyperdrive create test123 --host=example.com --database=neondb --user=test --password=password --port=1234 --ca-certificate-id=12345 --mtls-certificate-id=1234 --sslmode=verify-full"
632632
);
633633
await expect(reqProm).resolves.toMatchInlineSnapshot(`
634634
Object {
635635
"mtls": Object {
636636
"ca_certificate_id": "12345",
637637
"mtls_certificate_id": "1234",
638+
"sslmode": "verify-full",
638639
},
639640
"name": "test123",
640641
"origin": Object {
@@ -663,20 +664,99 @@ describe("hyperdrive commands", () => {
663664
`);
664665
});
665666

667+
it("should create a hyperdrive with mtls config sslmode require", async () => {
668+
const reqProm = mockHyperdriveCreate();
669+
await runWrangler(
670+
"hyperdrive create test123 --host=example.com --database=neondb --user=test --password=password --port=1234 --mtls-certificate-id=1234 --sslmode=require"
671+
);
672+
expect(std.out).toMatchInlineSnapshot(`
673+
"🚧 Creating 'test123'
674+
✅ Created new Hyperdrive PostgreSQL config: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
675+
📋 To start using your config from a Worker, add the following binding configuration to your Wrangler configuration file:
676+
677+
{
678+
\\"hyperdrive\\": [
679+
{
680+
\\"binding\\": \\"HYPERDRIVE\\",
681+
\\"id\\": \\"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\\"
682+
}
683+
]
684+
}"
685+
`);
686+
});
687+
688+
it("should error a hyperdrive with mtls config sslmode require with CA", async () => {
689+
const reqProm = mockHyperdriveCreate();
690+
await expect(() =>
691+
runWrangler(
692+
"hyperdrive create test123 --host=example.com --database=neondb --user=test --password=password --port=1234 --ca-certificate-id=1234 --sslmode=require"
693+
)
694+
).rejects.toThrow();
695+
expect(std.err).toMatchInlineSnapshot(`
696+
"X [ERROR] CA not allowed when sslmode = 'require' is set
697+
698+
"
699+
`);
700+
});
701+
702+
it("should error a hyperdrive with mtls config sslmode verify-ca missing CA", async () => {
703+
const reqProm = mockHyperdriveCreate();
704+
await expect(() =>
705+
runWrangler(
706+
"hyperdrive create test123 --host=example.com --database=neondb --user=test --password=password --port=1234 --mtls-certificate-id=1234 --sslmode=verify-ca"
707+
)
708+
).rejects.toThrow();
709+
expect(std.err).toMatchInlineSnapshot(`
710+
"X [ERROR] CA required when sslmode = 'verify-ca' or 'verify-full' is set
711+
712+
"
713+
`);
714+
});
715+
716+
it("should error a hyperdrive with mtls config sslmode verify-full missing CA", async () => {
717+
const reqProm = mockHyperdriveCreate();
718+
await expect(() =>
719+
runWrangler(
720+
"hyperdrive create test123 --host=example.com --database=neondb --user=test --password=password --port=1234 --mtls-certificate-id=1234 --sslmode=verify-full"
721+
)
722+
).rejects.toThrow();
723+
expect(std.err).toMatchInlineSnapshot(`
724+
"X [ERROR] CA required when sslmode = 'verify-ca' or 'verify-full' is set
725+
726+
"
727+
`);
728+
});
729+
730+
it("should error a hyperdrive with mtls config sslmode random", async () => {
731+
const reqProm = mockHyperdriveCreate();
732+
await expect(() =>
733+
runWrangler(
734+
"hyperdrive create test123 --host=example.com --database=neondb --user=test --password=password --port=1234 --mtls-certificate-id=1234 --sslmode=random"
735+
)
736+
).rejects.toThrow();
737+
expect(std.err).toMatchInlineSnapshot(`
738+
"X [ERROR] Invalid values:
739+
740+
Argument: sslmode, Given: \\"random\\", Choices: \\"require\\", \\"verify-ca\\", \\"verify-full\\"
741+
742+
"
743+
`);
744+
});
745+
666746
it("should handle listing configs", async () => {
667747
mockHyperdriveGetListOrDelete();
668748
await runWrangler("hyperdrive list");
669749
expect(std.out).toMatchInlineSnapshot(`
670750
"📋 Listing Hyperdrive configs
671-
┌──────────────────────────────────────┬─────────────┬─────────┬────────────────┬──────┬────────────┬───────────┬──────────┬───────────────────────────────────────────────────────────┐
672-
│ id │ name │ user │ host │ port │ scheme │ database │ caching │ mtls │
673-
├──────────────────────────────────────┼─────────────┼─────────┼────────────────┼──────┼────────────┼───────────┼──────────┼───────────────────────────────────────────────────────────┤
674-
│ xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx │ test123 │ test │ example.com │ 5432 │ PostgreSQL │ neondb │ enabled │ │
675-
├──────────────────────────────────────┼─────────────┼─────────┼────────────────┼──────┼────────────┼───────────┼──────────┼───────────────────────────────────────────────────────────┤
676-
│ yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy │ new-db │ dbuser │ www.google.com │ 3211 │ PostgreSQL │ mydb │ disabled │ │
677-
├──────────────────────────────────────┼─────────────┼─────────┼────────────────┼──────┼────────────┼───────────┼──────────┼───────────────────────────────────────────────────────────┤
678-
│ zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz │ new-db-mtls │ pg-mtls │ www.mtls.com │ 3212 │ │ mydb-mtls │ enabled │ {\\"ca_certificate_id\\":\\"1234\\",\\"mtls_certificate_id\\":\\"1234\\"} │
679-
└──────────────────────────────────────┴─────────────┴─────────┴────────────────┴──────┴────────────┴───────────┴──────────┴───────────────────────────────────────────────────────────┘"
751+
┌──────────────────────────────────────┬─────────────┬─────────┬────────────────┬──────┬────────────┬───────────┬──────────┬───────────────────────────────────────────────────────────────────────────────────
752+
│ id │ name │ user │ host │ port │ scheme │ database │ caching │ mtls
753+
├──────────────────────────────────────┼─────────────┼─────────┼────────────────┼──────┼────────────┼───────────┼──────────┼───────────────────────────────────────────────────────────────────────────────────
754+
│ xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx │ test123 │ test │ example.com │ 5432 │ PostgreSQL │ neondb │ enabled │
755+
├──────────────────────────────────────┼─────────────┼─────────┼────────────────┼──────┼────────────┼───────────┼──────────┼───────────────────────────────────────────────────────────────────────────────────
756+
│ yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy │ new-db │ dbuser │ www.google.com │ 3211 │ PostgreSQL │ mydb │ disabled │
757+
├──────────────────────────────────────┼─────────────┼─────────┼────────────────┼──────┼────────────┼───────────┼──────────┼───────────────────────────────────────────────────────────────────────────────────
758+
│ zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz │ new-db-mtls │ pg-mtls │ www.mtls.com │ 3212 │ │ mydb-mtls │ enabled │ {\\"ca_certificate_id\\":\\"1234\\",\\"mtls_certificate_id\\":\\"1234\\",\\"sslmode\\":\\"verify-full\\"} │
759+
└──────────────────────────────────────┴─────────────┴─────────┴────────────────┴──────┴────────────┴───────────┴──────────┴───────────────────────────────────────────────────────────────────────────────────┘"
680760
`);
681761
});
682762

@@ -985,13 +1065,14 @@ describe("hyperdrive commands", () => {
9851065
it("should handle updating a hyperdrive config's mtls configuration", async () => {
9861066
const reqProm = mockHyperdriveUpdate();
9871067
await runWrangler(
988-
"hyperdrive update xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx --ca-certificate-id=2345 --mtls-certificate-id=234"
1068+
"hyperdrive update xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx --ca-certificate-id=2345 --mtls-certificate-id=234 --sslmode=verify-full"
9891069
);
9901070
await expect(reqProm).resolves.toMatchInlineSnapshot(`
9911071
Object {
9921072
"mtls": Object {
9931073
"ca_certificate_id": "2345",
9941074
"mtls_certificate_id": "234",
1075+
"sslmode": "verify-full",
9951076
},
9961077
}
9971078
`);
@@ -1010,7 +1091,8 @@ describe("hyperdrive commands", () => {
10101091
},
10111092
\\"mtls\\": {
10121093
\\"ca_certificate_id\\": \\"2345\\",
1013-
\\"mtls_certificate_id\\": \\"234\\"
1094+
\\"mtls_certificate_id\\": \\"234\\",
1095+
\\"sslmode\\": \\"verify-full\\"
10141096
}
10151097
}"
10161098
`);
@@ -1080,6 +1162,7 @@ function mockHyperdriveGetListOrDelete() {
10801162
mtls: {
10811163
ca_certificate_id: "1234",
10821164
mtls_certificate_id: "1234",
1165+
sslmode: "verify-full",
10831166
},
10841167
},
10851168
],

packages/wrangler/src/hyperdrive/client.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { fetchPagedListResult, fetchResult } from "../cfetch";
22
import { requireAuth } from "../user";
33
import type { Config } from "../config";
4+
import { array } from "yargs";
45

56
export type HyperdriveConfig = {
67
id: string;
@@ -87,8 +88,11 @@ export type PatchHyperdriveBody = {
8788
export type Mtls = {
8889
ca_certificate_id?: string;
8990
mtls_certificate_id?: string;
91+
sslmode?: string;
9092
};
9193

94+
export const Sslmode = ['require', 'verify-ca', 'verify-full'];
95+
9296
export async function createConfig(
9397
config: Config,
9498
body: CreateUpdateHyperdriveBody

packages/wrangler/src/hyperdrive/index.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import type {
1919
OriginWithSecretsPartial,
2020
} from "./client";
2121

22+
import { Sslmode } from "./client";
23+
2224
export function hyperdrive(yargs: CommonYargsArgv) {
2325
return yargs
2426
.command(
@@ -127,6 +129,12 @@ export function upsertOptions<T>(yargs: Argv<T>) {
127129
describe:
128130
"Sets custom mTLS client certificates when connecting to origin database. Must be valid UUID of already uploaded public/private key certificates.",
129131
},
132+
"sslmode": {
133+
type: "string",
134+
choices: ["require", "verify-ca", "verify-full"],
135+
describe:
136+
"Sets CA sslmode for connecting to database.",
137+
},
130138
})
131139
.group(
132140
["connection-string"],
@@ -323,11 +331,30 @@ export function getMtlsFromArgs(
323331
const mtls = {
324332
ca_certificate_id: args.caCertificateId,
325333
mtls_certificate_id: args.mtlsCertificateId,
334+
sslmode: args.sslmode,
326335
};
327336

328337
if (JSON.stringify(mtls) === "{}") {
329338
return undefined;
330339
} else {
340+
if(!!mtls.sslmode &&
341+
!Sslmode.includes(mtls.sslmode)) {
342+
throw new UserError(
343+
"sslmode must be one of following: 'require', 'verify-ca', or 'verify-full'"
344+
);
345+
}
346+
347+
if(mtls.sslmode == "require" && mtls.ca_certificate_id?.trim()) {
348+
throw new UserError(
349+
"CA not allowed when sslmode = 'require' is set"
350+
);
351+
}
352+
353+
if((mtls.sslmode == "verify-ca" || mtls.sslmode == "verify-full") && !mtls.ca_certificate_id?.trim()) {
354+
throw new UserError(
355+
"CA required when sslmode = 'verify-ca' or 'verify-full' is set"
356+
);
357+
}
331358
return mtls;
332359
}
333360
}

0 commit comments

Comments
 (0)