diff --git a/auth/identity.go b/auth/identity.go index b8d8b4a71..0e174bda1 100644 --- a/auth/identity.go +++ b/auth/identity.go @@ -16,7 +16,7 @@ type Identity interface { // IdentityResolver defines the interface through which an Identity is // retrieved. type IdentityResolver interface { - GetIdentity(ctx context.Context, params *smithy.Properties) (Identity, error) + GetIdentity(context.Context, smithy.Properties) (Identity, error) } // IdentityResolverOptions defines the interface through which an entity can be diff --git a/auth/option.go b/auth/option.go index bf442554e..d5dabff04 100644 --- a/auth/option.go +++ b/auth/option.go @@ -2,9 +2,24 @@ package auth import "github.com/aws/smithy-go" +type ( + authOptionsKey struct{} +) + // Option represents a possible authentication method for an operation. type Option struct { SchemeID string IdentityProperties smithy.Properties SignerProperties smithy.Properties } + +// GetAuthOptions gets auth Options from Properties. +func GetAuthOptions(p *smithy.Properties) ([]*Option, bool) { + v, ok := p.Get(authOptionsKey{}).([]*Option) + return v, ok +} + +// SetAuthOptions sets auth Options on Properties. +func SetAuthOptions(p *smithy.Properties, options []*Option) { + p.Set(authOptionsKey{}, options) +} diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoStackStepMiddlewareGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoStackStepMiddlewareGenerator.java index a53816e99..6a92f6a3d 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoStackStepMiddlewareGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoStackStepMiddlewareGenerator.java @@ -97,6 +97,22 @@ public static GoStackStepMiddlewareGenerator createSerializeStepMiddleware(Strin SymbolUtils.createValueSymbolBuilder("SerializeHandler", SmithyGoDependency.SMITHY_MIDDLEWARE).build()); } + /** + * Create a new FinalizeStep middleware generator with the provided type name. + * + * @param name is the type name to identify the middleware. + * @param id the unique ID for the middleware. + * @return the middleware generator. + */ + public static GoStackStepMiddlewareGenerator createFinalizeStepMiddleware(String name, MiddlewareIdentifier id) { + return createMiddleware(name, + id, + "HandleFinalize", + SmithyGoTypes.Middleware.FinalizeInput, + SmithyGoTypes.Middleware.FinalizeOutput, + SmithyGoTypes.Middleware.FinalizeHandler); + } + /** * Create a new DeserializeStep middleware generator with the provided type name. * @@ -216,6 +232,20 @@ public void writeMiddleware( }); } + /** + * Creates a Writable which renders the middleware. + * @param body A Writable that renders the middleware body. + * @param fields A Writable that renders the middleware struct's fields. + * @return the writable. + */ + public GoWriter.Writable asWritable(GoWriter.Writable body, GoWriter.Writable fields) { + return writer -> writeMiddleware( + writer, + (generator, bodyWriter) -> bodyWriter.write("$W", body), + (generator, fieldWriter) -> fieldWriter.write("$W", fields) + ); + } + /** * Returns a new middleware generator builder. * diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoWriter.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoWriter.java index 4b91ccf54..7c1d35b51 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoWriter.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoWriter.java @@ -46,6 +46,7 @@ import software.amazon.smithy.model.traits.RequiredTrait; import software.amazon.smithy.model.traits.StringTrait; import software.amazon.smithy.utils.AbstractCodeWriter; +import software.amazon.smithy.utils.ListUtils; import software.amazon.smithy.utils.StringUtils; /** @@ -998,6 +999,18 @@ public ChainWritable() { writables = new ArrayList<>(); } + public static ChainWritable of(GoWriter.Writable... writables) { + var chain = new ChainWritable(); + chain.writables.addAll(ListUtils.of(writables)); + return chain; + } + + public static ChainWritable of(List writables) { + var chain = new ChainWritable(); + chain.writables.addAll(writables); + return chain; + } + public boolean isEmpty() { return writables.isEmpty(); } @@ -1019,11 +1032,11 @@ public ChainWritable add(boolean include, GoWriter.Writable writable) { return this; } - public GoWriter.Writable compose() { + public GoWriter.Writable compose(boolean writeNewlines) { return (GoWriter writer) -> { var hasPrevious = false; for (GoWriter.Writable writable : writables) { - if (hasPrevious) { + if (hasPrevious && writeNewlines) { writer.write(""); } hasPrevious = true; @@ -1031,5 +1044,9 @@ public GoWriter.Writable compose() { } }; } + + public GoWriter.Writable compose() { + return compose(true); + } } } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/OperationGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/OperationGenerator.java index 7a2812130..3bde2401c 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/OperationGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/OperationGenerator.java @@ -22,6 +22,9 @@ import software.amazon.smithy.codegen.core.CodegenException; import software.amazon.smithy.codegen.core.Symbol; import software.amazon.smithy.codegen.core.SymbolProvider; +import software.amazon.smithy.go.codegen.auth.GetIdentityMiddlewareGenerator; +import software.amazon.smithy.go.codegen.auth.ResolveAuthSchemeMiddlewareGenerator; +import software.amazon.smithy.go.codegen.auth.SignRequestMiddlewareGenerator; import software.amazon.smithy.go.codegen.endpoints.EndpointParameterOperationBindingsGenerator; import software.amazon.smithy.go.codegen.integration.MiddlewareRegistrar; import software.amazon.smithy.go.codegen.integration.ProtocolGenerator; @@ -131,17 +134,8 @@ public void run() { .renderStructure(() -> { }, true); - writer.write(""" - func (*$T) operationName() string { - return $S - } - - $W - """, - inputSymbol, - operationSymbol.getName(), - new EndpointParameterOperationBindingsGenerator(operation, inputShape, inputSymbol).generate() - ); + writer.write("$W", + new EndpointParameterOperationBindingsGenerator(operation, inputShape, inputSymbol).generate()); // The output structure gets a metadata member added. Symbol metadataSymbol = SymbolUtils.createValueSymbolBuilder("Metadata", SmithyGoDependency.SMITHY_MIDDLEWARE) @@ -235,6 +229,12 @@ private void generateAddOperationMiddleware() { } }); + writer.write("$W", GoWriter.ChainWritable.of( + ResolveAuthSchemeMiddlewareGenerator.generateAddMiddleware(operationSymbol.getName()), + SignRequestMiddlewareGenerator.generateAddMiddleware(), + GetIdentityMiddlewareGenerator.generateAddMiddleware() + ).compose()); + writer.write("return nil"); }); } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java index 6a0454e08..4f84e2779 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java @@ -15,13 +15,18 @@ package software.amazon.smithy.go.codegen; +import static software.amazon.smithy.go.codegen.GoWriter.goTemplate; + import java.util.ArrayList; import java.util.Comparator; import java.util.List; +import java.util.Map; import java.util.function.Predicate; import java.util.stream.Collectors; import software.amazon.smithy.codegen.core.Symbol; import software.amazon.smithy.codegen.core.SymbolProvider; +import software.amazon.smithy.go.codegen.auth.AuthSchemeResolverGenerator; +import software.amazon.smithy.go.codegen.integration.AuthSchemeDefinition; import software.amazon.smithy.go.codegen.integration.ClientMember; import software.amazon.smithy.go.codegen.integration.ClientMemberResolver; import software.amazon.smithy.go.codegen.integration.ConfigField; @@ -29,7 +34,9 @@ import software.amazon.smithy.go.codegen.integration.GoIntegration; import software.amazon.smithy.go.codegen.integration.RuntimeClientPlugin; import software.amazon.smithy.model.Model; +import software.amazon.smithy.model.knowledge.ServiceIndex; import software.amazon.smithy.model.shapes.ServiceShape; +import software.amazon.smithy.model.shapes.ShapeId; /** * Generates a service client and configuration. @@ -46,6 +53,7 @@ final class ServiceGenerator implements Runnable { private final List integrations; private final List runtimePlugins; private final ApplicationProtocol applicationProtocol; + private final Map authSchemes; ServiceGenerator( GoSettings settings, @@ -65,6 +73,10 @@ final class ServiceGenerator implements Runnable { this.integrations = integrations; this.runtimePlugins = runtimePlugins; this.applicationProtocol = applicationProtocol; + this.authSchemes = integrations.stream() + .flatMap(it -> it.getClientPlugins(model, service).stream()) + .flatMap(it -> it.getAuthSchemeDefinitions().entrySet().stream()) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); } @Override @@ -96,6 +108,7 @@ public void run() { generateConstructor(serviceSymbol); generateConfig(); generateClientInvokeOperation(); + generateProtocolResolvers(); } private void writeClientMemberResolvers( @@ -153,6 +166,7 @@ private void generateConstructor(Symbol serviceSymbol) { resolver.getLocation() == ConfigFieldResolver.Location.CLIENT && resolver.getTarget() == ConfigFieldResolver.Target.INITIALIZATION); } + writeProtocolResolvers(); writer.openBlock("for _, fn := range optFns {", "}", () -> writer.write("fn(&options)")); writer.write(""); @@ -203,6 +217,8 @@ private void generateConfig() { generateApplicationProtocolConfig(); }).write(""); + generateGetIdentityResolver(); + writer.writeDocs("WithAPIOptions returns a functional option for setting the Client's APIOptions option."); writer.openBlock("func WithAPIOptions(optFns ...func(*middleware.Stack) error) func(*Options) {", "}", () -> { writer.openBlock("return func(o *Options) {", "}", () -> { @@ -284,6 +300,12 @@ private void generateApplicationProtocolConfig() { writer.writeDocs( "The HTTP client to invoke API calls with. Defaults to client's default HTTP implementation if nil."); writer.write("HTTPClient HTTPClient").write(""); + + writer.writeDocs("The auth scheme resolver which determines how to authenticate for each operation."); + writer.write("AuthSchemeResolver $L", AuthSchemeResolverGenerator.INTERFACE_NAME).write(""); + + writer.writeDocs("The list of auth schemes supported by the client."); + writer.write("AuthSchemes []$T", SmithyGoTypes.Transport.Http.AuthScheme).write(""); } private void generateApplicationProtocolTypes() { @@ -294,6 +316,71 @@ private void generateApplicationProtocolTypes() { }).write(""); } + private void writeProtocolResolvers() { + ensureSupportedProtocol(); + + writer.write(""" + resolveAuthSchemeResolver(&options) + + resolveAuthSchemes(&options) + """); + } + + private void generateProtocolResolvers() { + ensureSupportedProtocol(); + + var schemeMappings = GoWriter.ChainWritable.of( + ServiceIndex.of(model) + .getEffectiveAuthSchemes(service).keySet().stream() + .filter(authSchemes::containsKey) + .map(authSchemes::get) + .map(it -> goTemplate("$W, ", it.generateDefaultAuthScheme())) + .toList() + ).compose(false); + + writer.write(""" + func resolveAuthSchemeResolver(options *Options) { + options.AuthSchemeResolver = &$L{} + } + + func resolveAuthSchemes(options *Options) { + options.AuthSchemes = []$T{ + $W + } + } + """, + AuthSchemeResolverGenerator.DEFAULT_NAME, + SmithyGoTypes.Transport.Http.AuthScheme, + schemeMappings); + } + + private void generateGetIdentityResolver() { + var resolverMappings = GoWriter.ChainWritable.of( + ServiceIndex.of(model) + .getEffectiveAuthSchemes(service).keySet().stream() + .filter(authSchemes::containsKey) + .map(trait -> generateGetIdentityResolverMapping(trait, authSchemes.get(trait))) + .toList() + ); + + writer.write(""" + func (o $L) GetIdentityResolver(schemeID string) $T { + $W + return nil + } + """, + CONFIG_NAME, + SmithyGoTypes.Auth.IdentityResolver, + resolverMappings.compose(false)); + } + + private GoWriter.Writable generateGetIdentityResolverMapping(ShapeId schemeId, AuthSchemeDefinition scheme) { + return goTemplate(""" + if schemeID == $S { + return $W + }""", schemeId.toString(), scheme.generateOptionsIdentityResolver()); + } + private void generateClientInvokeOperation() { writer.addUseImports(SmithyGoDependency.CONTEXT); writer.addUseImports(SmithyGoDependency.SMITHY); diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoTypes.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoTypes.java index 7743a9360..9f01791ac 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoTypes.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoTypes.java @@ -35,16 +35,28 @@ public static final class Ptr { public static final class Middleware { public static final Symbol Stack = SmithyGoDependency.SMITHY_MIDDLEWARE.pointableSymbol("Stack"); + public static final Symbol Metadata = SmithyGoDependency.SMITHY_MIDDLEWARE.pointableSymbol("Metadata"); + public static final Symbol WithStackValue = SmithyGoDependency.SMITHY_MIDDLEWARE.valueSymbol("WithStackValue"); + public static final Symbol GetStackValue = SmithyGoDependency.SMITHY_MIDDLEWARE.valueSymbol("GetStackValue"); + public static final Symbol After = SmithyGoDependency.SMITHY_MIDDLEWARE.valueSymbol("After"); + public static final Symbol Before = SmithyGoDependency.SMITHY_MIDDLEWARE.valueSymbol("Before"); + public static final Symbol SerializeInput = SmithyGoDependency.SMITHY_MIDDLEWARE.pointableSymbol("SerializeInput"); public static final Symbol SerializeOutput = SmithyGoDependency.SMITHY_MIDDLEWARE.pointableSymbol("SerializeOutput"); public static final Symbol SerializeHandler = SmithyGoDependency.SMITHY_MIDDLEWARE.pointableSymbol("SerializeHandler"); - public static final Symbol Metadata = SmithyGoDependency.SMITHY_MIDDLEWARE.pointableSymbol("Metadata"); + public static final Symbol FinalizeInput = SmithyGoDependency.SMITHY_MIDDLEWARE.pointableSymbol("FinalizeInput"); + public static final Symbol FinalizeOutput = SmithyGoDependency.SMITHY_MIDDLEWARE.pointableSymbol("FinalizeOutput"); + public static final Symbol FinalizeHandler = SmithyGoDependency.SMITHY_MIDDLEWARE.pointableSymbol("FinalizeHandler"); } public static final class Transport { public static final class Http { public static final Symbol Request = SmithyGoDependency.SMITHY_HTTP_TRANSPORT.pointableSymbol("Request"); + public static final Symbol AuthScheme = SmithyGoDependency.SMITHY_HTTP_TRANSPORT.valueSymbol("AuthScheme"); + public static final Symbol SchemeIDAnonymous = SmithyGoDependency.SMITHY_HTTP_TRANSPORT.valueSymbol("SchemeIDAnonymous"); + public static final Symbol NewAnonymousScheme = SmithyGoDependency.SMITHY_HTTP_TRANSPORT.valueSymbol("NewAnonymousScheme"); + public static final Symbol NewSigV4Option = SmithyGoDependency.SMITHY_HTTP_TRANSPORT.valueSymbol("NewSigV4Option"); public static final Symbol NewSigV4AOption = SmithyGoDependency.SMITHY_HTTP_TRANSPORT.valueSymbol("NewSigV4AOption"); public static final Symbol NewBearerOption = SmithyGoDependency.SMITHY_HTTP_TRANSPORT.valueSymbol("NewBearerOption"); @@ -52,10 +64,27 @@ public static final class Http { public static final Symbol SigV4Properties = SmithyGoDependency.SMITHY_HTTP_TRANSPORT.pointableSymbol("SigV4Properties"); public static final Symbol SigV4AProperties = SmithyGoDependency.SMITHY_HTTP_TRANSPORT.pointableSymbol("SigV4AProperties"); + + public static final Symbol SetSigV4SigningName = SmithyGoDependency.SMITHY_HTTP_TRANSPORT.valueSymbol("SetSigV4SigningName"); + public static final Symbol SetSigV4SigningRegion = SmithyGoDependency.SMITHY_HTTP_TRANSPORT.valueSymbol("SetSigV4SigningRegion"); + public static final Symbol SetSigV4ASigningName = SmithyGoDependency.SMITHY_HTTP_TRANSPORT.valueSymbol("SetSigV4ASigningName"); + public static final Symbol SetSigV4ASigningRegions = SmithyGoDependency.SMITHY_HTTP_TRANSPORT.valueSymbol("SetSigV4ASigningRegions"); + public static final Symbol SetIsUnsignedPayload = SmithyGoDependency.SMITHY_HTTP_TRANSPORT.valueSymbol("SetIsUnsignedPayload"); + public static final Symbol SetDisableDoubleEncoding = SmithyGoDependency.SMITHY_HTTP_TRANSPORT.valueSymbol("SetDisableDoubleEncoding"); } } public static final class Auth { public static final Symbol Option = SmithyGoDependency.SMITHY_AUTH.pointableSymbol("Option"); + public static final Symbol IdentityResolver = SmithyGoDependency.SMITHY_AUTH.valueSymbol("IdentityResolver"); + public static final Symbol Identity = SmithyGoDependency.SMITHY_AUTH.valueSymbol("Identity"); + public static final Symbol GetAuthOptions = SmithyGoDependency.SMITHY_AUTH.valueSymbol("GetAuthOptions"); + public static final Symbol SetAuthOptions = SmithyGoDependency.SMITHY_AUTH.valueSymbol("SetAuthOptions"); + + public static final class Bearer { + public static final Symbol TokenProvider = SmithyGoDependency.SMITHY_AUTH_BEARER.valueSymbol("TokenProvider"); + public static final Symbol Signer = SmithyGoDependency.SMITHY_AUTH_BEARER.valueSymbol("Signer"); + public static final Symbol NewSignHTTPSMessage = SmithyGoDependency.SMITHY_AUTH_BEARER.valueSymbol("NewSignHTTPSMessage"); + } } } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/AuthGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/AuthGenerator.java index ba43f238a..e20c5bc0b 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/AuthGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/AuthGenerator.java @@ -34,11 +34,12 @@ public void generate() { } context.getWriter().get() - .write("$W", new AuthParametersGenerator(context).generate()) - .write("") - .write("$W", new AuthParametersResolverGenerator(context).generate()) - .write("") - .write("$W", getResolverGenerator().generate()); + .write("$W\n", new AuthParametersGenerator(context).generate()) + .write("$W\n", new AuthParametersResolverGenerator(context).generate()) + .write("$W\n", getResolverGenerator().generate()) + .write("$W\n", new ResolveAuthSchemeMiddlewareGenerator(context).generate()) + .write("$W\n", new GetIdentityMiddlewareGenerator(context).generate()) + .write("$W\n", new SignRequestMiddlewareGenerator(context).generate()); } // TODO(i&a): allow consuming generators to overwrite diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/AuthParametersResolverGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/AuthParametersResolverGenerator.java index 9b7452391..8dda1b970 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/AuthParametersResolverGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/AuthParametersResolverGenerator.java @@ -18,9 +18,7 @@ import static software.amazon.smithy.go.codegen.GoWriter.goTemplate; import java.util.ArrayList; -import software.amazon.smithy.codegen.core.Symbol; import software.amazon.smithy.go.codegen.GoWriter; -import software.amazon.smithy.go.codegen.SymbolUtils; import software.amazon.smithy.go.codegen.integration.ProtocolGenerator; import software.amazon.smithy.utils.MapUtils; @@ -32,8 +30,6 @@ public class AuthParametersResolverGenerator { public static final String FUNC_NAME = "bindAuthResolverParams"; - public static final Symbol FUNC_SYMBOL = SymbolUtils.createValueSymbolBuilder(FUNC_NAME).build(); - private final ProtocolGenerator.GenerationContext context; private final ArrayList resolvers = new ArrayList<>(); @@ -46,11 +42,9 @@ public GoWriter.Writable generate() { loadResolvers(); return goTemplate(""" - $operationNamer:W - - func $name:L(input interface{}, options Options) $params:P { + func $name:L(operation string, input interface{}, options Options) $params:P { params := &$params:T{ - Operation: input.(operationNamer).operationName(), + Operation: operation, } $bindings:W @@ -60,22 +54,11 @@ public GoWriter.Writable generate() { """, MapUtils.of( "name", FUNC_NAME, - "operationNamer", generateOperationNamer(), "params", AuthParametersGenerator.STRUCT_SYMBOL, "bindings", generateResolvers() )); } - private GoWriter.Writable generateOperationNamer() { - return (writer) -> { - writer.write(""" - type operationNamer interface { - operationName() string - } - """); - }; - } - private GoWriter.Writable generateResolvers() { return (writer) -> { for (var resolver: resolvers) { diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/GetIdentityMiddlewareGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/GetIdentityMiddlewareGenerator.java new file mode 100644 index 000000000..4d6f8fa44 --- /dev/null +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/GetIdentityMiddlewareGenerator.java @@ -0,0 +1,108 @@ +/* + * Copyright 2023 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.go.codegen.auth; + +import static software.amazon.smithy.go.codegen.GoStackStepMiddlewareGenerator.createFinalizeStepMiddleware; +import static software.amazon.smithy.go.codegen.GoWriter.goTemplate; + +import software.amazon.smithy.go.codegen.GoStdlibTypes; +import software.amazon.smithy.go.codegen.GoWriter; +import software.amazon.smithy.go.codegen.MiddlewareIdentifier; +import software.amazon.smithy.go.codegen.SmithyGoTypes; +import software.amazon.smithy.go.codegen.integration.ProtocolGenerator; +import software.amazon.smithy.utils.MapUtils; + +public class GetIdentityMiddlewareGenerator { + public static final String MIDDLEWARE_NAME = "getIdentityMiddleware"; + public static final String MIDDLEWARE_ID = "GetIdentity"; + + private final ProtocolGenerator.GenerationContext context; + + public GetIdentityMiddlewareGenerator(ProtocolGenerator.GenerationContext context) { + this.context = context; + } + + public static GoWriter.Writable generateAddMiddleware() { + return goTemplate(""" + err = stack.Finalize.Add(&$L{ + options: options, + }, $T) + if err != nil { + return err + } + """, MIDDLEWARE_NAME, SmithyGoTypes.Middleware.Before); + } + + public GoWriter.Writable generate() { + return GoWriter.ChainWritable.of( + createFinalizeStepMiddleware(MIDDLEWARE_NAME, MiddlewareIdentifier.string(MIDDLEWARE_ID)) + .asWritable(generateBody(), generateFields()), + generateContextFuncs() + ).compose(); + } + + private GoWriter.Writable generateFields() { + return goTemplate(""" + options Options + """); + } + + private GoWriter.Writable generateBody() { + return goTemplate(""" + rscheme := getResolvedAuthScheme(ctx) + if rscheme == nil { + return out, metadata, $errorf:T("no resolved auth scheme") + } + + resolver := rscheme.Scheme.IdentityResolver(m.options) + if resolver == nil { + return out, metadata, $errorf:T("no identity resolver") + } + + identity, err := resolver.GetIdentity(ctx, rscheme.IdentityProperties) + if err != nil { + return out, metadata, $errorf:T("get identity: %v", err) + } + + ctx = setIdentity(ctx, identity) + return next.HandleFinalize(ctx, in) + """, + MapUtils.of( + "errorf", GoStdlibTypes.Fmt.Errorf + )); + } + + private GoWriter.Writable generateContextFuncs() { + return goTemplate(""" + type identityKey struct{} + + func setIdentity(ctx $context:T, identity $identity:T) $context:T { + return $withStackValue:T(ctx, identityKey{}, identity) + } + + func getIdentity(ctx $context:T) $identity:T { + v, _ := $getStackValue:T(ctx, identityKey{}).($identity:T) + return v + } + """, + MapUtils.of( + "context", GoStdlibTypes.Context.Context, + "withStackValue", SmithyGoTypes.Middleware.WithStackValue, + "getStackValue", SmithyGoTypes.Middleware.GetStackValue, + "identity", SmithyGoTypes.Auth.Identity + )); + } +} diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/ResolveAuthSchemeMiddlewareGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/ResolveAuthSchemeMiddlewareGenerator.java new file mode 100644 index 000000000..18de4a725 --- /dev/null +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/ResolveAuthSchemeMiddlewareGenerator.java @@ -0,0 +1,168 @@ +/* + * Copyright 2023 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.go.codegen.auth; + +import static software.amazon.smithy.go.codegen.GoStackStepMiddlewareGenerator.createSerializeStepMiddleware; +import static software.amazon.smithy.go.codegen.GoWriter.goTemplate; + +import software.amazon.smithy.codegen.core.Symbol; +import software.amazon.smithy.go.codegen.GoStdlibTypes; +import software.amazon.smithy.go.codegen.GoWriter; +import software.amazon.smithy.go.codegen.MiddlewareIdentifier; +import software.amazon.smithy.go.codegen.SmithyGoTypes; +import software.amazon.smithy.go.codegen.endpoints.EndpointMiddlewareGenerator; +import software.amazon.smithy.go.codegen.integration.ProtocolGenerator; +import software.amazon.smithy.utils.MapUtils; + +public class ResolveAuthSchemeMiddlewareGenerator { + public static final String MIDDLEWARE_NAME = "resolveAuthSchemeMiddleware"; + public static final String MIDDLEWARE_ID = "ResolveAuthScheme"; + + private final ProtocolGenerator.GenerationContext context; + + public ResolveAuthSchemeMiddlewareGenerator(ProtocolGenerator.GenerationContext context) { + this.context = context; + } + + public static GoWriter.Writable generateAddMiddleware(String operation) { + return goTemplate(""" + err = stack.Serialize.Insert(&$name:L{ + operation: $operation:S, + options: options, + }, $resolveEndpointV2:S, $before:T) + if err != nil { + return err + } + """, + MapUtils.of( + "name", MIDDLEWARE_NAME, + "operation", operation, + "resolveEndpointV2", EndpointMiddlewareGenerator.MIDDLEWARE_ID, + "before", SmithyGoTypes.Middleware.Before + )); + } + + public GoWriter.Writable generate() { + return GoWriter.ChainWritable.of( + createSerializeStepMiddleware(MIDDLEWARE_NAME, MiddlewareIdentifier.string(MIDDLEWARE_ID)) + .asWritable(generateBody(), generateFields()), + generateSelectScheme(), + generateContextFuncs() + ).compose(); + } + + private GoWriter.Writable generateFields() { + return goTemplate(""" + operation string + options Options + """); + } + + private GoWriter.Writable generateBody() { + return goTemplate(""" + params := $1L(m.operation, in.Parameters, m.options) + options, err := m.options.AuthSchemeResolver.ResolveAuthSchemes(ctx, params) + if err != nil { + return out, metadata, $2T("resolve auth scheme: %v", err) + } + + scheme, ok := m.selectScheme(options) + if !ok { + return out, metadata, $2T("could not select an auth scheme") + } + + ctx = setResolvedAuthScheme(ctx, scheme) + return next.HandleSerialize(ctx, in) + """, + AuthParametersResolverGenerator.FUNC_NAME, + GoStdlibTypes.Fmt.Errorf + ); + } + + private GoWriter.Writable generateSelectScheme() { + return goTemplate(""" + func (m *$middlewareName:L) selectScheme(options []$option:P) (*resolvedAuthScheme, bool) { + for _, option := range options { + if option.SchemeID == $schemeIDAnonymous:T { + return newResolvedAuthScheme($newAnonymousScheme:T(), option), true + } + + for _, scheme := range m.options.AuthSchemes { + if scheme.SchemeID() != option.SchemeID { + continue + } + + if scheme.IdentityResolver(m.options) != nil { + return newResolvedAuthScheme(scheme, option), true + } + } + } + + return nil, false + } + """, + MapUtils.of( + "middlewareName", MIDDLEWARE_NAME, + "option", SmithyGoTypes.Auth.Option, + "schemeIDAnonymous", SmithyGoTypes.Transport.Http.SchemeIDAnonymous, + "newAnonymousScheme", SmithyGoTypes.Transport.Http.NewAnonymousScheme + ) + ); + } + + private GoWriter.Writable generateContextFuncs() { + return goTemplate(""" + type resolvedAuthSchemeKey struct{} + + type resolvedAuthScheme struct { + Scheme $authScheme:T + IdentityProperties $properties:T + SignerProperties $properties:T + } + + func newResolvedAuthScheme(scheme $authScheme:T, option $option:P) *resolvedAuthScheme { + return &resolvedAuthScheme{ + Scheme: scheme, + IdentityProperties: option.IdentityProperties, + SignerProperties: option.SignerProperties, + } + } + + func setResolvedAuthScheme(ctx $context:T, scheme *resolvedAuthScheme) $context:T { + return $withStackValue:T(ctx, resolvedAuthSchemeKey{}, scheme) + } + + func getResolvedAuthScheme(ctx $context:T) *resolvedAuthScheme { + v, _ := $getStackValue:T(ctx, resolvedAuthSchemeKey{}).(*resolvedAuthScheme) + return v + } + """, + MapUtils.of( + "authScheme", getAuthSchemeSymbol(), + "option", SmithyGoTypes.Auth.Option, + "properties", SmithyGoTypes.Smithy.Properties, + "context", GoStdlibTypes.Context.Context, + "withStackValue", SmithyGoTypes.Middleware.WithStackValue, + "getStackValue", SmithyGoTypes.Middleware.GetStackValue + )); + } + + // FUTURE(#458): when protocols are defined here, they should supply the auth scheme symbol, for + // now it's pinned to the HTTP variant + private Symbol getAuthSchemeSymbol() { + return SmithyGoTypes.Transport.Http.AuthScheme; + } +} diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/SignRequestMiddlewareGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/SignRequestMiddlewareGenerator.java new file mode 100644 index 000000000..e38da278e --- /dev/null +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/SignRequestMiddlewareGenerator.java @@ -0,0 +1,91 @@ +/* + * Copyright 2023 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.go.codegen.auth; + +import static software.amazon.smithy.go.codegen.GoStackStepMiddlewareGenerator.createFinalizeStepMiddleware; +import static software.amazon.smithy.go.codegen.GoWriter.emptyGoTemplate; +import static software.amazon.smithy.go.codegen.GoWriter.goTemplate; + +import software.amazon.smithy.go.codegen.GoStdlibTypes; +import software.amazon.smithy.go.codegen.GoWriter; +import software.amazon.smithy.go.codegen.MiddlewareIdentifier; +import software.amazon.smithy.go.codegen.SmithyGoTypes; +import software.amazon.smithy.go.codegen.integration.ProtocolGenerator; +import software.amazon.smithy.utils.MapUtils; + +public class SignRequestMiddlewareGenerator { + public static final String MIDDLEWARE_NAME = "signRequestMiddleware"; + public static final String MIDDLEWARE_ID = "Signing"; + + private final ProtocolGenerator.GenerationContext context; + + public SignRequestMiddlewareGenerator(ProtocolGenerator.GenerationContext context) { + this.context = context; + } + + public static GoWriter.Writable generateAddMiddleware() { + return goTemplate(""" + err = stack.Finalize.Add(&$L{}, $T) + if err != nil { + return err + } + """, MIDDLEWARE_NAME, SmithyGoTypes.Middleware.Before); + } + + public GoWriter.Writable generate() { + return createFinalizeStepMiddleware(MIDDLEWARE_NAME, MiddlewareIdentifier.string(MIDDLEWARE_ID)) + .asWritable(generateBody(), generateFields()); + } + + private GoWriter.Writable generateFields() { + return emptyGoTemplate(); + } + + private GoWriter.Writable generateBody() { + return goTemplate(""" + req, ok := in.Request.($request:P) + if !ok { + return out, metadata, $errorf:T("unexpected transport type %T", in.Request) + } + + rscheme := getResolvedAuthScheme(ctx) + if rscheme == nil { + return out, metadata, $errorf:T("no resolved auth scheme") + } + + identity := getIdentity(ctx) + if identity == nil { + return out, metadata, $errorf:T("no identity") + } + + signer := rscheme.Scheme.Signer() + if signer == nil { + return out, metadata, $errorf:T("no signer") + } + + if err := signer.SignRequest(ctx, req, identity, rscheme.SignerProperties); err != nil { + return out, metadata, $errorf:T("sign request: %v", err) + } + + return next.HandleFinalize(ctx, in) + """, + MapUtils.of( + // FUTURE(#458) protocol generator should specify the transport type + "request", SmithyGoTypes.Transport.Http.Request, + "errorf", GoStdlibTypes.Fmt.Errorf + )); + } +} diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/endpoints/EndpointMiddlewareGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/endpoints/EndpointMiddlewareGenerator.java index bcd99d9a1..cb21f5e77 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/endpoints/EndpointMiddlewareGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/endpoints/EndpointMiddlewareGenerator.java @@ -80,6 +80,8 @@ private void generateBody(GoStackStepMiddlewareGenerator generator, GoWriter wri $resolveEndpoint:W + $mergeAuthProperties:W + $post:W return next.HandleSerialize(ctx, in) @@ -89,6 +91,7 @@ private void generateBody(GoStackStepMiddlewareGenerator generator, GoWriter wri "assertRequest", generateAssertRequest(), "assertResolver", generateAssertResolver(), "resolveEndpoint", generateResolveEndpoint(), + "mergeAuthProperties", generateMergeAuthProperties(), "post", generatePostResolutionHooks() )); } @@ -123,7 +126,7 @@ private GoWriter.Writable generateAssertResolver() { private GoWriter.Writable generateResolveEndpoint() { return goTemplate(""" - params := bindEndpointParams(in.Parameters.(endpointParamsBinder), m.options) + params := bindEndpointParams(in.Parameters, m.options) resolvedEndpoint, err := m.options.EndpointResolverV2.ResolveEndpoint(ctx, *params) if err != nil { return out, metadata, $T("failed to resolve service endpoint, %w", err) @@ -137,6 +140,20 @@ private GoWriter.Writable generateResolveEndpoint() { GoStdlibTypes.Fmt.Errorf); } + private GoWriter.Writable generateMergeAuthProperties() { + return goTemplate(""" + rscheme := getResolvedAuthScheme(ctx) + if rscheme == nil { + return out, metadata, $T("no resolved auth scheme") + } + + opts, _ := $T(&resolvedEndpoint.Properties) + for _, o := range opts { + rscheme.SignerProperties.SetAll(&o.SignerProperties) + } + """, GoStdlibTypes.Fmt.Errorf, SmithyGoTypes.Auth.GetAuthOptions); + } + private GoWriter.Writable generatePostResolutionHooks() { return (GoWriter writer) -> { for (GoIntegration integration : context.getIntegrations()) { diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/endpoints/EndpointResolverGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/endpoints/EndpointResolverGenerator.java index 37bee724b..d2accd8bb 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/endpoints/EndpointResolverGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/endpoints/EndpointResolverGenerator.java @@ -32,9 +32,11 @@ import java.util.Optional; import java.util.TreeMap; import java.util.logging.Logger; +//import software.amazon.smithy.codegen.core.CodegenException; import software.amazon.smithy.codegen.core.Symbol; import software.amazon.smithy.go.codegen.GoWriter; import software.amazon.smithy.go.codegen.SmithyGoDependency; +import software.amazon.smithy.go.codegen.SmithyGoTypes; import software.amazon.smithy.go.codegen.SymbolUtils; import software.amazon.smithy.model.SourceException; import software.amazon.smithy.model.SourceLocation; @@ -49,6 +51,9 @@ import software.amazon.smithy.rulesengine.language.syntax.expressions.functions.FunctionDefinition; import software.amazon.smithy.rulesengine.language.syntax.expressions.functions.IsSet; import software.amazon.smithy.rulesengine.language.syntax.expressions.literal.Literal; +import software.amazon.smithy.rulesengine.language.syntax.expressions.literal.RecordLiteral; +import software.amazon.smithy.rulesengine.language.syntax.expressions.literal.StringLiteral; +import software.amazon.smithy.rulesengine.language.syntax.expressions.literal.TupleLiteral; import software.amazon.smithy.rulesengine.language.syntax.parameters.Parameter; import software.amazon.smithy.rulesengine.language.syntax.parameters.Parameters; import software.amazon.smithy.rulesengine.language.syntax.rule.Condition; @@ -91,6 +96,14 @@ private EndpointResolverGenerator(Builder builder) { "fmtErrorf", SymbolUtils.createValueSymbolBuilder("Errorf", SmithyGoDependency.FMT).build()); } + public static String mapEndpointPropertyAuthSchemeName(String name) { + return switch (name) { + case "sigv4" -> "aws.auth#sigv4"; + case "sigv4a" -> "aws.auth#sigv4a"; + default -> throw new IllegalStateException("Unexpected value: " + name); + }; + } + public GoWriter.Writable generate(Optional ruleset) { if (ruleset.isPresent()) { return generateResolverType(generateResolveMethodBody(ruleset.get())); @@ -422,37 +435,95 @@ private GoWriter.Writable generateNewHeaderValue(String headerName, List properties, Scope scope) { - Map propertyTypeArg = MapUtils.of( - "memberName", "Properties", - "propertyType", SymbolUtils.createValueSymbolBuilder("Properties", - SmithyGoDependency.SMITHY).build()); - if (properties.isEmpty()) { return emptyGoTemplate(); } - var writableProperties = new TreeMap(); var generator = new ExpressionGenerator(scope, this.fnProvider); - properties.forEach((k, v) -> { - writableProperties.put(k.toString(), generator.generate(v)); - }); + return goTemplate(""" + Properties: func() $1T { + var out $1T + $2W + return out + }(), + """, + SmithyGoTypes.Smithy.Properties, + GoWriter.ChainWritable.of( + properties.entrySet().stream() + .map(it -> generateSetProperty(generator, it.getKey(), it.getValue())) + .toList() + ).compose(false)); + } - return goBlockTemplate( - """ - $memberName:L: func() $propertyType:T{ - var out $propertyType:T - """, - """ - return out - }(), - """, propertyTypeArg, - (w) -> { - writableProperties.forEach((k, v) -> { - // TODO these properties should be typed, and ignore properties that are - // unknown. - w.write("out.Set($S, $W)", k, v); - }); - }); + private GoWriter.Writable generateSetProperty(ExpressionGenerator generator, Identifier ident, Expression expr) { + // FUTURE: add these via GoIntegration? + return ident.toString().equals("authSchemes") + ? generateSetAuthOptionsProperty(generator, expr) + : goTemplate("out.Set($S, $W)", ident.toString(), generator.generate(expr)); + } + + private GoWriter.Writable generateSetAuthOptionsProperty(ExpressionGenerator generator, Expression expr) { + return goTemplate(""" + $T(&out, []$P{ + $W + }) + """, + SmithyGoTypes.Auth.SetAuthOptions, + SmithyGoTypes.Auth.Option, + GoWriter.ChainWritable.of( + ((TupleLiteral) expr).members().stream() + .map(it -> generateAuthOption(generator, (RecordLiteral) it)) + .toList() + ).compose(false)); + } + + private GoWriter.Writable generateAuthOption(ExpressionGenerator generator, RecordLiteral scheme) { + var members = scheme.members(); + var schemeName = ((StringLiteral) members.get(Identifier.of("name"))).value().expectLiteral(); + return goTemplate(""" + { + SchemeID: $1S, + SignerProperties: func() $2T { + var sp $2T + $3W + return sp + }(), + },""", + mapEndpointPropertyAuthSchemeName(schemeName), + SmithyGoTypes.Smithy.Properties, + generateAuthOptionSignerProperties(generator, scheme)); + } + + private GoWriter.Writable generateAuthOptionSignerProperties(ExpressionGenerator generator, RecordLiteral scheme) { + var props = new GoWriter.ChainWritable(); + scheme.members().forEach((ident, expr) -> { + var name = ident.getName().expectStringNode().getValue(); + switch (name) { // properties that don't apply to the scheme would just be ignored by the signer impl. + case "signingName" -> props.add(goTemplate(""" + $1T(&sp, $3W) + $2T(&sp, $3W)""", + SmithyGoTypes.Transport.Http.SetSigV4SigningName, + SmithyGoTypes.Transport.Http.SetSigV4ASigningName, + generator.generate(expr))); + case "signingRegion" -> props.add(goTemplate("$T(&sp, $W)", + SmithyGoTypes.Transport.Http.SetSigV4SigningRegion, generator.generate(expr))); + case "signingRegionSet" -> { + var regions = GoWriter.ChainWritable.of( + ((TupleLiteral) expr).members().stream() + .map(generator::generate) + .toList() + ).compose(); + props.add(goTemplate("$T(&sp, []string{$W})", + SmithyGoTypes.Transport.Http.SetSigV4ASigningRegions, regions)); + } + case "disableDoubleEncoding" -> props.add(goTemplate("$T(&sp, $W)", + SmithyGoTypes.Transport.Http.SetDisableDoubleEncoding, generator.generate(expr))); + default -> { + return; + } + } + }); + return props.compose(); } class RuleVisitor implements RuleValueVisitor { diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/endpoints/ExpressionGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/endpoints/ExpressionGenerator.java index 7c9d33d9e..29e27d1d8 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/endpoints/ExpressionGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/endpoints/ExpressionGenerator.java @@ -21,7 +21,6 @@ import java.util.List; import java.util.Map; import java.util.function.Function; -import java.util.stream.Stream; import software.amazon.smithy.go.codegen.GoWriter; import software.amazon.smithy.go.codegen.SmithyGoDependency; import software.amazon.smithy.go.codegen.SymbolUtils; @@ -132,8 +131,9 @@ public GoWriter.Writable visitBoolean(boolean b) { @Override public GoWriter.Writable visitString(Template value) { - Stream parts = value.accept( - new TemplateGeneratorVisitor((expr) -> new ExpressionGenerator(scope, fnProvider).generate(expr))); + var parts = value.accept( + new TemplateGeneratorVisitor((expr) -> new ExpressionGenerator(scope, fnProvider).generate(expr)) + ).toList(); return (GoWriter w) -> { parts.forEach((p) -> w.write("$W", p)); diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/AuthSchemeDefinition.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/AuthSchemeDefinition.java index 56bfb75a9..015e1c69b 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/AuthSchemeDefinition.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/AuthSchemeDefinition.java @@ -17,6 +17,9 @@ package software.amazon.smithy.go.codegen.integration; +import static software.amazon.smithy.go.codegen.GoWriter.emptyGoTemplate; +import static software.amazon.smithy.go.codegen.GoWriter.goTemplate; + import software.amazon.smithy.go.codegen.GoWriter; import software.amazon.smithy.model.shapes.OperationShape; import software.amazon.smithy.model.shapes.ServiceShape; @@ -35,4 +38,19 @@ public interface AuthSchemeDefinition { * an operation with auth overrides. */ GoWriter.Writable generateOperationOption(ProtocolGenerator.GenerationContext context, OperationShape operation); + + /** + * Generates a default auth scheme. Called within a context where client Options are available. + */ + default GoWriter.Writable generateDefaultAuthScheme() { + return emptyGoTemplate(); + } + + /** + * Generates the value to return from Options.GetIdentityResolver(schemeID). Called within a context where client + * Options are available. + */ + default GoWriter.Writable generateOptionsIdentityResolver() { + return goTemplate("nil"); + } } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/auth/HttpBearerAuth.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/auth/HttpBearerAuth.java deleted file mode 100644 index 94c9cf66d..000000000 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/auth/HttpBearerAuth.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright 2020 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.go.codegen.integration.auth; - -import java.util.List; -import java.util.Map; -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.AuthSchemeDefinition; -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.knowledge.ServiceIndex; -import software.amazon.smithy.model.shapes.OperationShape; -import software.amazon.smithy.model.shapes.ServiceShape; -import software.amazon.smithy.model.shapes.ShapeId; -import software.amazon.smithy.model.traits.HttpBearerAuthTrait; -import software.amazon.smithy.model.traits.OptionalAuthTrait; -import software.amazon.smithy.model.traits.Trait; -import software.amazon.smithy.utils.ListUtils; - -/** - * Integration to add support for httpBearerAuth authentication scheme to an API client. - */ -public class HttpBearerAuth implements GoIntegration { - - public static final String TOKEN_PROVIDER_OPTION_NAME = "BearerAuthTokenProvider"; - private static final String SIGNER_OPTION_NAME = "BearerAuthSigner"; - private static final String NEW_DEFAULT_SIGNER_NAME = "newDefault" + SIGNER_OPTION_NAME; - private static final String SIGNER_RESOLVER_NAME = "resolve" + SIGNER_OPTION_NAME; - private static final String REGISTER_MIDDLEWARE_NAME = "add" + SIGNER_OPTION_NAME + "Middleware"; - private static final AuthSchemeDefinition HTTP_BEARER_DEFINITION = new HttpBearerDefinition(); - - @Override - public void writeAdditionalFiles( - GoSettings settings, - Model model, - SymbolProvider symbolProvider, - GoDelegator goDelegator - ) { - var service = settings.getService(model); - if (!isSupportedAuthentication(model, service)) { - return; - } - - goDelegator.useShapeWriter(service, (writer) -> { - writeMiddlewareRegister(writer); - writeSignerConfigFieldResolver(writer); - writeNewSignerFunc(writer); - }); - } - - private void writeMiddlewareRegister(GoWriter writer) { - writer.pushState(); - - writer.putContext("funcName", REGISTER_MIDDLEWARE_NAME); - writer.putContext("stack", SymbolUtils.createValueSymbolBuilder("Stack", - SmithyGoDependency.SMITHY_MIDDLEWARE).build()); - writer.putContext("addMiddleware", SymbolUtils.createValueSymbolBuilder("AddAuthenticationMiddleware", - SmithyGoDependency.SMITHY_AUTH_BEARER).build()); - writer.putContext("signerOption", SIGNER_OPTION_NAME); - writer.putContext("providerOption", TOKEN_PROVIDER_OPTION_NAME); - - writer.write(""" - func $funcName:L(stack *$stack:T, o Options) error { - return $addMiddleware:T(stack, o.$signerOption:L, o.$providerOption:L) - } - """); - - writer.popState(); - } - - private void writeSignerConfigFieldResolver(GoWriter writer) { - writer.pushState(); - - writer.putContext("funcName", SIGNER_RESOLVER_NAME); - writer.putContext("signerOption", SIGNER_OPTION_NAME); - writer.putContext("newDefaultSigner", NEW_DEFAULT_SIGNER_NAME); - - writer.write(""" - func $funcName:L(o *Options) { - if o.$signerOption:L != nil { - return - } - o.$signerOption:L = $newDefaultSigner:L(*o) - } - """); - - writer.popState(); - } - - private void writeNewSignerFunc(GoWriter writer) { - writer.pushState(); - - writer.putContext("funcName", NEW_DEFAULT_SIGNER_NAME); - writer.putContext("signerInterface", SymbolUtils.createValueSymbolBuilder("Signer", - SmithyGoDependency.SMITHY_AUTH_BEARER).build()); - - // TODO this is HTTP specific, should be based on protocol/transport of API. - writer.putContext("newDefaultSigner", SymbolUtils.createValueSymbolBuilder("NewSignHTTPSMessage", - SmithyGoDependency.SMITHY_AUTH_BEARER).build()); - - writer.write(""" - func $funcName:L(o Options) $signerInterface:T { - return $newDefaultSigner:T() - } - """); - - writer.popState(); - } - - @Override - public List getClientPlugins() { - return ListUtils.of( - RuntimeClientPlugin.builder() - .servicePredicate(HttpBearerAuth::isSupportedAuthentication) - .addConfigField(ConfigField.builder() - .name(TOKEN_PROVIDER_OPTION_NAME) - .type(SymbolUtils.createValueSymbolBuilder("TokenProvider", - SmithyGoDependency.SMITHY_AUTH_BEARER).build()) - .documentation("Bearer token value provider") - .build()) - .build(), - RuntimeClientPlugin.builder() - .servicePredicate(HttpBearerAuth::isSupportedAuthentication) - .addConfigField(ConfigField.builder() - .name(SIGNER_OPTION_NAME) - .type(SymbolUtils.createValueSymbolBuilder("Signer", - SmithyGoDependency.SMITHY_AUTH_BEARER).build()) - .documentation("Signer for authenticating requests with bearer auth") - .build()) - .addConfigFieldResolver(ConfigFieldResolver.builder() - .location(ConfigFieldResolver.Location.CLIENT) - .target(ConfigFieldResolver.Target.INITIALIZATION) - .resolver(SymbolUtils.createValueSymbolBuilder(SIGNER_RESOLVER_NAME).build()) - .build()) - .build(), - - // TODO this is incorrect for an API client/operation that supports multiple auth schemes. - RuntimeClientPlugin.builder() - .operationPredicate(HttpBearerAuth::hasBearerAuthScheme) - .registerMiddleware(MiddlewareRegistrar.builder() - .resolvedFunction(SymbolUtils.createValueSymbolBuilder( - REGISTER_MIDDLEWARE_NAME).build()) - .useClientOptions() - .build()) - .build(), - - RuntimeClientPlugin.builder() - .addAuthSchemeDefinition(HttpBearerAuthTrait.ID, HTTP_BEARER_DEFINITION) - .build() - ); - } - - /** - * Returns if the service has the httpBearerAuth trait. - * - * @param model model definition - * @param service service shape for the API - * @return if the httpBearerAuth trait is used by the service - */ - public static boolean isSupportedAuthentication(Model model, ServiceShape service) { - return ServiceIndex.of(model).getAuthSchemes(service).values().stream().anyMatch(trait -> trait.getClass() - .equals(HttpBearerAuthTrait.class)); - - } - - /** - * Returns if the service and operation support the httpBearerAuthTrait. - * - * @param model model definition - * @param service service shape for the API - * @param operation operation shape - * @return if the service and operation support the httpBearerAuthTrait - */ - public static boolean hasBearerAuthScheme(Model model, ServiceShape service, OperationShape operation) { - Map auth = ServiceIndex.of(model).getEffectiveAuthSchemes(service.getId(), operation.getId()); - return auth.containsKey(HttpBearerAuthTrait.ID) && !operation.hasTrait(OptionalAuthTrait.class); - } -} diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/auth/SigV4AuthScheme.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/auth/SigV4AuthScheme.java index 06dd77b42..087be72ab 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/auth/SigV4AuthScheme.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/auth/SigV4AuthScheme.java @@ -18,7 +18,6 @@ import java.util.List; import software.amazon.smithy.aws.traits.auth.SigV4Trait; import software.amazon.smithy.go.codegen.auth.AuthParameter; -import software.amazon.smithy.go.codegen.integration.AuthSchemeDefinition; import software.amazon.smithy.go.codegen.integration.GoIntegration; import software.amazon.smithy.go.codegen.integration.RuntimeClientPlugin; import software.amazon.smithy.model.Model; @@ -29,21 +28,18 @@ * Code generation for SigV4. */ public class SigV4AuthScheme implements GoIntegration { - private static final AuthSchemeDefinition SIGV4_DEFINITION = new SigV4Definition(); - private boolean isSigV4Service(Model model, ServiceShape service) { return service.hasTrait(SigV4Trait.class); } @Override public List getClientPlugins() { - // FUTURE: add default Region client option and resolver - we need a more structured way of suppressing - // elements of a GoIntegration before we do so, for now those live on the SDK side + // FUTURE: add default Region client option, scheme definition, and resolver - we need a more structured way of + // suppressing elements of a GoIntegration before we do so, for now those live on the SDK side return ListUtils.of( RuntimeClientPlugin.builder() .servicePredicate(this::isSigV4Service) .addAuthParameter(AuthParameter.REGION) - .addAuthSchemeDefinition(SigV4Trait.ID, SIGV4_DEFINITION) .build() ); } diff --git a/codegen/smithy-go-codegen/src/main/resources/META-INF/services/software.amazon.smithy.go.codegen.integration.GoIntegration b/codegen/smithy-go-codegen/src/main/resources/META-INF/services/software.amazon.smithy.go.codegen.integration.GoIntegration index ec8358730..af72bfc17 100644 --- a/codegen/smithy-go-codegen/src/main/resources/META-INF/services/software.amazon.smithy.go.codegen.integration.GoIntegration +++ b/codegen/smithy-go-codegen/src/main/resources/META-INF/services/software.amazon.smithy.go.codegen.integration.GoIntegration @@ -11,5 +11,4 @@ software.amazon.smithy.go.codegen.endpoints.EndpointClientPluginsGenerator # modeled auth schemes software.amazon.smithy.go.codegen.integration.auth.SigV4AuthScheme -software.amazon.smithy.go.codegen.integration.auth.HttpBearerAuth software.amazon.smithy.go.codegen.integration.auth.AnonymousAuthScheme diff --git a/properties.go b/properties.go index fb72c788f..df0465905 100644 --- a/properties.go +++ b/properties.go @@ -44,3 +44,11 @@ func (m *Properties) Has(key interface{}) bool { _, ok := m.values[key] return ok } + +// SetAll accepts all of the given Properties into the receiver, overwriting +// any existing keys in the case of conflicts. +func (m *Properties) SetAll(other *Properties) { + for k, v := range other.values { + m.values[k] = v + } +} diff --git a/transport/http/auth.go b/transport/http/auth.go index c37c7b68c..58e1ab5ef 100644 --- a/transport/http/auth.go +++ b/transport/http/auth.go @@ -2,7 +2,6 @@ package http import ( "context" - "net/http" smithy "github.com/aws/smithy-go" "github.com/aws/smithy-go/auth" @@ -18,5 +17,5 @@ type AuthScheme interface { // Signer defines the interface through which HTTP requests are supplemented // with an Identity. type Signer interface { - SignRequest(context.Context, *http.Request, auth.Identity, *smithy.Properties) error + SignRequest(context.Context, *Request, auth.Identity, smithy.Properties) error } diff --git a/transport/http/auth_schemes.go b/transport/http/auth_schemes.go index 34ace31fc..9ba157064 100644 --- a/transport/http/auth_schemes.go +++ b/transport/http/auth_schemes.go @@ -2,7 +2,6 @@ package http import ( "context" - "net/http" smithy "github.com/aws/smithy-go" "github.com/aws/smithy-go/auth" @@ -79,6 +78,6 @@ type nopSigner struct{} var _ Signer = (*nopSigner)(nil) -func (*nopSigner) SignRequest(context.Context, *http.Request, auth.Identity, *smithy.Properties) error { +func (*nopSigner) SignRequest(context.Context, *Request, auth.Identity, smithy.Properties) error { return nil } diff --git a/transport/http/properties.go b/transport/http/properties.go index 2608a779d..c65aa3932 100644 --- a/transport/http/properties.go +++ b/transport/http/properties.go @@ -2,67 +2,79 @@ package http import smithy "github.com/aws/smithy-go" -var ( +type ( sigV4SigningNameKey struct{} sigV4SigningRegionKey struct{} sigV4ASigningNameKey struct{} sigV4ASigningRegionsKey struct{} - isUnsignedPayloadKey struct{} + isUnsignedPayloadKey struct{} + disableDoubleEncodingKey struct{} ) // GetSigV4SigningName gets the signing name from Properties. func GetSigV4SigningName(p *smithy.Properties) (string, bool) { - v, ok := p.Get(sigV4SigningNameKey).(string) + v, ok := p.Get(sigV4SigningNameKey{}).(string) return v, ok } // SetSigV4SigningName sets the signing name on Properties. func SetSigV4SigningName(p *smithy.Properties, name string) { - p.Set(sigV4SigningNameKey, name) + p.Set(sigV4SigningNameKey{}, name) } // GetSigV4SigningRegion gets the signing region from Properties. func GetSigV4SigningRegion(p *smithy.Properties) (string, bool) { - v, ok := p.Get(sigV4SigningRegionKey).(string) + v, ok := p.Get(sigV4SigningRegionKey{}).(string) return v, ok } // SetSigV4SigningRegion sets the signing region on Properties. func SetSigV4SigningRegion(p *smithy.Properties, region string) { - p.Set(sigV4SigningRegionKey, region) + p.Set(sigV4SigningRegionKey{}, region) } // GetSigV4ASigningName gets the v4a signing name from Properties. func GetSigV4ASigningName(p *smithy.Properties) (string, bool) { - v, ok := p.Get(sigV4ASigningNameKey).(string) + v, ok := p.Get(sigV4ASigningNameKey{}).(string) return v, ok } // SetSigV4ASigningName sets the signing name on Properties. func SetSigV4ASigningName(p *smithy.Properties, name string) { - p.Set(sigV4ASigningNameKey, name) + p.Set(sigV4ASigningNameKey{}, name) } // GetSigV4ASigningRegion gets the v4a signing region set from Properties. func GetSigV4ASigningRegions(p *smithy.Properties) ([]string, bool) { - v, ok := p.Get(sigV4ASigningRegionsKey).([]string) + v, ok := p.Get(sigV4ASigningRegionsKey{}).([]string) return v, ok } // SetSigV4ASigningRegions sets the v4a signing region set on Properties. func SetSigV4ASigningRegions(p *smithy.Properties, regions []string) { - p.Set(sigV4ASigningRegionsKey, regions) + p.Set(sigV4ASigningRegionsKey{}, regions) } // GetIsUnsignedPayload gets whether the payload is unsigned from Properties. func GetIsUnsignedPayload(p *smithy.Properties) (bool, bool) { - v, ok := p.Get(isUnsignedPayloadKey).(bool) + v, ok := p.Get(isUnsignedPayloadKey{}).(bool) return v, ok } // SetIsUnsignedPayload sets whether the payload is unsigned on Properties. func SetIsUnsignedPayload(p *smithy.Properties, isUnsignedPayload bool) { - p.Set(isUnsignedPayloadKey, isUnsignedPayload) + p.Set(isUnsignedPayloadKey{}, isUnsignedPayload) +} + +// GetDisableDoubleEncoding gets whether the payload is unsigned from Properties. +func GetDisableDoubleEncoding(p *smithy.Properties) (bool, bool) { + v, ok := p.Get(disableDoubleEncodingKey{}).(bool) + return v, ok +} + +// SetDisableDoubleEncoding sets whether the payload is unsigned on Properties. +func SetDisableDoubleEncoding(p *smithy.Properties, disableDoubleEncoding bool) { + p.Set(disableDoubleEncodingKey{}, disableDoubleEncoding) }