@binsky/passman-client-ts 0.1.9 → 0.2.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.
Files changed (66) hide show
  1. package/README.md +12 -0
  2. package/lib/Interfaces/Credential/{CredentialInterface.d.ts → DecryptedCredentialInterface.d.ts} +5 -1
  3. package/lib/Interfaces/Credential/EncryptedCredentialInterface.d.ts +12 -11
  4. package/lib/Interfaces/Credential/EncryptedOwnedCredentialFromServerInterface.d.ts +35 -0
  5. package/lib/Interfaces/Credential/EncryptedOwnedCredentialFromServerInterface.js +2 -0
  6. package/lib/Interfaces/Credential/EncryptedOwnedCredentialToUpdateForServerInterface.d.ts +8 -0
  7. package/lib/Interfaces/Credential/EncryptedOwnedCredentialToUpdateForServerInterface.js +2 -0
  8. package/lib/Interfaces/Credential/OTPConfigInterface.d.ts +6 -1
  9. package/lib/Interfaces/Credential/OTPConfigInterface.js +6 -0
  10. package/lib/Interfaces/Credential/SerializableTransferCredentialInterface.d.ts +7 -0
  11. package/lib/Interfaces/Credential/SerializableTransferCredentialInterface.js +2 -0
  12. package/lib/Interfaces/DecryptedDataCachingHandlerInterface.d.ts +20 -0
  13. package/lib/Interfaces/DecryptedDataCachingHandlerInterface.js +2 -0
  14. package/lib/Interfaces/NextcloudServer/NextcloudServerInterface.d.ts +8 -3
  15. package/lib/Interfaces/PassmanCrypto/EncryptedStringType.d.ts +4 -0
  16. package/lib/Interfaces/PassmanCrypto/EncryptedStringType.js +2 -0
  17. package/lib/Interfaces/PersistenceInterface.d.ts +10 -0
  18. package/lib/Interfaces/PersistenceInterface.js +2 -0
  19. package/lib/Interfaces/RequestCachingHandlerInterface.d.ts +5 -1
  20. package/lib/Interfaces/Revision/RevisionInterface.d.ts +2 -2
  21. package/lib/Interfaces/ShareService/CredentialShareRequestInterface.d.ts +2 -2
  22. package/lib/Interfaces/ShareService/SerializableACLInterface.d.ts +14 -0
  23. package/lib/Interfaces/ShareService/SerializableACLInterface.js +2 -0
  24. package/lib/Interfaces/Vault/GenericVaultInformationFromServerInterface.d.ts +17 -0
  25. package/lib/Interfaces/Vault/GenericVaultInformationFromServerInterface.js +2 -0
  26. package/lib/Interfaces/Vault/SerializableSpecificVaultInformationFromServerInterface.d.ts +12 -0
  27. package/lib/Interfaces/Vault/SerializableSpecificVaultInformationFromServerInterface.js +2 -0
  28. package/lib/Interfaces/Vault/SerializableTransferFullVaultInterface.d.ts +6 -0
  29. package/lib/Interfaces/Vault/SerializableTransferFullVaultInterface.js +2 -0
  30. package/lib/Interfaces/Vault/SpecificVaultInformationFromServerInterface.d.ts +14 -0
  31. package/lib/Interfaces/Vault/SpecificVaultInformationFromServerInterface.js +2 -0
  32. package/lib/Interfaces/Vault/VaultCreateServerResponseInterface.d.ts +8 -0
  33. package/lib/Interfaces/Vault/VaultCreateServerResponseInterface.js +2 -0
  34. package/lib/Model/Credential.d.ts +70 -19
  35. package/lib/Model/Credential.js +138 -25
  36. package/lib/Model/File.d.ts +7 -7
  37. package/lib/Model/NextcloudServer.d.ts +9 -8
  38. package/lib/Model/NextcloudServer.js +14 -14
  39. package/lib/Model/PreloadedVault.d.ts +20 -0
  40. package/lib/Model/PreloadedVault.js +54 -0
  41. package/lib/Model/Revision.d.ts +3 -3
  42. package/lib/Model/Revision.js +3 -3
  43. package/lib/Model/SharingACL.d.ts +3 -2
  44. package/lib/Model/SharingACL.js +9 -6
  45. package/lib/Model/Vault.d.ts +48 -5
  46. package/lib/Model/Vault.js +141 -61
  47. package/lib/PassmanClient.d.ts +51 -10
  48. package/lib/PassmanClient.js +101 -35
  49. package/lib/Service/CredentialFilterService.d.ts +2 -1
  50. package/lib/Service/CredentialFilterService.js +24 -9
  51. package/lib/Service/CustomMathsService.js +1 -11
  52. package/lib/Service/DefaultLoggingService.d.ts +3 -0
  53. package/lib/Service/DefaultLoggingService.js +3 -0
  54. package/lib/Service/DefaultPersistenceService.d.ts +12 -0
  55. package/lib/Service/DefaultPersistenceService.js +20 -0
  56. package/lib/Service/OTPService.d.ts +6 -6
  57. package/lib/Service/OTPService.js +21 -8
  58. package/lib/Service/PassmanCrypto.d.ts +9 -4
  59. package/lib/Service/PassmanCrypto.js +6 -6
  60. package/lib/Service/ReEncryptionService.js +2 -2
  61. package/lib/Service/RequestCachingService.d.ts +5 -2
  62. package/lib/Service/RequestCachingService.js +3 -0
  63. package/lib/Service/ShareService.js +2 -4
  64. package/lib/tsconfig.tsbuildinfo +1 -1
  65. package/package.json +3 -1
  66. /package/lib/Interfaces/Credential/{CredentialInterface.js → DecryptedCredentialInterface.js} +0 -0
