@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.
- package/README.md +12 -0
- package/lib/Interfaces/Credential/{CredentialInterface.d.ts → DecryptedCredentialInterface.d.ts} +5 -1
- package/lib/Interfaces/Credential/EncryptedCredentialInterface.d.ts +12 -11
- package/lib/Interfaces/Credential/EncryptedOwnedCredentialFromServerInterface.d.ts +35 -0
- package/lib/Interfaces/Credential/EncryptedOwnedCredentialFromServerInterface.js +2 -0
- package/lib/Interfaces/Credential/EncryptedOwnedCredentialToUpdateForServerInterface.d.ts +8 -0
- package/lib/Interfaces/Credential/EncryptedOwnedCredentialToUpdateForServerInterface.js +2 -0
- package/lib/Interfaces/Credential/OTPConfigInterface.d.ts +6 -1
- package/lib/Interfaces/Credential/OTPConfigInterface.js +6 -0
- package/lib/Interfaces/Credential/SerializableTransferCredentialInterface.d.ts +7 -0
- package/lib/Interfaces/Credential/SerializableTransferCredentialInterface.js +2 -0
- package/lib/Interfaces/DecryptedDataCachingHandlerInterface.d.ts +20 -0
- package/lib/Interfaces/DecryptedDataCachingHandlerInterface.js +2 -0
- package/lib/Interfaces/NextcloudServer/NextcloudServerInterface.d.ts +8 -3
- package/lib/Interfaces/PassmanCrypto/EncryptedStringType.d.ts +4 -0
- package/lib/Interfaces/PassmanCrypto/EncryptedStringType.js +2 -0
- package/lib/Interfaces/PersistenceInterface.d.ts +10 -0
- package/lib/Interfaces/PersistenceInterface.js +2 -0
- package/lib/Interfaces/RequestCachingHandlerInterface.d.ts +5 -1
- package/lib/Interfaces/Revision/RevisionInterface.d.ts +2 -2
- package/lib/Interfaces/ShareService/CredentialShareRequestInterface.d.ts +2 -2
- package/lib/Interfaces/ShareService/SerializableACLInterface.d.ts +14 -0
- package/lib/Interfaces/ShareService/SerializableACLInterface.js +2 -0
- package/lib/Interfaces/Vault/GenericVaultInformationFromServerInterface.d.ts +17 -0
- package/lib/Interfaces/Vault/GenericVaultInformationFromServerInterface.js +2 -0
- package/lib/Interfaces/Vault/SerializableSpecificVaultInformationFromServerInterface.d.ts +12 -0
- package/lib/Interfaces/Vault/SerializableSpecificVaultInformationFromServerInterface.js +2 -0
- package/lib/Interfaces/Vault/SerializableTransferFullVaultInterface.d.ts +6 -0
- package/lib/Interfaces/Vault/SerializableTransferFullVaultInterface.js +2 -0
- package/lib/Interfaces/Vault/SpecificVaultInformationFromServerInterface.d.ts +14 -0
- package/lib/Interfaces/Vault/SpecificVaultInformationFromServerInterface.js +2 -0
- package/lib/Interfaces/Vault/VaultCreateServerResponseInterface.d.ts +8 -0
- package/lib/Interfaces/Vault/VaultCreateServerResponseInterface.js +2 -0
- package/lib/Model/Credential.d.ts +70 -19
- package/lib/Model/Credential.js +138 -25
- package/lib/Model/File.d.ts +7 -7
- package/lib/Model/NextcloudServer.d.ts +9 -8
- package/lib/Model/NextcloudServer.js +14 -14
- package/lib/Model/PreloadedVault.d.ts +20 -0
- package/lib/Model/PreloadedVault.js +54 -0
- package/lib/Model/Revision.d.ts +3 -3
- package/lib/Model/Revision.js +3 -3
- package/lib/Model/SharingACL.d.ts +3 -2
- package/lib/Model/SharingACL.js +9 -6
- package/lib/Model/Vault.d.ts +48 -5
- package/lib/Model/Vault.js +141 -61
- package/lib/PassmanClient.d.ts +51 -10
- package/lib/PassmanClient.js +101 -35
- package/lib/Service/CredentialFilterService.d.ts +2 -1
- package/lib/Service/CredentialFilterService.js +24 -9
- package/lib/Service/CustomMathsService.js +1 -11
- package/lib/Service/DefaultLoggingService.d.ts +3 -0
- package/lib/Service/DefaultLoggingService.js +3 -0
- package/lib/Service/DefaultPersistenceService.d.ts +12 -0
- package/lib/Service/DefaultPersistenceService.js +20 -0
- package/lib/Service/OTPService.d.ts +6 -6
- package/lib/Service/OTPService.js +21 -8
- package/lib/Service/PassmanCrypto.d.ts +9 -4
- package/lib/Service/PassmanCrypto.js +6 -6
- package/lib/Service/ReEncryptionService.js +2 -2
- package/lib/Service/RequestCachingService.d.ts +5 -2
- package/lib/Service/RequestCachingService.js +3 -0
- package/lib/Service/ShareService.js +2 -4
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -1
- /package/lib/Interfaces/Credential/{CredentialInterface.js → DecryptedCredentialInterface.js} +0 -0
package/lib/Model/Credential.js
CHANGED
|
@@ -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
|
|
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
|
|
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.
|
|
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
|
-
|
|
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
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
682
|
+
return this._spacialServerUpdateFields.set_share_key;
|
|
577
683
|
}
|
|
578
684
|
set set_share_key(value) {
|
|
579
|
-
this.
|
|
685
|
+
this._spacialServerUpdateFields.set_share_key = value;
|
|
580
686
|
}
|
|
581
687
|
get skip_revision() {
|
|
582
|
-
return this.
|
|
688
|
+
return this._spacialServerUpdateFields.skip_revision;
|
|
583
689
|
}
|
|
584
690
|
set skip_revision(value) {
|
|
585
|
-
this.
|
|
691
|
+
this._spacialServerUpdateFields.skip_revision = value;
|
|
586
692
|
}
|
|
587
693
|
get acl() {
|
|
588
|
-
return this.
|
|
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.
|
|
701
|
+
this._spacialServerUpdateFields.acl = value;
|
|
702
|
+
}
|
|
703
|
+
get spacialServerUpdateFields() {
|
|
704
|
+
return this._spacialServerUpdateFields;
|
|
592
705
|
}
|
|
593
706
|
}
|
|
594
707
|
exports.default = Credential;
|
package/lib/Model/File.d.ts
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
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
|
|
14
|
+
* @param persistence
|
|
14
15
|
* @throws ConfigurationError
|
|
15
16
|
*/
|
|
16
|
-
constructor(serverData: NextcloudServerInfoInterface, logger: LoggingHandlerInterface,
|
|
17
|
+
constructor(serverData: NextcloudServerInfoInterface, logger: LoggingHandlerInterface, persistence: PersistenceInterface);
|
|
17
18
|
getBaseUrl(): string;
|
|
18
|
-
setBaseUrl(value: string):
|
|
19
|
+
setBaseUrl(value: string): void;
|
|
19
20
|
getUser(): string;
|
|
20
|
-
setUser(value: string):
|
|
21
|
+
setUser(value: string): void;
|
|
21
22
|
getToken(): string;
|
|
22
|
-
setToken(value: 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
|
-
|
|
11
|
+
persistence;
|
|
12
|
+
static getRequestCachePrefix = 'cache-getJson-';
|
|
12
13
|
/**
|
|
13
14
|
* Create NextcloudServer instance.
|
|
14
15
|
* @param serverData
|
|
15
16
|
* @param logger
|
|
16
|
-
* @param
|
|
17
|
+
* @param persistence
|
|
17
18
|
* @throws ConfigurationError
|
|
18
19
|
*/
|
|
19
|
-
constructor(serverData, logger,
|
|
20
|
+
constructor(serverData, logger, persistence) {
|
|
20
21
|
this.serverData = serverData;
|
|
21
22
|
this.logger = logger;
|
|
22
|
-
this.
|
|
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
|
-
|
|
38
|
+
this.serverData.baseUrl = value;
|
|
38
39
|
}
|
|
39
40
|
getUser() {
|
|
40
41
|
return this.serverData.user;
|
|
41
42
|
}
|
|
42
43
|
setUser(value) {
|
|
43
|
-
|
|
44
|
+
this.serverData.user = value;
|
|
44
45
|
}
|
|
45
46
|
getToken() {
|
|
46
47
|
return this.serverData.token;
|
|
47
48
|
}
|
|
48
49
|
setToken(value) {
|
|
49
|
-
|
|
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
|
-
|
|
59
|
-
|
|
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.
|
|
87
|
-
await this.
|
|
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;
|
package/lib/Model/Revision.d.ts
CHANGED
|
@@ -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:
|
|
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
|
}
|
package/lib/Model/Revision.js
CHANGED
|
@@ -19,15 +19,15 @@ class Revision extends Credential_1.default {
|
|
|
19
19
|
* @param vault
|
|
20
20
|
* @param server
|
|
21
21
|
*/
|
|
22
|
-
static
|
|
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.
|
|
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
|
|
3
|
-
constructor(
|
|
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
|
}
|
package/lib/Model/SharingACL.js
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SharingACL = void 0;
|
|
4
4
|
class SharingACL {
|
|
5
|
-
|
|
6
|
-
constructor(
|
|
7
|
-
this.
|
|
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.
|
|
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.
|
|
37
|
+
this._permission = this.permission & ~permission;
|
|
38
38
|
}
|
|
39
39
|
;
|
|
40
40
|
togglePermission(permission) {
|
|
41
|
-
this.
|
|
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;
|