@binsky/passman-client-ts 0.1.10 → 0.2.1

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 (63) 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/SerializableTransferCredentialInterface.d.ts +7 -0
  9. package/lib/Interfaces/Credential/SerializableTransferCredentialInterface.js +2 -0
  10. package/lib/Interfaces/DecryptedDataCachingHandlerInterface.d.ts +20 -0
  11. package/lib/Interfaces/DecryptedDataCachingHandlerInterface.js +2 -0
  12. package/lib/Interfaces/NextcloudServer/NextcloudServerInterface.d.ts +8 -3
  13. package/lib/Interfaces/PassmanCrypto/EncryptedStringType.d.ts +4 -0
  14. package/lib/Interfaces/PassmanCrypto/EncryptedStringType.js +2 -0
  15. package/lib/Interfaces/PersistenceInterface.d.ts +10 -0
  16. package/lib/Interfaces/PersistenceInterface.js +2 -0
  17. package/lib/Interfaces/RequestCachingHandlerInterface.d.ts +6 -2
  18. package/lib/Interfaces/Revision/RevisionInterface.d.ts +2 -2
  19. package/lib/Interfaces/ShareService/CredentialShareRequestInterface.d.ts +2 -2
  20. package/lib/Interfaces/ShareService/SerializableACLInterface.d.ts +14 -0
  21. package/lib/Interfaces/ShareService/SerializableACLInterface.js +2 -0
  22. package/lib/Interfaces/Vault/GenericVaultInformationFromServerInterface.d.ts +17 -0
  23. package/lib/Interfaces/Vault/GenericVaultInformationFromServerInterface.js +2 -0
  24. package/lib/Interfaces/Vault/SerializableSpecificVaultInformationFromServerInterface.d.ts +12 -0
  25. package/lib/Interfaces/Vault/SerializableSpecificVaultInformationFromServerInterface.js +2 -0
  26. package/lib/Interfaces/Vault/SerializableTransferFullVaultInterface.d.ts +6 -0
  27. package/lib/Interfaces/Vault/SerializableTransferFullVaultInterface.js +2 -0
  28. package/lib/Interfaces/Vault/SpecificVaultInformationFromServerInterface.d.ts +14 -0
  29. package/lib/Interfaces/Vault/SpecificVaultInformationFromServerInterface.js +2 -0
  30. package/lib/Interfaces/Vault/VaultCreateServerResponseInterface.d.ts +8 -0
  31. package/lib/Interfaces/Vault/VaultCreateServerResponseInterface.js +2 -0
  32. package/lib/Model/Credential.d.ts +70 -19
  33. package/lib/Model/Credential.js +146 -25
  34. package/lib/Model/File.d.ts +7 -7
  35. package/lib/Model/NextcloudServer.d.ts +9 -8
  36. package/lib/Model/NextcloudServer.js +14 -14
  37. package/lib/Model/PreloadedVault.d.ts +20 -0
  38. package/lib/Model/PreloadedVault.js +54 -0
  39. package/lib/Model/Revision.d.ts +3 -3
  40. package/lib/Model/Revision.js +3 -3
  41. package/lib/Model/SharingACL.d.ts +3 -2
  42. package/lib/Model/SharingACL.js +9 -6
  43. package/lib/Model/Vault.d.ts +48 -5
  44. package/lib/Model/Vault.js +141 -61
  45. package/lib/PassmanClient.d.ts +51 -10
  46. package/lib/PassmanClient.js +101 -35
  47. package/lib/Service/CredentialFilterService.d.ts +2 -1
  48. package/lib/Service/CredentialFilterService.js +24 -9
  49. package/lib/Service/DefaultLoggingService.d.ts +3 -0
  50. package/lib/Service/DefaultLoggingService.js +3 -0
  51. package/lib/Service/DefaultPersistenceService.d.ts +12 -0
  52. package/lib/Service/DefaultPersistenceService.js +20 -0
  53. package/lib/Service/OTPService.d.ts +6 -6
  54. package/lib/Service/OTPService.js +18 -12
  55. package/lib/Service/PassmanCrypto.d.ts +9 -4
  56. package/lib/Service/PassmanCrypto.js +6 -6
  57. package/lib/Service/ReEncryptionService.js +2 -2
  58. package/lib/Service/RequestCachingService.d.ts +5 -2
  59. package/lib/Service/RequestCachingService.js +3 -0
  60. package/lib/Service/ShareService.js +2 -4
  61. package/lib/tsconfig.tsbuildinfo +1 -1
  62. package/package.json +3 -1
  63. /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,67 @@ 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 (Array.isArray(this.tags)) {
110
+ this.tags.forEach(value => value?.text && this.vault.collectedTags.add(value.text));
111
+ }
112
+ else {
113
+ // recover correct tags structure
114
+ this.tags = [];
115
+ }
84
116
  }
