@binsky/passman-client-ts 0.1.0-7
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 +9 -0
- package/lib/BrowserStorage.d.ts +39 -0
- package/lib/BrowserStorage.js +137 -0
- package/lib/Exception/ConfigurationError.d.ts +6 -0
- package/lib/Exception/ConfigurationError.js +11 -0
- package/lib/Exception/FormFieldError.d.ts +4 -0
- package/lib/Exception/FormFieldError.js +2 -0
- package/lib/Interfaces/Credential/CredentialInterface.d.ts +33 -0
- package/lib/Interfaces/Credential/CredentialInterface.js +2 -0
- package/lib/Interfaces/Credential/CustomFieldInterface.d.ts +6 -0
- package/lib/Interfaces/Credential/CustomFieldInterface.js +2 -0
- package/lib/Interfaces/Credential/EncryptedCredentialInterface.d.ts +31 -0
- package/lib/Interfaces/Credential/EncryptedCredentialInterface.js +2 -0
- package/lib/Interfaces/Credential/IconInterface.d.ts +4 -0
- package/lib/Interfaces/Credential/IconInterface.js +2 -0
- package/lib/Interfaces/Credential/OTPConfigInterface.d.ts +14 -0
- package/lib/Interfaces/Credential/OTPConfigInterface.js +2 -0
- package/lib/Interfaces/Credential/TagInterface.d.ts +3 -0
- package/lib/Interfaces/Credential/TagInterface.js +2 -0
- package/lib/Interfaces/CredentialFilterService/FilterStatsInterface.d.ts +10 -0
- package/lib/Interfaces/CredentialFilterService/FilterStatsInterface.js +2 -0
- package/lib/Interfaces/File/DeleteFilesRequestBodyInterface.d.ts +3 -0
- package/lib/Interfaces/File/DeleteFilesRequestBodyInterface.js +2 -0
- package/lib/Interfaces/File/DeleteFilesResponseInterface.d.ts +4 -0
- package/lib/Interfaces/File/DeleteFilesResponseInterface.js +2 -0
- package/lib/Interfaces/File/FileInterface.d.ts +9 -0
- package/lib/Interfaces/File/FileInterface.js +2 -0
- package/lib/Interfaces/File/FileUploadResponseInterface.d.ts +9 -0
- package/lib/Interfaces/File/FileUploadResponseInterface.js +2 -0
- package/lib/Interfaces/LoggingHandlerInterface.d.ts +9 -0
- package/lib/Interfaces/LoggingHandlerInterface.js +2 -0
- package/lib/Interfaces/NextcloudServer/NextcloudServerInterface.d.ts +6 -0
- package/lib/Interfaces/NextcloudServer/NextcloudServerInterface.js +2 -0
- package/lib/Interfaces/PassmanCrypto/GenerateKeypairResponseInterface.d.ts +5 -0
- package/lib/Interfaces/PassmanCrypto/GenerateKeypairResponseInterface.js +2 -0
- package/lib/Interfaces/PassmanCrypto/PEMRSAKeypairInterface.d.ts +4 -0
- package/lib/Interfaces/PassmanCrypto/PEMRSAKeypairInterface.js +2 -0
- package/lib/Interfaces/PassmanCrypto/RSAKeypairInterface.d.ts +5 -0
- package/lib/Interfaces/PassmanCrypto/RSAKeypairInterface.js +2 -0
- package/lib/Interfaces/PasswordGeneratorService/PasswordGeneratorConfigurationInterface.d.ts +9 -0
- package/lib/Interfaces/PasswordGeneratorService/PasswordGeneratorConfigurationInterface.js +2 -0
- package/lib/Interfaces/ReEncryptionService/ReEncryptionProgressInterface.d.ts +7 -0
- package/lib/Interfaces/ReEncryptionService/ReEncryptionProgressInterface.js +2 -0
- package/lib/Interfaces/ReEncryptionService/ReEncryptionStageProgressInterface.d.ts +5 -0
- package/lib/Interfaces/ReEncryptionService/ReEncryptionStageProgressInterface.js +2 -0
- package/lib/Interfaces/Revision/RevisionInterface.d.ts +8 -0
- package/lib/Interfaces/Revision/RevisionInterface.js +2 -0
- package/lib/Interfaces/ShareService/ACLInterface.d.ts +15 -0
- package/lib/Interfaces/ShareService/ACLInterface.js +2 -0
- package/lib/Interfaces/ShareService/CredentialShareRequestInterface.d.ts +16 -0
- package/lib/Interfaces/ShareService/CredentialShareRequestInterface.js +2 -0
- package/lib/Interfaces/Vault/VaultInterface.d.ts +15 -0
- package/lib/Interfaces/Vault/VaultInterface.js +2 -0
- package/lib/Interfaces/Vault/VaultResponseInterface.d.ts +15 -0
- package/lib/Interfaces/Vault/VaultResponseInterface.js +2 -0
- package/lib/LocalPassmanStore.d.ts +4 -0
- package/lib/LocalPassmanStore.js +14 -0
- package/lib/Model/Credential.d.ts +173 -0
- package/lib/Model/Credential.js +594 -0
- package/lib/Model/File.d.ts +15 -0
- package/lib/Model/File.js +55 -0
- package/lib/Model/NextcloudServer.d.ts +27 -0
- package/lib/Model/NextcloudServer.js +108 -0
- package/lib/Model/Revision.d.ts +21 -0
- package/lib/Model/Revision.js +33 -0
- package/lib/Model/SharingACL.d.ts +28 -0
- package/lib/Model/SharingACL.js +48 -0
- package/lib/Model/Vault.d.ts +38 -0
- package/lib/Model/Vault.js +265 -0
- package/lib/PassmanClient.d.ts +20 -0
- package/lib/PassmanClient.js +80 -0
- package/lib/Service/CredentialFilterService.d.ts +24 -0
- package/lib/Service/CredentialFilterService.js +209 -0
- package/lib/Service/CustomMathsService.d.ts +7 -0
- package/lib/Service/CustomMathsService.js +52 -0
- package/lib/Service/DefaultLoggingService.d.ts +10 -0
- package/lib/Service/DefaultLoggingService.js +27 -0
- package/lib/Service/DownloadService.d.ts +12 -0
- package/lib/Service/DownloadService.js +121 -0
- package/lib/Service/OTPService.d.ts +22 -0
- package/lib/Service/OTPService.js +134 -0
- package/lib/Service/PassmanCrypto.d.ts +10 -0
- package/lib/Service/PassmanCrypto.js +57 -0
- package/lib/Service/PasswordGeneratorService.d.ts +6 -0
- package/lib/Service/PasswordGeneratorService.js +73 -0
- package/lib/Service/ReEncryptionService.d.ts +23 -0
- package/lib/Service/ReEncryptionService.js +318 -0
- package/lib/Service/ShareService.d.ts +15 -0
- package/lib/Service/ShareService.js +56 -0
- package/lib/tsconfig.tsbuildinfo +1 -0
- package/package.json +36 -0
|
@@ -0,0 +1,594 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const PassmanCrypto_1 = require("../Service/PassmanCrypto");
|
|
4
|
+
const DownloadService_1 = require("../Service/DownloadService");
|
|
5
|
+
const File_1 = require("./File");
|
|
6
|
+
const html_escaper_1 = require("html-escaper");
|
|
7
|
+
const SharingACL_1 = require("./SharingACL");
|
|
8
|
+
class Credential {
|
|
9
|
+
vault;
|
|
10
|
+
server;
|
|
11
|
+
ENCRYPTED_FIELDS = ['description', 'username', 'password', 'files', 'custom_fields', 'otp', 'email', 'tags', 'url', 'compromised', 'shared_key'];
|
|
12
|
+
encryptedData;
|
|
13
|
+
decryptedDataCache;
|
|
14
|
+
sharedCredentialEncryptionKey; // this is set if the credential is shared with us (injected by ShareService)
|
|
15
|
+
foundUnspecifiedEncryptionError = false;
|
|
16
|
+
// can be used to re-encrypt credential data, if the original vault key is not changed yet
|
|
17
|
+
overwriteVaultKey = undefined;
|
|
18
|
+
constructor(vault, server, encryptedData = undefined) {
|
|
19
|
+
this.vault = vault;
|
|
20
|
+
this.server = server;
|
|
21
|
+
this.encryptedData = encryptedData;
|
|
22
|
+
this.decryptedDataCache = {};
|
|
23
|
+
if (encryptedData === undefined) {
|
|
24
|
+
this.encryptedData = {};
|
|
25
|
+
this.initializeAllFields();
|
|
26
|
+
}
|
|
27
|
+
this.vault_id = vault.vaultId;
|
|
28
|
+
}
|
|
29
|
+
initializeAllFields() {
|
|
30
|
+
this.user_id = null;
|
|
31
|
+
this.vault_id = null;
|
|
32
|
+
this.label = null;
|
|
33
|
+
this.description = null;
|
|
34
|
+
this.tags = [];
|
|
35
|
+
this.email = null;
|
|
36
|
+
this.username = null;
|
|
37
|
+
this.password = null;
|
|
38
|
+
this.url = null;
|
|
39
|
+
this.files = [];
|
|
40
|
+
this.custom_fields = [];
|
|
41
|
+
this.otp = {};
|
|
42
|
+
this.compromised = null;
|
|
43
|
+
this.shared_key = null;
|
|
44
|
+
this.favicon = null;
|
|
45
|
+
this.icon = null;
|
|
46
|
+
this.renew_interval = null;
|
|
47
|
+
this.expire_time = 0;
|
|
48
|
+
this.delete_time = 0;
|
|
49
|
+
this.hidden = null;
|
|
50
|
+
this.created = null;
|
|
51
|
+
this.changed = null;
|
|
52
|
+
this.set_share_key = null;
|
|
53
|
+
this.skip_revision = null;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Save new credential on the server.
|
|
57
|
+
* The current credential object will be updated with the server response data if possible.
|
|
58
|
+
*/
|
|
59
|
+
async save() {
|
|
60
|
+
let credentialResponse = await this.server.postJson('/credentials', this.encryptedData, (response) => {
|
|
61
|
+
this.server.logger.onError(response.message);
|
|
62
|
+
});
|
|
63
|
+
if (credentialResponse) {
|
|
64
|
+
this.encryptedData = credentialResponse;
|
|
65
|
+
this.decryptedDataCache = {};
|
|
66
|
+
}
|
|
67
|
+
return credentialResponse;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Update / edit an existing credential on the server.
|
|
71
|
+
* The current credential object will be updated with the server response data if possible.
|
|
72
|
+
*/
|
|
73
|
+
async update() {
|
|
74
|
+
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) => {
|
|
76
|
+
this.server.logger.onError(response.message);
|
|
77
|
+
}, 'PATCH');
|
|
78
|
+
if (credentialResponse) {
|
|
79
|
+
if (this.encryptedData.acl) {
|
|
80
|
+
credentialResponse.acl = this.encryptedData.acl;
|
|
81
|
+
}
|
|
82
|
+
this.encryptedData = credentialResponse;
|
|
83
|
+
this.decryptedDataCache = {};
|
|
84
|
+
}
|
|
85
|
+
return credentialResponse;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* 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.
|
|
91
|
+
*/
|
|
92
|
+
async refresh() {
|
|
93
|
+
if (this.sharedCredentialEncryptionKey) {
|
|
94
|
+
// credential is shared with us
|
|
95
|
+
// no credential refresh possible, since there is no backend api to do that
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
let credentialResponse = await this.server.getJson('/credentials/' + this.guid, (response) => {
|
|
99
|
+
this.server.logger.onError(response.message);
|
|
100
|
+
});
|
|
101
|
+
if (credentialResponse) {
|
|
102
|
+
this.encryptedData = credentialResponse;
|
|
103
|
+
this.decryptedDataCache = {};
|
|
104
|
+
}
|
|
105
|
+
return credentialResponse;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Destroys the credential on the server and removes itself from its local vault.
|
|
110
|
+
*/
|
|
111
|
+
async destroy() {
|
|
112
|
+
if (this.acl === undefined || this.acl.permissions.hasPermission(SharingACL_1.SharingACL.permissions.WRITE)) {
|
|
113
|
+
let credentialResponse = await this.server.delete('/credentials/' + this.guid, (response) => {
|
|
114
|
+
this.server.logger.onError(response.message);
|
|
115
|
+
});
|
|
116
|
+
const pos = this.vault.credentials.indexOf(this);
|
|
117
|
+
this.vault.credentials.splice(pos, 1);
|
|
118
|
+
return credentialResponse;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
clearDecryptedDataCache() {
|
|
122
|
+
this.decryptedDataCache = {};
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Create a credential object based on its encrypted data.
|
|
126
|
+
* @param data
|
|
127
|
+
* @param vault
|
|
128
|
+
* @param server
|
|
129
|
+
*/
|
|
130
|
+
static async fromData(data, vault, server) {
|
|
131
|
+
return new Credential(vault, server, data);
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Create a credential object based on its guid. This will fetch the current credential data from the server.
|
|
135
|
+
* @param guid
|
|
136
|
+
* @param vault
|
|
137
|
+
* @param server
|
|
138
|
+
*/
|
|
139
|
+
static async fromGuid(guid, vault, server) {
|
|
140
|
+
let cred = new Credential(vault, server);
|
|
141
|
+
cred.guid = guid;
|
|
142
|
+
await cred.refresh();
|
|
143
|
+
return cred;
|
|
144
|
+
}
|
|
145
|
+
async getRevisions() {
|
|
146
|
+
return await this.server.getJson('/credentials/' + this.guid + '/revision', (response) => {
|
|
147
|
+
this.server.logger.onError(response.message);
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Update credential (encryptedData store) with the new values, if they have changed.
|
|
152
|
+
* This does not update the servers credential instance.
|
|
153
|
+
* @param decryptedCredentialData
|
|
154
|
+
*/
|
|
155
|
+
updateData(decryptedCredentialData) {
|
|
156
|
+
// todo: fill Interfaces folder
|
|
157
|
+
Object.keys(decryptedCredentialData).forEach((key) => {
|
|
158
|
+
if (key !== 'files' ||
|
|
159
|
+
(key === 'files' &&
|
|
160
|
+
this.acl === undefined ||
|
|
161
|
+
this.acl.permissions.hasPermission(SharingACL_1.SharingACL.permissions.FILES))) {
|
|
162
|
+
if (this.ENCRYPTED_FIELDS.indexOf(key) > -1) {
|
|
163
|
+
// todo: improve check if array or object types have changed
|
|
164
|
+
if (this.encryptedData[key] !== decryptedCredentialData[key] || typeof decryptedCredentialData[key] !== 'string') {
|
|
165
|
+
if (key === 'shared_key') {
|
|
166
|
+
// this special field is not encrypted, if it only contains a null value, since it won't be json stringified
|
|
167
|
+
if (decryptedCredentialData[key] !== undefined && decryptedCredentialData[key] !== null && decryptedCredentialData[key] !== 'null' && decryptedCredentialData[key] !== '') {
|
|
168
|
+
this.encryptedData[key] = decryptedCredentialData[key];
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
this.setEncryptFieldData(key, decryptedCredentialData[key]);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
this.encryptedData[key] = decryptedCredentialData[key];
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
exportData() {
|
|
183
|
+
return {
|
|
184
|
+
user_id: this.user_id,
|
|
185
|
+
vault_id: this.vault_id,
|
|
186
|
+
label: this.label,
|
|
187
|
+
description: this.description,
|
|
188
|
+
tags: this.tags,
|
|
189
|
+
email: this.email,
|
|
190
|
+
username: this.username,
|
|
191
|
+
password: this.password,
|
|
192
|
+
url: this.url,
|
|
193
|
+
files: (this.acl === undefined || this.acl.permissions.hasPermission(SharingACL_1.SharingACL.permissions.FILES)) ? this.files : [],
|
|
194
|
+
custom_fields: this.custom_fields,
|
|
195
|
+
otp: this.otp,
|
|
196
|
+
compromised: this.compromised,
|
|
197
|
+
shared_key: this.shared_key,
|
|
198
|
+
favicon: this.favicon,
|
|
199
|
+
icon: this.icon,
|
|
200
|
+
renew_interval: this.renew_interval,
|
|
201
|
+
expire_time: this.expire_time,
|
|
202
|
+
delete_time: this.delete_time,
|
|
203
|
+
hidden: this.hidden,
|
|
204
|
+
created: this.created,
|
|
205
|
+
changed: this.changed,
|
|
206
|
+
set_share_key: this.set_share_key,
|
|
207
|
+
skip_revision: this.skip_revision
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Creates a local 100% clone of the current credential.
|
|
212
|
+
*/
|
|
213
|
+
clone() {
|
|
214
|
+
const newCredential = new Credential(this.vault, this.server, this.encryptedData);
|
|
215
|
+
newCredential.sharedCredentialEncryptionKey = this.sharedCredentialEncryptionKey;
|
|
216
|
+
return newCredential;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Check whether the credential has a valid shared_key.
|
|
220
|
+
* If true, it is a credential, that's encrypted with the decrypted shared_key field content.
|
|
221
|
+
*/
|
|
222
|
+
hasValidSharedKey() {
|
|
223
|
+
return this.encryptedData.shared_key !== undefined
|
|
224
|
+
&& this.encryptedData.shared_key !== null
|
|
225
|
+
&& this.encryptedData.shared_key !== 'null'
|
|
226
|
+
&& this.encryptedData.shared_key !== '';
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Creates a local 100% clone of the current credential and re-encrypts it with the given key.
|
|
230
|
+
*/
|
|
231
|
+
reEncryptAsClone(newVaultKey) {
|
|
232
|
+
const newCredential = this.clone();
|
|
233
|
+
if (this.hasValidSharedKey()) {
|
|
234
|
+
// only re-encrypt the shared key, if the credential is shared and not encrypted with the vault key like default credentials
|
|
235
|
+
const decrypted_shared_key = newCredential.shared_key;
|
|
236
|
+
newCredential.set_share_key = true;
|
|
237
|
+
newCredential.skip_revision = true;
|
|
238
|
+
newCredential.overwriteVaultKey = newVaultKey;
|
|
239
|
+
newCredential.encryptedData.shared_key = PassmanCrypto_1.PassmanCrypto.encryptString(decrypted_shared_key, newVaultKey);
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
// default (100% owned) credential
|
|
243
|
+
const decryptedCredentialData = newCredential.exportData();
|
|
244
|
+
newCredential.overwriteVaultKey = newVaultKey;
|
|
245
|
+
newCredential.updateData(decryptedCredentialData);
|
|
246
|
+
newCredential.skip_revision = true;
|
|
247
|
+
}
|
|
248
|
+
return newCredential;
|
|
249
|
+
}
|
|
250
|
+
getFieldEncryptionKey(fieldName = null) {
|
|
251
|
+
if (fieldName !== 'shared_key' &&
|
|
252
|
+
this.encryptedData.shared_key !== undefined &&
|
|
253
|
+
this.encryptedData.shared_key !== null &&
|
|
254
|
+
this.encryptedData.shared_key !== 'null' &&
|
|
255
|
+
this.encryptedData.shared_key !== '') {
|
|
256
|
+
// got a credential that is shared with others
|
|
257
|
+
if (this.decryptedDataCache.shared_key === undefined) {
|
|
258
|
+
this.decryptedDataCache.shared_key = PassmanCrypto_1.PassmanCrypto.decryptString(this.encryptedData.shared_key, this.overwriteVaultKey ?? this.vault.vaultKey);
|
|
259
|
+
}
|
|
260
|
+
if (this.decryptedDataCache.shared_key != null) {
|
|
261
|
+
// is this.encryptedData.shared_key is set, but a null value was encrypted, do not return it
|
|
262
|
+
return this.decryptedDataCache.shared_key;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
if (this.sharedCredentialEncryptionKey !== undefined) {
|
|
266
|
+
return this.sharedCredentialEncryptionKey;
|
|
267
|
+
}
|
|
268
|
+
return this.overwriteVaultKey ?? this.vault.vaultKey;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Returns (json parsed) field data and decrypt / caches it if required.
|
|
272
|
+
*
|
|
273
|
+
* @param property key of ILEncryptedCredential
|
|
274
|
+
* @private
|
|
275
|
+
*/
|
|
276
|
+
getCacheDecryptFieldData(property) {
|
|
277
|
+
if (this.ENCRYPTED_FIELDS.indexOf(property) > -1) {
|
|
278
|
+
if (this.decryptedDataCache[property] === undefined) {
|
|
279
|
+
if (this.encryptedData[property] === null) {
|
|
280
|
+
this.decryptedDataCache[property] = null;
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
let decryptedData;
|
|
284
|
+
try {
|
|
285
|
+
decryptedData = PassmanCrypto_1.PassmanCrypto.decryptString(this.encryptedData[property], this.getFieldEncryptionKey(property));
|
|
286
|
+
// check for non-json content field
|
|
287
|
+
if (property === 'shared_key') {
|
|
288
|
+
this.decryptedDataCache[property] = decryptedData;
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
this.decryptedDataCache[property] = JSON.parse(decryptedData);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
catch (e) {
|
|
295
|
+
this.foundUnspecifiedEncryptionError = true;
|
|
296
|
+
this.server.logger.anyError(e);
|
|
297
|
+
this.server.logger.anyError(decryptedData);
|
|
298
|
+
this.server.logger.onError('Failed to decrypt field: ' + property + ' of credential: ' + this.label);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return this.decryptedDataCache[property];
|
|
303
|
+
}
|
|
304
|
+
// no need to cache nor json parse fields without encryption
|
|
305
|
+
return this.encryptedData[property];
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Pass the plain field data so that it can be stored stringified and encrypted.
|
|
309
|
+
*
|
|
310
|
+
* @param property
|
|
311
|
+
* @param data
|
|
312
|
+
* @private
|
|
313
|
+
*/
|
|
314
|
+
setEncryptFieldData(property, data) {
|
|
315
|
+
if (this.ENCRYPTED_FIELDS.indexOf(property) > -1) {
|
|
316
|
+
try {
|
|
317
|
+
// check for non-json content field
|
|
318
|
+
if (property === 'shared_key') {
|
|
319
|
+
this.encryptedData[property] = PassmanCrypto_1.PassmanCrypto.encryptString(data, this.getFieldEncryptionKey(property));
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
this.encryptedData[property] = PassmanCrypto_1.PassmanCrypto.encryptString(JSON.stringify(data), this.getFieldEncryptionKey(property));
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
catch (e) {
|
|
326
|
+
this.foundUnspecifiedEncryptionError = true;
|
|
327
|
+
this.server.logger.anyError(e);
|
|
328
|
+
this.server.logger.onError('Failed to encrypt field: ' + property + ' of credential: ' + this.label);
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
this.decryptedDataCache[property] = data;
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
this.encryptedData[property] = data;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
downloadFile(file, directDownload = true) {
|
|
338
|
+
if (file.filename === undefined || file.mimetype === undefined) {
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
const key = this.getFieldEncryptionKey();
|
|
342
|
+
const logger = this.server.logger;
|
|
343
|
+
const callback = function (result) {
|
|
344
|
+
if (!result.hasOwnProperty('file_data')) {
|
|
345
|
+
logger.onError('Error downloading file (' + file.filename + '), you probably have insufficient permissions');
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
try {
|
|
349
|
+
const file_data = PassmanCrypto_1.PassmanCrypto.decryptString(result.file_data, key);
|
|
350
|
+
if (directDownload) {
|
|
351
|
+
DownloadService_1.DownloadService.download(file_data, (0, html_escaper_1.escape)(file.filename), file.mimetype);
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
return file_data;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
catch (e) {
|
|
358
|
+
logger.anyError(e);
|
|
359
|
+
logger.onError('Failed to decrypt file: ' + file.filename);
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
if (!this.encryptedData.acl) {
|
|
363
|
+
return File_1.File.downloadFile(file, this.server).then(callback);
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
return File_1.File.downloadSharedFile(this, file, this.server).then(callback);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Takes plain file data, encrypts and uploads it.
|
|
371
|
+
* This method does *not* add the file to the local credential files list!
|
|
372
|
+
* Adding it to the files list as well as a call to save() or update() is required to associate the file persistent with the credential.
|
|
373
|
+
* @param plainFile
|
|
374
|
+
*/
|
|
375
|
+
encryptUploadFile(plainFile) {
|
|
376
|
+
try {
|
|
377
|
+
const encryptedFile = {
|
|
378
|
+
data: PassmanCrypto_1.PassmanCrypto.encryptString(plainFile.data, this.getFieldEncryptionKey()),
|
|
379
|
+
filename: PassmanCrypto_1.PassmanCrypto.encryptString(JSON.stringify(plainFile.filename), this.getFieldEncryptionKey()),
|
|
380
|
+
mimetype: plainFile.mimetype,
|
|
381
|
+
size: plainFile.size
|
|
382
|
+
};
|
|
383
|
+
const callback = function (uploadFileResponse) {
|
|
384
|
+
return uploadFileResponse;
|
|
385
|
+
};
|
|
386
|
+
if (!this.encryptedData.acl) {
|
|
387
|
+
return File_1.File.uploadFile(encryptedFile, this.server).then(callback);
|
|
388
|
+
}
|
|
389
|
+
else {
|
|
390
|
+
return File_1.File.uploadSharedFile(this, encryptedFile, this.server).then(callback);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
catch (e) {
|
|
394
|
+
this.foundUnspecifiedEncryptionError = true;
|
|
395
|
+
this.server.logger.anyError(e);
|
|
396
|
+
this.server.logger.onError('Failed to encrypt new file (' + plainFile.filename + ') of credential: ' + this.label);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Deletes the given file from the server.
|
|
401
|
+
* This method does *not* delete the file from the local credential files list!
|
|
402
|
+
* @param file
|
|
403
|
+
*/
|
|
404
|
+
deleteFile(file) {
|
|
405
|
+
return File_1.File.deleteFile(file, this.server);
|
|
406
|
+
}
|
|
407
|
+
getVaultGuid() {
|
|
408
|
+
return this.vault.guid;
|
|
409
|
+
}
|
|
410
|
+
getEncrypted() {
|
|
411
|
+
return this.encryptedData;
|
|
412
|
+
}
|
|
413
|
+
hasUnspecifiedEncryptionError() {
|
|
414
|
+
return this.foundUnspecifiedEncryptionError;
|
|
415
|
+
}
|
|
416
|
+
get credential_id() {
|
|
417
|
+
return this.encryptedData.credential_id;
|
|
418
|
+
}
|
|
419
|
+
set credential_id(value) {
|
|
420
|
+
this.encryptedData.credential_id = value;
|
|
421
|
+
}
|
|
422
|
+
get guid() {
|
|
423
|
+
return this.encryptedData.guid;
|
|
424
|
+
}
|
|
425
|
+
set guid(value) {
|
|
426
|
+
this.encryptedData.guid = value;
|
|
427
|
+
}
|
|
428
|
+
get user_id() {
|
|
429
|
+
return this.encryptedData.user_id;
|
|
430
|
+
}
|
|
431
|
+
set user_id(value) {
|
|
432
|
+
this.encryptedData.user_id = value;
|
|
433
|
+
}
|
|
434
|
+
get vault_id() {
|
|
435
|
+
return this.encryptedData.vault_id;
|
|
436
|
+
}
|
|
437
|
+
set vault_id(value) {
|
|
438
|
+
this.encryptedData.vault_id = value;
|
|
439
|
+
}
|
|
440
|
+
get label() {
|
|
441
|
+
return this.encryptedData.label;
|
|
442
|
+
}
|
|
443
|
+
set label(value) {
|
|
444
|
+
this.encryptedData.label = value;
|
|
445
|
+
}
|
|
446
|
+
get description() {
|
|
447
|
+
return this.getCacheDecryptFieldData('description');
|
|
448
|
+
}
|
|
449
|
+
set description(value) {
|
|
450
|
+
this.setEncryptFieldData('description', value);
|
|
451
|
+
}
|
|
452
|
+
get tags() {
|
|
453
|
+
return this.getCacheDecryptFieldData('tags');
|
|
454
|
+
}
|
|
455
|
+
set tags(value) {
|
|
456
|
+
this.setEncryptFieldData('tags', value);
|
|
457
|
+
}
|
|
458
|
+
get email() {
|
|
459
|
+
return this.getCacheDecryptFieldData('email');
|
|
460
|
+
}
|
|
461
|
+
set email(value) {
|
|
462
|
+
this.setEncryptFieldData('email', value);
|
|
463
|
+
}
|
|
464
|
+
get username() {
|
|
465
|
+
return this.getCacheDecryptFieldData('username');
|
|
466
|
+
}
|
|
467
|
+
set username(value) {
|
|
468
|
+
this.setEncryptFieldData('username', value);
|
|
469
|
+
}
|
|
470
|
+
get password() {
|
|
471
|
+
return this.getCacheDecryptFieldData('password');
|
|
472
|
+
}
|
|
473
|
+
set password(value) {
|
|
474
|
+
this.setEncryptFieldData('password', value);
|
|
475
|
+
}
|
|
476
|
+
get url() {
|
|
477
|
+
return this.getCacheDecryptFieldData('url');
|
|
478
|
+
}
|
|
479
|
+
set url(value) {
|
|
480
|
+
this.setEncryptFieldData('url', value);
|
|
481
|
+
}
|
|
482
|
+
get files() {
|
|
483
|
+
return this.getCacheDecryptFieldData('files');
|
|
484
|
+
}
|
|
485
|
+
set files(value) {
|
|
486
|
+
this.setEncryptFieldData('files', value);
|
|
487
|
+
}
|
|
488
|
+
get custom_fields() {
|
|
489
|
+
return this.getCacheDecryptFieldData('custom_fields');
|
|
490
|
+
}
|
|
491
|
+
set custom_fields(value) {
|
|
492
|
+
this.setEncryptFieldData('custom_fields', value);
|
|
493
|
+
}
|
|
494
|
+
get otp() {
|
|
495
|
+
return this.getCacheDecryptFieldData('otp');
|
|
496
|
+
}
|
|
497
|
+
set otp(value) {
|
|
498
|
+
this.setEncryptFieldData('otp', value);
|
|
499
|
+
}
|
|
500
|
+
get compromised() {
|
|
501
|
+
return this.getCacheDecryptFieldData('compromised');
|
|
502
|
+
}
|
|
503
|
+
set compromised(value) {
|
|
504
|
+
this.setEncryptFieldData('compromised', value);
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* This special field is not encrypted, if it only contains a null value.
|
|
508
|
+
*/
|
|
509
|
+
get shared_key() {
|
|
510
|
+
if (this.encryptedData.shared_key !== undefined && this.encryptedData.shared_key !== null && this.encryptedData.shared_key !== 'null' && this.encryptedData.shared_key !== '') {
|
|
511
|
+
return this.getCacheDecryptFieldData('shared_key');
|
|
512
|
+
}
|
|
513
|
+
return null;
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* This special field is not encrypted, if it only contains a null value.
|
|
517
|
+
* @param value
|
|
518
|
+
*/
|
|
519
|
+
set shared_key(value) {
|
|
520
|
+
if (value === null || value === '' || value === 'null') {
|
|
521
|
+
this.encryptedData.shared_key = null;
|
|
522
|
+
}
|
|
523
|
+
else {
|
|
524
|
+
this.setEncryptFieldData('shared_key', value);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
get favicon() {
|
|
528
|
+
return this.encryptedData.favicon;
|
|
529
|
+
}
|
|
530
|
+
set favicon(value) {
|
|
531
|
+
this.encryptedData.favicon = value;
|
|
532
|
+
}
|
|
533
|
+
get icon() {
|
|
534
|
+
return this.encryptedData.icon;
|
|
535
|
+
}
|
|
536
|
+
set icon(value) {
|
|
537
|
+
this.encryptedData.icon = value;
|
|
538
|
+
}
|
|
539
|
+
get renew_interval() {
|
|
540
|
+
return this.encryptedData.renew_interval;
|
|
541
|
+
}
|
|
542
|
+
set renew_interval(value) {
|
|
543
|
+
this.encryptedData.renew_interval = value;
|
|
544
|
+
}
|
|
545
|
+
get expire_time() {
|
|
546
|
+
return this.encryptedData.expire_time;
|
|
547
|
+
}
|
|
548
|
+
set expire_time(value) {
|
|
549
|
+
this.encryptedData.expire_time = value;
|
|
550
|
+
}
|
|
551
|
+
get delete_time() {
|
|
552
|
+
return this.encryptedData.delete_time;
|
|
553
|
+
}
|
|
554
|
+
set delete_time(value) {
|
|
555
|
+
this.encryptedData.delete_time = value;
|
|
556
|
+
}
|
|
557
|
+
get hidden() {
|
|
558
|
+
return this.encryptedData.hidden;
|
|
559
|
+
}
|
|
560
|
+
set hidden(value) {
|
|
561
|
+
this.encryptedData.hidden = value;
|
|
562
|
+
}
|
|
563
|
+
get created() {
|
|
564
|
+
return this.encryptedData.created;
|
|
565
|
+
}
|
|
566
|
+
set created(value) {
|
|
567
|
+
this.encryptedData.created = value;
|
|
568
|
+
}
|
|
569
|
+
get changed() {
|
|
570
|
+
return this.encryptedData.changed;
|
|
571
|
+
}
|
|
572
|
+
set changed(value) {
|
|
573
|
+
this.encryptedData.changed = value;
|
|
574
|
+
}
|
|
575
|
+
get set_share_key() {
|
|
576
|
+
return this.encryptedData.set_share_key;
|
|
577
|
+
}
|
|
578
|
+
set set_share_key(value) {
|
|
579
|
+
this.encryptedData.set_share_key = value;
|
|
580
|
+
}
|
|
581
|
+
get skip_revision() {
|
|
582
|
+
return this.encryptedData.skip_revision;
|
|
583
|
+
}
|
|
584
|
+
set skip_revision(value) {
|
|
585
|
+
this.encryptedData.skip_revision = value;
|
|
586
|
+
}
|
|
587
|
+
get acl() {
|
|
588
|
+
return this.encryptedData.acl;
|
|
589
|
+
}
|
|
590
|
+
set acl(value) {
|
|
591
|
+
this.encryptedData.acl = value;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
exports.default = Credential;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import NextcloudServer from "./NextcloudServer";
|
|
2
|
+
import type Credential from "./Credential";
|
|
3
|
+
import { FileInterface } from "../Interfaces/File/FileInterface";
|
|
4
|
+
import { FileUploadResponseInterface } from "../Interfaces/File/FileUploadResponseInterface";
|
|
5
|
+
import { DeleteFilesRequestBodyInterface } from "../Interfaces/File/DeleteFilesRequestBodyInterface";
|
|
6
|
+
import { DeleteFilesResponseInterface } from "../Interfaces/File/DeleteFilesResponseInterface";
|
|
7
|
+
export declare class File {
|
|
8
|
+
static downloadFile: (file: FileInterface, server: NextcloudServer) => Promise<any>;
|
|
9
|
+
static downloadSharedFile: (credential: Credential, file: FileInterface, server: NextcloudServer) => Promise<any>;
|
|
10
|
+
static uploadFile: (fileWithEncryptedFields: FileInterface, server: NextcloudServer) => Promise<void | FileUploadResponseInterface>;
|
|
11
|
+
static uploadSharedFile: (credential: Credential, fileWithEncryptedFields: FileInterface, server: NextcloudServer) => Promise<void | FileUploadResponseInterface>;
|
|
12
|
+
static updateFile: (fileWithEncryptedFields: FileInterface, server: NextcloudServer) => Promise<void | FileUploadResponseInterface>;
|
|
13
|
+
static deleteFile: (file: FileInterface, server: NextcloudServer) => Promise<any>;
|
|
14
|
+
static deleteFiles: (deleteFilesRequestBody: DeleteFilesRequestBodyInterface, server: NextcloudServer) => Promise<void | DeleteFilesResponseInterface>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.File = void 0;
|
|
4
|
+
class File {
|
|
5
|
+
static downloadFile = async (file, server) => {
|
|
6
|
+
return server.get('file/' + file.file_id, () => {
|
|
7
|
+
}).then(async (response) => {
|
|
8
|
+
if (response) {
|
|
9
|
+
return await response.json();
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
return response;
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
static downloadSharedFile = async (credential, file, server) => {
|
|
17
|
+
return server.get('sharing/credential/' + credential.guid + '/file/' + file.guid, () => {
|
|
18
|
+
}).then(async (response) => {
|
|
19
|
+
if (response) {
|
|
20
|
+
return await response.json();
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
return response;
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
static uploadFile = (fileWithEncryptedFields, server) => {
|
|
28
|
+
return server.postJson('file', fileWithEncryptedFields, () => {
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
static uploadSharedFile = (credential, fileWithEncryptedFields, server) => {
|
|
32
|
+
return server.postJson('sharing/credential/' + credential.guid + '/file', fileWithEncryptedFields, () => {
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
static updateFile = (fileWithEncryptedFields, server) => {
|
|
36
|
+
return server.postJson('file', fileWithEncryptedFields, () => {
|
|
37
|
+
}, 'PATCH');
|
|
38
|
+
};
|
|
39
|
+
static deleteFile = (file, server) => {
|
|
40
|
+
return server.delete('file/' + file.file_id, () => {
|
|
41
|
+
}).then(async (response) => {
|
|
42
|
+
if (response) {
|
|
43
|
+
return await response.json();
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
return response;
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
static deleteFiles = (deleteFilesRequestBody, server) => {
|
|
51
|
+
return server.postJson('files/delete', deleteFilesRequestBody, () => {
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
exports.File = File;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { LoggingHandlerInterface } from "../Interfaces/LoggingHandlerInterface";
|
|
2
|
+
import { NextcloudServerInterface } from "../Interfaces/NextcloudServer/NextcloudServerInterface";
|
|
3
|
+
export default class NextcloudServer {
|
|
4
|
+
private serverData;
|
|
5
|
+
logger: LoggingHandlerInterface;
|
|
6
|
+
constructor(serverData: NextcloudServerInterface, logger: LoggingHandlerInterface);
|
|
7
|
+
getBaseUrl(): string;
|
|
8
|
+
setBaseUrl(value: string): string;
|
|
9
|
+
getUser(): string;
|
|
10
|
+
setUser(value: string): string;
|
|
11
|
+
getToken(): string;
|
|
12
|
+
setToken(value: string): string;
|
|
13
|
+
getApiUrl(): string;
|
|
14
|
+
private getEncodedLogin;
|
|
15
|
+
get: (endpoint: string, errorCallback: (response: Error) => void) => Promise<Response | void>;
|
|
16
|
+
getJson: <T>(endpoint: string, errorCallback: (response: Error) => void) => Promise<T | void>;
|
|
17
|
+
delete: (endpoint: string, errorCallback: (response: Error) => void) => Promise<Response | void>;
|
|
18
|
+
/**
|
|
19
|
+
* Do a post request.
|
|
20
|
+
*
|
|
21
|
+
* @param endpoint
|
|
22
|
+
* @param data will be converted to a json string
|
|
23
|
+
* @param errorCallback
|
|
24
|
+
* @param method
|
|
25
|
+
*/
|
|
26
|
+
postJson: <T>(endpoint: string, data: [] | object | null, errorCallback: (response: Error) => void, method?: string) => Promise<T | void>;
|
|
27
|
+
}
|