Skip to content

Commit dbae11e

Browse files
Decouple auth from http
This decouples auth from HTTP, allowing it to be defined centrally. A number of changes have been made to various interfaces. Notably identity resolvers are generally expected to have zero-arg constructors, instead getting everything they need from their properties where possible. Construction of event signers has been moved into AuthScheme. Right now it takes the entire context of the request during construction, but we should consider passing identity and signing properties in at signing time like normal signers so that they can be reused.
1 parent 6a85f06 commit dbae11e

File tree

43 files changed

+1155
-844
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1155
-844
lines changed

codegen/aws/core/src/main/java/software/amazon/smithy/python/aws/codegen/AwsAuthIntegration.java

Lines changed: 19 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,20 @@
77
import static software.amazon.smithy.python.aws.codegen.AwsConfiguration.REGION;
88

99
import java.util.List;
10+
import java.util.Set;
1011
import software.amazon.smithy.aws.traits.auth.SigV4Trait;
1112
import software.amazon.smithy.codegen.core.Symbol;
13+
import software.amazon.smithy.model.shapes.ServiceShape;
1214
import software.amazon.smithy.model.shapes.ShapeId;
1315
import software.amazon.smithy.python.codegen.ApplicationProtocol;
1416
import software.amazon.smithy.python.codegen.CodegenUtils;
1517
import software.amazon.smithy.python.codegen.ConfigProperty;
16-
import software.amazon.smithy.python.codegen.DerivedProperty;
1718
import software.amazon.smithy.python.codegen.GenerationContext;
1819
import software.amazon.smithy.python.codegen.SmithyPythonDependency;
1920
import software.amazon.smithy.python.codegen.integrations.AuthScheme;
2021
import software.amazon.smithy.python.codegen.integrations.PythonIntegration;
2122
import software.amazon.smithy.python.codegen.integrations.RuntimeClientPlugin;
23+
import software.amazon.smithy.python.codegen.writer.PythonWriter;
2224
import software.amazon.smithy.utils.SmithyInternalApi;
2325

