Skip to content

Commit ac2b4db

Browse files
authored
fix: DB implementation (#217)
This PR fixes the DB implementation since it has been updated in [thats PR](gnolang/gno#4656). The main difference is that native function now return error (or exception). Signed-off-by: D4ryl00 <[email protected]>
1 parent 768c3e2 commit ac2b4db

File tree

3 files changed

+247
-69
lines changed

3 files changed

+247
-69
lines changed

expo/android/src/main/java/land/gno/gnonative/NativeDBManager.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ class NativeDBManager(
6565
val b64 = synchronized(lock) { prefs.getString("$entryPrefix$hex", null) }
6666
?: return ByteArray(0) // gomobile generated non-null return -> use empty on miss
6767
val blob = Base64.decode(b64, Base64.NO_WRAP)
68-
return decrypt(blob) ?: ByteArray(0)
68+
return decrypt(blob) ?: throw Exception("Failed to decrypt value for key: $hex")
6969
}
7070

7171
override fun has(p0: ByteArray?): Boolean {
@@ -112,7 +112,8 @@ class NativeDBManager(
112112
val pairs = ArrayList<Pair<ByteArray, ByteArray>>(page.size)
113113
for (h in page) {
114114
val b64 = prefs.getString("$entryPrefix$h", null) ?: continue
115-
val v = decrypt(Base64.decode(b64, Base64.NO_WRAP)) ?: continue
115+
val blob = Base64.decode(b64, Base64.NO_WRAP)
116+
val v = decrypt(blob) ?: throw Exception("Failed to decrypt value for key: $h")
116117
pairs += (unhex(h) to v)
117118
}
118119

expo/ios/NativeDBManager.swift

Lines changed: 85 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,10 @@ public class NativeDBManager: NSObject, GnoGnonativeNativeDBProtocol {
2424

2525
// MARK: - Public Interface Implementation
2626

27-
public func get(_ key: Data?) -> Data? {
28-
guard let key = key else { return nil }
29-
27+
public func get(_ key: Data?) throws -> Data {
28+
guard let key = key, !key.isEmpty else {
29+
throw NativeDBError.invalidArgument(description: "Key must not be nil or empty.")
30+
}
3031
let account = keyToAccount(key)
3132

3233
var query: [String: Any] = [
@@ -44,21 +45,25 @@ public class NativeDBManager: NSObject, GnoGnonativeNativeDBProtocol {
4445
var result: AnyObject?
4546
let status = SecItemCopyMatching(query as CFDictionary, &result)
4647

47-
guard status == errSecSuccess else {
48-
return nil
48+
switch status {
49+
case errSecSuccess:
50+
return (result as? Data) ?? Data()
51+
case errSecItemNotFound:
52+
// This is not an error; it's the expected result for a missing key.
53+
return Data()
54+
default:
55+
throw NativeDBError.keychainError(status: status, message: "Failed to get item.")
4956
}
50-
51-
return result as? Data
5257
}
5358

54-
public func delete(_ key: Data?) {
55-
DispatchQueue.global(qos: .utility).async {
56-
self.deleteSync(key)
57-
}
59+
public func delete(_ key: Data?) throws {
60+
try self.deleteSync(key)
5861
}
5962

60-
public func deleteSync(_ key: Data?) {
61-
guard let key = key else { return }
63+
public func deleteSync(_ key: Data?) throws {
64+
guard let key = key, !key.isEmpty else {
65+
throw NativeDBError.invalidArgument(description: "Key must not be nil or empty.")
66+
}
6267

6368
let account = keyToAccount(key)
6469

@@ -72,38 +77,63 @@ public class NativeDBManager: NSObject, GnoGnonativeNativeDBProtocol {
7277
query[kSecAttrAccessGroup as String] = accessGroup
7378
}
7479

75-
SecItemDelete(query as CFDictionary)
80+
let status = SecItemDelete(query as CFDictionary)
81+
82+
// Deleting a non-existent item is not considered an error.
83+
if status != errSecSuccess && status != errSecItemNotFound {
84+
throw NativeDBError.keychainError(status: status, message: "Failed to delete item.")
85+
}
7686
}
7787

78-
public func has(_ key: Data?) -> Bool {
79-
guard let key = key else { return false }
88+
public func has(_ key: Data?, ret0_ returnPointer: UnsafeMutablePointer<ObjCBool>?) throws {
89+
// 1. Ensure the return pointer provided by the caller is valid.
90+
guard let returnPointer = returnPointer else {
91+
throw NativeDBError.invalidArgument(description: "Return pointer must not be nil.")
92+
}
93+
94+
// 2. Validate the input key.
95+
guard let key = key, !key.isEmpty else {
96+
throw NativeDBError.invalidArgument(description: "Key must not be nil or empty.")
97+
}
8098

8199
let account = keyToAccount(key)
82100

83101
var query: [String: Any] = [
84102
kSecClass as String: kSecClassGenericPassword,
85103
kSecAttrService as String: service,
86-
kSecAttrAccount as String: account,
87-
kSecReturnData as String: false,
88-
kSecMatchLimit as String: kSecMatchLimitOne
104+
kSecAttrAccount as String: account
89105
]
90-
91106
if let accessGroup = accessGroup {
92107
query[kSecAttrAccessGroup as String] = accessGroup
93108
}
94109

95110
let status = SecItemCopyMatching(query as CFDictionary, nil)
96-
return status == errSecSuccess
111+
112+
// 3. Handle the result from the Keychain.
113+
switch status {
114+
case errSecSuccess:
115+
// Key exists. Write `true` to the pointer's memory location.
116+
returnPointer.pointee = ObjCBool(true)
117+
case errSecItemNotFound:
118+
// Key does not exist. Write `false` to the pointer's memory location.
119+
returnPointer.pointee = ObjCBool(false)
120+
default:
121+
// A real keychain error occurred. Throw an error.
122+
throw NativeDBError.keychainError(status: status, message: "Failed to check for item existence.")
123+
}
97124
}
98125

99-
public func set(_ key: Data?, p1 value: Data?) {
100-
DispatchQueue.global(qos: .utility).async {
101-
self.setSync(key, p1: value)
102-
}
126+
public func set(_ key: Data?, value: Data?) throws {
127+
try self.setSync(key, value: value)
103128
}
104129

105-
public func setSync(_ key: Data?, p1 value: Data?) {
106-
guard let key = key, let value = value else { return }
130+
public func setSync(_ key: Data?, value: Data?) throws {
131+
guard let key = key, !key.isEmpty else {
132+
throw NativeDBError.invalidArgument(description: "Key must not be nil or empty.")
133+
}
134+
guard let value = value else {
135+
throw NativeDBError.invalidArgument(description: "Value must not be nil.")
136+
}
107137

108138
let account = keyToAccount(key)
109139

@@ -124,13 +154,23 @@ public class NativeDBManager: NSObject, GnoGnonativeNativeDBProtocol {
124154

125155
let updateStatus = SecItemUpdate(query as CFDictionary, attributes as CFDictionary)
126156

127-
if updateStatus == errSecItemNotFound {
128-
// Item doesn't exist, create new one
157+
switch updateStatus {
158+
case errSecSuccess:
159+
// Update was successful.
160+
return
161+
case errSecItemNotFound:
162+
// Item doesn't exist, so add it.
129163
var newItem = query
130164
newItem[kSecValueData as String] = value
131165
newItem[kSecAttrAccessible as String] = kSecAttrAccessibleWhenUnlockedThisDeviceOnly
132166

133-
SecItemAdd(newItem as CFDictionary, nil)
167+
let addStatus = SecItemAdd(newItem as CFDictionary, nil)
168+
if addStatus != errSecSuccess {
169+
throw NativeDBError.keychainError(status: addStatus, message: "Failed to add new item.")
170+
}
171+
default:
172+
// Another error occurred during update.
173+
throw NativeDBError.keychainError(status: updateStatus, message: "Failed to update item.")
134174
}
135175
}
136176

@@ -260,3 +300,18 @@ public class NativeDBManager: NSObject, GnoGnonativeNativeDBProtocol {
260300
return true
261301
}
262302
}
303+
304+
public enum NativeDBError: Error, LocalizedError {
305+
case invalidArgument(description: String)
306+
case keychainError(status: OSStatus, message: String)
307+
308+
public var errorDescription: String? {
309+
switch self {
310+
case .invalidArgument(let description):
311+
return "Invalid Argument: \(description)"
312+
case .keychainError(let status, let message):
313+
let statusMessage = SecCopyErrorMessageString(status, nil) as String? ?? "Unknown error"
314+
return "Keychain Error: \(message) (status: \(status), reason: \(statusMessage))"
315+
}
316+
}
317+
}

0 commit comments

Comments
 (0)