Skip to content

Commit 357d8de

Browse files
committed
fix: address third round of PR review feedback
1 parent aa574cc commit 357d8de

1 file changed

Lines changed: 77 additions & 61 deletions

File tree

Plugins/EtcdDriverPlugin/EtcdHttpClient.swift

Lines changed: 77 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import TableProPluginKit
1010

1111
// MARK: - Error Types
1212

13-
enum EtcdError: Error, LocalizedError {
13+
internal enum EtcdError: Error, LocalizedError {
1414
case notConnected
1515
case connectionFailed(String)
1616
case serverError(String)
@@ -35,7 +35,7 @@ enum EtcdError: Error, LocalizedError {
3535

3636
// MARK: - Codable Types
3737

38-
struct EtcdResponseHeader: Decodable {
38+
internal struct EtcdResponseHeader: Decodable {
3939
let clusterId: String?
4040
let memberId: String?
4141
let revision: String?
@@ -49,7 +49,7 @@ struct EtcdResponseHeader: Decodable {
4949
}
5050
}
5151

52-
struct EtcdKeyValue: Decodable {
52+
internal struct EtcdKeyValue: Decodable {
5353
let key: String
5454
let value: String?
5555
let version: String?
@@ -69,7 +69,7 @@ struct EtcdKeyValue: Decodable {
6969

7070
// KV Request/Response
7171

72-
struct EtcdRangeRequest: Encodable {
72+
internal struct EtcdRangeRequest: Encodable {
7373
let key: String
7474
var rangeEnd: String?
7575
var limit: Int64?
@@ -89,13 +89,13 @@ struct EtcdRangeRequest: Encodable {
8989
}
9090
}
9191

92-
struct EtcdRangeResponse: Decodable {
92+
internal struct EtcdRangeResponse: Decodable {
9393
let kvs: [EtcdKeyValue]?
9494
let count: String?
9595
let more: Bool?
9696
}
9797

98-
struct EtcdPutRequest: Encodable {
98+
internal struct EtcdPutRequest: Encodable {
9999
let key: String
100100
let value: String
101101
var lease: String?
@@ -109,7 +109,7 @@ struct EtcdPutRequest: Encodable {
109109
}
110110
}
111111

112-
struct EtcdPutResponse: Decodable {
112+
internal struct EtcdPutResponse: Decodable {
113113
let header: EtcdResponseHeader?
114114
let prevKv: EtcdKeyValue?
115115

@@ -119,7 +119,7 @@ struct EtcdPutResponse: Decodable {
119119
}
120120
}
121121

122-
struct EtcdDeleteRequest: Encodable {
122+
internal struct EtcdDeleteRequest: Encodable {
123123
let key: String
124124
var rangeEnd: String?
125125
var prevKv: Bool?
@@ -131,7 +131,7 @@ struct EtcdDeleteRequest: Encodable {
131131
}
132132
}
133133

134-
struct EtcdDeleteResponse: Decodable {
134+
internal struct EtcdDeleteResponse: Decodable {
135135
let deleted: String?
136136
let prevKvs: [EtcdKeyValue]?
137137

@@ -143,57 +143,57 @@ struct EtcdDeleteResponse: Decodable {
143143

144144
// Lease
145145

146-
struct EtcdLeaseGrantRequest: Encodable {
146+
internal struct EtcdLeaseGrantRequest: Encodable {
147147
let TTL: String
148148
var ID: String?
149149
}
150150

151-
struct EtcdLeaseGrantResponse: Decodable {
151+
internal struct EtcdLeaseGrantResponse: Decodable {
152152
let ID: String?
153153
let TTL: String?
154154
let error: String?
155155
}
156156

157-
struct EtcdLeaseRevokeRequest: Encodable {
157+
internal struct EtcdLeaseRevokeRequest: Encodable {
158158
let ID: String
159159
}
160160

161-
struct EtcdLeaseTimeToLiveRequest: Encodable {
161+
internal struct EtcdLeaseTimeToLiveRequest: Encodable {
162162
let ID: String
163163
let keys: Bool?
164164
}
165165

166-
struct EtcdLeaseTimeToLiveResponse: Decodable {
166+
internal struct EtcdLeaseTimeToLiveResponse: Decodable {
167167
let ID: String?
168168
let TTL: String?
169169
let grantedTTL: String?
170170
let keys: [String]?
171171
}
172172

173-
struct EtcdLeaseListResponse: Decodable {
173+
internal struct EtcdLeaseListResponse: Decodable {
174174
let leases: [EtcdLeaseStatus]?
175175
}
176176

177-
struct EtcdLeaseStatus: Decodable {
177+
internal struct EtcdLeaseStatus: Decodable {
178178
let ID: String
179179
}
180180

181181
// Cluster
182182

183-
struct EtcdMemberListResponse: Decodable {
183+
internal struct EtcdMemberListResponse: Decodable {
184184
let members: [EtcdMember]?
185185
let header: EtcdResponseHeader?
186186
}
187187

188-
struct EtcdMember: Decodable {
188+
internal struct EtcdMember: Decodable {
189189
let ID: String?
190190
let name: String?
191191
let peerURLs: [String]?
192192
let clientURLs: [String]?
193193
let isLearner: Bool?
194194
}
195195

196-
struct EtcdStatusResponse: Decodable {
196+
internal struct EtcdStatusResponse: Decodable {
197197
let version: String?
198198
let dbSize: String?
199199
let leader: String?
@@ -204,15 +204,15 @@ struct EtcdStatusResponse: Decodable {
204204

205205
// Watch
206206

207-
struct EtcdWatchRequest: Encodable {
207+
internal struct EtcdWatchRequest: Encodable {
208208
let createRequest: EtcdWatchCreateRequest
209209

210210
private enum CodingKeys: String, CodingKey {
211211
case createRequest = "create_request"
212212
}
213213
}
214214

215-
struct EtcdWatchCreateRequest: Encodable {
215+
internal struct EtcdWatchCreateRequest: Encodable {
216216
let key: String
217217
var rangeEnd: String?
218218

@@ -222,16 +222,16 @@ struct EtcdWatchCreateRequest: Encodable {
222222
}
223223
}
224224

225-
struct EtcdWatchStreamResponse: Decodable {
225+
internal struct EtcdWatchStreamResponse: Decodable {
226226
let result: EtcdWatchResult?
227227
}
228228

229-
struct EtcdWatchResult: Decodable {
229+
internal struct EtcdWatchResult: Decodable {
230230
let events: [EtcdWatchEvent]?
231231
let header: EtcdResponseHeader?
232232
}
233233

234-
struct EtcdWatchEvent: Decodable {
234+
internal struct EtcdWatchEvent: Decodable {
235235
let type: String?
236236
let kv: EtcdKeyValue?
237237
let prevKv: EtcdKeyValue?
@@ -245,53 +245,53 @@ struct EtcdWatchEvent: Decodable {
245245

246246
// Auth
247247

248-
struct EtcdAuthRequest: Encodable {
248+
internal struct EtcdAuthRequest: Encodable {
249249
let name: String
250250
let password: String
251251
}
252252

253-
struct EtcdAuthResponse: Decodable {
253+
internal struct EtcdAuthResponse: Decodable {
254254
let token: String?
255255
}
256256

257-
struct EtcdUserAddRequest: Encodable {
257+
internal struct EtcdUserAddRequest: Encodable {
258258
let name: String
259259
let password: String
260260
}
261261

262-
struct EtcdUserDeleteRequest: Encodable {
262+
internal struct EtcdUserDeleteRequest: Encodable {
263263
let name: String
264264
}
265265

266-
struct EtcdUserListResponse: Decodable {
266+
internal struct EtcdUserListResponse: Decodable {
267267
let users: [String]?
268268
}
269269

270-
struct EtcdRoleAddRequest: Encodable {
270+
internal struct EtcdRoleAddRequest: Encodable {
271271
let name: String
272272
}
273273

274-
struct EtcdRoleDeleteRequest: Encodable {
274+
internal struct EtcdRoleDeleteRequest: Encodable {
275275
let name: String
276276
}
277277

278-
struct EtcdRoleListResponse: Decodable {
278+
internal struct EtcdRoleListResponse: Decodable {
279279
let roles: [String]?
280280
}
281281

282-
struct EtcdUserGrantRoleRequest: Encodable {
282+
internal struct EtcdUserGrantRoleRequest: Encodable {
283283
let user: String
284284
let role: String
285285
}
286286

287-
struct EtcdUserRevokeRoleRequest: Encodable {
287+
internal struct EtcdUserRevokeRoleRequest: Encodable {
288288
let user: String
289289
let role: String
290290
}
291291

292292
// Maintenance
293293

294-
struct EtcdCompactionRequest: Encodable {
294+
internal struct EtcdCompactionRequest: Encodable {
295295
let revision: String
296296
let physical: Bool?
297297
}
@@ -306,7 +306,7 @@ private struct EtcdErrorResponse: Decodable {
306306

307307
// MARK: - HTTP Client
308308

309-
final class EtcdHttpClient: @unchecked Sendable {
309+
internal final class EtcdHttpClient: @unchecked Sendable {
310310
private let config: DriverConnectionConfig
311311
private let lock = NSLock()
312312
private var session: URLSession?
@@ -344,12 +344,8 @@ final class EtcdHttpClient: @unchecked Sendable {
344344
let delegate: URLSessionDelegate?
345345
switch tlsMode {
346346
case "Required":
347-
delegate = EtcdTlsDelegate(
348-
caCertPath: nil,
349-
clientCertPath: config.additionalFields["etcdClientCertPath"],
350-
clientKeyPath: config.additionalFields["etcdClientKeyPath"],
351-
verifyHostname: false
352-
)
347+
// Encryption without certificate verification — matches UI "Required (skip verify)"
348+
delegate = InsecureTlsDelegate()
353349
case "VerifyCA", "VerifyIdentity":
354350
delegate = EtcdTlsDelegate(
355351
caCertPath: config.additionalFields["etcdCaCertPath"],
@@ -505,15 +501,15 @@ final class EtcdHttpClient: @unchecked Sendable {
505501
self.lock.lock()
506502
self.currentTask = task
507503
self.lock.unlock()
508-
collectedData.task = task
504+
collectedData.setTask(task)
509505
task.resume()
510506
}
511507
return Self.parseWatchEvents(from: data)
512508
}
513509

514510
group.addTask {
515511
try await Task.sleep(nanoseconds: UInt64(timeout * 1_000_000_000))
516-
collectedData.task?.cancel()
512+
collectedData.cancelTask()
517513
return []
518514
}
519515

@@ -827,7 +823,21 @@ final class EtcdHttpClient: @unchecked Sendable {
827823
// MARK: - Data Collector for Watch
828824

829825
private final class DataCollector: @unchecked Sendable {
830-
var task: URLSessionDataTask?
826+
private let lock = NSLock()
827+
private var _task: URLSessionDataTask?
828+
829+
func setTask(_ task: URLSessionDataTask) {
830+
lock.lock()
831+
_task = task
832+
lock.unlock()
833+
}
834+
835+
func cancelTask() {
836+
lock.lock()
837+
let task = _task
838+
lock.unlock()
839+
task?.cancel()
840+
}
831841
}
832842

833843
// MARK: - TLS Delegates
@@ -945,8 +955,10 @@ final class EtcdHttpClient: @unchecked Sendable {
945955
return
946956
}
947957

948-
// swiftlint:disable:next force_cast
949-
let identity = identityRef as! SecIdentity
958+
guard let identity = identityRef as? SecIdentity else {
959+
completionHandler(.cancelAuthenticationChallenge, nil)
960+
return
961+
}
950962
let credential = URLCredential(
951963
identity: identity,
952964
certificates: nil,
@@ -1027,38 +1039,42 @@ final class EtcdHttpClient: @unchecked Sendable {
10271039
}
10281040

10291041
private func createIdentity(certificate: SecCertificate, privateKey: SecKey) -> SecIdentity? {
1030-
// Add cert and key to a temporary keychain to get an identity
1042+
// Add cert and key to the keychain temporarily to create an identity
10311043
let addCertQuery: [String: Any] = [
10321044
kSecClass as String: kSecClassCertificate,
10331045
kSecValueRef as String: certificate,
10341046
kSecReturnRef as String: true
10351047
]
10361048
var certRef: CFTypeRef?
1037-
SecItemAdd(addCertQuery as CFDictionary, &certRef)
1049+
let certAddStatus = SecItemAdd(addCertQuery as CFDictionary, &certRef)
10381050

10391051
let addKeyQuery: [String: Any] = [
10401052
kSecClass as String: kSecClassKey,
10411053
kSecValueRef as String: privateKey,
10421054
kSecReturnRef as String: true
10431055
]
10441056
var keyRef: CFTypeRef?
1045-
SecItemAdd(addKeyQuery as CFDictionary, &keyRef)
1057+
let keyAddStatus = SecItemAdd(addKeyQuery as CFDictionary, &keyRef)
10461058

10471059
var identity: SecIdentity?
10481060
let status = SecIdentityCreateWithCertificate(nil, certificate, &identity)
10491061

1050-
// Clean up: remove imported items from keychain
1051-
let deleteCertQuery: [String: Any] = [
1052-
kSecClass as String: kSecClassCertificate,
1053-
kSecValueRef as String: certificate
1054-
]
1055-
SecItemDelete(deleteCertQuery as CFDictionary)
1062+
// Clean up: only delete items that this call actually inserted
1063+
if certAddStatus == errSecSuccess {
1064+
let deleteCertQuery: [String: Any] = [
1065+
kSecClass as String: kSecClassCertificate,
1066+
kSecValueRef as String: certRef ?? certificate
1067+
]
1068+
SecItemDelete(deleteCertQuery as CFDictionary)
1069+
}
10561070

1057-
let deleteKeyQuery: [String: Any] = [
1058-
kSecClass as String: kSecClassKey,
1059-
kSecValueRef as String: privateKey
1060-
]
1061-
SecItemDelete(deleteKeyQuery as CFDictionary)
1071+
if keyAddStatus == errSecSuccess {
1072+
let deleteKeyQuery: [String: Any] = [
1073+
kSecClass as String: kSecClassKey,
1074+
kSecValueRef as String: keyRef ?? privateKey
1075+
]
1076+
SecItemDelete(deleteKeyQuery as CFDictionary)
1077+
}
10621078

10631079
if status == errSecSuccess {
10641080
return identity

0 commit comments

Comments
 (0)