@@ -9,22 +9,46 @@ class Credential {
9
9
  vault;
10
10
  server;
11
11
  ENCRYPTED_FIELDS = ['description', 'username', 'password', 'files', 'custom_fields', 'otp', 'email', 'tags', 'url', 'compromised', 'shared_key'];
12
+ /**
13
+ * Contains all ENCRYPTED_FIELDS that matches to one of the types: string|number|boolean|null
14
+ */
15
+ SERIALIZABLE_ENCRYPTED_FIELDS = ['description', 'username', 'password', 'email', 'url', 'compromised', 'shared_key'];
16
+ /**
17
+ * encryptedData & spacialServerUpdateFields needs to be merged in order to save to credential to the server (api)
18
+ * @protected
19
+ */
12
20
  encryptedData;
21
+ /**
22
+ * Should not be overwritten/cleared during Credential instance lifetime, since it contains acl data managed by ShareService.
23
+ * @protected
24
+ */
25
+ _spacialServerUpdateFields;
26
+ /**
27
+ * This is always set if the credential is shared with us.
28
+ *
29
+ * The sharedCredentialEncryptionKey is encrypted with the vault key and injected by ShareService.
30
+ * To be able to hold this credential instance in an (initial, fully) unencrypted state (needed to fetch the full vault without vault key given),
31
+ * we need to split the encrypted and unencrypted shared credential encryption keys.
32
+ */
33
+ encryptedSharedCredentialEncryptionKey;
34
+ sharedCredentialEncryptionKey;
13
35
  decryptedDataCache;
14
- sharedCredentialEncryptionKey; // this is set if the credential is shared with us (injected by ShareService)
15
36
  foundUnspecifiedEncryptionError = false;
37
+ decryptedDataCacheName;
16
38
  // can be used to re-encrypt credential data, if the original vault key is not changed yet
17
39
  overwriteVaultKey = undefined;
18
40
  constructor(vault, server, encryptedData = undefined) {
19
41
  this.vault = vault;
20
42
  this.server = server;
21
43
  this.encryptedData = encryptedData;
44
+ this._spacialServerUpdateFields = {};
22
45
  this.decryptedDataCache = {};
23
46
  if (encryptedData === undefined) {
24
47
  this.encryptedData = {};
25
48
  this.initializeAllFields();
26
49
  }
27
50
  this.vault_id = vault.vaultId;
51
+ this.decryptedDataCacheName = this.getVaultGuid() + '_' + this.guid;
28
52
  }
29
53
  initializeAllFields() {
30
54
  this.user_id = null;
@@ -57,50 +81,59 @@ class Credential {
57
81
  * The current credential object will be updated with the server response data if possible.
58
82
  */
59
83
  async save() {
60
- let credentialResponse = await this.server.postJson('/credentials', this.encryptedData, (response) => {
84
+ let requestData = { ...this.encryptedData, ...this._spacialServerUpdateFields };
85
+ let credentialResponse = await this.server.postJson('/credentials', requestData, (response) => {
61
86
  this.server.logger.onError(response.message);
62
87
  });
63
88
  if (credentialResponse) {
64
89
  this.encryptedData = credentialResponse;
65
90
  this.decryptedDataCache = {};
91
+ this.tags.forEach(value => value.text && this.vault.collectedTags.add(value.text));
66
92
  }
67
93
  return credentialResponse;
68
94
  }
69
95
  /**
70
96
  * Update / edit an existing credential on the server.
71
97
  * The current credential object will be updated with the server response data if possible.
98
+ * This will not touch the virtual acl field.
72
99
  */
73
100
  async update() {
74
101
  if (this.acl === undefined || this.acl.permissions.hasPermission(SharingACL_1.SharingACL.permissions.WRITE)) {
75
- let credentialResponse = await this.server.postJson('/credentials/' + this.guid, this.encryptedData, (response) => {
102
+ let requestData = { ...this.encryptedData, ...this._spacialServerUpdateFields };
103
+ let credentialResponse = await this.server.postJson('/credentials/' + this.guid, requestData, (response) => {
76
104
  this.server.logger.onError(response.message);
77
105
  }, 'PATCH');
78
106
  if (credentialResponse) {
79
- if (this.encryptedData.acl) {
80
- credentialResponse.acl = this.encryptedData.acl;
81
- }
82
107
  this.encryptedData = credentialResponse;
83
108
  this.decryptedDataCache = {};
109
+ if (this.tags) {
110
+ this.tags.forEach(value => value?.text && this.vault.collectedTags.add(value.text));
111
+ }
84
112
  }
85
113
  return credentialResponse;
86
114
  }
87
115
  }
88
116
  /**
89
117
  * Refresh the local credential data based on the server, using the credentials guid.
90
- * It is not supported to do that for credentials, shared with us.
118
+ * It is not supported to do that for credentials, shared with us. (Therefore this will not touch the virtual acl field.)
91
119
  */
92
120
  async refresh() {
93
- if (this.sharedCredentialEncryptionKey) {
121
+ if (this.encryptedSharedCredentialEncryptionKey) {
94
122
  // credential is shared with us
95
- // no credential refresh possible, since there is no backend api to do that
123
+ // no credential refresh possible, since there is no backend api to do that for a specific shared credential
124
+ // if it will be implemented in the future, don't forget to update the acl field, that's currently injected by ShareService
96
125
  }
97
126
  else {
127
+ this.clearDecryptedDataCache(true);
98
128
  let credentialResponse = await this.server.getJson('/credentials/' + this.guid, (response) => {
99
129
  this.server.logger.onError(response.message);
100
130
  });
101
131
  if (credentialResponse) {
102
132
  this.encryptedData = credentialResponse;
103
133
  this.decryptedDataCache = {};
134
+ if (this.tags) {
135
+ this.tags.forEach(value => value?.text && this.vault.collectedTags.add(value.text));
136
+ }
104
137
  }
105
138
  return credentialResponse;
106
139
  }
@@ -110,6 +143,7 @@ class Credential {
110
143
  */
111
144
  async destroy() {
112
145
  if (this.acl === undefined || this.acl.permissions.hasPermission(SharingACL_1.SharingACL.permissions.WRITE)) {
146
+ this.clearDecryptedDataCache(true);
113
147
  let credentialResponse = await this.server.deleteJson('/credentials/' + this.guid, (response) => {
114
148
  this.server.logger.onError(response.message);
115
149
  });
@@ -118,8 +152,38 @@ class Credential {
118
152
  return credentialResponse;
119
153
  }
120
154
  }
121
- clearDecryptedDataCache() {
155
+ /**
156
+ * This is the correct function to "lock" the credential (to be called subsequently when the vault got locked).
157
+ * It clears the decrypted credential data cache as well as a potential sharedCredentialEncryptionKey.
158
+ * This will not clear the serialized decrypted data cache, managed by the used DecryptedDataCachingHandlerInterface implementation.
159
+ * Set clearCachingHandlerManagedDecryptedData=true to do so (by calling getDecryptedDataCacheHandler().clearCacheByName('...'))
160
+ */
161
+ clearDecryptedDataCache(clearCachingHandlerManagedDecryptedData = false) {
122
162
  this.decryptedDataCache = {};
163
+ this.sharedCredentialEncryptionKey = undefined;
164
+ if (clearCachingHandlerManagedDecryptedData) {
165
+ // atm. we are not interested in the result, so we keep the returned Promise running in background
166
+ this.server.persistence.getDecryptedDataCacheHandler().clearCacheByName(this.decryptedDataCacheName);
167
+ }
168
+ }
169
+ /**
170
+ * Restore only serializable fields of the CredentialInterface.
171
+ */
172
+ async restoreSerializedDecryptedDataCache() {
173
+ const cacheHandler = this.server.persistence.getDecryptedDataCacheHandler();
174
+ for (const propertyName of this.SERIALIZABLE_ENCRYPTED_FIELDS) {
175
+ this.decryptedDataCache[propertyName] = await cacheHandler?.get(this.decryptedDataCacheName, propertyName);
176
+ }
177
+ }
178
+ /**
179
+ * Update or delete an entry in the decrypted data cache.
180
+ * @param propertyName
181
+ * @param deleteFromCache
182
+ */
183
+ updateSerializedDecryptedDataCacheEntry(propertyName, deleteFromCache = false) {
184
+ if (this.SERIALIZABLE_ENCRYPTED_FIELDS.includes(propertyName) && this.server.persistence.getDecryptedDataCacheHandler()) {
185
+ return this.server.persistence.getDecryptedDataCacheHandler().set(this.decryptedDataCacheName, propertyName, deleteFromCache ? undefined : this.decryptedDataCache[propertyName]);
186
+ }
123
187
  }
124
188
  /**
125
189
  * Create a credential object based on its encrypted data.
@@ -127,7 +191,7 @@ class Credential {
127
191
  * @param vault
128
192
  * @param server
129
193
  */
130
- static async fromData(data, vault, server) {
194
+ static fromData(data, vault, server) {
131
195
  return new Credential(vault, server, data);
132
196
  }
133
197
  /**
@@ -142,6 +206,17 @@ class Credential {
142
206
  await cred.refresh();
143
207
  return cred;
144
208
  }
209
+ static fromSerializable(serialized, vault, server) {
210
+ const credential = new Credential(vault, server, serialized.encryptedData);
211
+ credential.encryptedSharedCredentialEncryptionKey = serialized.encryptedSharedCredentialEncryptionKey;
212
+ if (serialized.acl) {
213
+ credential.acl = {
214
+ permissions: new SharingACL_1.SharingACL(serialized.acl.permission),
215
+ ...serialized.acl
216
+ };
217
+ }
218
+ return credential;
219
+ }
145
220
  async getRevisions() {
146
221
  return await this.server.getJson('/credentials/' + this.guid + '/revision', (response) => {
147
222
  this.server.logger.onError(response.message);
@@ -208,11 +283,11 @@ class Credential {
208
283
  };
209
284
  }
210
285
  /**
211
- * Creates a local 100% clone of the current credential.
286
+ * Creates a local 100% clone of the current credential. The clone contains only encrypted data.
212
287
  */
213
288
  clone() {
214
289
  const newCredential = new Credential(this.vault, this.server, this.encryptedData);
215
- newCredential.sharedCredentialEncryptionKey = this.sharedCredentialEncryptionKey;
290
+ newCredential.encryptedSharedCredentialEncryptionKey = this.encryptedSharedCredentialEncryptionKey;
216
291
  return newCredential;
217
292
  }
218
293
  /**
@@ -262,7 +337,10 @@ class Credential {
262
337
  return this.decryptedDataCache.shared_key;
263
338
  }
264
339
  }
265
- if (this.sharedCredentialEncryptionKey !== undefined) {
340
+ if (this.encryptedSharedCredentialEncryptionKey !== undefined) {
341
+ if (!this.sharedCredentialEncryptionKey) {
342
+ this.sharedCredentialEncryptionKey = PassmanCrypto_1.PassmanCrypto.decryptString(this.encryptedSharedCredentialEncryptionKey, this.vault.vaultKey);
343
+ }
266
344
  return this.sharedCredentialEncryptionKey;
267
345
  }
268
346
  return this.overwriteVaultKey ?? this.vault.vaultKey;
@@ -275,11 +353,17 @@ class Credential {
275
353
  */
276
354
  getCacheDecryptFieldData(property) {
277
355
  if (this.ENCRYPTED_FIELDS.indexOf(property) > -1) {
278
- if (this.decryptedDataCache[property] === undefined) {
356
+ if (!Object.hasOwn(this.decryptedDataCache, property) || this.decryptedDataCache[property] === undefined) {
279
357
  if (this.encryptedData[property] === null) {
280
358
  this.decryptedDataCache[property] = null;
281
359
  }
282
360
  else {
361
+ // should never happen, but who knows xD
362
+ if (this.encryptedData[property] === undefined) {
363
+ this.server.logger.anyError('Corrupt credential data detected. Undefined value for encryptedData.' + property + ' not allowed! encryptedData: ');
364
+ this.server.logger.anyError(this.encryptedData);
365
+ throw new Error('Corrupt credential data detected. Undefined value for encryptedData.' + property + ' not allowed!');
366
+ }
283
367
  let decryptedData;
284
368
  try {
285
369
  decryptedData = PassmanCrypto_1.PassmanCrypto.decryptString(this.encryptedData[property], this.getFieldEncryptionKey(property));
@@ -290,11 +374,13 @@ class Credential {
290
374
  else {
291
375
  this.decryptedDataCache[property] = JSON.parse(decryptedData);
292
376
  }
377
+ // should work, even if we do not wait for the returned Promise
378
+ this.updateSerializedDecryptedDataCacheEntry(property);
293
379
  }
294
380
  catch (e) {
295
381
  this.foundUnspecifiedEncryptionError = true;
296
382
  this.server.logger.anyError(e);
297
- this.server.logger.anyError(decryptedData);
383
+ this.server.logger.anyError(decryptedData ?? 'no decrypted data: (undefined)');
298
384
  this.server.logger.onError('Failed to decrypt field: ' + property + ' of credential: ' + this.label);
299
385
  }
300
386
  }
@@ -319,7 +405,8 @@ class Credential {
319
405
  this.encryptedData[property] = PassmanCrypto_1.PassmanCrypto.encryptString(data, this.getFieldEncryptionKey(property));
320
406
  }
321
407
  else {
322
- this.encryptedData[property] = PassmanCrypto_1.PassmanCrypto.encryptString(JSON.stringify(data), this.getFieldEncryptionKey(property));
408
+ // use data ?? null to prevent undefined value (which could lead to an error in decryption)
409
+ this.encryptedData[property] = PassmanCrypto_1.PassmanCrypto.encryptString(JSON.stringify(data ?? null), this.getFieldEncryptionKey(property));
323
410
  }
324
411
  }
325
412
  catch (e) {
@@ -329,6 +416,7 @@ class Credential {
329
416
  return;
330
417
  }
331
418
  this.decryptedDataCache[property] = data;
419
+ this.updateSerializedDecryptedDataCacheEntry(property);
332
420
  }
333
421
  else {
334
422
  this.encryptedData[property] = data;
@@ -359,7 +447,7 @@ class Credential {
359
447
  logger.onError('Failed to decrypt file: ' + file.filename);
360
448
  }
361
449
  };
362
- if (!this.encryptedData.acl) {
450
+ if (!this.acl) {
363
451
  return File_1.File.downloadFile(file, this.server).then(callback);
364
452
  }
365
453
  else {
@@ -383,7 +471,7 @@ class Credential {
383
471
  const callback = function (uploadFileResponse) {
384
472
  return uploadFileResponse;
385
473
  };
386
- if (!this.encryptedData.acl) {
474
+ if (!this.acl) {
387
475
  return File_1.File.uploadFile(encryptedFile, this.server).then(callback);
388
476
  }
389
477
  else {
@@ -396,6 +484,24 @@ class Credential {
396
484
  this.server.logger.onError('Failed to encrypt new file (' + plainFile.filename + ') of credential: ' + this.label);
397
485
  }
398
486
  }
487
+ /**
488
+ * Serialized, encrypted credential data from non-object (string only) transfer methods (like WebExtension messaging api).
489
+ */
490
+ getAsSerializable() {
491
+ let acl = undefined;
492
+ if (this.acl) {
493
+ let { permissions, ...newAcl } = {
494
+ permission: this.acl.permissions.permission,
495
+ ...this.acl
496
+ };
497
+ acl = newAcl;
498
+ }
499
+ return {
500
+ encryptedData: this.getEncrypted(),
501
+ encryptedSharedCredentialEncryptionKey: this.encryptedSharedCredentialEncryptionKey,
502
+ acl
503
+ };
504
+ }
399
505
  /**
400
506
  * Deletes the given file from the server.
401
507
  * This method does *not* delete the file from the local credential files list!
@@ -573,22 +679,29 @@ class Credential {
573
679
  this.encryptedData.changed = value;
574
680
  }
575
681
  get set_share_key() {
576
- return this.encryptedData.set_share_key;
682
+ return this._spacialServerUpdateFields.set_share_key;
577
683
  }
578
684
  set set_share_key(value) {
579
- this.encryptedData.set_share_key = value;
685
+ this._spacialServerUpdateFields.set_share_key = value;
580
686
  }
581
687
  get skip_revision() {
582
- return this.encryptedData.skip_revision;
688
+ return this._spacialServerUpdateFields.skip_revision;
583
689
  }
584
690
  set skip_revision(value) {
585
- this.encryptedData.skip_revision = value;
691
+ this._spacialServerUpdateFields.skip_revision = value;
586
692
  }
587
693
  get acl() {
588
- return this.encryptedData.acl;
694
+ return this._spacialServerUpdateFields.acl;
589
695
  }
696
+ /**
697
+ * Will be called short after credential instantiation by the ShareService.
698
+ * @param value
699
+ */
590
700
  set acl(value) {
591
- this.encryptedData.acl = value;
701
+ this._spacialServerUpdateFields.acl = value;
702
+ }
703
+ get spacialServerUpdateFields() {
704
+ return this._spacialServerUpdateFields;
592
705
  }
593
706
  }
594
707
  exports.default = Credential;
@@ -6,11 +6,11 @@ import { DeleteFilesRequestBodyInterface } from "../Interfaces/File/DeleteFilesR
6
6
  import { DeleteFilesResponseInterface } from "../Interfaces/File/DeleteFilesResponseInterface";
7
7
  import { FileDownloadResponseInterface } from "../Interfaces/File/FileDownloadResponseInterface";
8
8
  export declare class File {
9
- static downloadFile: (file: FileInterface, server: NextcloudServerInterface) => Promise<void | FileDownloadResponseInterface>;
10
- static downloadSharedFile: (credential: Credential, file: FileInterface, server: NextcloudServerInterface) => Promise<void | FileDownloadResponseInterface>;
11
- static uploadFile: (fileWithEncryptedFields: FileInterface, server: NextcloudServerInterface) => Promise<void | FileUploadResponseInterface>;
12
- static uploadSharedFile: (credential: Credential, fileWithEncryptedFields: FileInterface, server: NextcloudServerInterface) => Promise<void | FileUploadResponseInterface>;
13
- static updateFile: (fileWithEncryptedFields: FileInterface, server: NextcloudServerInterface) => Promise<void | FileUploadResponseInterface>;
14
- static deleteFile: (file: FileInterface, server: NextcloudServerInterface) => Promise<void | FileUploadResponseInterface>;
15
- static deleteFiles: (deleteFilesRequestBody: DeleteFilesRequestBodyInterface, server: NextcloudServerInterface) => Promise<void | DeleteFilesResponseInterface>;
9
+ static readonly downloadFile: (file: FileInterface, server: NextcloudServerInterface) => Promise<void | FileDownloadResponseInterface>;
10
+ static readonly downloadSharedFile: (credential: Credential, file: FileInterface, server: NextcloudServerInterface) => Promise<void | FileDownloadResponseInterface>;
11
+ static readonly uploadFile: (fileWithEncryptedFields: FileInterface, server: NextcloudServerInterface) => Promise<void | FileUploadResponseInterface>;
12
+ static readonly uploadSharedFile: (credential: Credential, fileWithEncryptedFields: FileInterface, server: NextcloudServerInterface) => Promise<void | FileUploadResponseInterface>;
13
+ static readonly updateFile: (fileWithEncryptedFields: FileInterface, server: NextcloudServerInterface) => Promise<void | FileUploadResponseInterface>;
14
+ static readonly deleteFile: (file: FileInterface, server: NextcloudServerInterface) => Promise<void | FileUploadResponseInterface>;
15
+ static readonly deleteFiles: (deleteFilesRequestBody: DeleteFilesRequestBodyInterface, server: NextcloudServerInterface) => Promise<void | DeleteFilesResponseInterface>;
16
16
  }
@@ -1,25 +1,26 @@
1
1
  import type { LoggingHandlerInterface } from "../Interfaces/LoggingHandlerInterface";
2
2
  import type { NextcloudServerInfoInterface } from "../Interfaces/NextcloudServer/NextcloudServerInfoInterface";
3
3
  import type { NextcloudServerInterface } from "../Interfaces/NextcloudServer/NextcloudServerInterface";
4
- import { RequestCachingHandlerInterface } from "../Interfaces/RequestCachingHandlerInterface";
4
+ import { PersistenceInterface } from "../Interfaces/PersistenceInterface";
5
5
  export declare class NextcloudServer implements NextcloudServerInterface {
6
- private serverData;
6
+ private readonly serverData;
7
7
  logger: LoggingHandlerInterface;
8
- cache?: RequestCachingHandlerInterface;
8
+ persistence: PersistenceInterface;
9
+ static readonly getRequestCachePrefix = "cache-getJson-";
9
10
  /**
10
11
  * Create NextcloudServer instance.
11
12
  * @param serverData
12
13
  * @param logger
13
- * @param cache
14
+ * @param persistence
14
15
  * @throws ConfigurationError
15
16
  */
16
- constructor(serverData: NextcloudServerInfoInterface, logger: LoggingHandlerInterface, cache?: RequestCachingHandlerInterface);
17
+ constructor(serverData: NextcloudServerInfoInterface, logger: LoggingHandlerInterface, persistence: PersistenceInterface);
17
18
  getBaseUrl(): string;
18
- setBaseUrl(value: string): string;
19
+ setBaseUrl(value: string): void;
19
20
  getUser(): string;
20
- setUser(value: string): string;
21
+ setUser(value: string): void;
21
22
  getToken(): string;
22
- setToken(value: string): string;
23
+ setToken(value: string): void;
23
24
  getApiUrl(): string;
24
25
  protected getEncodedLogin(): string;
25
26
  getJson: <T>(endpoint: string, errorCallback: (response: Error) => void, getCachedIfPossible?: boolean) => Promise<T | void>;
@@ -8,18 +8,19 @@ const ConfigurationError_1 = __importDefault(require("../Exception/Configuration
8
8
  class NextcloudServer {
9
9
  serverData;
10
10
  logger;
11
- cache;
11
+ persistence;
12
+ static getRequestCachePrefix = 'cache-getJson-';
12
13
  /**
13
14
  * Create NextcloudServer instance.
14
15
  * @param serverData
15
16
  * @param logger
16
- * @param cache
17
+ * @param persistence
17
18
  * @throws ConfigurationError
18
19
  */
19
- constructor(serverData, logger, cache) {
20
+ constructor(serverData, logger, persistence) {
20
21
  this.serverData = serverData;
21
22
  this.logger = logger;
22
- this.cache = cache;
23
+ this.persistence = persistence;
23
24
  if (!serverData.baseUrl.startsWith('https://') && !serverData.baseUrl.startsWith('http://')) {
24
25
  this.logger.onThrow(new ConfigurationError_1.default('Base URL (or protocol) is invalid'));
25
26
  }
@@ -31,22 +32,22 @@ class NextcloudServer {
31
32
  return this.serverData.baseUrl;
32
33
  }
33
34
  setBaseUrl(value) {
34
- if (!value.startsWith('https://')) {
35
- this.logger.onThrow(new ConfigurationError_1.default('Base URL is invalid'));
35
+ if (!value.startsWith('https://') && !value.startsWith('http://')) {
36
+ this.logger.onThrow(new ConfigurationError_1.default('Base URL (or protocol) is invalid'));
36
37
  }
37
- return this.serverData.baseUrl = value;
38
+ this.serverData.baseUrl = value;
38
39
  }
39
40
  getUser() {
40
41
  return this.serverData.user;
41
42
  }
42
43
  setUser(value) {
43
- return this.serverData.user = value;
44
+ this.serverData.user = value;
44
45
  }
45
46
  getToken() {
46
47
  return this.serverData.token;
47
48
  }
48
49
  setToken(value) {
49
- return this.serverData.token = value;
50
+ this.serverData.token = value;
50
51
  }
51
52
  getApiUrl() {
52
53
  return `${this.getBaseUrl()}/index.php/apps/passman/api/v2/`;
@@ -55,9 +56,8 @@ class NextcloudServer {
55
56
  return btoa(this.getUser() + ":" + this.getToken());
56
57
  }
57
58
  getJson = async (endpoint, errorCallback, getCachedIfPossible = false) => {
58
- const cachePrefix = 'cache-getJson-';
59
- if (getCachedIfPossible && this.cache) {
60
- const cachedValue = await this.cache.get(cachePrefix + endpoint);
59
+ if (getCachedIfPossible && this.persistence.getRequestCacheHandler()) {
60
+ const cachedValue = await this.persistence.getRequestCacheHandler().get(NextcloudServer.getRequestCachePrefix + endpoint);
61
61
  if (cachedValue && cachedValue !== '') {
62
62
  try {
63
63
  return JSON.parse(cachedValue);
@@ -83,8 +83,8 @@ class NextcloudServer {
83
83
  return;
84
84
  }
85
85
  const jsonResponse = await res.json();
86
- if (this.cache) {
87
- await this.cache.set(cachePrefix + endpoint, JSON.stringify(jsonResponse));
86
+ if (this.persistence.getRequestCacheHandler()) {
87
+ await this.persistence.getRequestCacheHandler().set(NextcloudServer.getRequestCachePrefix + endpoint, JSON.stringify(jsonResponse));
88
88
  }
89
89
  return (jsonResponse);
90
90
  };
@@ -0,0 +1,20 @@
1
+ import { GenericVaultInformationFromServerInterface, VaultsGetResponseFromServer } from "../Interfaces/Vault/GenericVaultInformationFromServerInterface";
2
+ import { NextcloudServerInterface } from "../Interfaces/NextcloudServer/NextcloudServerInterface";
3
+ /**
4
+ * The PreloadedVault contains only generic vault information, got from the /api/v1/vaults GET request.
5
+ * It can be used as fast and small data structure during vault authentication, before any modification or credential is needed.
6
+ */
7
+ export default class PreloadedVault {
8
+ protected genericVaultInformation: GenericVaultInformationFromServerInterface;
9
+ protected server: NextcloudServerInterface;
10
+ constructor(genericVaultInformation: GenericVaultInformationFromServerInterface, server: NextcloudServerInterface);
11
+ /**
12
+ * Tests the vault key on the challenge_password
13
+ * @param vaultKey
14
+ */
15
+ testVaultKey(vaultKey: string): boolean;
16
+ static parseResponse(vaultsResponse: void | VaultsGetResponseFromServer, server: NextcloudServerInterface): void | PreloadedVault[];
17
+ get id(): number;
18
+ get guid(): string;
19
+ get name(): string;
20
+ }
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const PassmanCrypto_1 = require("../Service/PassmanCrypto");
4
+ /**
5
+ * The PreloadedVault contains only generic vault information, got from the /api/v1/vaults GET request.
6
+ * It can be used as fast and small data structure during vault authentication, before any modification or credential is needed.
7
+ */
8
+ class PreloadedVault {
9
+ genericVaultInformation;
10
+ server;
11
+ constructor(genericVaultInformation, server) {
12
+ this.genericVaultInformation = genericVaultInformation;
13
+ this.server = server;
14
+ }
15
+ /**
16
+ * Tests the vault key on the challenge_password
17
+ * @param vaultKey
18
+ */
19
+ testVaultKey(vaultKey) {
20
+ try {
21
+ if (this.genericVaultInformation.challenge_password) {
22
+ PassmanCrypto_1.PassmanCrypto.decryptString(this.genericVaultInformation.challenge_password, vaultKey);
23
+ return true;
24
+ }
25
+ else {
26
+ console.error('May we need credentials to test the vault key!');
27
+ // would require fetching the full vault, instead of using this PreloadedVault
28
+ return false;
29
+ }
30
+ }
31
+ catch (e) {
32
+ }
33
+ return false;
34
+ }
35
+ static parseResponse(vaultsResponse, server) {
36
+ if (vaultsResponse) {
37
+ let newPreloadedVaults = [];
38
+ vaultsResponse.forEach((vaultInformationFromServer) => {
39
+ newPreloadedVaults.push(new PreloadedVault(vaultInformationFromServer, server));
40
+ });
41
+ return newPreloadedVaults;
42
+ }
43
+ }
44
+ get id() {
45
+ return this.genericVaultInformation.vault_id;
46
+ }
47
+ get guid() {
48
+ return this.genericVaultInformation.guid;
49
+ }
50
+ get name() {
51
+ return this.genericVaultInformation.name;
52
+ }
53
+ }
54
+ exports.default = PreloadedVault;
@@ -1,9 +1,9 @@
1
1
  import Credential from "./Credential";
2
2
  import { NextcloudServerInterface } from "../Interfaces/NextcloudServer/NextcloudServerInterface";
3
3
  import type Vault from "./Vault";
4
- import { EncryptedCredentialInterface } from "../Interfaces/Credential/EncryptedCredentialInterface";
5
4
  import { FileUploadResponseInterface } from "../Interfaces/File/FileUploadResponseInterface";
6
5
  import { RevisionInterface } from "../Interfaces/Revision/RevisionInterface";
6
+ import { EncryptedOwnedCredentialFromServerInterface } from "../Interfaces/Credential/EncryptedOwnedCredentialFromServerInterface";
7
7
  export default class Revision extends Credential {
8
8
  ENCRYPTED_FIELDS: string[];
9
9
  static updateRevision(revision: RevisionInterface, server: NextcloudServerInterface): Promise<void | FileUploadResponseInterface>;
@@ -13,9 +13,9 @@ export default class Revision extends Credential {
13
13
  * @param vault
14
14
  * @param server
15
15
  */
16
- static fromData(data: EncryptedCredentialInterface, vault: Vault, server: NextcloudServerInterface): Promise<Revision>;
16
+ static fromData(data: EncryptedOwnedCredentialFromServerInterface, vault: Vault, server: NextcloudServerInterface): Revision;
17
17
  /**
18
- * Creates a local 100% clone of the current credential.
18
+ * Creates a local 100% clone of the current credential. The clone contains only encrypted data.
19
19
  */
20
20
  clone(): Revision;
21
21
  }
@@ -19,15 +19,15 @@ class Revision extends Credential_1.default {
19
19
  * @param vault
20
20
  * @param server
21
21
  */
22
- static async fromData(data, vault, server) {
22
+ static fromData(data, vault, server) {
23
23
  return new Revision(vault, server, data);
24
24
  }
25
25
  /**
26
- * Creates a local 100% clone of the current credential.
26
+ * Creates a local 100% clone of the current credential. The clone contains only encrypted data.
27
27
  */
28
28
  clone() {
29
29
  const newCredential = new Revision(this.vault, this.server, this.encryptedData);
30
- newCredential.sharedCredentialEncryptionKey = this.sharedCredentialEncryptionKey;
30
+ newCredential.encryptedSharedCredentialEncryptionKey = this.encryptedSharedCredentialEncryptionKey;
31
31
  return newCredential;
32
32
  }
33
33
  }
@@ -1,6 +1,6 @@
1
1
  export declare class SharingACL {
2
- private permission;
3
- constructor(permission: number);
2
+ private _permission;
3
+ constructor(_permission: number);
4
4
  static readonly permissions: {
5
5
  READ: number;
6
6
  WRITE: number;
@@ -25,4 +25,5 @@ export declare class SharingACL {
25
25
  removePermission(permission: any): void;
26
26
  togglePermission(permission: any): void;
27
27
  getAccessLevel(): number;
28
+ get permission(): number;
28
29
  }
@@ -2,9 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SharingACL = void 0;
4
4
  class SharingACL {
5
- permission;
6
- constructor(permission) {
7
- this.permission = permission;
5
+ _permission;
6
+ constructor(_permission) {
7
+ this._permission = _permission;
8
8
  }
9
9
  static permissions = {
10
10
  READ: 0x01,
@@ -26,7 +26,7 @@ class SharingACL {
26
26
  * @param permission
27
27
  */
28
28
  addPermission(permission) {
29
- this.permission = this.permission | permission;
29
+ this._permission = this.permission | permission;
30
30
  }
31
31
  ;
32
32
  /**
@@ -34,15 +34,18 @@ class SharingACL {
34
34
  * @param permission
35
35
  */
36
36
  removePermission(permission) {
37
- this.permission = this.permission & ~permission;
37
+ this._permission = this.permission & ~permission;
38
38
  }
39
39
  ;
40
40
  togglePermission(permission) {
41
- this.permission ^= permission;
41
+ this._permission ^= permission;
42
42
  }
43
43
  ;
44
44
  getAccessLevel() {
45
45
  return this.permission;
46
46
  }
47
+ get permission() {
48
+ return this._permission;
49
+ }
47
50
  }
48
51
  exports.SharingACL = SharingACL;