85
117
  return credentialResponse;
86
118
  }
87
119
  }
88
120
  /**
89
121
  * 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.
122
+ * It is not supported to do that for credentials, shared with us. (Therefore this will not touch the virtual acl field.)
91
123
  */
92
124
  async refresh() {
93
- if (this.sharedCredentialEncryptionKey) {
125
+ if (this.encryptedSharedCredentialEncryptionKey) {
94
126
  // credential is shared with us
95
- // no credential refresh possible, since there is no backend api to do that
127
+ // no credential refresh possible, since there is no backend api to do that for a specific shared credential
128
+ // if it will be implemented in the future, don't forget to update the acl field, that's currently injected by ShareService
96
129
  }
97
130
  else {
131
+ this.clearDecryptedDataCache(true);
98
132
  let credentialResponse = await this.server.getJson('/credentials/' + this.guid, (response) => {
99
133
  this.server.logger.onError(response.message);
100
134
  });
101
135
  if (credentialResponse) {
102
136
  this.encryptedData = credentialResponse;
103
137
  this.decryptedDataCache = {};
138
+ if (Array.isArray(this.tags)) {
139
+ this.tags.forEach(value => value?.text && this.vault.collectedTags.add(value.text));
140
+ }
141
+ else {
142
+ // recover correct tags structure
143
+ this.tags = [];
144
+ }
104
145
  }
105
146
  return credentialResponse;
106
147
  }
@@ -110,6 +151,7 @@ class Credential {
110
151
  */
111
152
  async destroy() {
112
153
  if (this.acl === undefined || this.acl.permissions.hasPermission(SharingACL_1.SharingACL.permissions.WRITE)) {
154
+ this.clearDecryptedDataCache(true);
113
155
  let credentialResponse = await this.server.deleteJson('/credentials/' + this.guid, (response) => {
114
156
  this.server.logger.onError(response.message);
115
157
  });
@@ -118,8 +160,38 @@ class Credential {
118
160
  return credentialResponse;
119
161
  }
120
162
  }
121
- clearDecryptedDataCache() {
163
+ /**
164
+ * This is the correct function to "lock" the credential (to be called subsequently when the vault got locked).
165
+ * It clears the decrypted credential data cache as well as a potential sharedCredentialEncryptionKey.
166
+ * This will not clear the serialized decrypted data cache, managed by the used DecryptedDataCachingHandlerInterface implementation.
167
+ * Set clearCachingHandlerManagedDecryptedData=true to do so (by calling getDecryptedDataCacheHandler().clearCacheByName('...'))
168
+ */
169
+ clearDecryptedDataCache(clearCachingHandlerManagedDecryptedData = false) {
122
170
  this.decryptedDataCache = {};
171
+ this.sharedCredentialEncryptionKey = undefined;
172
+ if (clearCachingHandlerManagedDecryptedData) {
173
+ // atm. we are not interested in the result, so we keep the returned Promise running in background
174
+ this.server.persistence.getDecryptedDataCacheHandler().clearCacheByName(this.decryptedDataCacheName);
175
+ }
176
+ }
177
+ /**
178
+ * Restore only serializable fields of the CredentialInterface.
179
+ */
180
+ async restoreSerializedDecryptedDataCache() {
181
+ const cacheHandler = this.server.persistence.getDecryptedDataCacheHandler();
182
+ for (const propertyName of this.SERIALIZABLE_ENCRYPTED_FIELDS) {
183
+ this.decryptedDataCache[propertyName] = await cacheHandler?.get(this.decryptedDataCacheName, propertyName);
184
+ }
185
+ }
186
+ /**
187
+ * Update or delete an entry in the decrypted data cache.
188
+ * @param propertyName
189
+ * @param deleteFromCache
190
+ */
191
+ updateSerializedDecryptedDataCacheEntry(propertyName, deleteFromCache = false) {
192
+ if (this.SERIALIZABLE_ENCRYPTED_FIELDS.includes(propertyName) && this.server.persistence.getDecryptedDataCacheHandler()) {
193
+ return this.server.persistence.getDecryptedDataCacheHandler().set(this.decryptedDataCacheName, propertyName, deleteFromCache ? undefined : this.decryptedDataCache[propertyName]);
194
+ }
123
195
  }
124
196
  /**
125
197
  * Create a credential object based on its encrypted data.
@@ -127,7 +199,7 @@ class Credential {
127
199
  * @param vault
128
200
  * @param server
129
201
  */
130
- static async fromData(data, vault, server) {
202
+ static fromData(data, vault, server) {
131
203
  return new Credential(vault, server, data);
132
204
  }
133
205
  /**
@@ -142,6 +214,17 @@ class Credential {
142
214
  await cred.refresh();
143
215
  return cred;
144
216
  }
217
+ static fromSerializable(serialized, vault, server) {
218
+ const credential = new Credential(vault, server, serialized.encryptedData);
219
+ credential.encryptedSharedCredentialEncryptionKey = serialized.encryptedSharedCredentialEncryptionKey;
220
+ if (serialized.acl) {
221
+ credential.acl = {
222
+ permissions: new SharingACL_1.SharingACL(serialized.acl.permission),
223
+ ...serialized.acl
224
+ };
225
+ }
226
+ return credential;
227
+ }
145
228
  async getRevisions() {
146
229
  return await this.server.getJson('/credentials/' + this.guid + '/revision', (response) => {
147
230
  this.server.logger.onError(response.message);
@@ -208,11 +291,11 @@ class Credential {
208
291
  };
209
292
  }
210
293
  /**
211
- * Creates a local 100% clone of the current credential.
294
+ * Creates a local 100% clone of the current credential. The clone contains only encrypted data.
212
295
  */
213
296
  clone() {
214
297
  const newCredential = new Credential(this.vault, this.server, this.encryptedData);
215
- newCredential.sharedCredentialEncryptionKey = this.sharedCredentialEncryptionKey;
298
+ newCredential.encryptedSharedCredentialEncryptionKey = this.encryptedSharedCredentialEncryptionKey;
216
299
  return newCredential;
217
300
  }
218
301
  /**
@@ -262,7 +345,10 @@ class Credential {
262
345
  return this.decryptedDataCache.shared_key;
263
346
  }
264
347
  }
265
- if (this.sharedCredentialEncryptionKey !== undefined) {
348
+ if (this.encryptedSharedCredentialEncryptionKey !== undefined) {
349
+ if (!this.sharedCredentialEncryptionKey) {
350
+ this.sharedCredentialEncryptionKey = PassmanCrypto_1.PassmanCrypto.decryptString(this.encryptedSharedCredentialEncryptionKey, this.vault.vaultKey);
351
+ }
266
352
  return this.sharedCredentialEncryptionKey;
267
353
  }
268
354
  return this.overwriteVaultKey ?? this.vault.vaultKey;
@@ -275,11 +361,17 @@ class Credential {
275
361
  */
276
362
  getCacheDecryptFieldData(property) {
277
363
  if (this.ENCRYPTED_FIELDS.indexOf(property) > -1) {
278
- if (this.decryptedDataCache[property] === undefined) {
364
+ if (!Object.hasOwn(this.decryptedDataCache, property) || this.decryptedDataCache[property] === undefined) {
279
365
  if (this.encryptedData[property] === null) {
280
366
  this.decryptedDataCache[property] = null;
281
367
  }
282
368
  else {
369
+ // should never happen, but who knows xD
370
+ if (this.encryptedData[property] === undefined) {
371
+ this.server.logger.anyError('Corrupt credential data detected. Undefined value for encryptedData.' + property + ' not allowed! encryptedData: ');
372
+ this.server.logger.anyError(this.encryptedData);
373
+ throw new Error('Corrupt credential data detected. Undefined value for encryptedData.' + property + ' not allowed!');
374
+ }
283
375
  let decryptedData;
284
376
  try {
285
377
  decryptedData = PassmanCrypto_1.PassmanCrypto.decryptString(this.encryptedData[property], this.getFieldEncryptionKey(property));
@@ -290,11 +382,13 @@ class Credential {
290
382
  else {
291
383
  this.decryptedDataCache[property] = JSON.parse(decryptedData);
292
384
  }
385
+ // should work, even if we do not wait for the returned Promise
386
+ this.updateSerializedDecryptedDataCacheEntry(property);
293
387
  }
294
388
  catch (e) {
295
389
  this.foundUnspecifiedEncryptionError = true;
296
390
  this.server.logger.anyError(e);
297
- this.server.logger.anyError(decryptedData);
391
+ this.server.logger.anyError(decryptedData ?? 'no decrypted data: (undefined)');
298
392
  this.server.logger.onError('Failed to decrypt field: ' + property + ' of credential: ' + this.label);
299
393
  }
300
394
  }
@@ -319,7 +413,8 @@ class Credential {
319
413
  this.encryptedData[property] = PassmanCrypto_1.PassmanCrypto.encryptString(data, this.getFieldEncryptionKey(property));
320
414
  }
321
415
  else {
322
- this.encryptedData[property] = PassmanCrypto_1.PassmanCrypto.encryptString(JSON.stringify(data), this.getFieldEncryptionKey(property));
416
+ // use data ?? null to prevent undefined value (which could lead to an error in decryption)
417
+ this.encryptedData[property] = PassmanCrypto_1.PassmanCrypto.encryptString(JSON.stringify(data ?? null), this.getFieldEncryptionKey(property));
323
418
  }
324
419
  }
325
420
  catch (e) {
@@ -329,6 +424,7 @@ class Credential {
329
424
  return;
330
425
  }
331
426
  this.decryptedDataCache[property] = data;
427
+ this.updateSerializedDecryptedDataCacheEntry(property);
332
428
  }
333
429
  else {
334
430
  this.encryptedData[property] = data;
@@ -359,7 +455,7 @@ class Credential {
359
455
  logger.onError('Failed to decrypt file: ' + file.filename);
360
456
  }
361
457
  };
362
- if (!this.encryptedData.acl) {
458
+ if (!this.acl) {
363
459
  return File_1.File.downloadFile(file, this.server).then(callback);
364
460
  }
365
461
  else {
@@ -383,7 +479,7 @@ class Credential {
383
479
  const callback = function (uploadFileResponse) {
384
480
  return uploadFileResponse;
385
481
  };
386
- if (!this.encryptedData.acl) {
482
+ if (!this.acl) {
387
483
  return File_1.File.uploadFile(encryptedFile, this.server).then(callback);
388
484
  }
389
485
  else {
@@ -396,6 +492,24 @@ class Credential {
396
492
  this.server.logger.onError('Failed to encrypt new file (' + plainFile.filename + ') of credential: ' + this.label);
397
493
  }
398
494
  }
495
+ /**
496
+ * Serialized, encrypted credential data from non-object (string only) transfer methods (like WebExtension messaging api).
497
+ */
498
+ getAsSerializable() {
499
+ let acl = undefined;
500
+ if (this.acl) {
501
+ let { permissions, ...newAcl } = {
502
+ permission: this.acl.permissions.permission,
503
+ ...this.acl
504
+ };
505
+ acl = newAcl;
506
+ }
507
+ return {
508
+ encryptedData: this.getEncrypted(),
509
+ encryptedSharedCredentialEncryptionKey: this.encryptedSharedCredentialEncryptionKey,
510
+ acl
511
+ };
512
+ }
399
513
  /**
400
514
  * Deletes the given file from the server.
401
515
  * This method does *not* delete the file from the local credential files list!
@@ -573,22 +687,29 @@ class Credential {
573
687
  this.encryptedData.changed = value;
574
688
  }
575
689
  get set_share_key() {
576
- return this.encryptedData.set_share_key;
690
+ return this._spacialServerUpdateFields.set_share_key;
577
691
  }
578
692
  set set_share_key(value) {
579
- this.encryptedData.set_share_key = value;
693
+ this._spacialServerUpdateFields.set_share_key = value;
580
694
  }
581
695
  get skip_revision() {
582
- return this.encryptedData.skip_revision;
696
+ return this._spacialServerUpdateFields.skip_revision;
583
697
  }
584
698
  set skip_revision(value) {
585
- this.encryptedData.skip_revision = value;
699
+ this._spacialServerUpdateFields.skip_revision = value;
586
700
  }
587
701
  get acl() {
588
- return this.encryptedData.acl;
702
+ return this._spacialServerUpdateFields.acl;
589
703
  }
704
+ /**
705
+ * Will be called short after credential instantiation by the ShareService.
706
+ * @param value
707
+ */
590
708
  set acl(value) {
591
- this.encryptedData.acl = value;
709
+ this._spacialServerUpdateFields.acl = value;
710
+ }
711
+ get spacialServerUpdateFields() {
712
+ return this._spacialServerUpdateFields;
592
713
  }
593
714
  }
594
715
  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;