Skip to content
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

codegen: normalize adding of sigv4a config based on trait presence #2465

Merged
merged 1 commit into from
Jan 24, 2024
Merged
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
Original file line number Diff line number Diff line change
@@ -63,7 +63,7 @@ public List<RuntimeClientPlugin> getClientPlugins() {
// Add streaming events payload middleware to operation stack
RuntimeClientPlugin.builder()
.operationPredicate((model, service, operation) -> {
if (!AwsSignatureVersion4.hasSigV4AuthScheme(
if (!AwsSignatureVersion4.hasSigV4X(
model, service, operation)) {
return false;
}
@@ -79,7 +79,7 @@ public List<RuntimeClientPlugin> getClientPlugins() {
// Add unsigned payload middleware to operation stack
RuntimeClientPlugin.builder()
.operationPredicate((model, service, operation) -> {
if (!AwsSignatureVersion4.hasSigV4AuthScheme(
if (!AwsSignatureVersion4.hasSigV4X(
model, service, operation)) {
return false;
}
@@ -96,7 +96,7 @@ public List<RuntimeClientPlugin> getClientPlugins() {
// Add signed payload middleware to operation stack
RuntimeClientPlugin.builder()
.operationPredicate((model, service, operation) -> {
if (!AwsSignatureVersion4.hasSigV4AuthScheme(
if (!AwsSignatureVersion4.hasSigV4X(
model, service, operation)) {
return false;
}
@@ -113,7 +113,7 @@ public List<RuntimeClientPlugin> getClientPlugins() {
// Add content-sha256 payload header middleware to operation stack
RuntimeClientPlugin.builder()
.operationPredicate((model, service, operation) -> {
if (!AwsSignatureVersion4.hasSigV4AuthScheme(
if (!AwsSignatureVersion4.hasSigV4X(
model, service, operation)) {
return false;
}
Original file line number Diff line number Diff line change
@@ -17,6 +17,8 @@

import java.util.List;
import java.util.Map;

import software.amazon.smithy.aws.traits.auth.SigV4ATrait;
import software.amazon.smithy.aws.traits.auth.SigV4Trait;
import software.amazon.smithy.codegen.core.Symbol;
import software.amazon.smithy.codegen.core.SymbolProvider;
@@ -154,4 +156,10 @@ public static boolean hasSigV4AuthScheme(Model model, ServiceShape service, Oper
Map<ShapeId, Trait> auth = ServiceIndex.of(model).getEffectiveAuthSchemes(service.getId(), operation.getId());
return auth.containsKey(SigV4Trait.ID) && !operation.hasTrait(OptionalAuthTrait.class);
}

public static boolean hasSigV4X(Model model, ServiceShape service, OperationShape operation) {
var auth = ServiceIndex.of(model)
.getEffectiveAuthSchemes(service.getId(), operation.getId());
return auth.containsKey(SigV4Trait.ID) || auth.containsKey(SigV4ATrait.ID);
}
}
Original file line number Diff line number Diff line change
@@ -16,56 +16,20 @@
package software.amazon.smithy.aws.go.codegen;

import software.amazon.smithy.aws.go.codegen.customization.AwsCustomGoDependency;
import software.amazon.smithy.go.codegen.GoDependency;
import software.amazon.smithy.go.codegen.GoWriter;
import software.amazon.smithy.go.codegen.SmithyGoDependency;
import software.amazon.smithy.go.codegen.SymbolUtils;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.shapes.ServiceShape;

/**
* Generates Client Configuration, Middleware, and Config Resolvers for AWS Signature Version 4a support.
*/
public final class AwsSignatureVersion4aUtils {
public static final String RESOLVE_CREDENTIAL_PROVIDER = "resolveCredentialProvider";
public static final String REGISTER_MIDDLEWARE_FUNCTION = "swapWithCustomHTTPSignerMiddleware";
public static final String V4A_SIGNER_INTERFACE_NAME = "httpSignerV4a";
public static final String SIGNER_OPTION_FIELD_NAME = V4A_SIGNER_INTERFACE_NAME;
public static final String NEW_SIGNER_FUNC_NAME = "newDefaultV4aSigner";
public static final String SIGNER_RESOLVER = "resolveHTTPSignerV4a";

public static void writeCredentialProviderResolver(GoWriter writer) {
writer.pushState();
writer.putContext("resolverName", RESOLVE_CREDENTIAL_PROVIDER);
writer.putContext("fieldName", AddAwsConfigFields.CREDENTIALS_CONFIG_NAME);
writer.putContext("credType", SymbolUtils.createPointableSymbolBuilder("CredentialsProvider",
AwsCustomGoDependency.INTERNAL_SIGV4A).build());
writer.putContext("anonType", SymbolUtils.createPointableSymbolBuilder("AnonymousCredentials",
AwsCustomGoDependency.AWS_CORE).build());
writer.putContext("isProvider", SymbolUtils.createValueSymbolBuilder("IsCredentialsProvider",
AwsCustomGoDependency.AWS_CORE).build());
writer.putContext("adapType", SymbolUtils.createPointableSymbolBuilder("SymmetricCredentialAdaptor",
AwsCustomGoDependency.INTERNAL_SIGV4A).build());
writer.write("""
func $resolverName:L(o *Options) {
if o.$fieldName:L == nil {
return
}

if _, ok := o.$fieldName:L.($credType:T); ok {
return
}

if $isProvider:T(o.$fieldName:L, ($anonType:P)(nil)) {
return
}

o.$fieldName:L = &$adapType:T{SymmetricProvider: o.$fieldName:L}
}
""");
writer.popState();
}

public static void writerSignerInterface(GoWriter writer) {
writer.pushState();
writer.putContext("ifaceName", V4A_SIGNER_INTERFACE_NAME);
@@ -106,14 +70,6 @@ public static void writerConfigFieldResolver(GoWriter writer, ServiceShape servi
}

public static void writeNewV4ASignerFunc(GoWriter writer, ServiceShape serviceShape) {
writeNewV4ASignerFunc(writer, serviceShape, false);
}

public static void writeNewV4ASignerFunc(
GoWriter writer,
ServiceShape serviceShape,
boolean disableURIPathEscaping
) {
writer.pushState();
writer.putContext("funcName", NEW_SIGNER_FUNC_NAME);
writer.putContext("signerType", SymbolUtils.createPointableSymbolBuilder("Signer",
@@ -124,49 +80,12 @@ public static void writeNewV4ASignerFunc(
AwsCustomGoDependency.INTERNAL_SIGV4A).build());
writer.putContext("loggerField", AddAwsConfigFields.LOGGER_CONFIG_NAME);
writer.putContext("modeField", AddAwsConfigFields.LOG_MODE_CONFIG_NAME);
writer.putContext("disableEscape", disableURIPathEscaping);
writer.write("""
func $funcName:L(o Options) $signerType:P {
return $newSigner:T(func(so $signerOptions:P){
so.Logger = o.$loggerField:L
so.LogSigning = o.$modeField:L.IsSigning()
so.DisableURIPathEscaping = $disableEscape:L
})
}
""");
writer.popState();
}

public static void writeMiddlewareRegister(
Model model,
GoWriter writer,
ServiceShape serviceShape,
GoDependency signerMiddleware
) {
writer.pushState();
writer.putContext("funcName", REGISTER_MIDDLEWARE_FUNCTION);
writer.putContext("stackType", SymbolUtils.createPointableSymbolBuilder("Stack",
SmithyGoDependency.SMITHY_MIDDLEWARE).build());
writer.putContext("newMiddleware", SymbolUtils.createValueSymbolBuilder(
"NewSignHTTPRequestMiddleware", signerMiddleware).build());
writer.putContext("middleOptions", SymbolUtils.createValueSymbolBuilder(
"SignHTTPRequestMiddlewareOptions", signerMiddleware).build());
writer.putContext("registerMiddleware", SymbolUtils.createValueSymbolBuilder(
"RegisterSigningMiddleware", signerMiddleware).build());
writer.putContext("credFileName", AddAwsConfigFields.CREDENTIALS_CONFIG_NAME);
writer.putContext("v4Signer", AwsSignatureVersion4.SIGNER_CONFIG_FIELD_NAME);
writer.putContext("v4aSigner", SIGNER_OPTION_FIELD_NAME);
writer.putContext("logMode", AddAwsConfigFields.LOG_MODE_CONFIG_NAME);
writer.write("""
func $funcName:L(stack $stackType:P, o Options) error {
mw := $newMiddleware:T($middleOptions:T{
CredentialsProvider: o.$credFileName:L,
V4Signer: o.$v4Signer:L,
V4aSigner: o.$v4aSigner:L,
LogSigning: o.$logMode:L.IsSigning(),
})

return $registerMiddleware:T(stack, mw)
}
""");
writer.popState();
Original file line number Diff line number Diff line change
@@ -1,36 +1,39 @@
/*
* Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*
*
*/

package software.amazon.smithy.aws.go.codegen.customization;

import java.util.List;
import java.util.ArrayList;
import software.amazon.smithy.aws.go.codegen.AddAwsConfigFields;
import software.amazon.smithy.aws.go.codegen.AwsGoDependency;
import software.amazon.smithy.aws.go.codegen.AwsSignatureVersion4;
import software.amazon.smithy.aws.go.codegen.AwsSignatureVersion4aUtils;
import software.amazon.smithy.aws.traits.ServiceTrait;
import software.amazon.smithy.aws.traits.auth.SigV4ATrait;
import software.amazon.smithy.aws.traits.auth.SigV4Trait;
import software.amazon.smithy.codegen.core.Symbol;
import software.amazon.smithy.codegen.core.SymbolProvider;
import software.amazon.smithy.go.codegen.GoDelegator;
import software.amazon.smithy.go.codegen.GoSettings;
import software.amazon.smithy.go.codegen.GoWriter;
import software.amazon.smithy.go.codegen.SmithyGoDependency;
import software.amazon.smithy.go.codegen.SymbolUtils;
import software.amazon.smithy.go.codegen.integration.ConfigField;
import software.amazon.smithy.go.codegen.integration.ConfigFieldResolver;
import software.amazon.smithy.go.codegen.integration.GoIntegration;
import software.amazon.smithy.go.codegen.integration.MiddlewareRegistrar;
import software.amazon.smithy.go.codegen.integration.RuntimeClientPlugin;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.shapes.ServiceShape;
import software.amazon.smithy.model.traits.AuthTrait;
import software.amazon.smithy.utils.ListUtils;
import software.amazon.smithy.utils.SetUtils;

/**
* This integration configures the CloudFront Key Value Store client for Signature Version 4a
*/
public class CloudFrontKVSSigV4a implements GoIntegration {
// hardcoded from model so we don't have to extract it from whatever auth trait
private static final String SIGNING_NAME = "cloudfront-keyvaluestore";

/**
* Return true if service is CFKVS.
*
@@ -43,106 +46,23 @@ private static boolean isCFKVSService(Model model, ServiceShape service) {
return serviceId.equalsIgnoreCase("cloudfrontkeyvaluestore");
}

private final List<RuntimeClientPlugin> runtimeClientPlugins = new ArrayList<>();


@Override
public List<RuntimeClientPlugin> getClientPlugins() {
return runtimeClientPlugins;
}

@Override
public Model preprocessModel(Model model, GoSettings settings) {
ServiceShape service = settings.getService(model);
if (!isCFKVSService(model, service)) {
return model;
}

if (settings.getService(model).hasTrait(SigV4ATrait.class)) {
return model;
}

var v4a = SigV4ATrait.builder()
.name(service.expectTrait(SigV4Trait.class).getName())
.build();

// we MUST preserve the sigv4 trait as released since it affects the exported API
// (signer interface and config field)
return model.toBuilder()
.addShape(
service.toBuilder()
.addTrait(v4a)
// FUTURE: https://github.com/aws/smithy-go/issues/493
// we are keeping sigv4 at the end of this list (it will never be selected)
// as a stopgap to drive codegen of payload checksum routines
.addTrait(SigV4ATrait.builder().name(SIGNING_NAME).build())
.addTrait(SigV4Trait.builder().name(SIGNING_NAME).build())
.addTrait(new AuthTrait(SetUtils.of(SigV4ATrait.ID, SigV4Trait.ID)))
.build()
)
.build();
}

@Override
public void processFinalizedModel(GoSettings settings, Model model) {
if (!isCFKVSService(model, settings.getService(model))) {
return;
}
runtimeClientPlugins.add(
RuntimeClientPlugin.builder()
.configFields(
ListUtils.of(
ConfigField.builder()
.name(AwsSignatureVersion4aUtils.V4A_SIGNER_INTERFACE_NAME)
.type(SymbolUtils.buildPackageSymbol(
AwsSignatureVersion4aUtils.V4A_SIGNER_INTERFACE_NAME)
)
.documentation("Signature Version 4a (SigV4a) Signer")
.build()
)
)
.build());
runtimeClientPlugins.add(
RuntimeClientPlugin.builder()
.servicePredicate(CloudFrontKVSSigV4a::isCFKVSService)
.addConfigFieldResolver(
ConfigFieldResolver.builder()
.location(ConfigFieldResolver.Location.CLIENT)
.target(ConfigFieldResolver.Target.INITIALIZATION)
.resolver(SymbolUtils.createValueSymbolBuilder(
AwsSignatureVersion4aUtils.SIGNER_RESOLVER).build())
.build())
.build());
}

@Override
public void writeAdditionalFiles(
GoSettings settings,
Model model,
SymbolProvider symbolProvider,
GoDelegator goDelegator
) {

if (!isCFKVSService(model, model.expectShape(settings.getService(), ServiceShape.class))) {
return;
}

ServiceShape serviceShape = settings.getService(model);
goDelegator.useShapeWriter(serviceShape, writer -> {
writerSignerInterface(writer);
writerConfigFieldResolver(writer, serviceShape);
writeNewV4ASignerFunc(writer, serviceShape);
});

}


private void writerSignerInterface(GoWriter writer) {
AwsSignatureVersion4aUtils.writerSignerInterface(writer);
}

private void writeNewV4ASignerFunc(GoWriter writer, ServiceShape serviceShape) {
AwsSignatureVersion4aUtils.writeNewV4ASignerFunc(writer, serviceShape);
}

private void writerConfigFieldResolver(GoWriter writer, ServiceShape serviceShape) {
AwsSignatureVersion4aUtils.writerConfigFieldResolver(writer, serviceShape);
}

}
Loading

Unchanged files with check annotations Beta

// PEM encode CA certificate and private key
var caPEMBuf bytes.Buffer
pem.Encode(&caPEMBuf, &pem.Block{

Check failure on line 96 in internal/awstesting/certificate_utils.go

GitHub Actions / lint (1.20.x, ubuntu-latest)

Error return value of `pem.Encode` is not checked (errcheck)
Type: "CERTIFICATE",
Bytes: caBytes,
})
var caPrivKeyPEMBuf bytes.Buffer
pem.Encode(&caPrivKeyPEMBuf, &pem.Block{

Check failure on line 102 in internal/awstesting/certificate_utils.go

GitHub Actions / lint (1.20.x, ubuntu-latest)

Error return value of `pem.Encode` is not checked (errcheck)
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(caPrivKey),
})
// PEM encode certificate and private key
var certPEMBuf bytes.Buffer
pem.Encode(&certPEMBuf, &pem.Block{

Check failure on line 147 in internal/awstesting/certificate_utils.go

GitHub Actions / lint (1.20.x, ubuntu-latest)

Error return value of `pem.Encode` is not checked (errcheck)
Type: "CERTIFICATE",
Bytes: certBytes,
})
return "", err
}
defer bundleFile.Close()

Check failure on line 289 in internal/awstesting/certificate_utils.go

GitHub Actions / lint (1.20.x, ubuntu-latest)

Error return value of `bundleFile.Close` is not checked (errcheck)
return bundleFile.Name(), nil
}
originalEnv := os.Environ()
os.Clearenv() // clear env
for key, val := range extraEnv {
os.Setenv(key, val)

Check failure on line 109 in internal/awstesting/util.go

GitHub Actions / lint (1.20.x, ubuntu-latest)

Error return value of `os.Setenv` is not checked (errcheck)
}
return originalEnv
}
if len(p) > 1 {
v = p[1]
}
os.Setenv(k, v)

Check failure on line 126 in internal/awstesting/util.go

GitHub Actions / lint (1.20.x, ubuntu-latest)

Error return value of `os.Setenv` is not checked (errcheck)
}
}
return c.Creds(), nil
}))
p.Retrieve(context.Background())

Check failure on line 117 in aws/credential_cache_test.go

GitHub Actions / lint (1.20.x, ubuntu-latest)

Error return value of `p.Retrieve` is not checked (errcheck)
p.Retrieve(context.Background())

Check failure on line 118 in aws/credential_cache_test.go

GitHub Actions / lint (1.20.x, ubuntu-latest)

Error return value of `p.Retrieve` is not checked (errcheck)
p.Retrieve(context.Background())

Check failure on line 119 in aws/credential_cache_test.go

GitHub Actions / lint (1.20.x, ubuntu-latest)

Error return value of `p.Retrieve` is not checked (errcheck)
mockTime = mockTime.Add(10)
wg.Add(2)
for i := 0; i < 2; i++ {
go func() {
provider.Retrieve(context.Background())

Check failure on line 291 in aws/credential_cache_test.go

GitHub Actions / lint (1.20.x, ubuntu-latest)

Error return value of `provider.Retrieve` is not checked (errcheck)
wg.Done()
}()
}