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

feat: codegen changes #609

Merged
merged 74 commits into from
Nov 9, 2023
Merged
Show file tree
Hide file tree
Changes from 64 commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
86c7d2c
Add signer protocol.
Oct 4, 2023
5eb2c8d
Add http context changes.
Oct 4, 2023
75646fc
Swiftlint issues.
Oct 4, 2023
dd339a0
Fix swiftlint.
Oct 4, 2023
f5808fa
Add identity type
Oct 4, 2023
6371b28
Use nil coalescing operator to check attribute existence
Oct 4, 2023
9327f98
Clean up changes.
Oct 4, 2023
9b7b4c5
New protocols and structs.
Oct 4, 2023
cebf798
New middlewares.
Oct 4, 2023
d7040f5
Merge branch 'feat/auth-scheme-changes' into feat/middleware-changes
Oct 4, 2023
39b386e
Delete auth scheme related change.
Oct 4, 2023
8d90e28
Update signer middleware
Oct 4, 2023
d87e82f
Update selected auth scheme
Oct 4, 2023
ad8cd2c
Change signer protocol to use generic instead of associatedtype
Oct 4, 2023
61b79a6
merge commit
Oct 4, 2023
37a66b5
Merge branch 'feat/auth-scheme-changes' into feat/middleware-changes
Oct 4, 2023
cca514b
Add back identity type and aws id resolver key
Oct 4, 2023
b73adf0
Merge branch 'feat/auth-scheme-changes' into feat/middleware-changes
Oct 4, 2023
1205170
Finish rough draft of middlewares
Oct 4, 2023
5bf2df2
Provide hook in auth scheme for signing properties customization.
Oct 5, 2023
a05e6cd
Rename field.
Oct 5, 2023
9420eb6
Merge branch 'feat/auth-scheme-changes' into feat/middleware-changes
Oct 5, 2023
1bd5ac5
Change attribute keys namespace from struct to enum.
Oct 5, 2023
99323e6
Merge attribute key namespace change.
Oct 6, 2023
1f7d2ac
Add auth scheme resolver to httpcontext, and change return values of …
Oct 6, 2023
6476d09
Merge branch 'feat/auth-scheme-changes' into feat/middleware-changes
Oct 6, 2023
a71a09e
Update to match optional return type of middleware context.
Oct 6, 2023
00ebb21
Remove ASR and ASRP from AS middleware constructor. Retrieve ASR from…
Oct 6, 2023
422721c
Add method to ASR protocol, used in AS middleware to construct servi…
Oct 6, 2023
bda4f75
Merge branch 'feat/auth-scheme-changes' into feat/middleware-changes
Oct 6, 2023
337f7b6
Typo.
Oct 6, 2023
9e044dd
Merge main to authscheme branch.
Oct 9, 2023
f7f9e0e
Swiftlint resolved & additional comment.
Oct 9, 2023
d54fda8
Add clarification to attribute key.
Oct 9, 2023
111d8d6
Merge branch 'feat/auth-scheme-changes' into feat/middleware-changes
Oct 9, 2023
b0b7d9e
Resolve PR comments.
Oct 11, 2023
1794543
Update enum name in one more place.
Oct 11, 2023
07ff3ca
More places where identityType is changed to identityKind.
Oct 11, 2023
e0a6146
Remove unnecessary imports.
Oct 11, 2023
99e2ec2
Merge feat/auth-scheme-changes.
Oct 11, 2023
d6fcc51
Move keys to smithy-swift.
Oct 11, 2023
dd4324b
Change schemeId to schemeID in auth scheme too.
Oct 11, 2023
dbe5b19
Merge branch 'feat/auth-scheme-changes' into feat/middleware-changes
Oct 11, 2023
b47a4a9
Change schemeId to schemeID.
Oct 11, 2023
259baf8
Merge branch 'feat/middleware-changes' into feat/codegen-changes
Oct 11, 2023
24aa58d
Codegen changes.
Oct 17, 2023
57d31ec
Make ASR throw if passed in ASR params doesn't have region field for …
Oct 17, 2023
4ac7de3
Merge feat/sra-identity-and-auth.
Oct 18, 2023
e89eea7
Clean up middlewares.
Oct 18, 2023
1bd6ba5
Fix swiftlint.
Oct 18, 2023
fc12f7d
Merge feat/middleware-changes.
Oct 18, 2023
b29cbab
Delete extra argument.
Oct 19, 2023
65c0f36
Fix syntax error in codegen.
Oct 19, 2023
a7fac43
Fix semantic errors.
Oct 19, 2023
22e5e22
Fix internal access issue.
Oct 19, 2023
5951809
Fixes for making codegn flow work for STS::getCallerIdenity.
Oct 23, 2023
ef9c09b
Merge feat/sra-identity-and-auth.
Oct 25, 2023
442fd8d
Whitespace fix for swiftlint.
Oct 25, 2023
2ccb051
Refactor for better structure.
Oct 25, 2023
969b6e9
Stash conflict.
Oct 26, 2023
4f703e9
Remove auth scheme and signing middlewares from operation stack of pr…
Oct 26, 2023
eac996d
Update test cases to include new middlewares.
Oct 26, 2023
c1d00b8
Resolve David's PR comments.
Oct 31, 2023
129d8e9
Fix string constants.
Nov 2, 2023
d821214
Fix typo in codegen.
Nov 2, 2023
ea7f67c
Codegen more descriptive comment for empty service specific auth sche…
Nov 2, 2023
b5191c1
Kotlin syntax changes as per PR comments.
Nov 2, 2023
0f5b0e5
Add codegen tests for auth scheme resolver generation.
Nov 2, 2023
372b456
Fix ktlint.
Nov 2, 2023
b5e11d8
Fix typo.
Nov 2, 2023
3c8383c
Move region in middleware context from sdk side to smithy side.
Nov 8, 2023
0df4582
Remove AWSClientRuntime dependency - signingProperties will be set in…
Nov 8, 2023
a84b8fb
Move auth schemes from service specific config to general AWS config.
Nov 9, 2023
eefa88a
Update auth scheme resolver codegen test.
Nov 9, 2023
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
14 changes: 12 additions & 2 deletions Sources/ClientRuntime/Auth/HTTPAuthAPI/AuthOption.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@