2426
/**
@@ -38,7 +40,7 @@ public List<RuntimeClientPlugin> getClientPlugins(GenerationContext context) {
3840
.name("aws_credentials_identity_resolver")
3941
.documentation("Resolves AWS Credentials. Required for operations that use Sigv4 Auth.")
4042
.type(Symbol.builder()
41-
.name("IdentityResolver[AWSCredentialsIdentity, IdentityProperties]")
43+
.name("IdentityResolver[AWSCredentialsIdentity, AWSIdentityProperties]")
4244
.addReference(Symbol.builder()
4345
.addDependency(SmithyPythonDependency.SMITHY_CORE)
4446
.name("IdentityResolver")
@@ -51,8 +53,8 @@ public List<RuntimeClientPlugin> getClientPlugins(GenerationContext context) {
5153
.build())
5254
.addReference(Symbol.builder()
5355
.addDependency(SmithyPythonDependency.SMITHY_CORE)
54-
.name("IdentityProperties")
55-
.namespace("smithy_core.interfaces.identity", ".")
56+
.name("AWSIdentityProperties")
57+
.namespace("smithy_aws_core.identity", ".")
5658
.build())
5759
.build())
5860
// TODO: Initialize with the provider chain?
@@ -69,30 +71,26 @@ public void customize(GenerationContext context) {
6971
return;
7072
}
7173
var trait = context.settings().service(context.model()).expectTrait(SigV4Trait.class);
72-
var params = CodegenUtils.getHttpAuthParamsSymbol(context.settings());
7374
var resolver = CodegenUtils.getHttpAuthSchemeResolverSymbol(context.settings());
7475

7576
// Add a function that generates the http auth option for api key auth.
7677
// This needs to be generated because there's modeled parameters that
7778
// must be accounted for.
7879
context.writerDelegator().useFileWriter(resolver.getDefinitionFile(), resolver.getNamespace(), writer -> {
7980
writer.addDependency(SmithyPythonDependency.SMITHY_HTTP);
80-
writer.addImport("smithy_http.aio.interfaces.auth", "HTTPAuthOption");
81+
writer.addImport("smithy_core.interfaces.auth", "AuthOption", "AuthOptionProtocol");
82+
writer.addImports("smithy_core.auth", Set.of("AuthOption", "AuthParams"));
8183
writer.pushState();
8284

8385
writer.write("""
84-
def $1L(auth_params: $2T) -> HTTPAuthOption | None:
85-
return HTTPAuthOption(
86-
scheme_id=$3S,
87-
identity_properties={},
88-
signer_properties={
89-
"service": $4S,
90-
"region": auth_params.region
91-
}
86+
def $1L(auth_params: AuthParams[Any, Any]) -> AuthOptionProtocol | None:
87+
return AuthOption(
88+
scheme_id=$2S,
89+
identity_properties={}, # type: ignore
90+
signer_properties={} # type: ignore
9291
)
9392
""",
9493
SIGV4_OPTION_GENERATOR_NAME,
95-
params,
9694
SigV4Trait.ID.toString(),
9795
trait.getName());
9896
writer.popState();
@@ -119,17 +117,6 @@ public ApplicationProtocol getApplicationProtocol() {
119117
return ApplicationProtocol.createDefaultHttpApplicationProtocol();
120118
}
121119

122-
@Override
123-
public List<DerivedProperty> getAuthProperties() {
124-
return List.of(
125-
DerivedProperty.builder()
126-
.name("region")
127-
.source(DerivedProperty.Source.CONFIG)
128-
.type(Symbol.builder().name("str").build())
129-
.sourcePropertyName("region")
130-
.build());
131-
}
132-
133120
@Override
134121
public Symbol getAuthOptionGenerator(GenerationContext context) {
135122
var resolver = CodegenUtils.getHttpAuthSchemeResolverSymbol(context.settings());
@@ -148,5 +135,11 @@ public Symbol getAuthSchemeSymbol(GenerationContext context) {
148135
.addDependency(AwsPythonDependency.SMITHY_AWS_CORE)
149136
.build();
150137
}
138+
139+
@Override
140+
public void initializeScheme(GenerationContext context, PythonWriter writer, ServiceShape service) {
141+
var trait = service.expectTrait(SigV4Trait.class);
142+
writer.write("$T(service=$S)", getAuthSchemeSymbol(context), trait.getName());
143+
}
151144
}
152145
}

codegen/core/src/main/java/software/amazon/smithy/python/codegen/ClientGenerator.java

Lines changed: 43 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -256,9 +256,11 @@ private void generateOperationExecutor(PythonWriter writer) {
256256
writer.consumer(w -> context.protocolGenerator().wrapInputStream(context, w)),
257257
writer.consumer(w -> context.protocolGenerator().wrapOutputStream(context, w)));
258258
}
259+
259260
writer.addStdlibImport("typing", "Any");
260261
writer.addStdlibImport("asyncio", "iscoroutine");
261262
writer.addImports("smithy_core.exceptions", Set.of("SmithyError", "CallError", "RetryError"));
263+
writer.addImport("smithy_core.auth", "AuthParams");
262264
writer.pushState();
263265
writer.putContext("request", transportRequest);
264266
writer.putContext("response", transportResponse);
@@ -438,53 +440,60 @@ await sleep(retry_token.retry_delay)
438440

439441
boolean supportsAuth = !ServiceIndex.of(model).getAuthSchemes(service).isEmpty();
440442
writer.pushState(new ResolveIdentitySection());
441-
if (context.applicationProtocol().isHttpProtocol() && supportsAuth) {
442-
writer.pushState(new InitializeHttpAuthParametersSection());
443-
writer.write("""
444-
# Step 7b: Invoke service_auth_scheme_resolver.resolve_auth_scheme
445-
auth_parameters: $1T = $1T(
446-
operation=operation.schema.id.name,
447-
${2C|}
448-
)
449-
450-
""",
451-
CodegenUtils.getHttpAuthParamsSymbol(context.settings()),
452-
writer.consumer(this::initializeHttpAuthParameters));
453-
writer.popState();
443+
if (supportsAuth) {
444+
// TODO: delete InitializeHttpAuthParametersSection
454445

455446
writer.addDependency(SmithyPythonDependency.SMITHY_CORE);
456-
writer.addDependency(SmithyPythonDependency.SMITHY_HTTP);
457447
writer.addImport("smithy_core.interfaces.identity", "Identity");
458-
writer.addImports("smithy_http.aio.interfaces.auth", Set.of("HTTPSigner", "HTTPAuthOption"));
448+
writer.addImport("smithy_core.interfaces.auth", "AuthOption");
449+
writer.addImport("smithy_core.aio.interfaces.auth", "Signer");
450+
writer.addImport("smithy_core.shapes", "ShapeID");
459451
writer.addStdlibImport("typing", "Any");
460452
writer.write("""
461-
auth_options = config.http_auth_scheme_resolver.resolve_auth_scheme(
453+
auth_parameters = AuthParams(
454+
protocol_id=ShapeID($1S),
455+
operation=operation,
456+
context=context.properties,
457+
)
458+
auth_options = config.auth_scheme_resolver.resolve_auth_scheme(
462459
auth_parameters=auth_parameters
463460
)
464-
auth_option: HTTPAuthOption | None = None
461+
462+
auth_option: AuthOption | None = None
465463
for option in auth_options:
466-
if option.scheme_id in config.http_auth_schemes:
464+
if option.scheme_id in config.auth_schemes:
467465
auth_option = option
468466
break
469467
470-
signer: HTTPSigner[Any, Any] | None = None
468+
signer: Signer[$2T, Any, Any] | None = None
471469
identity: Identity | None = None
470+
auth_scheme: Any = None
472471
473472
if auth_option:
474-
auth_scheme = config.http_auth_schemes[auth_option.scheme_id]
473+
auth_scheme = config.auth_schemes[auth_option.scheme_id]
474+
context.properties["auth_scheme"] = auth_scheme
475475
476476
# Step 7c: Invoke auth_scheme.identity_resolver
477-
identity_resolver = auth_scheme.identity_resolver(config=config)
477+
identity_resolver = auth_scheme.identity_resolver(context=context.properties)
478+
context.properties["identity_resolver"] = identity_resolver
478479
479480
# Step 7d: Invoke auth_scheme.signer
480-
signer = auth_scheme.signer
481+
signer = auth_scheme.signer()
482+
483+
# TODO: merge from auth_option
484+
identity_properties = auth_scheme.identity_properties(
485+
context=context.properties
486+
)
487+
context.properties["identity_properties"] = identity_properties
481488
482489
# Step 7e: Invoke identity_resolver.get_identity
483490
identity = await identity_resolver.get_identity(
484-
identity_properties=auth_option.identity_properties
491+
identity_properties=identity_properties
485492
)
486493
487-
""");
494+
""",
495+
context.protocolGenerator().getProtocol(),
496+
transportRequest);
488497
}
489498
writer.popState();
490499

@@ -543,48 +552,29 @@ await sleep(retry_token.retry_delay)
543552

544553
writer.pushState(new SignRequestSection());
545554
writer.addStdlibImport("typing", "cast");
546-
if (context.applicationProtocol().isHttpProtocol() && supportsAuth) {
555+
if (supportsAuth) {
547556
writer.addStdlibImport("re");
548557
writer.addStdlibImport("typing", "Any");
549558
writer.addImport("smithy_core.interfaces.identity", "Identity");
550559
writer.addImport("smithy_core.types", "PropertyKey");
551560
writer.write("""
552561
# Step 7i: sign the request
553562
if auth_option and signer:
554-
logger.debug("HTTP request to sign: %s", context.transport_request)
555-
logger.debug(
556-
"Signer properties: %s",
557-
auth_option.signer_properties
558-
)
563+
signer_properties = auth_scheme.signer_properties(context=context.properties)
564+
context.properties["signer_properties"] = signer_properties
565+
566+
logger.debug("Request to sign: %s", context.transport_request)
567+
logger.debug("Signer properties: %s", signer_properties)
568+
559569
context = replace(
560570
context,
561-
transport_request= await signer.sign(
562-
http_request=context.transport_request,
571+
transport_request = await signer.sign(
572+
request=context.transport_request,
563573
identity=identity,
564-
signing_properties=auth_option.signer_properties,
574+
properties=signer_properties,
565575
)
566576
)
567577
logger.debug("Signed HTTP request: %s", context.transport_request)
568-
569-
# TODO - Move this to separate resolution/population function
570-
fields = context.transport_request.fields
571-
auth_value = fields["Authorization"].as_string() # type: ignore
572-
signature = re.split("Signature=", auth_value)[-1] # type: ignore
573-
context.properties["signature"] = signature.encode('utf-8')
574-
575-
identity_key = cast(
576-
PropertyKey[Identity | None],
577-
PropertyKey(
578-
key="identity",
579-
value_type=Identity | None # type: ignore
580-
)
581-
)
582-
sp_key: PropertyKey[dict[str, Any]] = PropertyKey(
583-
key="signer_properties",
584-
value_type=dict[str, Any] # type: ignore
585-
)
586-
context.properties[identity_key] = identity
587-
context.properties[sp_key] = auth_option.signer_properties
588578
""");
589579
}
590580
writer.popState();
@@ -757,28 +747,6 @@ private boolean hasEventStream() {
757747
return false;
758748
}
759749

760-
private void initializeHttpAuthParameters(PythonWriter writer) {
761-
var derived = new LinkedHashSet<DerivedProperty>();
762-
for (PythonIntegration integration : context.integrations()) {
763-
for (RuntimeClientPlugin plugin : integration.getClientPlugins(context)) {
764-
if (plugin.matchesService(model, service)
765-
&& plugin.getAuthScheme().isPresent()
766-
&& plugin.getAuthScheme().get().getApplicationProtocol().isHttpProtocol()) {
767-
derived.addAll(plugin.getAuthScheme().get().getAuthProperties());
768-
}
769-
}
770-
}
771-
772-
for (DerivedProperty property : derived) {
773-
var source = property.source().scopeLocation();
774-
if (property.initializationFunction().isPresent()) {
775-
writer.write("$L=$T($L),", property.name(), property.initializationFunction().get(), source);
776-
} else if (property.sourcePropertyName().isPresent()) {
777-
writer.write("$L=$L.$L,", property.name(), source, property.sourcePropertyName().get());
778-
}
779-
}
780-
}
781-
782750
private void writeDefaultPlugins(PythonWriter writer, Collection<SymbolReference> plugins) {
783751
for (SymbolReference plugin : plugins) {
784752
writer.write("$T,", plugin);

codegen/core/src/main/java/software/amazon/smithy/python/codegen/CodegenUtils.java

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -102,20 +102,6 @@ public static Symbol getServiceError(PythonSettings settings) {
102102
.build();
103103
}
104104

105-
/**
106-
* Gets the symbol for the http auth params.
107-
*
108-
* @param settings The client settings, used to account for module configuration.
109-
* @return Returns the http auth params symbol.
110-
*/
111-
public static Symbol getHttpAuthParamsSymbol(PythonSettings settings) {
112-
return Symbol.builder()
113-
.name("HTTPAuthParams")
114-
.namespace(String.format("%s.auth", settings.moduleName()), ".")
115-
.definitionFile(String.format("./src/%s/auth.py", settings.moduleName()))
116-
.build();
117-
}
118-
119105
/**
120106
* Gets the symbol for the http auth scheme resolver.
121107
*

0 commit comments

Comments
 (0)