@capacitor-community/sqlite 3.3.3 → 3.4.0-3

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.
Files changed (23) hide show
  1. package/README.md +31 -14
  2. package/android/build.gradle +1 -1
  3. package/android/src/main/java/com/getcapacitor/community/database/sqlite/CapacitorSQLite.java +127 -6
  4. package/android/src/main/java/com/getcapacitor/community/database/sqlite/CapacitorSQLitePlugin.java +95 -13
  5. package/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/BiometricListener.java +8 -0
  6. package/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/Database.java +5 -3
  7. package/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/SqliteConfig.java +32 -0
  8. package/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/UtilsBiometric.java +123 -0
  9. package/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/UtilsSQLCipher.java +7 -2
  10. package/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/UtilsSQLite.java +19 -0
  11. package/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/UtilsSecret.java +7 -2
  12. package/dist/esm/definitions.d.ts +7 -0
  13. package/dist/esm/definitions.js.map +1 -1
  14. package/ios/Plugin/BiometricIDAuthentication.swift +79 -0
  15. package/ios/Plugin/CapacitorSQLite.swift +144 -24
  16. package/ios/Plugin/CapacitorSQLitePlugin.swift +52 -9
  17. package/ios/Plugin/Database.swift +17 -4
  18. package/ios/Plugin/Extensions/Notification.Name.swift +6 -2
  19. package/ios/Plugin/Models/KeychainServices.swift +1 -1
  20. package/ios/Plugin/SqliteConfig.swift +3 -0
  21. package/ios/Plugin/Utils/UtilsSQLCipher.swift +5 -4
  22. package/ios/Plugin/Utils/UtilsSecret.swift +54 -22
  23. package/package.json +6 -6
@@ -1,4 +1,6 @@
1
1
  import Foundation
2
+ import Capacitor
3
+
2
4
  enum CapacitorSQLiteError: Error {
3
5
  case failed(message: String)
4
6
  }
