Skip to content

Commit 7dda335

Browse files
committed
fix: etcd plugin audit fixes, missing features, and comprehensive tests
1 parent 57def9c commit 7dda335

10 files changed

Lines changed: 2195 additions & 19 deletions

Plugins/EtcdDriverPlugin/EtcdHttpClient.swift

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -907,10 +907,10 @@ final class EtcdHttpClient: @unchecked Sendable {
907907
SecTrustSetPolicies(serverTrust, policy)
908908
}
909909

910-
var secResult: SecTrustResultType = .invalid
911-
SecTrustEvaluate(serverTrust, &secResult)
910+
var error: CFError?
911+
let isValid = SecTrustEvaluateWithError(serverTrust, &error)
912912

913-
if secResult == .unspecified || secResult == .proceed {
913+
if isValid {
914914
completionHandler(.useCredential, URLCredential(trust: serverTrust))
915915
} else {
916916
completionHandler(.cancelAuthenticationChallenge, nil)
@@ -944,8 +944,10 @@ final class EtcdHttpClient: @unchecked Sendable {
944944
return
945945
}
946946

947-
// swiftlint:disable:next force_cast
948-
let identity = identityRef as! SecIdentity
947+
guard let identity = identityRef as? SecIdentity else {
948+
completionHandler(.cancelAuthenticationChallenge, nil)
949+
return
950+
}
949951
let credential = URLCredential(
950952
identity: identity,
951953
certificates: nil,

Plugins/EtcdDriverPlugin/EtcdPluginDriver.swift

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ final class EtcdPluginDriver: PluginDatabaseDriver, @unchecked Sendable {
2323

2424
private static let logger = Logger(subsystem: "com.TablePro.EtcdDriver", category: "EtcdPluginDriver")
2525
private static let maxKeys = PluginRowLimits.defaultMax
26-
private static let maxOffset = 10_000
26+
2727

2828
private static let columns = ["Key", "Value", "Version", "ModRevision", "CreateRevision", "Lease"]
2929
private static let columnTypeNames = ["String", "String", "Int64", "Int64", "Int64", "String"]
@@ -34,17 +34,36 @@ final class EtcdPluginDriver: PluginDatabaseDriver, @unchecked Sendable {
3434

3535
var supportsTransactions: Bool { false }
3636

37+
// etcd has no transaction support — these are no-ops
3738
func beginTransaction() async throws {}
3839
func commitTransaction() async throws {}
3940
func rollbackTransaction() async throws {}
4041

4142
func quoteIdentifier(_ name: String) -> String { name }
4243

44+
func escapeStringLiteral(_ value: String) -> String { value }
45+
4346
func defaultExportQuery(table: String) -> String? {
4447
let prefix = resolvedPrefix(for: table)
4548
return "get \(escapeArgument(prefix)) --prefix"
4649
}
4750

51+
func truncateTableStatements(table: String, cascade: Bool) -> [String]? {
52+
let prefix = resolvedPrefix(for: table)
53+
if prefix.isEmpty {
54+
return ["del \"\" --prefix"]
55+
}
56+
return ["del \(escapeArgument(prefix)) --prefix"]
57+
}
58+
59+
func dropObjectStatement(name: String, type: String) -> String? {
60+
let prefix = resolvedPrefix(for: name)
61+
if prefix.isEmpty {
62+
return "del \"\" --prefix"
63+
}
64+
return "del \(escapeArgument(prefix)) --prefix"
65+
}
66+
4867
init(config: DriverConnectionConfig) {
4968
self.config = config
5069
self._rootPrefix = config.additionalFields["etcdKeyPrefix"] ?? config.database
@@ -59,9 +78,8 @@ final class EtcdPluginDriver: PluginDatabaseDriver, @unchecked Sendable {
5978
let status = try? await client.endpointStatus()
6079
lock.withLock {
6180
_serverVersion = status?.version
81+
_httpClient = client
6282
}
63-
64-
lock.withLock { _httpClient = client }
6583
}
6684

6785
func disconnect() {
@@ -280,7 +298,7 @@ final class EtcdPluginDriver: PluginDatabaseDriver, @unchecked Sendable {
280298
}
281299

282300
func fetchViewDefinition(view: String, schema: String?) async throws -> String {
283-
""
301+
throw EtcdError.serverError("etcd does not support views")
284302
}
285303

286304
func fetchTableMetadata(table: String, schema: String?) async throws -> PluginTableMetadata {
@@ -847,11 +865,12 @@ final class EtcdPluginDriver: PluginDatabaseDriver, @unchecked Sendable {
847865
let response = try await client.rangeRequest(req)
848866
var kvs = response.kvs ?? []
849867

850-
// Apply client-side filter if needed
868+
// Apply client-side filter if needed (checks both key and value)
851869
if needsClientFilter {
852870
kvs = kvs.filter { kv in
853871
let key = EtcdHttpClient.base64Decode(kv.key)
854-
return matchesFilter(key: key, filterType: filterType, filterValue: filterValue)
872+
let value = kv.value.map { EtcdHttpClient.base64Decode($0) }
873+
return matchesFilter(key: key, value: value, filterType: filterType, filterValue: filterValue)
855874
}
856875
}
857876

@@ -881,14 +900,16 @@ final class EtcdPluginDriver: PluginDatabaseDriver, @unchecked Sendable {
881900
return Int(response.count ?? "0") ?? 0
882901
}
883902

884-
// Need to fetch keys and filter client-side
885-
let req = EtcdRangeRequest(key: b64Key, rangeEnd: b64RangeEnd, limit: Int64(Self.maxKeys), keysOnly: true)
903+
// Need to fetch keys (and values for contains/startsWith filters) and filter client-side
904+
let needsValues = filterType == .contains || filterType == .startsWith
905+
let req = EtcdRangeRequest(key: b64Key, rangeEnd: b64RangeEnd, limit: Int64(Self.maxKeys), keysOnly: !needsValues)
886906
let response = try await client.rangeRequest(req)
887907
let kvs = response.kvs ?? []
888908

889909
return kvs.filter { kv in
890910
let key = EtcdHttpClient.base64Decode(kv.key)
891-
return matchesFilter(key: key, filterType: filterType, filterValue: filterValue)
911+
let value = kv.value.map { EtcdHttpClient.base64Decode($0) }
912+
return matchesFilter(key: key, value: value, filterType: filterType, filterValue: filterValue)
892913
}.count
893914
}
894915

@@ -916,7 +937,8 @@ final class EtcdPluginDriver: PluginDatabaseDriver, @unchecked Sendable {
916937
return table
917938
}
918939
let root = _rootPrefix.hasSuffix("/") ? _rootPrefix : _rootPrefix + "/"
919-
return root + table
940+
let cleanTable = table.hasPrefix("/") ? String(table.dropFirst()) : table
941+
return root + cleanTable
920942
}
921943

922944
private func stripRootPrefix(_ key: String) -> String {
@@ -928,14 +950,21 @@ final class EtcdPluginDriver: PluginDatabaseDriver, @unchecked Sendable {
928950
return key
929951
}
930952

931-
private func matchesFilter(key: String, filterType: EtcdFilterType, filterValue: String) -> Bool {
953+
private func matchesFilter(key: String, value: String? = nil, filterType: EtcdFilterType, filterValue: String) -> Bool {
932954
switch filterType {
933955
case .none:
934956
return true
935957
case .contains:
936-
return key.localizedCaseInsensitiveContains(filterValue)
958+
if key.localizedCaseInsensitiveContains(filterValue) {
959+
return true
960+
}
961+
return value?.localizedCaseInsensitiveContains(filterValue) ?? false
937962
case .startsWith:
938-
return key.lowercased().hasPrefix(filterValue.lowercased())
963+
let lowerFilter = filterValue.lowercased()
964+
if key.lowercased().hasPrefix(lowerFilter) {
965+
return true
966+
}
967+
return value?.lowercased().hasPrefix(lowerFilter) ?? false
939968
case .endsWith:
940969
return key.lowercased().hasSuffix(filterValue.lowercased())
941970
case .equals:

Plugins/EtcdDriverPlugin/EtcdStatementGenerator.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,12 @@ struct EtcdStatementGenerator {
7474
}
7575

7676
// Prepend the current browse prefix if the key doesn't already include it
77-
let fullKey = !prefix.isEmpty && !k.hasPrefix(prefix) ? prefix + k : k
77+
let fullKey: String
78+
if !prefix.isEmpty && !k.hasPrefix("/") {
79+
fullKey = prefix + k
80+
} else {
81+
fullKey = k
82+
}
7883
let v = value ?? ""
7984
var cmd = "put \(escapeArgument(fullKey)) \(escapeArgument(v))"
8085
if let lease = leaseId, !lease.isEmpty, lease != "0" {
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../Plugins/EtcdDriverPlugin/EtcdCommandParser.swift
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../Plugins/EtcdDriverPlugin/EtcdQueryBuilder.swift
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../Plugins/EtcdDriverPlugin/EtcdStatementGenerator.swift

0 commit comments

Comments
 (0)