-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add codegen for service specific auth scheme resolver protocol, service specific default auth scheme resolver struct, and service specific auth scheme resolver parameters struct. * Make ASR throw if passed in ASR params doesn't have region field for SigV4 auth scheme & fix ktlint issues. * Clean up middlewares. * Remove auth scheme and signing middlewares from operation stack of protocol tests. * Update test cases to include new middlewares. * Codegen more descriptive comment for empty service specific auth scheme resolver protocol. * Add codegen test for auth scheme resolver generation. * Move region in middleware context from sdk side to smithy side. * Remove AWSClientRuntime dependency - signingProperties will be set in auth scheme customization hooks instead. * Move auth schemes from service specific config to general AWS config. --------- Co-authored-by: Sichan Yoo <[email protected]>
- Loading branch information
Showing
20 changed files
with
631 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
241 changes: 241 additions & 0 deletions
241
...degen/src/main/kotlin/software/amazon/smithy/swift/codegen/AuthSchemeResolverGenerator.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,241 @@ | ||
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) | ||
} | ||
} | ||
|
||
private fun renderResolverParams( | ||
serviceIndex: ServiceIndex, | ||
ctx: ProtocolGenerator.GenerationContext, | ||
writer: SwiftWriter | ||
) { | ||
writer.apply { | ||
openBlock( | ||
"public struct ${getSdkId(ctx)}${ClientRuntimeTypes.Core.AuthSchemeResolverParameters.name}: \$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.") | ||
write("// This is the parent protocol that all auth scheme resolver implementations of") | ||
write("// the service ${getSdkId(ctx)} must conform to.") | ||
} | ||
} | ||
} | ||
|
||
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 { | ||
openBlock( | ||
"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.filter { op -> | ||
val opShape = ctx.model.getShape(op).get() as OperationShape | ||
opShape.hasTrait(AuthTrait::class.java) || | ||
opShape.hasTrait(OptionalAuthTrait::class.java) || | ||
opShape.hasTrait(UnsignedPayloadTrait::class.java) | ||
}.forEach { op -> | ||
val opName = op.name.toLowerCamelCase() | ||
val sdkId = getSdkId(ctx) | ||
val validSchemesForOp = serviceIndex.getEffectiveAuthSchemes( | ||
ctx.service, op, ServiceIndex.AuthSchemeMode.NO_AUTH_AWARE | ||
) | ||
renderOperationSwitchCase( | ||
sdkId, | ||
ctx.model.getShape(op).get() as OperationShape, | ||
opName, | ||
validSchemesForOp, | ||
writer | ||
) | ||
} | ||
// 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)") | ||
write("validAuthOptions.append(sigV4Option)") | ||
} else { | ||
write("validAuthOptions.append(AuthOption(schemeID: \"${it.key}\"))") | ||
} | ||
} | ||
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)") | ||
} | ||
write("sigV4Option.signingProperties.set(key: AttributeKeys.signingRegion, value: region)") | ||
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() | ||
} | ||
} |
Oops, something went wrong.