diff --git a/.changeset/kind-cameras-crash.md b/.changeset/kind-cameras-crash.md new file mode 100644 index 00000000000..869cd74461e --- /dev/null +++ b/.changeset/kind-cameras-crash.md @@ -0,0 +1,5 @@ +--- +"@smithy/experimental-identity-and-auth": patch +--- + +Add `@aws.auth#sigv4` integration tests. diff --git a/packages/experimental-identity-and-auth/src/integration/sigv4.integ.spec.ts b/packages/experimental-identity-and-auth/src/integration/sigv4.integ.spec.ts new file mode 100644 index 00000000000..f93b359546b --- /dev/null +++ b/packages/experimental-identity-and-auth/src/integration/sigv4.integ.spec.ts @@ -0,0 +1,117 @@ +import { + OnlySigv4AuthCommand, + OnlySigv4AuthOptionalCommand, + SameAsServiceCommand, + Sigv4ServiceClient, +} from "@smithy/identity-and-auth-sigv4-service"; +import { AwsCredentialIdentity } from "@smithy/types"; +import { requireRequestsFrom } from "@smithy/util-test"; + +describe("@aws.auth#sigv4 integration tests", () => { + // TODO(experimentalIdentityAndAuth): should match `Sigv4Service` `@aws.auth#sigv4` trait + const MOCK_CREDENTIALS: AwsCredentialIdentity = { + accessKeyId: "MOCK_ACCESS_KEY_ID", + secretAccessKey: "SECRET_ACCESS_KEY", + sessionToken: "SESSION_TOKEN", + }; + const MOCK_REGION = "us-east-1"; + + // Arbitrary mock endpoint (`requireRequestsFrom()` intercepts network requests) + const MOCK_ENDPOINT = "https://foo.bar"; + + describe("`@aws.auth#sigv4` `region` configuration", () => { + it("Client should throw if `region` is not configured", async () => { + const client = new Sigv4ServiceClient({ + endpoint: MOCK_ENDPOINT, + }); + requireRequestsFrom(client).toMatch({}); + await expect(client.send(new OnlySigv4AuthOptionalCommand({}))).rejects.toThrow( + "expected `region` to be configured for `aws.auth#sigv4`" + ); + }); + + it("Client should NOT throw if `region` is configured", async () => { + const client = new Sigv4ServiceClient({ + endpoint: MOCK_ENDPOINT, + region: MOCK_REGION, + }); + requireRequestsFrom(client).toMatch({ + headers: { + Authorization: (value) => expect(value).toBeUndefined(), + }, + }); + await client.send(new OnlySigv4AuthOptionalCommand({})); + }); + }); + + describe("Operation requires `@aws.auth#sigv4`", () => { + it("Request is thrown when `credentials` is not configured", async () => { + const client = new Sigv4ServiceClient({ + endpoint: MOCK_ENDPOINT, + region: MOCK_REGION, + }); + requireRequestsFrom(client).toMatch({}); + await expect(client.send(new OnlySigv4AuthCommand({}))).rejects.toThrow( + "HttpAuthScheme `aws.auth#sigv4` did not have an IdentityProvider configured." + ); + }); + + it("Request is signed given configured `credentials`", async () => { + const client = new Sigv4ServiceClient({ + endpoint: MOCK_ENDPOINT, + region: MOCK_REGION, + credentials: async () => MOCK_CREDENTIALS, + }); + requireRequestsFrom(client).toMatch({}); + await client.send(new OnlySigv4AuthCommand({})); + }); + }); + + describe("Operation has `@aws.auth#sigv4` and `@optionalAuth`", () => { + it("Request is NOT thrown and NOT signed when `credentials` is not configured", async () => { + const client = new Sigv4ServiceClient({ + endpoint: MOCK_ENDPOINT, + region: MOCK_REGION, + }); + requireRequestsFrom(client).toMatch({ + headers: { + Authorization: (value) => expect(value).toBeUndefined(), + }, + }); + await client.send(new OnlySigv4AuthOptionalCommand({})); + }); + + it("Request is signed given configured `credentials`", async () => { + const client = new Sigv4ServiceClient({ + endpoint: MOCK_ENDPOINT, + region: MOCK_REGION, + credentials: async () => MOCK_CREDENTIALS, + }); + requireRequestsFrom(client).toMatch({}); + await client.send(new OnlySigv4AuthOptionalCommand({})); + }); + }); + + describe("Service has `@aws.auth#sigv4`", () => { + it("Request is thrown when `credentials` is not configured", async () => { + const client = new Sigv4ServiceClient({ + endpoint: MOCK_ENDPOINT, + region: MOCK_REGION, + }); + requireRequestsFrom(client).toMatch({}); + await expect(client.send(new SameAsServiceCommand({}))).rejects.toThrow( + "HttpAuthScheme `aws.auth#sigv4` did not have an IdentityProvider configured." + ); + }); + + it("Request is signed given configured `credentials`", async () => { + const client = new Sigv4ServiceClient({ + endpoint: MOCK_ENDPOINT, + region: MOCK_REGION, + credentials: async () => MOCK_CREDENTIALS, + }); + requireRequestsFrom(client).toMatch({}); + await client.send(new SameAsServiceCommand({})); + }); + }); +}); diff --git a/scripts/build-generated-test-packages.js b/scripts/build-generated-test-packages.js index 892d95e7986..fbaf7581975 100644 --- a/scripts/build-generated-test-packages.js +++ b/scripts/build-generated-test-packages.js @@ -29,6 +29,13 @@ const weatherSsdkDir = path.join( "typescript-ssdk-codegen" ) +// TODO(experimentalIdentityAndAuth): add `@aws.auth#sigv4` client for integration tests +const sigv4ClientDir = path.join( + codegenTestDir, + "identity-and-auth-sigv4", + "typescript-codegen" +); + const nodeModulesDir = path.join(root, "node_modules"); const buildAndCopyToNodeModules = async (packageName, codegenDir, nodeModulesDir) => { @@ -52,6 +59,8 @@ const buildAndCopyToNodeModules = async (packageName, codegenDir, nodeModulesDir try { await buildAndCopyToNodeModules("weather", weatherClientDir, nodeModulesDir); await buildAndCopyToNodeModules("weather-ssdk", weatherSsdkDir, nodeModulesDir); + // TODO(experimentalIdentityAndAuth): add `@aws.auth#sigv4` client for integration tests + await buildAndCopyToNodeModules("@smithy/identity-and-auth-sigv4-service", sigv4ClientDir, nodeModulesDir); } catch (e) { console.log(e); process.exit(1); diff --git a/smithy-typescript-codegen-test/model/identity-and-auth/sigv4/Sigv4Service.smithy b/smithy-typescript-codegen-test/model/identity-and-auth/sigv4/Sigv4Service.smithy new file mode 100644 index 00000000000..4ac2dca99f3 --- /dev/null +++ b/smithy-typescript-codegen-test/model/identity-and-auth/sigv4/Sigv4Service.smithy @@ -0,0 +1,28 @@ +$version: "2.0" + +namespace identity.auth.sigv4 + +use aws.auth#sigv4 +use common#fakeProtocol + +@fakeProtocol +@sigv4(name: "weather") +service Sigv4Service { + operations: [ + OnlySigv4Auth + OnlySigv4AuthOptional + SameAsService + ] +} + +@http(method: "GET", uri: "/OnlySigv4Auth") +@auth([sigv4]) +operation OnlySigv4Auth {} + +@http(method: "GET", uri: "/OnlySigv4AuthOptional") +@auth([sigv4]) +@optionalAuth +operation OnlySigv4AuthOptional {} + +@http(method: "GET", uri: "/SameAsService") +operation SameAsService {} diff --git a/smithy-typescript-codegen-test/smithy-build.json b/smithy-typescript-codegen-test/smithy-build.json index f2166e741ba..5dca0966f00 100644 --- a/smithy-typescript-codegen-test/smithy-build.json +++ b/smithy-typescript-codegen-test/smithy-build.json @@ -74,6 +74,29 @@ } } } + }, + "identity-and-auth-sigv4": { + "transforms": [ + { + "name": "includeServices", + "args": { + "services": ["identity.auth.sigv4#Sigv4Service"] + } + } + ], + "plugins": { + "typescript-codegen": { + "service": "identity.auth.sigv4#Sigv4Service", + "targetNamespace": "Sigv4Service", + "package": "@smithy/identity-and-auth-sigv4-service", + "packageVersion": "0.0.1", + "packageJson": { + "license": "Apache-2.0", + "private": true + }, + "experimentalIdentityAndAuth": true + } + } } }, "plugins": {