@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.
- 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/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 +6 -2
- 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 +146 -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/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 +18 -12
- 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,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
|
|
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 (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.
|
|
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
|
-
|
|
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
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
690
|
+
return this._spacialServerUpdateFields.set_share_key;
|
|
577
691
|
}
|
|
578
692
|
set set_share_key(value) {
|
|
579
|
-
this.
|
|
693
|
+
this._spacialServerUpdateFields.set_share_key = value;
|
|
580
694
|
}
|
|
581
695
|
get skip_revision() {
|
|
582
|
-
return this.
|
|
696
|
+
return this._spacialServerUpdateFields.skip_revision;
|
|
583
697
|
}
|
|
584
698
|
set skip_revision(value) {
|
|
585
|
-
this.
|
|
699
|
+
this._spacialServerUpdateFields.skip_revision = value;
|
|
586
700
|
}
|
|
587
701
|
get acl() {
|
|
588
|
-
return this.
|
|
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.
|
|
709
|
+
this._spacialServerUpdateFields.acl = value;
|
|
710
|
+
}
|
|
711
|
+
get spacialServerUpdateFields() {
|
|
712
|
+
return this._spacialServerUpdateFields;
|
|
592
713
|
}
|
|
593
714
|
}
|
|
594
715
|
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;
|