Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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