Skip to content

Commit

Permalink
feat(experimentalIdentityAndAuth): customize @aws.auth#sigv4 identi…
Browse files Browse the repository at this point in the history
…ty providers for the AWS SDK (#5179)

* feat(experimentalIdentityAndAuth): customize `@aws.auth#sigv4` identity providers for the AWS SDK

Register `AwsCustomizeSigv4AuthPlugin` integration to customize
`@aws.auth#sigv4` to use:

- Browser: a function that throws an error saying `credentials` is
  missing
- Node.js: `decorateDefaultCredentialProvider(credentialDefaultProvider)` from
`@aws-sdk/credential-provider-node` and `aws-sdk/client-sts`.

* feat(experimentalIdentityAndAuth): copy smithy-typescript generic auth tests
  • Loading branch information
Steven Yuan authored Sep 8, 2023
1 parent d058d78 commit 53ef8f9
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 20 deletions.
1 change: 1 addition & 0 deletions codegen/generic-client-test-codegen/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ plugins {

dependencies {
implementation("software.amazon.smithy:smithy-aws-protocol-tests:$smithyVersion")
implementation("software.amazon.smithy:smithy-aws-traits:$smithyVersion")
implementation(project(":smithy-aws-typescript-codegen"))
}

Expand Down
84 changes: 84 additions & 0 deletions codegen/generic-client-test-codegen/model/weather.smithy
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
$version: "2.0"

namespace example.weather

use aws.auth#sigv4

@authDefinition
@trait
structure customAuth {}

@trait
@protocolDefinition
structure fakeProtocol {}

@fakeProtocol
@httpApiKeyAuth(name: "X-Api-Key", in: "header")
@httpBearerAuth
@sigv4(name: "weather")
@customAuth
@auth([sigv4])
service Weather {
version: "2006-03-01"
operations: [
// experimentalIdentityAndAuth
OnlyHttpApiKeyAuth
OnlyHttpApiKeyAuthOptional
OnlyHttpBearerAuth
OnlyHttpBearerAuthOptional
OnlyHttpApiKeyAndBearerAuth
OnlyHttpApiKeyAndBearerAuthReversed
OnlySigv4Auth
OnlySigv4AuthOptional
OnlyCustomAuth
OnlyCustomAuthOptional
SameAsService
]
}

@http(method: "GET", uri: "/OnlyHttpApiKeyAuth")
@auth([httpApiKeyAuth])
operation OnlyHttpApiKeyAuth {}

@http(method: "GET", uri: "/OnlyHttpBearerAuth")
@auth([httpBearerAuth])
operation OnlyHttpBearerAuth {}

@http(method: "GET", uri: "/OnlySigv4Auth")
@auth([sigv4])
operation OnlySigv4Auth {}

@http(method: "GET", uri: "/OnlyHttpApiKeyAndBearerAuth")
@auth([httpApiKeyAuth, httpBearerAuth])
operation OnlyHttpApiKeyAndBearerAuth {}

@http(method: "GET", uri: "/OnlyHttpApiKeyAndBearerAuthReversed")
@auth([httpBearerAuth, httpApiKeyAuth])
operation OnlyHttpApiKeyAndBearerAuthReversed {}

@http(method: "GET", uri: "/OnlyHttpApiKeyAuthOptional")
@auth([httpApiKeyAuth])
@optionalAuth
operation OnlyHttpApiKeyAuthOptional {}

@http(method: "GET", uri: "/OnlyHttpBearerAuthOptional")
@auth([httpBearerAuth])
@optionalAuth
operation OnlyHttpBearerAuthOptional {}

@http(method: "GET", uri: "/OnlySigv4AuthOptional")
@auth([sigv4])
@optionalAuth
operation OnlySigv4AuthOptional {}

@http(method: "GET", uri: "/OnlyCustomAuth")
@auth([customAuth])
operation OnlyCustomAuth {}

@http(method: "GET", uri: "/OnlyCustomAuthOptional")
@auth([customAuth])
@optionalAuth
operation OnlyCustomAuthOptional {}

@http(method: "GET", uri: "/SameAsService")
operation SameAsService {}
49 changes: 49 additions & 0 deletions codegen/generic-client-test-codegen/smithy-build.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,55 @@
"version": "1.0",
"imports": ["model/echo.smithy"],
"projections": {
"client-experimental-identity-and-auth": {
"transforms": [
{
"name": "includeServices",
"args": {
"services": ["example.weather#Weather"]
}
}
],
"plugins": {
"typescript-codegen": {
"package": "@aws-sdk/weather",
"packageVersion": "0.0.1",
"packageJson": {
"author": {
"name": "AWS SDK for JavaScript Team",
"url": "https://aws.amazon.com/javascript/"
},
"license": "Apache-2.0"
},
"private": true,
"experimentalIdentityAndAuth": true
}
}
},
"control-experimental-identity-and-auth": {
"transforms": [
{
"name": "includeServices",
"args": {
"services": ["example.weather#Weather"]
}
}
],
"plugins": {
"typescript-codegen": {
"package": "@aws-sdk/weather",
"packageVersion": "0.0.1",
"packageJson": {
"author": {
"name": "AWS SDK for JavaScript Team",
"url": "https://aws.amazon.com/javascript/"
},
"license": "Apache-2.0"
},
"private": true
}
}
},
"aws-echo-service": {
"transforms": [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@

/**
* Configure clients with AWS auth configurations and plugin.
*
* This is the existing control behavior for `experimentalIdentityAndAuth`.
*/
@SmithyInternalApi
public final class AddAwsAuthPlugin implements TypeScriptIntegration {
Expand All @@ -65,17 +67,21 @@ public final class AddAwsAuthPlugin implements TypeScriptIntegration {

private static final Logger LOGGER = Logger.getLogger(AddAwsAuthPlugin.class.getName());

/**
* Integration should only be used if `experimentalIdentityAndAuth` flag is false.
*/
@Override
public boolean matchesSettings(TypeScriptSettings settings) {
return !settings.getExperimentalIdentityAndAuth();
}

@Override
public void addConfigInterfaceFields(
TypeScriptSettings settings,
Model model,
SymbolProvider symbolProvider,
TypeScriptWriter writer
) {
if (settings.getExperimentalIdentityAndAuth()) {
return;
}
// feat(experimentalIdentityAndAuth): control branch for @aws.auth#sigv4
ServiceShape service = settings.getService(model);
if (!isSigV4Service(service) && isAwsService(service)) {
ServiceTrait serviceTrait = service.getTrait(ServiceTrait.class).get();
Expand Down Expand Up @@ -124,14 +130,12 @@ public List<RuntimeClientPlugin> getClientPlugins() {
&& isAwsService(s)
&& !testServiceId(s, "STS")
&& !areAllOptionalAuthOperations(m, s))
.settingsPredicate((m, s, settings) -> !settings.getExperimentalIdentityAndAuth())
.build(),
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.MIDDLEWARE_SIGNING.dependency, "SigV4Auth", HAS_CONFIG)
.servicePredicate((m, s) -> isSigV4Service(s)
&& !isAwsService(s)
&& !areAllOptionalAuthOperations(m, s))
.settingsPredicate((m, s, settings) -> !settings.getExperimentalIdentityAndAuth())
.build(),
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.STS_MIDDLEWARE.dependency,
Expand All @@ -140,7 +144,6 @@ && isAwsService(s)
put("stsClientCtor", Symbol.builder().name("STSClient").build());
}})
.servicePredicate((m, s) -> testServiceId(s, "STS"))
.settingsPredicate((m, s, settings) -> !settings.getExperimentalIdentityAndAuth())
.build(),
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.MIDDLEWARE_SIGNING.dependency, "AwsAuth", HAS_MIDDLEWARE)
Expand All @@ -149,29 +152,25 @@ && isAwsService(s)
&& isAwsService(s)
&& !testServiceId(s, "STS")
&& !hasOptionalAuthOperation(m, s))
.settingsPredicate((m, s, settings) -> !settings.getExperimentalIdentityAndAuth())
.build(),
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.MIDDLEWARE_SIGNING.dependency, "SigV4Auth", HAS_MIDDLEWARE)
// See operationUsesAwsAuth() below for AwsAuth Middleware customizations.
.servicePredicate((m, s) -> isSigV4Service(s)
&& !isAwsService(s)
&& !hasOptionalAuthOperation(m, s))
.settingsPredicate((m, s, settings) -> !settings.getExperimentalIdentityAndAuth())
.build(),
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.MIDDLEWARE_SIGNING.dependency, "AwsAuth", HAS_MIDDLEWARE)
.operationPredicate((m, s, o) -> isSigV4Service(s)
&& isAwsService(s)
&& operationUsesAwsAuth(m, s, o))
.settingsPredicate((m, s, settings) -> !settings.getExperimentalIdentityAndAuth())
.build(),
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.MIDDLEWARE_SIGNING.dependency, "SigV4Auth", HAS_MIDDLEWARE)
.operationPredicate((m, s, o) -> isSigV4Service(s)
&& !isAwsService(s)
&& operationUsesAwsAuth(m, s, o))
.settingsPredicate((m, s, settings) -> !settings.getExperimentalIdentityAndAuth())
.build()

);
Expand All @@ -184,10 +183,6 @@ public Map<String, Consumer<TypeScriptWriter>> getRuntimeConfigWriters(
SymbolProvider symbolProvider,
LanguageTarget target
) {
if (settings.getExperimentalIdentityAndAuth()) {
return Collections.emptyMap();
}
// feat(experimentalIdentityAndAuth): control branch for @aws.auth#sigv4
ServiceShape service = settings.getService(model);
if (!isSigV4Service(service) || areAllOptionalAuthOperations(model, service)) {
return Collections.emptyMap();
Expand Down Expand Up @@ -234,10 +229,6 @@ public Map<String, Consumer<TypeScriptWriter>> getRuntimeConfigWriters(

@Override
public void customize(TypeScriptCodegenContext codegenContext) {
if (codegenContext.settings().getExperimentalIdentityAndAuth()) {
return;
}
// feat(experimentalIdentityAndAuth): control branch for @aws.auth#sigv4
TypeScriptSettings settings = codegenContext.settings();
Model model = codegenContext.model();
BiConsumer<String, Consumer<TypeScriptWriter>> writerFactory = codegenContext.writerDelegator()::useFileWriter;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

package software.amazon.smithy.aws.typescript.codegen.auth.http.integration;

import java.util.List;
import software.amazon.smithy.aws.traits.auth.SigV4Trait;
import software.amazon.smithy.aws.typescript.codegen.AwsDependency;
import software.amazon.smithy.typescript.codegen.LanguageTarget;
import software.amazon.smithy.typescript.codegen.TypeScriptSettings;
import software.amazon.smithy.typescript.codegen.auth.http.HttpAuthScheme;
import software.amazon.smithy.typescript.codegen.auth.http.SupportedHttpAuthSchemesIndex;
import software.amazon.smithy.typescript.codegen.auth.http.integration.AddSigV4AuthPlugin;
import software.amazon.smithy.typescript.codegen.auth.http.integration.HttpAuthTypeScriptIntegration;
import software.amazon.smithy.utils.SmithyInternalApi;

/**
* Customize @aws.auth#sigv4 for AWS SDKs.
*
* This is the experimental behavior for `experimentalIdentityAndAuth`.
*/
@SmithyInternalApi
public class AwsCustomizeSigv4AuthPlugin implements HttpAuthTypeScriptIntegration {

/**
* Integration should only be used if `experimentalIdentityAndAuth` flag is true.
*/
@Override
public boolean matchesSettings(TypeScriptSettings settings) {
return settings.getExperimentalIdentityAndAuth();
}

/**
* Run after default AddSigV4AuthPlugin.
*/
@Override
public List<String> runAfter() {
return List.of(AddSigV4AuthPlugin.class.getCanonicalName());
}

@Override
public void customizeSupportedHttpAuthSchemes(SupportedHttpAuthSchemesIndex supportedHttpAuthSchemesIndex) {
HttpAuthScheme authScheme = supportedHttpAuthSchemesIndex.getHttpAuthScheme(SigV4Trait.ID).toBuilder()
// Current behavior of unconfigured `credentials` is to throw an error.
// This may need to be customized if a service is released with multiple auth schemes.
.putDefaultIdentityProvider(LanguageTarget.BROWSER, w ->
w.write("async () => { throw new Error(\"`credentials` is missing\"); }"))
// Use `@aws-sdk/credential-provider-node` with `@aws-sdk/client-sts` as the
// default identity provider chain for Node.js
.putDefaultIdentityProvider(LanguageTarget.NODE, w -> {
w.addDependency(AwsDependency.STS_CLIENT);
w.addImport("decorateDefaultCredentialProvider", null, AwsDependency.STS_CLIENT);
w.addDependency(AwsDependency.CREDENTIAL_PROVIDER_NODE);
w.addImport("defaultProvider", "credentialDefaultProvider",
AwsDependency.CREDENTIAL_PROVIDER_NODE);
w.write("decorateDefaultCredentialProvider(credentialDefaultProvider)");
})
.build();
supportedHttpAuthSchemesIndex.putHttpAuthScheme(authScheme.getSchemeId(), authScheme);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ software.amazon.smithy.aws.typescript.codegen.AddCrossRegionCopyingPlugin
software.amazon.smithy.aws.typescript.codegen.AddDocumentClientPlugin
software.amazon.smithy.aws.typescript.codegen.AddEndpointDiscoveryPlugin
software.amazon.smithy.aws.typescript.codegen.AddHttpChecksumDependency
software.amazon.smithy.aws.typescript.codegen.AddEventBridgePlugin
software.amazon.smithy.aws.typescript.codegen.AddEventBridgePlugin
software.amazon.smithy.aws.typescript.codegen.auth.http.integration.AwsCustomizeSigv4AuthPlugin

0 comments on commit 53ef8f9

Please sign in to comment.