@capacitor-community/sqlite 5.0.5 → 5.0.6
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/android/src/main/java/com/getcapacitor/community/database/sqlite/CapacitorSQLite.java +9 -0
- package/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/Database.java +9 -0
- package/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/ImportExportJson/UtilsEncryption.java +111 -0
- package/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/UtilsSecret.java +3 -3
- package/dist/esm/definitions.d.ts +66 -0
- package/dist/esm/definitions.js +37 -0
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +3 -1
- package/dist/esm/web.js +8 -0
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +45 -0
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +45 -0
- package/dist/plugin.js.map +1 -1
- package/electron/dist/plugin.js +138 -52
- package/electron/dist/plugin.js.map +1 -1
- package/electron/rollup.config.js +2 -0
- package/ios/Plugin/CapacitorSQLite.swift +117 -88
- package/ios/Plugin/Database.swift +17 -5
- package/ios/Plugin/ImportExportJson/ImportData.swift +434 -0
- package/ios/Plugin/ImportExportJson/ImportFromJson.swift +43 -57
- package/ios/Plugin/ImportExportJson/JsonSQLite.swift +7 -0
- package/ios/Plugin/Utils/UtilsJson.swift +123 -1
- package/ios/Plugin/Utils/UtilsSQLCipher.swift +0 -1
- package/package.json +1 -1
- package/src/definitions.ts +104 -0
- package/src/web.ts +10 -0
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
//
|
|
7
7
|
|
|
8
8
|
import Foundation
|
|
9
|
+
import CryptoKit
|
|
10
|
+
import CommonCrypto
|
|
9
11
|
|
|
10
12
|
enum UtilsJsonError: Error {
|
|
11
13
|
case tableNotExists(message: String)
|
|
@@ -20,7 +22,12 @@ enum UtilsJsonError: Error {
|
|
|
20
22
|
case isLastModified(message: String)
|
|
21
23
|
case isSqlDeleted(message: String)
|
|
22
24
|
case checkUpdate(message: String)
|
|
23
|
-
case checkValues(message: String)
|
|
25
|
+
case checkValues(message: String)
|
|
26
|
+
case encryptDictionaryToBase64(message: String)
|
|
27
|
+
case decryptBase64ToDictionary(message: String)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
var mSalt = "jeep_capacitor_sqlite"
|
|
24
31
|
|
|
25
32
|
// swiftlint:disable file_length
|
|
26
33
|
// swiftlint:disable type_body_length
|
|
@@ -466,6 +473,121 @@ class UtilsJson {
|
|
|
466
473
|
return isViews
|
|
467
474
|
}
|
|
468
475
|
|
|
476
|
+
// MARK: encryptDictionaryToBase64
|
|
477
|
+
|
|
478
|
+
class func encryptDictionaryToBase64(_ dictionary: [String: Any], forAccount account: String) throws -> String {
|
|
479
|
+
// Encryption using Swift Crypto and Keychain-stored passphrase
|
|
480
|
+
|
|
481
|
+
let jsonData = try JSONSerialization.data(withJSONObject: dictionary)
|
|
482
|
+
|
|
483
|
+
let isPassPhrase = try UtilsSecret.isPassphrase(account: account)
|
|
484
|
+
if !isPassPhrase {
|
|
485
|
+
throw UtilsJsonError.encryptDictionaryToBase64(
|
|
486
|
+
message: "No passphrase stored")
|
|
487
|
+
}
|
|
488
|
+
let passphrase = UtilsSecret.getPassphrase(account: account)
|
|
489
|
+
// Generate a constant salt
|
|
490
|
+
let salt = Data(mSalt.utf8)
|
|
491
|
+
|
|
492
|
+
// Derive the encryption key using the passphrase and salt
|
|
493
|
+
guard let key = deriveSymmetricKeyFromPassphrase(passphrase, salt: salt) else {
|
|
494
|
+
throw UtilsJsonError.encryptDictionaryToBase64(
|
|
495
|
+
message: "No Encryption key returned")
|
|
496
|
+
}
|
|
497
|
+
let symKey = SymmetricKey(data: key)
|
|
498
|
+
|
|
499
|
+
// Use Swift Crypto to perform AES encryption with GCM mode
|
|
500
|
+
let sealedBox = try AES.GCM.seal(jsonData, using: symKey, nonce: AES.GCM.Nonce())
|
|
501
|
+
|
|
502
|
+
if let combined = sealedBox.combined {
|
|
503
|
+
// combined the salt and the encrypted data
|
|
504
|
+
var saltAndEncryptedData = salt
|
|
505
|
+
saltAndEncryptedData.append(combined)
|
|
506
|
+
// Convert to base64 string
|
|
507
|
+
|
|
508
|
+
let base64String = saltAndEncryptedData.base64EncodedString()
|
|
509
|
+
return base64String
|
|
510
|
+
} else {
|
|
511
|
+
throw UtilsJsonError.encryptDictionaryToBase64(
|
|
512
|
+
message: "Conversion to base64String failed")
|
|
513
|
+
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// MARK: decrypt DictionaryToBase64
|
|
519
|
+
|
|
520
|
+
class func decryptBase64ToDictionary(_ base64String: String,
|
|
521
|
+
forAccount account: String)
|
|
522
|
+
throws -> [String: Any] {
|
|
523
|
+
guard let data = Data(base64Encoded: base64String) else {
|
|
524
|
+
throw UtilsJsonError.decryptBase64ToDictionary(
|
|
525
|
+
message: "Conversion from Base64 to Dictionary failed")
|
|
526
|
+
|
|
527
|
+
}
|
|
528
|
+
let isPassPhrase = try UtilsSecret.isPassphrase(account: account)
|
|
529
|
+
if !isPassPhrase {
|
|
530
|
+
throw UtilsJsonError.decryptBase64ToDictionary(
|
|
531
|
+
message: "No passphrase stored")
|
|
532
|
+
}
|
|
533
|
+
let passphrase = UtilsSecret.getPassphrase(account: account)
|
|
534
|
+
// Generate a constant salt
|
|
535
|
+
let salt = Data(mSalt.utf8)
|
|
536
|
+
|
|
537
|
+
// Derive the encryption key using the passphrase and salt
|
|
538
|
+
guard let key = deriveSymmetricKeyFromPassphrase(passphrase,
|
|
539
|
+
salt: salt) else {
|
|
540
|
+
throw UtilsJsonError.encryptDictionaryToBase64(
|
|
541
|
+
message: "No Encryption key returned")
|
|
542
|
+
}
|
|
543
|
+
let symKey = SymmetricKey(data: key)
|
|
544
|
+
|
|
545
|
+
// Extract the encrypted data from the remaining bytes
|
|
546
|
+
let encryptedData = data.suffix(from: salt.count)
|
|
547
|
+
|
|
548
|
+
// Convert the base64 string to a sealed box
|
|
549
|
+
let sealedBox = try AES.GCM.SealedBox(combined: encryptedData)
|
|
550
|
+
|
|
551
|
+
// Decrypt the ciphertext using AES GCM open
|
|
552
|
+
let decryptedData = try AES.GCM.open(sealedBox, using: symKey)
|
|
553
|
+
|
|
554
|
+
// Convert decrypted data to dictionary
|
|
555
|
+
let dictionary = try JSONSerialization.jsonObject(
|
|
556
|
+
with: decryptedData) as? [String: Any] ?? [:]
|
|
557
|
+
|
|
558
|
+
return dictionary
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// MARK: deriveSymmetricKeyFromPassphrase
|
|
562
|
+
|
|
563
|
+
class func deriveSymmetricKeyFromPassphrase(_ passphrase: String,
|
|
564
|
+
salt: Data)
|
|
565
|
+
-> Data? {
|
|
566
|
+
|
|
567
|
+
let passphraseData = Data(passphrase.utf8)
|
|
568
|
+
let keyLength = kCCKeySizeAES256
|
|
569
|
+
let iterations: UInt32 = 10000
|
|
570
|
+
var derivedKeyData = Data(count: keyLength)
|
|
571
|
+
let derivedCount = derivedKeyData.count
|
|
572
|
+
let derivationStatus = derivedKeyData.withUnsafeMutableBytes { derivedKeyUnsafeMutableRawBufferPointer in
|
|
573
|
+
passphraseData.withUnsafeBytes { passphraseUnsafeRawBufferPointer in
|
|
574
|
+
salt.withUnsafeBytes { saltUnsafeRawBufferPointer in
|
|
575
|
+
CCKeyDerivationPBKDF(
|
|
576
|
+
CCPBKDFAlgorithm(kCCPBKDF2),
|
|
577
|
+
passphraseUnsafeRawBufferPointer.baseAddress,
|
|
578
|
+
passphraseData.count,
|
|
579
|
+
saltUnsafeRawBufferPointer.baseAddress,
|
|
580
|
+
salt.count,
|
|
581
|
+
CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA256),
|
|
582
|
+
iterations,
|
|
583
|
+
derivedKeyUnsafeMutableRawBufferPointer.baseAddress,
|
|
584
|
+
derivedCount
|
|
585
|
+
)
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
return derivationStatus == kCCSuccess ? derivedKeyData : nil
|
|
590
|
+
}
|
|
469
591
|
}
|
|
470
592
|
// swiftlint:enable type_body_length
|
|
471
593
|
// swiftlint:enable file_length
|
package/package.json
CHANGED
package/src/definitions.ts
CHANGED
|
@@ -120,6 +120,20 @@ export interface CapacitorSQLitePlugin {
|
|
|
120
120
|
* @since 0.0.1
|
|
121
121
|
*/
|
|
122
122
|
close(options: capSQLiteOptions): Promise<void>;
|
|
123
|
+
/**
|
|
124
|
+
* Load a SQlite extension
|
|
125
|
+
* @param options :capSQLiteExtensionPath
|
|
126
|
+
* @returns Promise<void>
|
|
127
|
+
* @since 5.0.6
|
|
128
|
+
*/
|
|
129
|
+
// loadExtension(options: capSQLiteExtensionPath): Promise<void>;
|
|
130
|
+
/**
|
|
131
|
+
* Enable Or Disable Extension Loading
|
|
132
|
+
* @param options
|
|
133
|
+
* @returns Promise<void>
|
|
134
|
+
* @since 5.0.6
|
|
135
|
+
*/
|
|
136
|
+
// enableLoadExtension(options: capSQLiteExtensionEnable): Promise<void>;
|
|
123
137
|
/**
|
|
124
138
|
* GetUrl get the database Url
|
|
125
139
|
* @param options: capSQLiteOptions
|
|
@@ -391,6 +405,38 @@ export interface capEchoOptions {
|
|
|
391
405
|
*/
|
|
392
406
|
value?: string;
|
|
393
407
|
}
|
|
408
|
+
export interface capSQLiteExtensionPath {
|
|
409
|
+
/**
|
|
410
|
+
* The database name
|
|
411
|
+
*/
|
|
412
|
+
database?: string;
|
|
413
|
+
/**
|
|
414
|
+
* The extension path
|
|
415
|
+
*/
|
|
416
|
+
path?: string;
|
|
417
|
+
/**
|
|
418
|
+
* ReadOnly / ReadWrite
|
|
419
|
+
* default ReadWrite (false)
|
|
420
|
+
* @since 4.1.0-7
|
|
421
|
+
*/
|
|
422
|
+
readonly?: boolean;
|
|
423
|
+
}
|
|
424
|
+
export interface capSQLiteExtensionEnable {
|
|
425
|
+
/**
|
|
426
|
+
* The database name
|
|
427
|
+
*/
|
|
428
|
+
database?: string;
|
|
429
|
+
/**
|
|
430
|
+
* The enabling toggle (1: ON, 0: OFF)
|
|
431
|
+
*/
|
|
432
|
+
toggle?: boolean;
|
|
433
|
+
/**
|
|
434
|
+
* ReadOnly / ReadWrite
|
|
435
|
+
* default ReadWrite (false)
|
|
436
|
+
* @since 4.1.0-7
|
|
437
|
+
*/
|
|
438
|
+
readonly?: boolean;
|
|
439
|
+
}
|
|
394
440
|
export interface capConnectionOptions {
|
|
395
441
|
/**
|
|
396
442
|
* The database name
|
|
@@ -436,6 +482,7 @@ export interface capSQLiteOptions {
|
|
|
436
482
|
*/
|
|
437
483
|
readonly?: boolean;
|
|
438
484
|
}
|
|
485
|
+
|
|
439
486
|
export interface capNCDatabasePathOptions {
|
|
440
487
|
/**
|
|
441
488
|
* the database path
|
|
@@ -769,6 +816,12 @@ export interface capSQLiteSyncDate {
|
|
|
769
816
|
}
|
|
770
817
|
|
|
771
818
|
/* JSON Types */
|
|
819
|
+
export interface EncryptJson {
|
|
820
|
+
/**
|
|
821
|
+
* The encrypted JsonSQLite base64 string
|
|
822
|
+
*/
|
|
823
|
+
expData: string;
|
|
824
|
+
}
|
|
772
825
|
export interface JsonSQLite {
|
|
773
826
|
/**
|
|
774
827
|
* The database name
|
|
@@ -1699,6 +1752,20 @@ export interface ISQLiteDBConnection {
|
|
|
1699
1752
|
* @since 3.2.0
|
|
1700
1753
|
*/
|
|
1701
1754
|
getVersion(): Promise<capVersionResult>;
|
|
1755
|
+
/**
|
|
1756
|
+
* Load a SQlite extension
|
|
1757
|
+
* @param path :SQlite extension path
|
|
1758
|
+
* @returns Promise<void>
|
|
1759
|
+
* @since 5.0.6
|
|
1760
|
+
*/
|
|
1761
|
+
loadExtension(path: string): Promise<void>;
|
|
1762
|
+
/**
|
|
1763
|
+
* Enable Or Disable Extension Loading
|
|
1764
|
+
* @param toggle true:on false:off
|
|
1765
|
+
* @returns Promise<void>
|
|
1766
|
+
* @since 5.0.6
|
|
1767
|
+
*/
|
|
1768
|
+
enableLoadExtension(toggle: boolean): Promise<void>;
|
|
1702
1769
|
/**
|
|
1703
1770
|
* Execute SQLite DB Connection Statements
|
|
1704
1771
|
* @param statements
|
|
@@ -1849,6 +1916,43 @@ export class SQLiteDBConnection implements ISQLiteDBConnection {
|
|
|
1849
1916
|
return Promise.reject(err);
|
|
1850
1917
|
}
|
|
1851
1918
|
}
|
|
1919
|
+
|
|
1920
|
+
async loadExtension(path: string): Promise<void> {
|
|
1921
|
+
try {
|
|
1922
|
+
console.log(`database: ${this.dbName}`);
|
|
1923
|
+
console.log(`readonly: ${this.readonly}}`);
|
|
1924
|
+
console.log(`path: ${path}}`);
|
|
1925
|
+
await this.sqlite.loadExtension({
|
|
1926
|
+
database: this.dbName,
|
|
1927
|
+
path: path,
|
|
1928
|
+
readonly: this.readonly,
|
|
1929
|
+
});
|
|
1930
|
+
console.log(`loadExtension successful`);
|
|
1931
|
+
return Promise.resolve();
|
|
1932
|
+
} catch (err) {
|
|
1933
|
+
console.log(`loadExtension failed `);
|
|
1934
|
+
return Promise.reject(err);
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
async enableLoadExtension(toggle: boolean): Promise<void> {
|
|
1938
|
+
try {
|
|
1939
|
+
console.log(`database: ${this.dbName}`);
|
|
1940
|
+
console.log(`readonly: ${this.readonly}`);
|
|
1941
|
+
console.log(`toggle: ${toggle}`);
|
|
1942
|
+
await this.sqlite.enableLoadExtension({
|
|
1943
|
+
database: this.dbName,
|
|
1944
|
+
toggle: toggle,
|
|
1945
|
+
readonly: this.readonly,
|
|
1946
|
+
});
|
|
1947
|
+
console.log(`enableLoadExtension successful`);
|
|
1948
|
+
return Promise.resolve();
|
|
1949
|
+
} catch (err) {
|
|
1950
|
+
console.log(err);
|
|
1951
|
+
console.log(`enableLoadExtension failed `);
|
|
1952
|
+
return Promise.reject(err);
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1852
1956
|
async getUrl(): Promise<capSQLiteUrl> {
|
|
1853
1957
|
try {
|
|
1854
1958
|
const res: capSQLiteUrl = await this.sqlite.getUrl({
|
package/src/web.ts
CHANGED
|
@@ -32,6 +32,8 @@ import type {
|
|
|
32
32
|
capSQLiteUrl,
|
|
33
33
|
capSQLiteValues,
|
|
34
34
|
capVersionResult,
|
|
35
|
+
capSQLiteExtensionPath,
|
|
36
|
+
capSQLiteExtensionEnable,
|
|
35
37
|
} from './definitions';
|
|
36
38
|
|
|
37
39
|
export class CapacitorSQLiteWeb
|
|
@@ -591,4 +593,12 @@ export class CapacitorSQLiteWeb
|
|
|
591
593
|
async isInConfigBiometricAuth(): Promise<capSQLiteResult> {
|
|
592
594
|
throw this.unimplemented('Not implemented on web.');
|
|
593
595
|
}
|
|
596
|
+
async loadExtension(options: capSQLiteExtensionPath): Promise<void> {
|
|
597
|
+
console.log('loadExtension', options);
|
|
598
|
+
throw this.unimplemented('Not implemented on web.');
|
|
599
|
+
}
|
|
600
|
+
async enableLoadExtension(options: capSQLiteExtensionEnable): Promise<void> {
|
|
601
|
+
console.log('enableLoadExtension', options);
|
|
602
|
+
throw this.unimplemented('Not implemented on web.');
|
|
603
|
+
}
|
|
594
604
|
}
|