Skip to content

Commit bd2b045

Browse files
sichanyooSichan Yoo
and
Sichan Yoo
authored
feat: IAM auth token generator for RDS (#1851)
* Add AuthTokenGenerator * Move AuthTokenGenerator to Customizations/RDS/ * Add documentation comments to AuthTokenGenerator * Add codegen for wrapper class to expose AuthTokenGenerator via AWSRDS module instead of AWSClientRuntime module. * Include generated AuthTokenGenerator wrapper class for reference to reviewers * Swiftlint; sort imports * Ktlint; add newline at end of file --------- Co-authored-by: Sichan Yoo <[email protected]>
1 parent 9ad1268 commit bd2b045

File tree

4 files changed

+248
-0
lines changed

4 files changed

+248
-0
lines changed
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
//
2+
// Copyright Amazon.com Inc. or its affiliates.
3+
// All Rights Reserved.
4+
//
5+
// SPDX-License-Identifier: Apache-2.0
6+
//
7+
8+
import AwsCommonRuntimeKit
9+
import class AWSSDKHTTPAuth.AWSSigV4Signer
10+
import ClientRuntime
11+
import Smithy
12+
import SmithyHTTPAPI
13+
import SmithyHTTPAuth
14+
import SmithyHTTPAuthAPI
15+
import SmithyIdentity
16+
import struct Foundation.Date
17+
import struct Foundation.TimeInterval
18+
19+
/// A utility class with a single utility method that generates IAM authentication token used for connecting to RDS.
20+
@_spi(AuthTokenGenerator)
21+
public class AuthTokenGenerator {
22+
public var awsCredentialIdentity: AWSCredentialIdentity
23+
24+
/// The initializer that takes in AWSCredentialIdentity struct to use to generate the IAM authentication token.
25+
public init(awsCredentialIdentity: AWSCredentialIdentity) {
26+
self.awsCredentialIdentity = awsCredentialIdentity
27+
}
28+
29+
/// The initializer that takes in a specific AWSCredentialIdentityResolver, used to resolve the AWSCredentialIdentity used to generate the IAM authentication token.
30+
public init(awsCredentialIdentityResolver: any AWSCredentialIdentityResolver) async throws {
31+
self.awsCredentialIdentity = try await awsCredentialIdentityResolver.getIdentity()
32+
}
33+
34+
/// Updates the AWS credentials used to generate the IAM auth token.
35+
public func updateCredentials(newAWSCredentialIdentity: AWSCredentialIdentity) {
36+
self.awsCredentialIdentity = newAWSCredentialIdentity
37+
}
38+
39+
/// Updates the AWS credentials used to generate the IAM auth token by resolving credentials from passed in resolver.
40+
public func updateCredentials(awsCredentialIdentityResolver: any AWSCredentialIdentityResolver) async throws {
41+
self.awsCredentialIdentity = try await awsCredentialIdentityResolver.getIdentity()
42+
}
43+
44+
/// Generates authenetication token using given inputs to the method and credential identity instance variable.
45+
///
46+
/// - Parameters:
47+
/// - endpoint: The endpoint of the RDS instance. E.g., `rdsmysql.123456789012.us-west-2.rds.amazonaws.com`
48+
/// - port: The port of the RDS instance to connect to. E.g., `3306`
49+
/// - region: The region that RDS instance is located in. E.g., `us-west-2`
50+
/// - username: The username of the RDS database user. E.g., `admin`
51+
/// - expiration: The expiration for the token in seconds. Default is 900 seconds (15 minutes).
52+
public func generateAuthToken(
53+
endpoint: String,
54+
port: Int16,
55+
region: String,
56+
username: String,
57+
expiration: TimeInterval = 900
58+
) async throws -> String {
59+
CommonRuntimeKit.initialize()
60+
let requestBuilder = HTTPRequestBuilder()
61+
requestBuilder.withHost(endpoint)
62+
requestBuilder.withPort(port)
63+
64+
// Add the Host header and the required query items for the desired presigned URL.
65+
requestBuilder.withHeader(name: "Host", value: "\(endpoint):\(port)")
66+
requestBuilder.withQueryItem(URIQueryItem(name: "Action", value: "connect"))
67+
requestBuilder.withQueryItem(URIQueryItem(name: "DBUser", value: username))
68+
69+
let signingConfig = AWSSigningConfig(
70+
credentials: self.awsCredentialIdentity,
71+
expiration: expiration,
72+
signedBodyValue: .empty,
73+
flags: SigningFlags(
74+
useDoubleURIEncode: true,
75+
shouldNormalizeURIPath: true,
76+
omitSessionToken: false
77+
),
78+
date: Date(),
79+
service: "rds-db",
80+
region: region,
81+
signatureType: .requestQueryParams,
82+
signingAlgorithm: .sigv4
83+
)
84+
85+
let signedRequest = await AWSSigV4Signer().sigV4SignedRequest(
86+
requestBuilder: requestBuilder,
87+
signingConfig: signingConfig
88+
)
89+
90+
guard let presignedURL = signedRequest?.destination.url else {
91+
throw ClientError.authError("Failed to generate auth token for RDS.")
92+
}
93+
94+
// Remove https:// from the presigned URL to get final value for RDS auth token.
95+
let startIndex = presignedURL.absoluteString.index(presignedURL.absoluteString.startIndex, offsetBy: 8)
96+
let rdsAuthToken = String(presignedURL.absoluteString[startIndex...])
97+
98+
return rdsAuthToken
99+
}
100+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
//
2+
// Copyright Amazon.com Inc. or its affiliates.
3+
// All Rights Reserved.
4+
//
5+
// SPDX-License-Identifier: Apache-2.0
6+
//
7+
8+
// Code generated by smithy-swift-codegen. DO NOT EDIT!
9+
10+
11+
12+
@_spi(AuthTokenGenerator) import class AWSClientRuntime.AuthTokenGenerator
13+
import SmithyIdentity
14+
import struct Foundation.TimeInterval
15+
16+
/// A utility class with a single utility method that generates IAM authentication token used for connecting to RDS.
17+
public class AuthTokenGenerator {
18+
private let generator: AWSClientRuntime.AuthTokenGenerator
19+
20+
/// The initializer that takes in AWSCredentialIdentity struct to use to generate the IAM authentication token.
21+
public init(awsCredentialIdentity: AWSCredentialIdentity) {
22+
self.generator = AWSClientRuntime.AuthTokenGenerator(awsCredentialIdentity: awsCredentialIdentity)
23+
}
24+
25+
/// The initializer that takes in a specific AWSCredentialIdentityResolver, used to resolve the AWSCredentialIdentity used to generate the IAM authentication token.
26+
public init(awsCredentialIdentityResolver: any AWSCredentialIdentityResolver) async throws {
27+
self.generator = try await AWSClientRuntime.AuthTokenGenerator(awsCredentialIdentityResolver: awsCredentialIdentityResolver)
28+
}
29+
30+
/// Updates the AWS credentials used to generate the IAM auth token.
31+
public func updateCredentials(newAWSCredentialIdentity: AWSCredentialIdentity) {
32+
generator.updateCredentials(newAWSCredentialIdentity: newAWSCredentialIdentity)
33+
}
34+
35+
/// Updates the AWS credentials used to generate the IAM auth token by resolving credentials from passed in resolver.
36+
public func updateCredentials(awsCredentialIdentityResolver: any AWSCredentialIdentityResolver) async throws {
37+
try await generator.updateCredentials(awsCredentialIdentityResolver: awsCredentialIdentityResolver)
38+
}
39+
40+
/// Generates authenetication token using given inputs to the method and credential identity instance variable.
41+
///
42+
/// - Parameters:
43+
/// - endpoint: The endpoint of the RDS instance. E.g., `rdsmysql.123456789012.us-west-2.rds.amazonaws.com`
44+
/// - port: The port of the RDS instance to connect to. E.g., `3306`
45+
/// - region: The region that RDS instance is located in. E.g., `us-west-2`
46+
/// - username: The username of the RDS database user. E.g., `admin`
47+
/// - expiration: The expiration for the token in seconds. Default is 900 seconds (15 minutes).
48+
public func generateAuthToken(
49+
endpoint: String,
50+
port: Int16,
51+
region: String,
52+
username: String,
53+
expiration: TimeInterval = 900
54+
) async throws -> String {
55+
return try await generator.generateAuthToken(
56+
endpoint: endpoint,
57+
port: port,
58+
region: region,
59+
username: username,
60+
expiration: expiration
61+
)
62+
}
63+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
package software.amazon.smithy.aws.swift.codegen.customization.rds
6+
7+
import software.amazon.smithy.aws.swift.codegen.sdkId
8+
import software.amazon.smithy.model.Model
9+
import software.amazon.smithy.model.shapes.ServiceShape
10+
import software.amazon.smithy.swift.codegen.SwiftDelegator
11+
import software.amazon.smithy.swift.codegen.SwiftSettings
12+
import software.amazon.smithy.swift.codegen.core.SwiftCodegenContext
13+
import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator
14+
import software.amazon.smithy.swift.codegen.integration.SwiftIntegration
15+
import software.amazon.smithy.swift.codegen.model.expectShape
16+
17+
class AuthTokenGeneratorIntegration : SwiftIntegration {
18+
override fun enabledForService(model: Model, settings: SwiftSettings): Boolean =
19+
model.expectShape<ServiceShape>(settings.service).sdkId == "RDS"
20+
21+
override fun writeAdditionalFiles(
22+
ctx: SwiftCodegenContext,
23+
protocolGenerationContext: ProtocolGenerator.GenerationContext,
24+
delegator: SwiftDelegator
25+
) {
26+
delegator.useFileWriter("Sources/${ctx.settings.moduleName}/AuthTokenGenerator.swift") { writer ->
27+
val authTokenGeneratorWrapperClass = """
28+
@_spi(AuthTokenGenerator) import class AWSClientRuntime.AuthTokenGenerator
29+
import SmithyIdentity
30+
import struct Foundation.TimeInterval
31+
32+
/// A utility class with a single utility method that generates IAM authentication token used for connecting to RDS.
33+
public class AuthTokenGenerator {
34+
private let generator: AWSClientRuntime.AuthTokenGenerator
35+
36+
/// The initializer that takes in AWSCredentialIdentity struct to use to generate the IAM authentication token.
37+
public init(awsCredentialIdentity: AWSCredentialIdentity) {
38+
self.generator = AWSClientRuntime.AuthTokenGenerator(awsCredentialIdentity: awsCredentialIdentity)
39+
}
40+
41+
/// The initializer that takes in a specific AWSCredentialIdentityResolver, used to resolve the AWSCredentialIdentity used to generate the IAM authentication token.
42+
public init(awsCredentialIdentityResolver: any AWSCredentialIdentityResolver) async throws {
43+
self.generator = try await AWSClientRuntime.AuthTokenGenerator(awsCredentialIdentityResolver: awsCredentialIdentityResolver)
44+
}
45+
46+
/// Updates the AWS credentials used to generate the IAM auth token.
47+
public func updateCredentials(newAWSCredentialIdentity: AWSCredentialIdentity) {
48+
generator.updateCredentials(newAWSCredentialIdentity: newAWSCredentialIdentity)
49+
}
50+
51+
/// Updates the AWS credentials used to generate the IAM auth token by resolving credentials from passed in resolver.
52+
public func updateCredentials(awsCredentialIdentityResolver: any AWSCredentialIdentityResolver) async throws {
53+
try await generator.updateCredentials(awsCredentialIdentityResolver: awsCredentialIdentityResolver)
54+
}
55+
56+
/// Generates authenetication token using given inputs to the method and credential identity instance variable.
57+
///
58+
/// - Parameters:
59+
/// - endpoint: The endpoint of the RDS instance. E.g., `rdsmysql.123456789012.us-west-2.rds.amazonaws.com`
60+
/// - port: The port of the RDS instance to connect to. E.g., `3306`
61+
/// - region: The region that RDS instance is located in. E.g., `us-west-2`
62+
/// - username: The username of the RDS database user. E.g., `admin`
63+
/// - expiration: The expiration for the token in seconds. Default is 900 seconds (15 minutes).
64+
public func generateAuthToken(
65+
endpoint: String,
66+
port: Int16,
67+
region: String,
68+
username: String,
69+
expiration: TimeInterval = 900
70+
) async throws -> String {
71+
return try await generator.generateAuthToken(
72+
endpoint: endpoint,
73+
port: port,
74+
region: region,
75+
username: username,
76+
expiration: expiration
77+
)
78+
}
79+
}
80+
""".trimIndent()
81+
writer.write(authTokenGeneratorWrapperClass)
82+
}
83+
}
84+
}

codegen/smithy-aws-swift-codegen/src/main/resources/META-INF/services/software.amazon.smithy.swift.codegen.integration.SwiftIntegration

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,4 @@ software.amazon.smithy.aws.swift.codegen.AWSClientConfigurationIntegration
2626
software.amazon.smithy.swift.codegen.swiftintegrations.InitialRequestIntegration
2727
software.amazon.smithy.aws.swift.codegen.swiftintegrations.RegistryConfigIntegration
2828
software.amazon.smithy.aws.swift.codegen.swiftintegrations.AmzSdkRetryHeadersIntegration
29+
software.amazon.smithy.aws.swift.codegen.customization.rds.AuthTokenGeneratorIntegration

0 commit comments

Comments
 (0)