react-native-nitro-storage 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +334 -34
- package/android/CMakeLists.txt +2 -0
- package/android/src/main/cpp/AndroidStorageAdapterCpp.cpp +26 -2
- package/android/src/main/cpp/AndroidStorageAdapterCpp.hpp +4 -0
- package/android/src/main/java/com/nitrostorage/AndroidStorageAdapter.kt +90 -18
- package/cpp/bindings/HybridStorage.cpp +214 -23
- package/cpp/bindings/HybridStorage.hpp +31 -3
- package/cpp/core/NativeStorageAdapter.hpp +4 -0
- package/ios/IOSStorageAdapterCpp.hpp +17 -0
- package/ios/IOSStorageAdapterCpp.mm +140 -10
- package/lib/commonjs/index.js +555 -288
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.web.js +750 -309
- package/lib/commonjs/index.web.js.map +1 -1
- package/lib/commonjs/internal.js +25 -0
- package/lib/commonjs/internal.js.map +1 -1
- package/lib/commonjs/storage-hooks.js +36 -0
- package/lib/commonjs/storage-hooks.js.map +1 -0
- package/lib/module/index.js +537 -287
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.web.js +732 -308
- package/lib/module/index.web.js.map +1 -1
- package/lib/module/internal.js +24 -0
- package/lib/module/internal.js.map +1 -1
- package/lib/module/storage-hooks.js +30 -0
- package/lib/module/storage-hooks.js.map +1 -0
- package/lib/typescript/Storage.nitro.d.ts +4 -0
- package/lib/typescript/Storage.nitro.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +41 -4
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/index.web.d.ts +45 -4
- package/lib/typescript/index.web.d.ts.map +1 -1
- package/lib/typescript/internal.d.ts +1 -0
- package/lib/typescript/internal.d.ts.map +1 -1
- package/lib/typescript/storage-hooks.d.ts +10 -0
- package/lib/typescript/storage-hooks.d.ts.map +1 -0
- package/nitrogen/generated/shared/c++/HybridStorageSpec.cpp +4 -0
- package/nitrogen/generated/shared/c++/HybridStorageSpec.hpp +4 -0
- package/package.json +5 -3
- package/src/Storage.nitro.ts +4 -0
- package/src/index.ts +704 -324
- package/src/index.web.ts +929 -346
- package/src/internal.ts +28 -0
- package/src/storage-hooks.ts +48 -0
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
3
|
#include "../core/NativeStorageAdapter.hpp"
|
|
4
|
+
#include <mutex>
|
|
5
|
+
#include <unordered_set>
|
|
4
6
|
|
|
5
7
|
namespace NitroStorage {
|
|
6
8
|
|
|
@@ -14,6 +16,7 @@ public:
|
|
|
14
16
|
void deleteDisk(const std::string& key) override;
|
|
15
17
|
bool hasDisk(const std::string& key) override;
|
|
16
18
|
std::vector<std::string> getAllKeysDisk() override;
|
|
19
|
+
std::vector<std::string> getKeysByPrefixDisk(const std::string& prefix) override;
|
|
17
20
|
size_t sizeDisk() override;
|
|
18
21
|
void setDiskBatch(const std::vector<std::string>& keys, const std::vector<std::string>& values) override;
|
|
19
22
|
std::vector<std::optional<std::string>> getDiskBatch(const std::vector<std::string>& keys) override;
|
|
@@ -24,6 +27,7 @@ public:
|
|
|
24
27
|
void deleteSecure(const std::string& key) override;
|
|
25
28
|
bool hasSecure(const std::string& key) override;
|
|
26
29
|
std::vector<std::string> getAllKeysSecure() override;
|
|
30
|
+
std::vector<std::string> getKeysByPrefixSecure(const std::string& prefix) override;
|
|
27
31
|
size_t sizeSecure() override;
|
|
28
32
|
void setSecureBatch(const std::vector<std::string>& keys, const std::vector<std::string>& values) override;
|
|
29
33
|
std::vector<std::optional<std::string>> getSecureBatch(const std::vector<std::string>& keys) override;
|
|
@@ -33,9 +37,11 @@ public:
|
|
|
33
37
|
void clearSecure() override;
|
|
34
38
|
|
|
35
39
|
void setSecureAccessControl(int level) override;
|
|
40
|
+
void setSecureWritesAsync(bool enabled) override;
|
|
36
41
|
void setKeychainAccessGroup(const std::string& group) override;
|
|
37
42
|
|
|
38
43
|
void setSecureBiometric(const std::string& key, const std::string& value) override;
|
|
44
|
+
void setSecureBiometricWithLevel(const std::string& key, const std::string& value, int level) override;
|
|
39
45
|
std::optional<std::string> getSecureBiometric(const std::string& key) override;
|
|
40
46
|
void deleteSecureBiometric(const std::string& key) override;
|
|
41
47
|
bool hasSecureBiometric(const std::string& key) override;
|
|
@@ -44,6 +50,17 @@ public:
|
|
|
44
50
|
private:
|
|
45
51
|
int accessControlLevel_ = 0;
|
|
46
52
|
std::string keychainAccessGroup_;
|
|
53
|
+
mutable std::mutex secureKeysMutex_;
|
|
54
|
+
std::unordered_set<std::string> secureKeysCache_;
|
|
55
|
+
std::unordered_set<std::string> biometricKeysCache_;
|
|
56
|
+
bool secureKeyCacheHydrated_ = false;
|
|
57
|
+
|
|
58
|
+
void ensureSecureKeyCacheHydrated();
|
|
59
|
+
void markSecureKeySet(const std::string& key);
|
|
60
|
+
void markSecureKeyRemoved(const std::string& key);
|
|
61
|
+
void markBiometricKeySet(const std::string& key);
|
|
62
|
+
void markBiometricKeyRemoved(const std::string& key);
|
|
63
|
+
void clearSecureKeyCache();
|
|
47
64
|
};
|
|
48
65
|
|
|
49
66
|
} // namespace NitroStorage
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
#import <Security/Security.h>
|
|
4
4
|
#import <LocalAuthentication/LocalAuthentication.h>
|
|
5
5
|
#include <algorithm>
|
|
6
|
+
#include <unordered_set>
|
|
6
7
|
|
|
7
8
|
namespace NitroStorage {
|
|
8
9
|
|
|
@@ -78,6 +79,18 @@ std::vector<std::string> IOSStorageAdapterCpp::getAllKeysDisk() {
|
|
|
78
79
|
return keys;
|
|
79
80
|
}
|
|
80
81
|
|
|
82
|
+
std::vector<std::string> IOSStorageAdapterCpp::getKeysByPrefixDisk(const std::string& prefix) {
|
|
83
|
+
const auto keys = getAllKeysDisk();
|
|
84
|
+
std::vector<std::string> filtered;
|
|
85
|
+
filtered.reserve(keys.size());
|
|
86
|
+
for (const auto& key : keys) {
|
|
87
|
+
if (key.rfind(prefix, 0) == 0) {
|
|
88
|
+
filtered.push_back(key);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return filtered;
|
|
92
|
+
}
|
|
93
|
+
|
|
81
94
|
size_t IOSStorageAdapterCpp::sizeDisk() {
|
|
82
95
|
return [NitroDiskDefaults() dictionaryRepresentation].count;
|
|
83
96
|
}
|
|
@@ -176,11 +189,27 @@ void IOSStorageAdapterCpp::setSecure(const std::string& key, const std::string&
|
|
|
176
189
|
|
|
177
190
|
OSStatus status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)updateAttributes);
|
|
178
191
|
|
|
192
|
+
if (status == errSecSuccess) {
|
|
193
|
+
markSecureKeySet(key);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
179
197
|
if (status == errSecItemNotFound) {
|
|
180
198
|
query[(__bridge id)kSecValueData] = data;
|
|
181
199
|
query[(__bridge id)kSecAttrAccessible] = (__bridge id)accessControlAttr(accessControlLevel_);
|
|
182
|
-
SecItemAdd((__bridge CFDictionaryRef)query, NULL);
|
|
200
|
+
const OSStatus addStatus = SecItemAdd((__bridge CFDictionaryRef)query, NULL);
|
|
201
|
+
if (addStatus != errSecSuccess) {
|
|
202
|
+
throw std::runtime_error(
|
|
203
|
+
"NitroStorage: Secure set failed with status " + std::to_string(addStatus)
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
markSecureKeySet(key);
|
|
207
|
+
return;
|
|
183
208
|
}
|
|
209
|
+
|
|
210
|
+
throw std::runtime_error(
|
|
211
|
+
"NitroStorage: Secure set failed with status " + std::to_string(status)
|
|
212
|
+
);
|
|
184
213
|
}
|
|
185
214
|
|
|
186
215
|
std::optional<std::string> IOSStorageAdapterCpp::getSecure(const std::string& key) {
|
|
@@ -206,6 +235,8 @@ void IOSStorageAdapterCpp::deleteSecure(const std::string& key) {
|
|
|
206
235
|
SecItemDelete((__bridge CFDictionaryRef)secureQuery);
|
|
207
236
|
NSMutableDictionary* biometricQuery = baseKeychainQuery(nsKey, kBiometricKeychainService, group);
|
|
208
237
|
SecItemDelete((__bridge CFDictionaryRef)biometricQuery);
|
|
238
|
+
markSecureKeyRemoved(key);
|
|
239
|
+
markBiometricKeyRemoved(key);
|
|
209
240
|
}
|
|
210
241
|
|
|
211
242
|
bool IOSStorageAdapterCpp::hasSecure(const std::string& key) {
|
|
@@ -220,19 +251,36 @@ bool IOSStorageAdapterCpp::hasSecure(const std::string& key) {
|
|
|
220
251
|
}
|
|
221
252
|
|
|
222
253
|
std::vector<std::string> IOSStorageAdapterCpp::getAllKeysSecure() {
|
|
223
|
-
|
|
224
|
-
std::
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
254
|
+
ensureSecureKeyCacheHydrated();
|
|
255
|
+
std::lock_guard<std::mutex> lock(secureKeysMutex_);
|
|
256
|
+
std::unordered_set<std::string> combined = secureKeysCache_;
|
|
257
|
+
combined.insert(biometricKeysCache_.begin(), biometricKeysCache_.end());
|
|
258
|
+
std::vector<std::string> keys;
|
|
259
|
+
keys.reserve(combined.size());
|
|
260
|
+
for (const auto& key : combined) {
|
|
261
|
+
keys.push_back(key);
|
|
230
262
|
}
|
|
231
263
|
return keys;
|
|
232
264
|
}
|
|
233
265
|
|
|
266
|
+
std::vector<std::string> IOSStorageAdapterCpp::getKeysByPrefixSecure(const std::string& prefix) {
|
|
267
|
+
const auto keys = getAllKeysSecure();
|
|
268
|
+
std::vector<std::string> filtered;
|
|
269
|
+
filtered.reserve(keys.size());
|
|
270
|
+
for (const auto& key : keys) {
|
|
271
|
+
if (key.rfind(prefix, 0) == 0) {
|
|
272
|
+
filtered.push_back(key);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return filtered;
|
|
276
|
+
}
|
|
277
|
+
|
|
234
278
|
size_t IOSStorageAdapterCpp::sizeSecure() {
|
|
235
|
-
|
|
279
|
+
ensureSecureKeyCacheHydrated();
|
|
280
|
+
std::lock_guard<std::mutex> lock(secureKeysMutex_);
|
|
281
|
+
std::unordered_set<std::string> combined = secureKeysCache_;
|
|
282
|
+
combined.insert(biometricKeysCache_.begin(), biometricKeysCache_.end());
|
|
283
|
+
return combined.size();
|
|
236
284
|
}
|
|
237
285
|
|
|
238
286
|
void IOSStorageAdapterCpp::setSecureBatch(
|
|
@@ -280,6 +328,7 @@ void IOSStorageAdapterCpp::clearSecure() {
|
|
|
280
328
|
biometricQuery[(__bridge id)kSecAttrAccessGroup] = group;
|
|
281
329
|
}
|
|
282
330
|
SecItemDelete((__bridge CFDictionaryRef)biometricQuery);
|
|
331
|
+
clearSecureKeyCache();
|
|
283
332
|
}
|
|
284
333
|
|
|
285
334
|
// --- Configuration ---
|
|
@@ -288,13 +337,27 @@ void IOSStorageAdapterCpp::setSecureAccessControl(int level) {
|
|
|
288
337
|
accessControlLevel_ = level;
|
|
289
338
|
}
|
|
290
339
|
|
|
340
|
+
void IOSStorageAdapterCpp::setSecureWritesAsync(bool /*enabled*/) {
|
|
341
|
+
// iOS writes are synchronous by design; keep behavior unchanged.
|
|
342
|
+
}
|
|
343
|
+
|
|
291
344
|
void IOSStorageAdapterCpp::setKeychainAccessGroup(const std::string& group) {
|
|
292
345
|
keychainAccessGroup_ = group;
|
|
346
|
+
clearSecureKeyCache();
|
|
293
347
|
}
|
|
294
348
|
|
|
295
349
|
// --- Biometric (separate Keychain service with biometric ACL) ---
|
|
296
350
|
|
|
297
351
|
void IOSStorageAdapterCpp::setSecureBiometric(const std::string& key, const std::string& value) {
|
|
352
|
+
setSecureBiometricWithLevel(key, value, 2);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
void IOSStorageAdapterCpp::setSecureBiometricWithLevel(const std::string& key, const std::string& value, int level) {
|
|
356
|
+
if (level == 0) {
|
|
357
|
+
setSecure(key, value);
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
|
|
298
361
|
NSString* nsKey = [NSString stringWithUTF8String:key.c_str()];
|
|
299
362
|
NSData* data = [[NSString stringWithUTF8String:value.c_str()] dataUsingEncoding:NSUTF8StringEncoding];
|
|
300
363
|
NSString* group = keychainAccessGroup_.empty() ? nil : [NSString stringWithUTF8String:keychainAccessGroup_.c_str()];
|
|
@@ -304,10 +367,14 @@ void IOSStorageAdapterCpp::setSecureBiometric(const std::string& key, const std:
|
|
|
304
367
|
SecItemDelete((__bridge CFDictionaryRef)deleteQuery);
|
|
305
368
|
|
|
306
369
|
CFErrorRef error = NULL;
|
|
370
|
+
SecAccessControlCreateFlags flags = kSecAccessControlBiometryCurrentSet;
|
|
371
|
+
if (level == 1) {
|
|
372
|
+
flags = kSecAccessControlUserPresence;
|
|
373
|
+
}
|
|
307
374
|
SecAccessControlRef access = SecAccessControlCreateWithFlags(
|
|
308
375
|
kCFAllocatorDefault,
|
|
309
376
|
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
|
|
310
|
-
|
|
377
|
+
flags,
|
|
311
378
|
&error
|
|
312
379
|
);
|
|
313
380
|
|
|
@@ -324,6 +391,7 @@ void IOSStorageAdapterCpp::setSecureBiometric(const std::string& key, const std:
|
|
|
324
391
|
if (status != errSecSuccess) {
|
|
325
392
|
throw std::runtime_error("NitroStorage: Biometric set failed with status " + std::to_string(status));
|
|
326
393
|
}
|
|
394
|
+
markBiometricKeySet(key);
|
|
327
395
|
}
|
|
328
396
|
|
|
329
397
|
std::optional<std::string> IOSStorageAdapterCpp::getSecureBiometric(const std::string& key) {
|
|
@@ -351,6 +419,7 @@ void IOSStorageAdapterCpp::deleteSecureBiometric(const std::string& key) {
|
|
|
351
419
|
NSString* group = keychainAccessGroup_.empty() ? nil : [NSString stringWithUTF8String:keychainAccessGroup_.c_str()];
|
|
352
420
|
NSMutableDictionary* query = baseKeychainQuery(nsKey, kBiometricKeychainService, group);
|
|
353
421
|
SecItemDelete((__bridge CFDictionaryRef)query);
|
|
422
|
+
markBiometricKeyRemoved(key);
|
|
354
423
|
}
|
|
355
424
|
|
|
356
425
|
bool IOSStorageAdapterCpp::hasSecureBiometric(const std::string& key) {
|
|
@@ -370,6 +439,67 @@ void IOSStorageAdapterCpp::clearSecureBiometric() {
|
|
|
370
439
|
query[(__bridge id)kSecAttrAccessGroup] = group;
|
|
371
440
|
}
|
|
372
441
|
SecItemDelete((__bridge CFDictionaryRef)query);
|
|
442
|
+
std::lock_guard<std::mutex> lock(secureKeysMutex_);
|
|
443
|
+
biometricKeysCache_.clear();
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
void IOSStorageAdapterCpp::ensureSecureKeyCacheHydrated() {
|
|
447
|
+
{
|
|
448
|
+
std::lock_guard<std::mutex> lock(secureKeysMutex_);
|
|
449
|
+
if (secureKeyCacheHydrated_) {
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
NSString* group = keychainAccessGroup_.empty() ? nil : [NSString stringWithUTF8String:keychainAccessGroup_.c_str()];
|
|
455
|
+
const std::vector<std::string> secureKeys = keychainAccountsForService(kKeychainService, group);
|
|
456
|
+
const std::vector<std::string> biometricKeys = keychainAccountsForService(kBiometricKeychainService, group);
|
|
457
|
+
|
|
458
|
+
std::lock_guard<std::mutex> lock(secureKeysMutex_);
|
|
459
|
+
secureKeysCache_.clear();
|
|
460
|
+
biometricKeysCache_.clear();
|
|
461
|
+
secureKeysCache_.insert(secureKeys.begin(), secureKeys.end());
|
|
462
|
+
biometricKeysCache_.insert(biometricKeys.begin(), biometricKeys.end());
|
|
463
|
+
secureKeyCacheHydrated_ = true;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
void IOSStorageAdapterCpp::markSecureKeySet(const std::string& key) {
|
|
467
|
+
std::lock_guard<std::mutex> lock(secureKeysMutex_);
|
|
468
|
+
if (!secureKeyCacheHydrated_) {
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
secureKeysCache_.insert(key);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
void IOSStorageAdapterCpp::markSecureKeyRemoved(const std::string& key) {
|
|
475
|
+
std::lock_guard<std::mutex> lock(secureKeysMutex_);
|
|
476
|
+
if (!secureKeyCacheHydrated_) {
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
secureKeysCache_.erase(key);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
void IOSStorageAdapterCpp::markBiometricKeySet(const std::string& key) {
|
|
483
|
+
std::lock_guard<std::mutex> lock(secureKeysMutex_);
|
|
484
|
+
if (!secureKeyCacheHydrated_) {
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
biometricKeysCache_.insert(key);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
void IOSStorageAdapterCpp::markBiometricKeyRemoved(const std::string& key) {
|
|
491
|
+
std::lock_guard<std::mutex> lock(secureKeysMutex_);
|
|
492
|
+
if (!secureKeyCacheHydrated_) {
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
biometricKeysCache_.erase(key);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
void IOSStorageAdapterCpp::clearSecureKeyCache() {
|
|
499
|
+
std::lock_guard<std::mutex> lock(secureKeysMutex_);
|
|
500
|
+
secureKeysCache_.clear();
|
|
501
|
+
biometricKeysCache_.clear();
|
|
502
|
+
secureKeyCacheHydrated_ = false;
|
|
373
503
|
}
|
|
374
504
|
|
|
375
505
|
} // namespace NitroStorage
|