@@ -7,17 +9,72 @@ enum CapacitorSQLiteError: Error {
7
9
  @objc public class CapacitorSQLite: NSObject {
8
10
  private var config: SqliteConfig
9
11
  private var dbDict: [String: Database] = [:]
10
- private var databaseLocation: String
12
+ private var databaseLocation: String = "Documents"
11
13
  private var initMessage: String = ""
12
14
  private var isInit: Bool = false
15
+ private var isBiometricAuth: Bool = false
16
+ private var biometricTitle: String = ""
17
+ private var bioIdAuth: BiometricIDAuthentication =
18
+ BiometricIDAuthentication()
19
+ private var authMessage: String = ""
20
+ private var internalBiometricObserver: Any?
21
+ private var intBioMessage: String = ""
22
+ private var call: CAPPluginCall?
23
+ private var account: String = oldAccount
24
+ private var prefixKeychain: String = ""
13
25
 
26
+ // swiftlint:disable function_body_length
27
+ // swiftlint:disable cyclomatic_complexity
14
28
  init(config: SqliteConfig) {
15
29
  self.config = config
16
- if let isLocation = config.iosDatabaseLocation {
17
- self.databaseLocation = isLocation
30
+ super.init()
31
+ if let kcPrefix: String = config.iosKeychainPrefix {
32
+ account = "\(kcPrefix)_\(oldAccount)"
33
+ prefixKeychain = kcPrefix
34
+ }
35
+ if let isBioAuth = config.biometricAuth {
36
+ if isBioAuth == 1 {
37
+ if let bTitle = config.biometricTitle {
38
+ biometricTitle = bTitle
39
+ bioIdAuth.biometricTitle = bTitle
40
+ }
41
+ do {
42
+ let bioType: BiometricType = try
43
+ bioIdAuth.biometricType()
44
+ if bioType == BiometricType.faceID ||
45
+ bioType == BiometricType.touchID {
46
+ isBiometricAuth = true
47
+ isInit = true
48
+ bioIdAuth.authenticateUser { [weak self] message in
49
+ if let message = message {
50
+ self?.notifyBiometricEvents(name: .biometricEvent,
51
+ result: false,
52
+ msg: message)
53
+ } else {
54
+ self?.notifyBiometricEvents(name: .biometricEvent,
55
+ result: true,
56
+ msg: "")
57
+ }
58
+ }
59
+ } else {
60
+ self.notifyBiometricEvents(name: .biometricEvent,
61
+ result: false,
62
+ msg: "Biometric not set-up")
63
+ }
64
+ } catch BiometricIDAuthenticationError
65
+ .biometricType(let message) {
66
+ initMessage = message
67
+ } catch let error {
68
+ initMessage = "Init Plugin failed :"
69
+ initMessage.append(" \(error.localizedDescription)")
70
+ }
71
+ }
72
+ }
73
+ if let dbLocation = config.iosDatabaseLocation {
74
+ self.databaseLocation = dbLocation
18
75
  // create the databaseLocation directory
19
76
  do {
20
- try UtilsFile.createDatabaseLocation(location: isLocation)
77
+ try UtilsFile.createDatabaseLocation(location: dbLocation)
21
78
  isInit = true
22
79
  } catch UtilsFileError.createDatabaseLocationFailed(let message) {
23
80
  initMessage = message
@@ -30,8 +87,9 @@ enum CapacitorSQLiteError: Error {
30
87
  self.databaseLocation = "Documents"
31
88
  isInit = true
32
89
  }
33
- super.init()
34
90
  }
91
+ // swiftlint:enable cyclomatic_complexity
92
+ // swiftlint:enable function_body_length
35
93
 
36
94
  // MARK: - Echo
37
95
 
@@ -43,12 +101,18 @@ enum CapacitorSQLiteError: Error {
43
101
 
44
102
  @objc public func isSecretStored() throws -> NSNumber {
45
103
  if isInit {
46
- let isSecretExists: Bool = UtilsSecret.isPassphrase()
47
- if isSecretExists {
48
- return 1
49
- } else {
50
- return 0
104
+ do {
105
+ let isSecretExists: Bool = try UtilsSecret.isPassphrase(
106
+ account: account)
107
+ if isSecretExists {
108
+ return 1
109
+ } else {
110
+ return 0
111
+ }
112
+ } catch UtilsSecretError.prefixPassphrase(let message) {
113
+ throw CapacitorSQLiteError.failed(message: message)
51
114
  }
115
+
52
116
  } else {
53
117
  throw CapacitorSQLiteError.failed(message: initMessage)
54
118
  }
@@ -63,7 +127,8 @@ enum CapacitorSQLiteError: Error {
63
127
  try closeAllConnections()
64
128
  // set encryption secret
65
129
  try UtilsSecret
66
- .setEncryptionSecret(passphrase: passphrase,
130
+ .setEncryptionSecret(prefix: prefixKeychain,
131
+ passphrase: passphrase,
67
132
  databaseLocation: databaseLocation)
68
133
  return
69
134
  } catch UtilsSecretError.setEncryptionSecret(let message) {
@@ -78,28 +143,70 @@ enum CapacitorSQLiteError: Error {
78
143
 
79
144
  // MARK: - ChangeEncryptionSecret
80
145
 
81
- @objc public func changeEncryptionSecret(passphrase: String,
146
+ // swiftlint:disable function_body_length
147
+ @objc public func changeEncryptionSecret(call: CAPPluginCall, passphrase: String,
82
148
  oldPassphrase: String) throws {
83
149
  if isInit {
150
+ self.call = call
151
+ let retHandler: ReturnHandler = ReturnHandler()
84
152
  do {
85
153
  // close all connections
86
154
  try closeAllConnections()
87
- // set encryption secret
88
- try UtilsSecret
89
- .changeEncryptionSecret(passphrase: passphrase,
90
- oldPassphrase: oldPassphrase,
91
- databaseLocation: databaseLocation)
92
- return
155
+ if isBiometricAuth {
156
+ let dbLocation = databaseLocation
157
+ let mPrefixKeychain = prefixKeychain
158
+ bioIdAuth.authenticateUser { [weak self] message in
159
+ if let message = message {
160
+ self?.intBioMessage = message
161
+ return
162
+ } else {
163
+ do {
164
+ try UtilsSecret.changeEncryptionSecret(
165
+ prefix: mPrefixKeychain,
166
+ passphrase: passphrase,
167
+ oldPassphrase: oldPassphrase,
168
+ databaseLocation: dbLocation)
169
+ retHandler.rResult(call: call)
170
+ return
171
+ } catch UtilsSecretError.changeEncryptionSecret(let message) {
172
+ let msg = "ChangeEncryptionSecret: \(message)"
173
+ retHandler.rResult(call: call, message: msg)
174
+ return
175
+ } catch let error {
176
+ retHandler.rResult(
177
+ call: call,
178
+ message: "ChangeEncryptionSecret: \(error.localizedDescription)")
179
+ return
180
+ }
181
+ }
182
+ }
183
+ } else {
184
+ // set encryption secret
185
+ try UtilsSecret
186
+ .changeEncryptionSecret(prefix: prefixKeychain,
187
+ passphrase: passphrase,
188
+ oldPassphrase: oldPassphrase,
189
+ databaseLocation: databaseLocation)
190
+ retHandler.rResult(call: call)
191
+ return
192
+ }
93
193
  } catch UtilsSecretError.changeEncryptionSecret(let message) {
94
- throw CapacitorSQLiteError.failed(message: message)
194
+ let msg = "ChangeEncryptionSecret: \(message)"
195
+ retHandler.rResult(call: call, message: msg)
196
+ return
95
197
  } catch let error {
96
- throw CapacitorSQLiteError.failed(message: "\(error)")
198
+ retHandler.rResult(
199
+ call: call,
200
+ message: "ChangeEncryptionSecret: \(error.localizedDescription)")
201
+ return
97
202
  }
98
203
  } else {
99
204
  throw CapacitorSQLiteError.failed(message: initMessage)
100
205
  }
101
206
 
102
207
  }
208
+ // swiftlint:enable function_body_length
209
+
103
210
  // MARK: - getNCDatabasePath
104
211
 
105
212
  @objc public func getNCDatabasePath(_ folderPath: String, dbName: String ) throws -> String {
@@ -140,7 +247,8 @@ enum CapacitorSQLiteError: Error {
140
247
  let mDb: Database = try Database(
141
248
  databaseLocation: databaseLocation,
142
249
  databaseName: databasePath,
143
- encrypted: false, mode: "no-encryption", version: version,
250
+ encrypted: false, account: account,
251
+ mode: "no-encryption", version: version,
144
252
  vUpgDict: [:])
145
253
  dbDict[databasePath] = mDb
146
254
  return
@@ -194,7 +302,8 @@ enum CapacitorSQLiteError: Error {
194
302
  let mDb: Database = try Database(
195
303
  databaseLocation: databaseLocation,
196
304
  databaseName: "\(mDbName)SQLite.db",
197
- encrypted: encrypted, mode: mode, version: version,
305
+ encrypted: encrypted, account: account,
306
+ mode: mode, version: version,
198
307
  vUpgDict: vUpgDict)
199
308
  dbDict[mDbName] = mDb
200
309
  return
@@ -699,8 +808,8 @@ enum CapacitorSQLiteError: Error {
699
808
  // open the database
700
809
  do {
701
810
  mDb = try Database(
702
- databaseLocation: databaseLocation,
703
- databaseName: dbName, encrypted: encrypted,
811
+ databaseLocation: databaseLocation, databaseName: dbName,
812
+ encrypted: encrypted, account: account,
704
813
  mode: inMode, version: version, vUpgDict: [:])
705
814
  try mDb.open()
706
815
  } catch DatabaseError.open(let message) {
@@ -1087,6 +1196,17 @@ enum CapacitorSQLiteError: Error {
1087
1196
  }
1088
1197
  return
1089
1198
  }
1199
+
1200
+ func notifyBiometricEvents(name: Notification.Name, result: Bool, msg: String) {
1201
+ var vId: [String: Any] = [:]
1202
+ vId["result"] = result
1203
+ if msg.count > 0 {
1204
+ vId["message"] = msg
1205
+ }
1206
+ NotificationCenter.default.post(name: name, object: nil,
1207
+ userInfo: vId)
1208
+ }
1209
+
1090
1210
  }
1091
1211
  // swiftlint:enable type_body_length
1092
1212
  // swiftlint:enable file_length
@@ -11,14 +11,23 @@ public class CapacitorSQLitePlugin: CAPPlugin {
11
11
  private var versionUpgrades: [String: [Int: [String: Any]]] = [:]
12
12
  var importObserver: Any?
13
13
  var exportObserver: Any?
14
+ var biometricObserver: Any?
15
+ var config: SqliteConfig?
14
16
 
15
17
  override public func load() {
16
- self.implementation = CapacitorSQLite(config: sqliteConfig())
18
+ let mConfig = sqliteConfig()
19
+ self.config = mConfig
17
20
  self.addObserversToNotificationCenter()
21
+ self.implementation = CapacitorSQLite(config: mConfig)
18
22
  }
19
23
  deinit {
20
24
  NotificationCenter.default.removeObserver(importObserver as Any)
21
25
  NotificationCenter.default.removeObserver(exportObserver as Any)
26
+ if let biometricAuth = config?.biometricAuth {
27
+ if biometricAuth == 1 {
28
+ NotificationCenter.default.removeObserver(biometricObserver as Any)
29
+ }
30
+ }
22
31
  }
23
32
 
24
33
  // MARK: - Echo
@@ -99,8 +108,7 @@ public class CapacitorSQLitePlugin: CAPPlugin {
99
108
  return
100
109
  }
101
110
  do {
102
- try implementation?.changeEncryptionSecret(passphrase: passphrase, oldPassphrase: oldPassphrase)
103
- retHandler.rResult(call: call)
111
+ try implementation?.changeEncryptionSecret(call: call, passphrase: passphrase, oldPassphrase: oldPassphrase)
104
112
  return
105
113
  } catch CapacitorSQLiteError.failed(let message) {
106
114
  let msg = "ChangeEncryptionSecret: \(message)"
@@ -1167,10 +1175,19 @@ public class CapacitorSQLitePlugin: CAPPlugin {
1167
1175
 
1168
1176
  @objc func addObserversToNotificationCenter() {
1169
1177
  // add Observers
1170
- importObserver = NotificationCenter.default.addObserver(forName: .importJsonProgress, object: nil, queue: nil,
1171
- using: importJsonProgress)
1172
- exportObserver = NotificationCenter.default.addObserver(forName: .exportJsonProgress, object: nil, queue: nil,
1173
- using: exportJsonProgress)
1178
+ importObserver = NotificationCenter.default.addObserver(
1179
+ forName: .importJsonProgress, object: nil, queue: nil,
1180
+ using: importJsonProgress)
1181
+ exportObserver = NotificationCenter.default.addObserver(
1182
+ forName: .exportJsonProgress, object: nil, queue: nil,
1183
+ using: exportJsonProgress)
1184
+ if let biometricAuth = config?.biometricAuth {
1185
+ if biometricAuth == 1 {
1186
+ biometricObserver = NotificationCenter.default.addObserver(
1187
+ forName: .biometricEvent, object: nil, queue: nil,
1188
+ using: biometricEvent)
1189
+ }
1190
+ }
1174
1191
  }
1175
1192
 
1176
1193
  // MARK: - Handle Notifications
@@ -1189,12 +1206,38 @@ public class CapacitorSQLitePlugin: CAPPlugin {
1189
1206
  return
1190
1207
  }
1191
1208
  }
1209
+ @objc func biometricEvent(notification: Notification) {
1210
+ guard let info = notification.userInfo as? [String: Any] else { return }
1211
+ DispatchQueue.main.async {
1212
+ self.notifyListeners("sqliteBiometricEvent", data: info, retainUntilConsumed: true)
1213
+ return
1214
+ }
1215
+ }
1192
1216
  private func sqliteConfig() -> SqliteConfig {
1193
1217
  var config = SqliteConfig()
1194
-
1195
- if let iosDatabaseLocation = getConfigValue("iosDatabaseLocation") as? String {
1218
+ config.biometricAuth = 0
1219
+ config.iosKeychainPrefix = ""
1220
+ if let keychainPrefix = getConfigValue("iosKeychainPrefix") as? String {
1221
+ config.iosKeychainPrefix = keychainPrefix
1222
+ }
1223
+ if let iosDatabaseLocation = getConfigValue("iosDatabaseLocation")
1224
+ as? String {
1196
1225
  config.iosDatabaseLocation = iosDatabaseLocation
1197
1226
  }
1227
+
1228
+ if let iosBiometric = getConfigValue("iosBiometric") as? [String: Any] {
1229
+ if let bioAuth = iosBiometric["biometricAuth"] as? Bool {
1230
+ if bioAuth {
1231
+ config.biometricAuth = 1
1232
+ if let bioTitle = iosBiometric["biometricTitle"] as? String {
1233
+ config.biometricTitle = bioTitle.count > 0
1234
+ ? bioTitle
1235
+ : "Biometric login for capacitor sqlite"
1236
+ }
1237
+ }
1238
+ }
1239
+
1240
+ }
1198
1241
  return config
1199
1242
  }
1200
1243
 
@@ -34,6 +34,7 @@ class Database {
34
34
  var mode: String
35
35
  var vUpgDict: [Int: [String: Any]]
36
36
  var databaseLocation: String
37
+ var account: String
37
38
  var path: String = ""
38
39
  var mDb: OpaquePointer?
39
40
  let globalData: GlobalSQLite = GlobalSQLite()
@@ -43,10 +44,11 @@ class Database {
43
44
 
44
45
  // MARK: - Init
45
46
  init(databaseLocation: String, databaseName: String, encrypted: Bool,
46
- mode: String, version: Int,
47
+ account: String, mode: String, version: Int,
47
48
  vUpgDict: [Int: [String: Any]] = [:]) throws {
48
49
  self.dbVersion = version
49
50
  self.encrypted = encrypted
51
+ self.account = account
50
52
  self.dbName = databaseName
51
53
  self.mode = mode
52
54
  self.vUpgDict = vUpgDict
@@ -94,7 +96,7 @@ class Database {
94
96
  func open () throws {
95
97
  var password: String = ""
96
98
  if encrypted && (mode == "secret" || mode == "encryption") {
97
- password = UtilsSecret.getPassphrase()
99
+ password = UtilsSecret.getPassphrase(account: account)
98
100
  }
99
101
  if mode == "encryption" {
100
102
  do {
@@ -264,6 +266,16 @@ class Database {
264
266
  throw DatabaseError.executeSQL(
265
267
  message: "Failed in executeSQL : \(msg)" )
266
268
  }
269
+ } else {
270
+ if transaction {
271
+ do {
272
+ try UtilsSQLCipher
273
+ .rollbackTransaction(mDB: self)
274
+ } catch UtilsSQLCipherError
275
+ .rollbackTransaction(let message) {
276
+ msg.append(" rollback: \(message)")
277
+ }
278
+ }
267
279
  }
268
280
  return changes
269
281
  }
@@ -310,7 +322,7 @@ class Database {
310
322
  msg.append(" \(message)")
311
323
  throw DatabaseError.execSet(message: msg )
312
324
  }
313
- if changes > 0 && transaction {
325
+ if changes >= 0 && transaction {
314
326
  // commit the transaction
315
327
  do {
316
328
  try UtilsSQLCipher.commitTransaction(mDB: self)
@@ -321,6 +333,7 @@ class Database {
321
333
  }
322
334
  return changesDict
323
335
  }
336
+ // swiftlint:enable function_body_length
324
337
 
325
338
  // MARK: - RunSQL
326
339
 
@@ -368,8 +381,8 @@ class Database {
368
381
  throw DatabaseError.runSQL(message: msg )
369
382
  }
370
383
  }
371
- changes = UtilsSQLCipher.dbChanges(mDB: mDb) - initChanges
372
384
  }
385
+ changes = UtilsSQLCipher.dbChanges(mDB: mDb) - initChanges
373
386
  let result: [String: Int64] = ["changes": Int64(changes),
374
387
  "lastId": lastId]
375
388
  return result
@@ -7,6 +7,10 @@
7
7
  //
8
8
  import Foundation
9
9
  extension NSNotification.Name {
10
- static var importJsonProgress: Notification.Name {return .init(rawValue: "importJsonProgress")}
11
- static var exportJsonProgress: Notification.Name {return .init(rawValue: "exportJsonProgress")}
10
+ static var importJsonProgress: Notification.Name {
11
+ return .init(rawValue: "importJsonProgress")}
12
+ static var exportJsonProgress: Notification.Name {
13
+ return .init(rawValue: "exportJsonProgress")}
14
+ static var biometricEvent: Notification.Name {
15
+ return .init(rawValue: "biometricEvent")}
12
16
  }
@@ -52,7 +52,6 @@ class KeychainWrapper {
52
52
  print("Error converting value to data.")
53
53
  throw KeychainWrapperError(type: .badData)
54
54
  }
55
-
56
55
  // 1
57
56
  let query: [String: Any] = [
58
57
  // 2
@@ -91,6 +90,7 @@ class KeychainWrapper {
91
90
  // 2
92
91
  kSecMatchLimit as String: kSecMatchLimitOne,
93
92
  kSecReturnAttributes as String: true,
93
+
94
94
  // 3
95
95
  kSecReturnData as String: true
96
96
  ]
@@ -7,4 +7,7 @@
7
7
 
8
8
  public struct SqliteConfig {
9
9
  var iosDatabaseLocation: String?
10
+ var biometricAuth: Int?
11
+ var biometricTitle: String?
12
+ var iosKeychainPrefix: String?
10
13
  }
@@ -44,7 +44,8 @@ enum State: String {
44
44
  class UtilsSQLCipher {
45
45
 
46
46
  class func getDatabaseState(databaseLocation: String,
47
- databaseName: String) -> State {
47
+ databaseName: String,
48
+ account: String) -> State {
48
49
  do {
49
50
  let path: String = try UtilsFile
50
51
  .getFilePath(databaseLocation: databaseLocation,
@@ -56,7 +57,7 @@ class UtilsSQLCipher {
56
57
  } catch UtilsSQLCipherError.openDBNoPassword(let message) {
57
58
  if message == "Open" {
58
59
  do {
59
- try openDBStoredPassword(dBPath: path)
60
+ try openDBStoredPassword(dBPath: path, account: account)
60
61
  return State.ENCRYPTEDSECRET
61
62
  } catch UtilsSQLCipherError.openDBStoredPassword(let message) {
62
63
  if message == "Open" {
@@ -100,9 +101,9 @@ class UtilsSQLCipher {
100
101
  }
101
102
 
102
103
  }
103
- class func openDBStoredPassword(dBPath: String) throws {
104
+ class func openDBStoredPassword(dBPath: String, account: String) throws {
104
105
  do {
105
- let password: String = UtilsSecret.getPassphrase()
106
+ let password: String = UtilsSecret.getPassphrase(account: account)
106
107
  let oDb: OpaquePointer? = try openOrCreateDatabase(
107
108
  filename: dBPath, password: password, readonly: true)
108
109
  try close(oDB: oDb)