Skip to content

Commit 7ce9287

Browse files
authored
feat(connections): AWS IAM authentication for PostgreSQL and MySQL (#1291) (#1401)
* feat(connections): AWS IAM authentication for PostgreSQL and MySQL (#1291) * fix(connections): register AWS IAM auth fields in plugin metadata so the connection form shows them (#1291) * fix(connections): resolve AWS IAM credentials from Keychain-backed fields on connect (#1291) * fix(plugin-mysql): enable cleartext auth plugin for AWS IAM, add macOS libmariadb build (#1291) * build: rebuild libmariadb with cleartext auth plugin for AWS IAM (#1291) * refactor(plugin-mysql): signal cleartext via generic flag, add registry IAM-field test (#1291)
1 parent 18bbd4f commit 7ce9287

24 files changed

Lines changed: 1226 additions & 42 deletions

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Added
1111

1212
- Fill Column: right-click a column header and choose Fill Column to set one value across all loaded rows. The change is staged like a normal edit, so you review it and Save before it applies, and one undo reverts the whole fill. Not available on primary key columns. (#1304)
13+
- AWS IAM authentication for PostgreSQL and MySQL connections to RDS and Aurora. Pick AWS IAM in the connection's Authentication field and use an access key, a named AWS profile, or SSO. TablePro generates a fresh login token on every connect and reconnect, so you never paste an expiring token, and SSL is required automatically. (#1291)
1314

1415
## [0.44.0] - 2026-05-23
1516

Libs/checksums.sha256

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,60 @@
1-
6c16abade13041111a9551245043b195aa27ce79ce0ab32113962e762f63485f Libs/libbson_arm64.a
2-
9b7d7abb13b36a4856fd8b4a6cc2a2b312c1bb1815ae9891cc30fe605badeb3c Libs/libbson_universal.a
3-
adb2ade8531660c846df3dab6cafc0a6ba15bf4a9d0ae0a1bc8edaca98bf1ffa Libs/libbson_x86_64.a
4-
9b7d7abb13b36a4856fd8b4a6cc2a2b312c1bb1815ae9891cc30fe605badeb3c Libs/libbson.a
1+
b7716e3f295a54feee85c8771332505be2f9a4a430a088d476d60e358d737c9e Libs/libbson.a
2+
36e3a521b8da03bafd0f943c4f3b21c8c573bf9d640c6c9e764c0c3632672849 Libs/libbson_arm64.a
3+
b7716e3f295a54feee85c8771332505be2f9a4a430a088d476d60e358d737c9e Libs/libbson_universal.a
4+
1e502e7fb4edc79639140e18d433a1ed1be2931162daecee71a74d09e9f4c550 Libs/libbson_x86_64.a
5+
9bfd7d7cb4a7ee9823b4c5141e942a8534de63395983388722dc7c98e5d7731e Libs/libcassandra.a
56
8d7e31145470a339f4f57930831936db30412393a339598deece6f650214865a Libs/libcassandra_arm64.a
67
9bfd7d7cb4a7ee9823b4c5141e942a8534de63395983388722dc7c98e5d7731e Libs/libcassandra_universal.a
78
7f1d058c77b66273db2b3867103c19f62ed0518fb38611b178ce04029213d5d8 Libs/libcassandra_x86_64.a
8-
9bfd7d7cb4a7ee9823b4c5141e942a8534de63395983388722dc7c98e5d7731e Libs/libcassandra.a
9+
732adf315bc49f77e2511a9293e49a65e18eb54a3e6d01d8a24eee2d671d2a8a Libs/libcrypto.a
910
a891a67c2619e2ac1dce64dafc6a24bfde9cabe15312dac6b70a19385664ea84 Libs/libcrypto_arm64.a
1011
732adf315bc49f77e2511a9293e49a65e18eb54a3e6d01d8a24eee2d671d2a8a Libs/libcrypto_universal.a
1112
965ccd38fea5cd97bc878dbf58567e4eed2b2337120f8d46a2da62c094b3c821 Libs/libcrypto_x86_64.a
12-
732adf315bc49f77e2511a9293e49a65e18eb54a3e6d01d8a24eee2d671d2a8a Libs/libcrypto.a
13-
69953f30dbc41fb2d12af2471ccc3eea90c465ab775a18cb3eae502c2fa0dd68 Libs/libduckdb_arm64.a
14-
2af0158001439fc4c4f06a5fabb0a5ca4a66b468c0b2c6e808488a399682dc7a Libs/libduckdb_universal.a
15-
66ed8e2e6ac645c09d698028c772649e3276c21757beb4f2eb6cb6589e426eb3 Libs/libduckdb_x86_64.a
16-
2af0158001439fc4c4f06a5fabb0a5ca4a66b468c0b2c6e808488a399682dc7a Libs/libduckdb.a
13+
d95520ba0e250f7c5847cc9dab4bf8a2656fcefd64b35c859f8fae0d37f2f69f Libs/libduckdb.a
14+
1756e47a21076dbfd3bcfb937964dd0af231017f3adc549fdbc114464b304179 Libs/libduckdb_arm64.a
15+
d95520ba0e250f7c5847cc9dab4bf8a2656fcefd64b35c859f8fae0d37f2f69f Libs/libduckdb_universal.a
16+
aa5dfb4014c4b227d842ca20c2572434784cdad2de324afdc28fa8af83965ecd Libs/libduckdb_x86_64.a
17+
c855b0bf6fb8a2f52175a8e212c88a99ddf02890a1f88239613728c145607915 Libs/libhiredis.a
1718
7e63017fa22c2eb7744eccad13857361a5088aa7b2772ab02cd026c8c7b78341 Libs/libhiredis_arm64.a
19+
fb7a32c2c724cb4f3f880030cb19afbbc7db52121ad8e35e00a2e818da9562cf Libs/libhiredis_ssl.a
1820
f1cfc36a7ab47361e9705fe32b1c919b318f606989478e91a808707d93db55a5 Libs/libhiredis_ssl_arm64.a
1921
fb7a32c2c724cb4f3f880030cb19afbbc7db52121ad8e35e00a2e818da9562cf Libs/libhiredis_ssl_universal.a
2022
7eb76bcb7ad4c10da0a0a5d43de182619f74f11c1ae9096823adc5c85280e34b Libs/libhiredis_ssl_x86_64.a
21-
fb7a32c2c724cb4f3f880030cb19afbbc7db52121ad8e35e00a2e818da9562cf Libs/libhiredis_ssl.a
2223
c855b0bf6fb8a2f52175a8e212c88a99ddf02890a1f88239613728c145607915 Libs/libhiredis_universal.a
2324
5e89a8a3b48590f2c68bdcfc0cfde134145e3156d48264c1fd751dc9ef3be505 Libs/libhiredis_x86_64.a
24-
c855b0bf6fb8a2f52175a8e212c88a99ddf02890a1f88239613728c145607915 Libs/libhiredis.a
25-
b777f7a42766fb08c8e67b2310c67d2d463d77d3554c6092221c3352778622b2 Libs/libmariadb_arm64.a
26-
5326ed729b287ae5dbbcf073aaa70dce29a73c7431e446d5958271af19dac8d8 Libs/libmariadb_universal.a
27-
4f7bbb3d73be178d4211c3bd5b2726b4a12db8b808eaa5212bf8e9eb3c570814 Libs/libmariadb_x86_64.a
28-
5326ed729b287ae5dbbcf073aaa70dce29a73c7431e446d5958271af19dac8d8 Libs/libmariadb.a
29-
06268890fb365085d7f093b6941c507fb8f7fa2754fb22c62331ba8e8ae2068a Libs/libmongoc_arm64.a
30-
b063818886170377f6cd1de714157032e3948e8a9616d3488a503423d8045053 Libs/libmongoc_universal.a
31-
51b08ff457246e3032f1a13306f0e540658e91b1c560a7251ce5087a2ff17be0 Libs/libmongoc_x86_64.a
32-
b063818886170377f6cd1de714157032e3948e8a9616d3488a503423d8045053 Libs/libmongoc.a
25+
bdec9e92f4e94ccb20fcfc4c121aab42de510cd214e23c0e334057de11889b9a Libs/libmariadb.a
26+
1fa33b78f52d4815761aeeec20add208719e4f01e0585e417a4d0d6dc98d3e2d Libs/libmariadb_arm64.a
27+
bdec9e92f4e94ccb20fcfc4c121aab42de510cd214e23c0e334057de11889b9a Libs/libmariadb_universal.a
28+
6ca5ea9b190b515108ba577b1db89458c1ac2f3263f2a4e4edcc94d5a66c5984 Libs/libmariadb_x86_64.a
29+
0d7ddc82dc7327a4b5187ffbc68a1419b5e5ff7b2be7b927e16793eef4d34303 Libs/libmongoc.a
30+
9f4c87916ef65eae43b19d7568dc4fd4dffd884dc0cae15913b90965293339a7 Libs/libmongoc_arm64.a
31+
0d7ddc82dc7327a4b5187ffbc68a1419b5e5ff7b2be7b927e16793eef4d34303 Libs/libmongoc_universal.a
32+
635705c7dc8d689efdee5ec1bd8a8cbd0d09ae20db0869480271a293d492de50 Libs/libmongoc_x86_64.a
33+
3ca491a723b9d9dfc13b815659b44a82253b540dd6b115f03ac68c5154ec26db Libs/libpgcommon.a
3334
5dbf2cb5ef37d8adbf607db82461b36a3fd7037c11d891383e6e918378a33d78 Libs/libpgcommon_arm64.a
3435
3ca491a723b9d9dfc13b815659b44a82253b540dd6b115f03ac68c5154ec26db Libs/libpgcommon_universal.a
3536
4bfad7376aefa866d1ed0b7e54966ec6c9d70dcfed928e1311c20321bf08881c Libs/libpgcommon_x86_64.a
36-
3ca491a723b9d9dfc13b815659b44a82253b540dd6b115f03ac68c5154ec26db Libs/libpgcommon.a
37+
efba529b1ad767de988a58ca2c3fdcc26c38ce79df044a988f41fddbf9fde118 Libs/libpgport.a
3738
813b962c5ae1c317bf6facfe68bd1301fa766768e074f3063fc2e8243213fe13 Libs/libpgport_arm64.a
3839
efba529b1ad767de988a58ca2c3fdcc26c38ce79df044a988f41fddbf9fde118 Libs/libpgport_universal.a
3940
bf71cc776245c0ce44bfd7b0286664d5c9771992fd70ec32a0c27fc669e4422f Libs/libpgport_x86_64.a
40-
efba529b1ad767de988a58ca2c3fdcc26c38ce79df044a988f41fddbf9fde118 Libs/libpgport.a
41+
b86ecf68d2b0dd8aa7712d13607c9584df2297aca4cd651428e8ee974c6bdf80 Libs/libpq.a
4142
70cb70b88130c1c88ccf108e31e17d45dbbc2d10267db7ff33d63305a6a05baf Libs/libpq_arm64.a
4243
b86ecf68d2b0dd8aa7712d13607c9584df2297aca4cd651428e8ee974c6bdf80 Libs/libpq_universal.a
4344
1ce2b45af228915fad05e07f54e96621af7143e199e002e5100777261a7f4a13 Libs/libpq_x86_64.a
44-
b86ecf68d2b0dd8aa7712d13607c9584df2297aca4cd651428e8ee974c6bdf80 Libs/libpq.a
45+
445b51e6fdaa0a0eceb8090e6d552a551ec15d91e4370a4cc356c8f561e8b469 Libs/libssh2.a
4546
166e0e23ce60fd2edcae38b6005de106394f7e2bc922a4944317d6aa576f284c Libs/libssh2_arm64.a
4647
445b51e6fdaa0a0eceb8090e6d552a551ec15d91e4370a4cc356c8f561e8b469 Libs/libssh2_universal.a
4748
76681299c4305273cea62e59cfa366ceb5cc320831b87fd6a06143d342f8b7db Libs/libssh2_x86_64.a
48-
445b51e6fdaa0a0eceb8090e6d552a551ec15d91e4370a4cc356c8f561e8b469 Libs/libssh2.a
49+
3ca208dedf57dbae4f5cb0a22bfbedeba80dc6740d626484d9d815811d64a2aa Libs/libssl.a
4950
b3861975896ebf35255d8c3efccdc59ad39874c9b70fdd710ebd15f0a58c4e10 Libs/libssl_arm64.a
5051
3ca208dedf57dbae4f5cb0a22bfbedeba80dc6740d626484d9d815811d64a2aa Libs/libssl_universal.a
5152
34de647ccd0951095f987591562a5236348bac2d4b3e217877559a7b170cf4e4 Libs/libssl_x86_64.a
52-
3ca208dedf57dbae4f5cb0a22bfbedeba80dc6740d626484d9d815811d64a2aa Libs/libssl.a
53+
071e9853ec4bb1f6a19ed99eb91cfe823e83bad178e1e1997deee414cd0e4dfc Libs/libsybdb.a
5354
38a16ca8a041c1be3ca6d4884f7c5e196d14f60bee80004c8f54a41899c17e0f Libs/libsybdb_arm64.a
5455
071e9853ec4bb1f6a19ed99eb91cfe823e83bad178e1e1997deee414cd0e4dfc Libs/libsybdb_universal.a
5556
e437cf1fab3eaf675bdb5aab4443a891763e5325033ddfe369775bd64a22b57b Libs/libsybdb_x86_64.a
56-
071e9853ec4bb1f6a19ed99eb91cfe823e83bad178e1e1997deee414cd0e4dfc Libs/libsybdb.a
57+
8f8135b8214cfef035b49486a863f891979efc04d97d75e2bc14cb4e28aed233 Libs/libuv.a
5758
beff08628396ffb7c2e23b9f1db08ce92be215fbfd50c6e62088e216d73a0897 Libs/libuv_arm64.a
5859
8f8135b8214cfef035b49486a863f891979efc04d97d75e2bc14cb4e28aed233 Libs/libuv_universal.a
5960
2592a74df696709dcc631e9ad48894763157e9c5a34f0cb6a23a4036bce0c472 Libs/libuv_x86_64.a
60-
8f8135b8214cfef035b49486a863f891979efc04d97d75e2bc14cb4e28aed233 Libs/libuv.a

Plugins/MySQLDriverPlugin/MariaDBPluginConnection.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ final class MariaDBPluginConnection: @unchecked Sendable {
144144
private let password: String?
145145
private let database: String
146146
private let sslConfig: SSLConfiguration
147+
private let enableCleartextPlugin: Bool
147148

148149
private let stateLock = NSLock()
149150
private var _isConnected: Bool = false
@@ -176,14 +177,16 @@ final class MariaDBPluginConnection: @unchecked Sendable {
176177
user: String,
177178
password: String?,
178179
database: String,
179-
sslConfig: SSLConfiguration
180+
sslConfig: SSLConfiguration,
181+
enableCleartextPlugin: Bool = false
180182
) {
181183
self.host = host
182184
self.port = UInt32(port)
183185
self.user = user
184186
self.password = password
185187
self.database = database
186188
self.sslConfig = sslConfig
189+
self.enableCleartextPlugin = enableCleartextPlugin
187190
}
188191

189192
deinit {
@@ -291,6 +294,11 @@ final class MariaDBPluginConnection: @unchecked Sendable {
291294

292295
mysql_options(mysql, MYSQL_SET_CHARSET_NAME, "utf8mb4")
293296

297+
if enableCleartextPlugin {
298+
var enableCleartext: my_bool = 1
299+
mysql_options(mysql, MYSQL_ENABLE_CLEARTEXT_PLUGIN, &enableCleartext)
300+
}
301+
294302
let dbToUse = database.isEmpty ? nil : database
295303
let passToUse = password
296304

Plugins/MySQLDriverPlugin/MySQLPlugin.swift

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,58 @@ final class MySQLPlugin: NSObject, TableProPlugin, DriverPlugin {
2222
static let databaseDisplayName = "MySQL"
2323
static let iconName = "mysql-icon"
2424
static let defaultPort = 3306
25-
static let additionalConnectionFields: [ConnectionField] = []
25+
static let additionalConnectionFields: [ConnectionField] = [
26+
ConnectionField(
27+
id: "awsAuth",
28+
label: String(localized: "Authentication"),
29+
defaultValue: "off",
30+
fieldType: .dropdown(options: [
31+
.init(value: "off", label: String(localized: "Password")),
32+
.init(value: "accessKey", label: String(localized: "AWS IAM (Access Key)")),
33+
.init(value: "profile", label: String(localized: "AWS IAM (Profile)")),
34+
.init(value: "sso", label: String(localized: "AWS IAM (SSO)"))
35+
]),
36+
section: .authentication,
37+
hidesPassword: true
38+
),
39+
ConnectionField(
40+
id: "awsRegion",
41+
label: String(localized: "AWS Region"),
42+
placeholder: "us-east-1",
43+
section: .authentication,
44+
visibleWhen: FieldVisibilityRule(fieldId: "awsAuth", values: ["accessKey", "profile", "sso"])
45+
),
46+
ConnectionField(
47+
id: "awsAccessKeyId",
48+
label: String(localized: "Access Key ID"),
49+
placeholder: "AKIA...",
50+
section: .authentication,
51+
visibleWhen: FieldVisibilityRule(fieldId: "awsAuth", values: ["accessKey"])
52+
),
53+
ConnectionField(
54+
id: "awsSecretAccessKey",
55+
label: String(localized: "Secret Access Key"),
56+
placeholder: "wJalr...",
57+
fieldType: .secure,
58+
section: .authentication,
59+
visibleWhen: FieldVisibilityRule(fieldId: "awsAuth", values: ["accessKey"])
60+
),
61+
ConnectionField(
62+
id: "awsSessionToken",
63+
label: String(localized: "Session Token"),
64+
placeholder: String(localized: "Optional, for temporary credentials"),
65+
fieldType: .secure,
66+
section: .authentication,
67+
visibleWhen: FieldVisibilityRule(fieldId: "awsAuth", values: ["accessKey"])
68+
),
69+
ConnectionField(
70+
id: "awsProfileName",
71+
label: String(localized: "Profile Name"),
72+
placeholder: "default",
73+
section: .authentication,
74+
visibleWhen: FieldVisibilityRule(fieldId: "awsAuth", values: ["profile", "sso"])
75+
),
76+
]
2677
static let additionalDatabaseTypeIds: [String] = ["MariaDB"]
2778

2879
// MARK: - UI/Capability Metadata

Plugins/MySQLDriverPlugin/MySQLPluginDriver.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ final class MySQLPluginDriver: PluginDatabaseDriver, @unchecked Sendable {
7575
user: config.username,
7676
password: config.password,
7777
database: _activeDatabase,
78-
sslConfig: sslConfig
78+
sslConfig: sslConfig,
79+
enableCleartextPlugin: config.additionalFields["enableCleartextPlugin"] == "true"
7980
)
8081

8182
try await conn.connect()

Plugins/PostgreSQLDriverPlugin/PostgreSQLPlugin.swift

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,56 @@ final class PostgreSQLPlugin: NSObject, TableProPlugin, DriverPlugin {
3030
section: .authentication,
3131
hidesPassword: true
3232
),
33+
ConnectionField(
34+
id: "awsAuth",
35+
label: String(localized: "Authentication"),
36+
defaultValue: "off",
37+
fieldType: .dropdown(options: [
38+
.init(value: "off", label: String(localized: "Password")),
39+
.init(value: "accessKey", label: String(localized: "AWS IAM (Access Key)")),
40+
.init(value: "profile", label: String(localized: "AWS IAM (Profile)")),
41+
.init(value: "sso", label: String(localized: "AWS IAM (SSO)"))
42+
]),
43+
section: .authentication,
44+
hidesPassword: true
45+
),
46+
ConnectionField(
47+
id: "awsRegion",
48+
label: String(localized: "AWS Region"),
49+
placeholder: "us-east-1",
50+
section: .authentication,
51+
visibleWhen: FieldVisibilityRule(fieldId: "awsAuth", values: ["accessKey", "profile", "sso"])
52+
),
53+
ConnectionField(
54+
id: "awsAccessKeyId",
55+
label: String(localized: "Access Key ID"),
56+
placeholder: "AKIA...",
57+
section: .authentication,
58+
visibleWhen: FieldVisibilityRule(fieldId: "awsAuth", values: ["accessKey"])
59+
),
60+
ConnectionField(
61+
id: "awsSecretAccessKey",
62+
label: String(localized: "Secret Access Key"),
63+
placeholder: "wJalr...",
64+
fieldType: .secure,
65+
section: .authentication,
66+
visibleWhen: FieldVisibilityRule(fieldId: "awsAuth", values: ["accessKey"])
67+
),
68+
ConnectionField(
69+
id: "awsSessionToken",
70+
label: String(localized: "Session Token"),
71+
placeholder: String(localized: "Optional, for temporary credentials"),
72+
fieldType: .secure,
73+
section: .authentication,
74+
visibleWhen: FieldVisibilityRule(fieldId: "awsAuth", values: ["accessKey"])
75+
),
76+
ConnectionField(
77+
id: "awsProfileName",
78+
label: String(localized: "Profile Name"),
79+
placeholder: "default",
80+
section: .authentication,
81+
visibleWhen: FieldVisibilityRule(fieldId: "awsAuth", values: ["profile", "sso"])
82+
),
3383
ConnectionField(
3484
id: "connectionOptions",
3585
label: String(localized: "Connection Options"),

TablePro.xcodeproj/project.pbxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2884,6 +2884,7 @@
28842884
"-lssl.3",
28852885
"-lcrypto.3",
28862886
"-liconv",
2887+
"-lz",
28872888
);
28882889
PRODUCT_BUNDLE_IDENTIFIER = com.TablePro.MySQLDriver;
28892890
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -2923,6 +2924,7 @@
29232924
"-lssl.3",
29242925
"-lcrypto.3",
29252926
"-liconv",
2927+
"-lz",
29262928
);
29272929
PRODUCT_BUNDLE_IDENTIFIER = com.TablePro.MySQLDriver;
29282930
PRODUCT_NAME = "$(TARGET_NAME)";
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//
2+
// AWSAuthError.swift
3+
// TablePro
4+
//
5+
6+
import Foundation
7+
8+
enum AWSAuthError: Error, LocalizedError, Equatable {
9+
case missingAccessKey
10+
case credentialsFileUnreadable
11+
case profileIncomplete(String)
12+
case regionUnknown(host: String)
13+
14+
var errorDescription: String? {
15+
switch self {
16+
case .missingAccessKey:
17+
return String(localized: "Access Key ID and Secret Access Key are required for AWS IAM authentication.")
18+
case .credentialsFileUnreadable:
19+
return String(localized: "Cannot read ~/.aws/credentials.")
20+
case .profileIncomplete(let profile):
21+
return String(
22+
format: String(localized: "Profile \"%@\" was not found or is missing keys in ~/.aws/credentials."),
23+
profile
24+
)
25+
case .regionUnknown(let host):
26+
return String(
27+
format: String(localized: "Could not determine an AWS region for \"%@\". Set the AWS Region field."),
28+
host
29+
)
30+
}
31+
}
32+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
//
2+
// AWSCredentialResolver.swift
3+
// TablePro
4+
//
5+
6+
import Foundation
7+
8+
enum AWSCredentialResolver {
9+
static func resolve(source: String, fields: [String: String]) async throws -> AWSCredentials {
10+
switch source {
11+
case "profile":
12+
return try resolveProfile(fields: fields)
13+
case "sso":
14+
return try await resolveSSO(fields: fields)
15+
default:
16+
return try resolveAccessKey(fields: fields)
17+
}
18+
}
19+
20+
private static func resolveAccessKey(fields: [String: String]) throws -> AWSCredentials {
21+
let accessKeyId = fields["awsAccessKeyId"] ?? ""
22+
let secretAccessKey = fields["awsSecretAccessKey"] ?? ""
23+
let sessionToken = fields["awsSessionToken"]
24+
25+
guard !accessKeyId.isEmpty, !secretAccessKey.isEmpty else {
26+
throw AWSAuthError.missingAccessKey
27+
}
28+
29+
return AWSCredentials(
30+
accessKeyId: accessKeyId,
31+
secretAccessKey: secretAccessKey,
32+
sessionToken: sessionToken?.isEmpty == true ? nil : sessionToken
33+
)
34+
}
35+
36+
private static func resolveProfile(fields: [String: String]) throws -> AWSCredentials {
37+
let profileName = fields["awsProfileName"].flatMap { $0.isEmpty ? nil : $0 } ?? "default"
38+
let credentialsPath = NSString("~/.aws/credentials").expandingTildeInPath
39+
40+
guard let content = try? String(contentsOfFile: credentialsPath, encoding: .utf8) else {
41+
throw AWSAuthError.credentialsFileUnreadable
42+
}
43+
44+
let sections = AWSSSO.parseIniSections(content)
45+
guard let profile = sections[profileName] else {
46+
throw AWSAuthError.profileIncomplete(profileName)
47+
}
48+
49+
let accessKeyId = profile["aws_access_key_id"] ?? ""
50+
let secretAccessKey = profile["aws_secret_access_key"] ?? ""
51+
guard !accessKeyId.isEmpty, !secretAccessKey.isEmpty else {
52+
throw AWSAuthError.profileIncomplete(profileName)
53+
}
54+
55+
return AWSCredentials(
56+
accessKeyId: accessKeyId,
57+
secretAccessKey: secretAccessKey,
58+
sessionToken: profile["aws_session_token"]
59+
)
60+
}
61+
62+
private static func resolveSSO(fields: [String: String]) async throws -> AWSCredentials {
63+
let profileName = fields["awsProfileName"].flatMap { $0.isEmpty ? nil : $0 } ?? "default"
64+
let configPath = NSString("~/.aws/config").expandingTildeInPath
65+
let cacheDir = NSString("~/.aws/sso/cache").expandingTildeInPath
66+
67+
guard let configContent = try? String(contentsOfFile: configPath, encoding: .utf8) else {
68+
throw AWSSSOError.configReadFailed
69+
}
70+
71+
let settings = try AWSSSO.parseProfileSettings(configContent: configContent, profileName: profileName)
72+
let accessToken = try AWSSSO.readAccessToken(
73+
cacheDirectory: cacheDir,
74+
settings: settings,
75+
profileName: profileName
76+
)
77+
let credentials = try await AWSSSO.fetchRoleCredentials(
78+
accessToken: accessToken,
79+
settings: settings,
80+
profileName: profileName,
81+
session: URLSession.shared
82+
)
83+
return AWSCredentials(
84+
accessKeyId: credentials.accessKeyId,
85+
secretAccessKey: credentials.secretAccessKey,
86+
sessionToken: credentials.sessionToken
87+
)
88+
}
89+
}

0 commit comments

Comments
 (0)