public struct AuthOption {
let schemeID: String
var identityProperties: Attributes
var signingProperties: Attributes
public var identityProperties: Attributes
public var signingProperties: Attributes

public init (
schemeID: String,
identityProperties: Attributes = Attributes(),
signingProperties: Attributes = Attributes()
) {
self.schemeID = schemeID
self.identityProperties = identityProperties
self.signingProperties = signingProperties
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@
//

public protocol AuthSchemeResolver {
func resolveAuthScheme(params: AuthSchemeResolverParameters) -> [AuthOption]
func resolveAuthScheme(params: AuthSchemeResolverParameters) throws -> [AuthOption]
func constructParameters(context: HttpContext) throws -> AuthSchemeResolverParameters
}
4 changes: 4 additions & 0 deletions Sources/ClientRuntime/Middleware/Attribute.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,8 @@ public struct Attributes {
public mutating func remove<T>(key: AttributeKey<T>) {
attributes.removeValue(forKey: key.name)
}

public func getSize() -> Int {
return attributes.count
}
jbelkins marked this conversation as resolved.
Show resolved Hide resolved
}
36 changes: 35 additions & 1 deletion Sources/ClientRuntime/Networking/Http/HttpContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ public class HttpContext: MiddlewareContext {
return attributes.get(key: AttributeKeys.method)!
}

public func getOperation() -> String? {
return attributes.get(key: AttributeKeys.operation)
}

/// The partition ID to be used for this context.
///
/// Requests made with the same partition ID will be grouped together for retry throttling purposes.
Expand All @@ -87,6 +91,14 @@ public class HttpContext: MiddlewareContext {
return attributes.get(key: AttributeKeys.serviceName)!
}

public func getSigningName() -> String? {
return attributes.get(key: AttributeKeys.signingName)
}

public func getSigningRegion() -> String? {
return attributes.get(key: AttributeKeys.signingRegion)
}

public func isBidirectionalStreamingEnabled() -> Bool {
return attributes.get(key: AttributeKeys.bidirectionalStreaming) ?? false
}
Expand Down Expand Up @@ -128,6 +140,14 @@ public class HttpContextBuilder {
return self
}

@discardableResult
public func withAuthSchemes(value: [AuthScheme]) -> HttpContextBuilder {
for scheme in value {
self.withAuthScheme(value: scheme)
}
return self
}

@discardableResult
public func withDecoder(value: ResponseDecoder) -> HttpContextBuilder {
self.attributes.set(key: AttributeKeys.decoder, value: value)
Expand Down Expand Up @@ -220,6 +240,18 @@ public class HttpContextBuilder {
return self
}

@discardableResult
public func withSigningName(value: String) -> HttpContextBuilder {
self.attributes.set(key: AttributeKeys.signingName, value: value)
return self
}

@discardableResult
public func withSigningRegion(value: String?) -> HttpContextBuilder {
self.attributes.set(key: AttributeKeys.signingRegion, value: value)
return self
}

public func build() -> HttpContext {
return HttpContext(attributes: attributes)
}
Expand All @@ -246,7 +278,9 @@ public enum AttributeKeys {
public static let path = AttributeKey<String>(name: "Path")
public static let selectedAuthScheme = AttributeKey<SelectedAuthScheme>(name: "SelectedAuthScheme")
public static let serviceName = AttributeKey<String>(name: "ServiceName")
public static let signingName = AttributeKey<String>(name: "SigningName")
public static let signingRegion = AttributeKey<String>(name: "SigningRegion")

// The attribute key used to store a credentials provider configured on service client config onto middleware context.
public static let awsIdResolver = AttributeKey<any IdentityResolver>(name: "AWSIDResolver")
public static let awsIdResolver = AttributeKey<any IdentityResolver>(name: "\(IdentityKind.aws)")
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,11 @@ public struct AuthSchemeMiddleware<OperationStackOutput: HttpResponseBinding,
// Construct auth scheme resolver parameters
let resolverParams = try resolver.constructParameters(context: context)
// Retrieve valid auth options for the operation at hand
let validAuthOptions = resolver.resolveAuthScheme(params: resolverParams)
let validAuthOptions = try resolver.resolveAuthScheme(params: resolverParams)

// Create IdentityResolverConfiguration
guard let identityResolvers = context.getIdentityResolvers() else {
let identityResolvers = context.getIdentityResolvers()
guard let identityResolvers, identityResolvers.getSize() > 0 else {
throw ClientError.authError("No identity resolver has been configured on the service.")
}
let identityResolverConfig = DefaultIdentityResolverConfiguration(configuredIdResolvers: identityResolvers)
Expand Down Expand Up @@ -94,7 +95,7 @@ public struct AuthSchemeMiddleware<OperationStackOutput: HttpResponseBinding,
// If no auth scheme could be resolved, throw an error
guard let selectedAuthScheme else {
throw ClientError.authError(
"Could not resolve auth scheme for the operation call.\nLog:\n\(log.joined(separator: "\n"))"
"Could not resolve auth scheme for the operation call. Log: \(log.joined(separator: ","))"
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
package software.amazon.smithy.swift.codegen

import software.amazon.smithy.aws.traits.ServiceTrait
import software.amazon.smithy.aws.traits.auth.SigV4Trait
import software.amazon.smithy.aws.traits.auth.UnsignedPayloadTrait
import software.amazon.smithy.model.knowledge.ServiceIndex
import software.amazon.smithy.model.shapes.OperationShape
import software.amazon.smithy.model.shapes.ShapeId
import software.amazon.smithy.model.traits.AuthTrait
import software.amazon.smithy.model.traits.OptionalAuthTrait
import software.amazon.smithy.model.traits.Trait
import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator
import software.amazon.smithy.swift.codegen.integration.ServiceTypes
import software.amazon.smithy.swift.codegen.utils.clientName
import software.amazon.smithy.swift.codegen.utils.toLowerCamelCase

class AuthSchemeResolverGenerator() {
fun render(ctx: ProtocolGenerator.GenerationContext) {
val rootNamespace = ctx.settings.moduleName
val serviceIndex = ServiceIndex(ctx.model)

ctx.delegator.useFileWriter("./$rootNamespace/${ClientRuntimeTypes.Core.AuthSchemeResolver.name}.swift") {
renderResolverParams(serviceIndex, ctx, it)
it.write("")
renderResolverProtocol(ctx, it)
it.write("")
renderDefaultResolver(serviceIndex, ctx, it)
it.write("")
it.addImport(SwiftDependency.CLIENT_RUNTIME.target)
it.addImport("AWSClientRuntime")
jbelkins marked this conversation as resolved.
Show resolved Hide resolved
}
}

private fun renderResolverParams(
serviceIndex: ServiceIndex,
ctx: ProtocolGenerator.GenerationContext,
writer: SwiftWriter
) {
writer.apply {
openBlock(
"public struct ${getSdkId(ctx)}${ClientRuntimeTypes.Core.AuthSchemeResolverParameters.name}S: \$L {",
"}",
ServiceTypes.AuthSchemeResolverParams
) {
write("public let operation: String")
// If service supports SigV4 auth scheme, it's a special-case
// Region has to be in params in addition to operation string from AuthSchemeResolver protocol
if (serviceIndex.getEffectiveAuthSchemes(ctx.service).contains(SigV4Trait.ID)) {
write("// Region is used for SigV4 auth scheme")
write("public let region: String?")
}
}
}
}

private fun renderResolverProtocol(ctx: ProtocolGenerator.GenerationContext, writer: SwiftWriter) {
writer.apply {
openBlock(
"public protocol ${getSdkId(ctx)}${ClientRuntimeTypes.Core.AuthSchemeResolver.name}: \$L {",
"}",
ServiceTypes.AuthSchemeResolver
) {
// This is just a parent protocol that all auth scheme resolvers of a given service must conform to.
write("// Intentionally empty.")
dayaffe marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

private fun renderDefaultResolver(
serviceIndex: ServiceIndex,
ctx: ProtocolGenerator.GenerationContext,
writer: SwiftWriter
) {
val sdkId = getSdkId(ctx)
val defaultResolverName = "Default$sdkId${ClientRuntimeTypes.Core.AuthSchemeResolver.name}"
val serviceProtocolName = sdkId + ClientRuntimeTypes.Core.AuthSchemeResolver.name

writer.apply {
writer.openBlock(
dayaffe marked this conversation as resolved.
Show resolved Hide resolved
"public struct \$L: \$L {",
"}",
defaultResolverName,
serviceProtocolName
) {
renderResolveAuthSchemeMethod(serviceIndex, ctx, writer)
write("")
renderConstructParametersMethod(
serviceIndex.getEffectiveAuthSchemes(ctx.service).contains(SigV4Trait.ID),
sdkId + ClientRuntimeTypes.Core.AuthSchemeResolverParameters.name,
writer
)
}
}
}

private fun renderResolveAuthSchemeMethod(
serviceIndex: ServiceIndex,
ctx: ProtocolGenerator.GenerationContext,
writer: SwiftWriter
) {
val sdkId = getSdkId(ctx)
val serviceParamsName = sdkId + ClientRuntimeTypes.Core.AuthSchemeResolverParameters.name

writer.apply {
openBlock(
"public func resolveAuthScheme(params: \$L) throws -> [AuthOption] {",
"}",
ServiceTypes.AuthSchemeResolverParams
) {
// Return value of array of auth options
write("var validAuthOptions = Array<AuthOption>()")

// Cast params to service specific params object
openBlock(
"guard let serviceParams = params as? \$L else {",
"}",
serviceParamsName
) {
write("throw ClientError.authError(\"Service specific auth scheme parameters type must be passed to auth scheme resolver.\")")
}

renderSwitchBlock(serviceIndex, ctx, this)
}
}
}

private fun renderSwitchBlock(
serviceIndex: ServiceIndex,
ctx: ProtocolGenerator.GenerationContext,
writer: SwiftWriter
) {
writer.apply {
// Switch block for iterating over operation name cases
openBlock("switch serviceParams.operation {", "}") {
// Handle each operation name case
val operations = ctx.service.operations
operations.forEach {
val opShape = ctx.model.getShape(it).get() as OperationShape
if (
opShape.hasTrait(AuthTrait::class.java) ||
opShape.hasTrait(OptionalAuthTrait::class.java) ||
opShape.hasTrait(UnsignedPayloadTrait::class.java)
) {
val opName = it.name.toLowerCamelCase()
val sdkId = getSdkId(ctx)
val validSchemesForOp = serviceIndex.getEffectiveAuthSchemes(
ctx.service,
it,
ServiceIndex.AuthSchemeMode.NO_AUTH_AWARE
)
renderOperationSwitchCase(sdkId, opShape, opName, validSchemesForOp, writer)
}
}
dayaffe marked this conversation as resolved.
Show resolved Hide resolved
// Handle default case, where operations default to auth schemes defined on service shape
val validSchemesForService = serviceIndex.getEffectiveAuthSchemes(ctx.service, ServiceIndex.AuthSchemeMode.NO_AUTH_AWARE)
renderDefaultSwitchCase(getSdkId(ctx), validSchemesForService, writer)
}

// Return result
write("return validAuthOptions")
}
}

private fun renderOperationSwitchCase(sdkId: String, opShape: OperationShape, opName: String, schemes: Map<ShapeId, Trait>, writer: SwiftWriter) {
writer.apply {
write("case \"$opName\":")
indent()
schemes.forEach {
if (it.key == SigV4Trait.ID) {
write("var sigV4Option = AuthOption(schemeID: \"${it.key}\")")
write("sigV4Option.signingProperties.set(key: AttributeKeys.signingName, value: ${(it.value as SigV4Trait).name})")
openBlock("guard let region = serviceParams.region else {", "}") {
val errorMessage = "\"Missing region in auth scheme parameters for SigV4 auth scheme.\""
write("throw ClientError.authError($errorMessage)")
}
write("sigV4Option.signingProperties.set(key: AttributeKeys.signingRegion, value: region)")

val unsignedBody = opShape.hasTrait(UnsignedPayloadTrait::class.java)
val signedBodyHeader = if ((sdkId == "s3" || sdkId == "glacier") && !unsignedBody) ".contentSha256" else ".none"
// Set .unsignedBody to true IFF operation has UnsignedPayloadTrait
write("sigV4Option.signingProperties.set(key: AttributeKeys.unsignedBody, value: $unsignedBody)")
// Set .signedBodyHeader to .contentSha256 IFF service is S3 / Glacier AND operation does not have UnsignedPayloadTrait.
// Set to .none otherwise.
write("sigV4Option.signingProperties.set(key: AttributeKeys.signedBodyHeader, value: $signedBodyHeader)")

write("validAuthOptions.append(sigV4Option)")
} else {
write("validAuthOptions.append(AuthOption(schemeID: \"${it.key}\"))")
}
dayaffe marked this conversation as resolved.
Show resolved Hide resolved
}
dedent()
}
}

private fun renderDefaultSwitchCase(sdkId: String, schemes: Map<ShapeId, Trait>, writer: SwiftWriter) {
writer.apply {
write("default:")
indent()
schemes.forEach {
if (it.key == SigV4Trait.ID) {
write("var sigV4Option = AuthOption(schemeID: \"${it.key}\")")
write("sigV4Option.signingProperties.set(key: AttributeKeys.signingName, value: \"${(it.value as SigV4Trait).name}\")")
openBlock("guard let region = serviceParams.region else {", "}") {
val errorMessage = "\"Missing region in auth scheme parameters for SigV4 auth scheme.\""
write("throw ClientError.authError($errorMessage)")
}
val signedBodyHeader = if (sdkId == "s3" || sdkId == "glacier") ".contentSha256" else ".none"
// Set .unsignedBody to false
write("sigV4Option.signingProperties.set(key: AttributeKeys.unsignedBody, value: false)")
// Set .signedBodyHeader to .contentSha256 IFF service is S3 / Glacier, set to .none otherwise.
dayaffe marked this conversation as resolved.
Show resolved Hide resolved
write("sigV4Option.signingProperties.set(key: AttributeKeys.signedBodyHeader, value: $signedBodyHeader)")

write("validAuthOptions.append(sigV4Option)")
} else {
write("validAuthOptions.append(AuthOption(schemeID: \"${it.key}\"))")
}
}
dedent()
}
}

private fun renderConstructParametersMethod(
hasSigV4: Boolean,
returnTypeName: String,
writer: SwiftWriter
) {
writer.apply {
openBlock(
"public func constructParameters(context: HttpContext) throws -> \$L {",
"}",
ServiceTypes.AuthSchemeResolverParams
) {
openBlock("guard let opName = context.getOperation() else {", "}") {
write("throw ClientError.dataNotFound(\"Operation name not configured in middleware context for auth scheme resolver params construction.\")")
}
if (hasSigV4) {
write("let opRegion = context.getRegion()")
write("return $returnTypeName(operation: opName, region: opRegion)")
} else {
write("return $returnTypeName(operation: opName)")
}
}
}
}

// Utility function for returning sdkId from generation context
fun getSdkId(ctx: ProtocolGenerator.GenerationContext): String {
return if (ctx.service.hasTrait(ServiceTrait::class.java))
ctx.service.getTrait(ServiceTrait::class.java).get().sdkId.clientName()
else ctx.settings.sdkId.clientName()
}
}
Loading
Loading