@binsky/passman-client-ts 0.1.10 → 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/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/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 +17 -7
- 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/Vault.d.ts
CHANGED
|
@@ -1,21 +1,64 @@
|
|
|
1
1
|
import { NextcloudServerInterface } from "../Interfaces/NextcloudServer/NextcloudServerInterface";
|
|
2
2
|
import Credential from "./Credential";
|
|
3
|
-
import {
|
|
3
|
+
import { SpecificVaultInformationFromServerInterface } from "../Interfaces/Vault/SpecificVaultInformationFromServerInterface";
|
|
4
|
+
import { SerializableTransferFullVaultInterface } from "../Interfaces/Vault/SerializableTransferFullVaultInterface";
|
|
4
5
|
export default class Vault {
|
|
5
|
-
private
|
|
6
|
-
private server;
|
|
6
|
+
private _specificVaultInformation;
|
|
7
|
+
private readonly server;
|
|
7
8
|
private _vaultKey;
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
collectedTags: Set<string>;
|
|
10
|
+
private _credentials;
|
|
11
|
+
private constructor();
|
|
12
|
+
/**
|
|
13
|
+
* Filler for the now private Vault constructor. Use this carefully, or even better: do not use it!
|
|
14
|
+
* The constructor was previously used for custom re-creation from cache, this is now handled using a custom persistence service (when constructing PassmanClient).
|
|
15
|
+
* @param _specificVaultInformation
|
|
16
|
+
* @param server
|
|
17
|
+
* @deprecated use Vault.create() or Vault.fetchFullVaultFromServer() (with the option of setting getCachedIfPossible: boolean) instead.
|
|
18
|
+
*/
|
|
19
|
+
static createManually(_specificVaultInformation: SpecificVaultInformationFromServerInterface, server: NextcloudServerInterface): Vault;
|
|
20
|
+
/**
|
|
21
|
+
* Create a new vault on the server and return an unlocked full-featured vault instance with fresh data.
|
|
22
|
+
* @param vaultName
|
|
23
|
+
* @param vaultPassword
|
|
24
|
+
* @param server
|
|
25
|
+
* @return new fresh vault instance or void if something went wrong
|
|
26
|
+
*/
|
|
27
|
+
static create(vaultName: string, vaultPassword: string, server: NextcloudServerInterface): Promise<Vault | void>;
|
|
28
|
+
/**
|
|
29
|
+
* Creates a locked full-featured vault instance with fresh data (metadata, credentials, shared credentials) from the passman server.
|
|
30
|
+
* Optional: directly unlock by providing the correct vault key.
|
|
31
|
+
*/
|
|
32
|
+
static fetchFullVaultFromServer(server: NextcloudServerInterface, guid: string, vaultKey?: string, getCachedIfPossible?: boolean): Promise<Vault | void>;
|
|
33
|
+
/**
|
|
34
|
+
* Clear all request caches for this vault. (not for the specific credentials that's managed by the Credential model)
|
|
35
|
+
*/
|
|
36
|
+
clearRequestCache(): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* Reload current vault's metadata and credentials (as well as shared credentials) from the server.
|
|
39
|
+
* @param getCachedIfPossible
|
|
40
|
+
*/
|
|
10
41
|
refresh(getCachedIfPossible?: boolean): Promise<boolean>;
|
|
11
42
|
update(): Promise<boolean>;
|
|
12
43
|
delete(currentPassword: string): Promise<boolean>;
|
|
13
44
|
lock(): void;
|
|
45
|
+
/**
|
|
46
|
+
* Set the given sharing keys for the current vault on the server. Does not (!) store the given keys in the current vault object.
|
|
47
|
+
* @param public_sharing_key
|
|
48
|
+
* @param private_sharing_key
|
|
49
|
+
*/
|
|
14
50
|
updateSharingKeys(public_sharing_key: string, private_sharing_key: string): Promise<void>;
|
|
15
51
|
testVaultKey(vaultKey: string): boolean;
|
|
16
52
|
private getChallengingFieldValue;
|
|
17
53
|
private getFirstOwnedCredential;
|
|
18
54
|
getCredentialByGuid(guid: string): Credential | undefined;
|
|
55
|
+
getAsSerializable(): SerializableTransferFullVaultInterface;
|
|
56
|
+
/**
|
|
57
|
+
* Returns a locked vault, made out of SerializableTransferFullVaultInterface data.
|
|
58
|
+
* @param serializable
|
|
59
|
+
* @param server
|
|
60
|
+
*/
|
|
61
|
+
static fromSerializable(serializable: SerializableTransferFullVaultInterface, server: NextcloudServerInterface): Vault;
|
|
19
62
|
getServer(): NextcloudServerInterface;
|
|
20
63
|
get vaultId(): number | null;
|
|
21
64
|
get vaultKey(): string;
|
package/lib/Model/Vault.js
CHANGED
|
@@ -7,27 +7,48 @@ const Credential_1 = __importDefault(require("./Credential"));
|
|
|
7
7
|
const PassmanCrypto_1 = require("../Service/PassmanCrypto");
|
|
8
8
|
const ShareService_1 = require("../Service/ShareService");
|
|
9
9
|
const File_1 = require("./File");
|
|
10
|
+
const NextcloudServer_1 = require("./NextcloudServer");
|
|
10
11
|
class Vault {
|
|
11
|
-
|
|
12
|
+
_specificVaultInformation;
|
|
12
13
|
server;
|
|
13
14
|
_vaultKey;
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
collectedTags = new Set();
|
|
16
|
+
_credentials;
|
|
17
|
+
constructor(_specificVaultInformation, server) {
|
|
18
|
+
this._specificVaultInformation = _specificVaultInformation;
|
|
16
19
|
this.server = server;
|
|
20
|
+
this._credentials = [];
|
|
17
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Filler for the now private Vault constructor. Use this carefully, or even better: do not use it!
|
|
24
|
+
* The constructor was previously used for custom re-creation from cache, this is now handled using a custom persistence service (when constructing PassmanClient).
|
|
25
|
+
* @param _specificVaultInformation
|
|
26
|
+
* @param server
|
|
27
|
+
* @deprecated use Vault.create() or Vault.fetchFullVaultFromServer() (with the option of setting getCachedIfPossible: boolean) instead.
|
|
28
|
+
*/
|
|
29
|
+
static createManually(_specificVaultInformation, server) {
|
|
30
|
+
return new Vault(_specificVaultInformation, server);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Create a new vault on the server and return an unlocked full-featured vault instance with fresh data.
|
|
34
|
+
* @param vaultName
|
|
35
|
+
* @param vaultPassword
|
|
36
|
+
* @param server
|
|
37
|
+
* @return new fresh vault instance or void if something went wrong
|
|
38
|
+
*/
|
|
18
39
|
static async create(vaultName, vaultPassword, server) {
|
|
19
40
|
let vaultResponse = await server.postJson('/vaults', { vault_name: vaultName }, (response) => {
|
|
20
41
|
server.logger.onError(response.message);
|
|
21
42
|
});
|
|
22
43
|
if (vaultResponse) {
|
|
23
|
-
const
|
|
24
|
-
|
|
44
|
+
const creator_only_vault = new Vault(vaultResponse, server);
|
|
45
|
+
creator_only_vault._vaultKey = vaultPassword;
|
|
25
46
|
await PassmanCrypto_1.PassmanCrypto.generateRSAKeypair(2048).then(async (value) => {
|
|
26
47
|
if (value.keypair) {
|
|
27
48
|
const pemKeyPair = PassmanCrypto_1.PassmanCrypto.rsaKeyPairToPEM(value.keypair);
|
|
28
|
-
await
|
|
49
|
+
await creator_only_vault.updateSharingKeys(pemKeyPair.publicKey, pemKeyPair.privateKey);
|
|
29
50
|
// todo: create hidden test credential
|
|
30
|
-
let testCredential = new Credential_1.default(
|
|
51
|
+
let testCredential = new Credential_1.default(creator_only_vault, server);
|
|
31
52
|
testCredential.label = 'Test key for vault ' + vaultName;
|
|
32
53
|
testCredential.hidden = true;
|
|
33
54
|
testCredential.password = 'lorum ipsum';
|
|
@@ -43,58 +64,88 @@ class Vault {
|
|
|
43
64
|
server.logger.onError('Failed to create a new vault rsa key-pair');
|
|
44
65
|
}
|
|
45
66
|
});
|
|
46
|
-
return
|
|
67
|
+
return await Vault.fetchFullVaultFromServer(server, creator_only_vault.guid, vaultPassword, false);
|
|
47
68
|
}
|
|
48
69
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
70
|
+
/**
|
|
71
|
+
* Creates a locked full-featured vault instance with fresh data (metadata, credentials, shared credentials) from the passman server.
|
|
72
|
+
* Optional: directly unlock by providing the correct vault key.
|
|
73
|
+
*/
|
|
74
|
+
static async fetchFullVaultFromServer(server, guid, vaultKey, getCachedIfPossible = false) {
|
|
75
|
+
let specificVaultResponse = await server.getJson('/vaults/' + guid, (response) => {
|
|
76
|
+
server.logger.onError(response.message);
|
|
52
77
|
}, getCachedIfPossible);
|
|
53
|
-
if (
|
|
78
|
+
if (specificVaultResponse) {
|
|
79
|
+
const vault = new Vault(specificVaultResponse, server);
|
|
80
|
+
// test vault key with the challenge_password value, to have a definite unlock state for restoring decrypted credential data from cache
|
|
81
|
+
let vaultIsUnlocked = false;
|
|
82
|
+
if (vaultKey && vault.testVaultKey(vaultKey)) {
|
|
83
|
+
vault.vaultKey = vaultKey;
|
|
84
|
+
vaultIsUnlocked = true;
|
|
85
|
+
}
|
|
54
86
|
const credentials = [];
|
|
55
|
-
|
|
87
|
+
let collectedTags = [];
|
|
88
|
+
for (const encryptedCredentialDataFromServer of specificVaultResponse.credentials) {
|
|
56
89
|
try {
|
|
57
|
-
|
|
90
|
+
const credential = Credential_1.default.fromData(encryptedCredentialDataFromServer, vault, server);
|
|
91
|
+
// restore and counting tags only if the correct vaultKey is given (current vault can then be assumed as unlocked)
|
|
92
|
+
if (vaultIsUnlocked === true) {
|
|
93
|
+
if (getCachedIfPossible) {
|
|
94
|
+
await credential.restoreSerializedDecryptedDataCache();
|
|
95
|
+
}
|
|
96
|
+
if (credential.tags && credential.tags.length > 0) {
|
|
97
|
+
collectedTags = collectedTags.concat(credential.tags.map(value => value.text).filter(Boolean));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
credentials.push(credential);
|
|
58
101
|
}
|
|
59
102
|
catch (e) {
|
|
60
|
-
|
|
61
|
-
|
|
103
|
+
server.logger.anyError(e);
|
|
104
|
+
server.logger.onError('Failed to decrypt credential: ' + encryptedCredentialDataFromServer.label);
|
|
62
105
|
}
|
|
63
106
|
}
|
|
64
|
-
|
|
107
|
+
vault.collectedTags = new Set(collectedTags);
|
|
108
|
+
const credentialsSharedWithUs = await ShareService_1.ShareService.getCredentialsSharedWithUs(vault, server, getCachedIfPossible);
|
|
65
109
|
for (const credential of credentialsSharedWithUs) {
|
|
66
110
|
credentials.push(credential);
|
|
67
111
|
}
|
|
112
|
+
vault._credentials = credentials;
|
|
113
|
+
// we could also test vault key here when we have all credentials, but it would make it harder to restore decrypted credential cache
|
|
114
|
+
return vault;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Clear all request caches for this vault. (not for the specific credentials that's managed by the Credential model)
|
|
119
|
+
*/
|
|
120
|
+
clearRequestCache() {
|
|
121
|
+
return this.server.persistence.getRequestCacheHandler().set(NextcloudServer_1.NextcloudServer.getRequestCachePrefix + '/vaults/' + this.guid, undefined);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Reload current vault's metadata and credentials (as well as shared credentials) from the server.
|
|
125
|
+
* @param getCachedIfPossible
|
|
126
|
+
*/
|
|
127
|
+
async refresh(getCachedIfPossible = false) {
|
|
128
|
+
const vaultFromServer = await Vault.fetchFullVaultFromServer(this.server, this.guid, this.vaultKey, getCachedIfPossible);
|
|
129
|
+
if (vaultFromServer) {
|
|
130
|
+
this.collectedTags = vaultFromServer.collectedTags;
|
|
131
|
+
this._credentials = vaultFromServer.credentials;
|
|
68
132
|
// todo: check if a creation of vault.private_sharing_key for old vaults is required
|
|
69
|
-
this.
|
|
70
|
-
challenge_password: vaultResponse.challenge_password,
|
|
71
|
-
created: vaultResponse.created,
|
|
72
|
-
credentials: credentials,
|
|
73
|
-
delete_request_pending: vaultResponse.delete_request_pending,
|
|
74
|
-
guid: vaultResponse.guid,
|
|
75
|
-
last_access: vaultResponse.last_access,
|
|
76
|
-
name: vaultResponse.name,
|
|
77
|
-
private_sharing_key: vaultResponse.private_sharing_key,
|
|
78
|
-
public_sharing_key: vaultResponse.public_sharing_key,
|
|
79
|
-
sharing_keys_generated: vaultResponse.sharing_keys_generated,
|
|
80
|
-
vault_id: vaultResponse.vault_id,
|
|
81
|
-
vault_settings: vaultResponse.vault_settings,
|
|
82
|
-
};
|
|
133
|
+
this._specificVaultInformation = vaultFromServer._specificVaultInformation;
|
|
83
134
|
return true;
|
|
84
135
|
}
|
|
85
136
|
return false;
|
|
86
137
|
}
|
|
87
138
|
async update() {
|
|
88
|
-
const result = await this.server.postJson('/vaults/' + this.
|
|
89
|
-
guid: this.
|
|
90
|
-
vault_id: this.
|
|
91
|
-
name: this.
|
|
92
|
-
created: this.
|
|
93
|
-
public_sharing_key: this.
|
|
94
|
-
last_access: this.
|
|
95
|
-
delete_request_pending: this.
|
|
96
|
-
sharing_keys_generated: this.
|
|
97
|
-
vault_settings: this.
|
|
139
|
+
const result = await this.server.postJson('/vaults/' + this._specificVaultInformation.guid, {
|
|
140
|
+
guid: this._specificVaultInformation.guid,
|
|
141
|
+
vault_id: this._specificVaultInformation.vault_id,
|
|
142
|
+
name: this._specificVaultInformation.name,
|
|
143
|
+
created: this._specificVaultInformation.created,
|
|
144
|
+
public_sharing_key: this._specificVaultInformation.public_sharing_key,
|
|
145
|
+
last_access: this._specificVaultInformation.last_access,
|
|
146
|
+
delete_request_pending: this._specificVaultInformation.delete_request_pending,
|
|
147
|
+
sharing_keys_generated: this._specificVaultInformation.sharing_keys_generated,
|
|
148
|
+
vault_settings: this._specificVaultInformation.vault_settings
|
|
98
149
|
}, (response) => {
|
|
99
150
|
this.server.logger.onError(response.message);
|
|
100
151
|
}, 'PATCH');
|
|
@@ -135,14 +186,21 @@ class Vault {
|
|
|
135
186
|
}
|
|
136
187
|
lock() {
|
|
137
188
|
this.vaultKey = null;
|
|
189
|
+
this.collectedTags.clear();
|
|
138
190
|
// clear decrypted credential cache from memory
|
|
139
|
-
|
|
191
|
+
// no need to touch _specificVaultInformation.credentials since these are fully encrypted
|
|
192
|
+
for (const credential of this._credentials) {
|
|
140
193
|
credential.clearDecryptedDataCache();
|
|
141
194
|
}
|
|
142
195
|
}
|
|
196
|
+
/**
|
|
197
|
+
* Set the given sharing keys for the current vault on the server. Does not (!) store the given keys in the current vault object.
|
|
198
|
+
* @param public_sharing_key
|
|
199
|
+
* @param private_sharing_key
|
|
200
|
+
*/
|
|
143
201
|
async updateSharingKeys(public_sharing_key, private_sharing_key) {
|
|
144
|
-
return await this.server.postJson('/vaults/' + this.
|
|
145
|
-
guid: this.
|
|
202
|
+
return await this.server.postJson('/vaults/' + this._specificVaultInformation.guid + '/sharing-keys', {
|
|
203
|
+
guid: this._specificVaultInformation.guid,
|
|
146
204
|
public_sharing_key: public_sharing_key,
|
|
147
205
|
private_sharing_key: PassmanCrypto_1.PassmanCrypto.encryptString(private_sharing_key, this.vaultKey),
|
|
148
206
|
}, (response) => {
|
|
@@ -166,6 +224,7 @@ class Vault {
|
|
|
166
224
|
}
|
|
167
225
|
getChallengingFieldValue() {
|
|
168
226
|
const testCredential = this.getFirstOwnedCredential();
|
|
227
|
+
// use getEncrypted to prevent trying to decrypt an already decrypted or cached value
|
|
169
228
|
if (testCredential.getEncrypted().username != null) {
|
|
170
229
|
return testCredential.getEncrypted().username;
|
|
171
230
|
}
|
|
@@ -179,7 +238,7 @@ class Vault {
|
|
|
179
238
|
}
|
|
180
239
|
getFirstOwnedCredential() {
|
|
181
240
|
for (let credential of this.credentials) {
|
|
182
|
-
if (!credential.hasValidSharedKey() && credential.
|
|
241
|
+
if (!credential.hasValidSharedKey() && credential.encryptedSharedCredentialEncryptionKey === undefined) {
|
|
183
242
|
return credential;
|
|
184
243
|
}
|
|
185
244
|
}
|
|
@@ -191,11 +250,32 @@ class Vault {
|
|
|
191
250
|
}
|
|
192
251
|
}
|
|
193
252
|
}
|
|
253
|
+
getAsSerializable() {
|
|
254
|
+
const { credentials, ...serializableSpecificVaultInformation } = this._specificVaultInformation;
|
|
255
|
+
return {
|
|
256
|
+
serializableSpecificVaultInformation,
|
|
257
|
+
encryptedSerializableCredentials: this.credentials.map(credential => credential.getAsSerializable()),
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Returns a locked vault, made out of SerializableTransferFullVaultInterface data.
|
|
262
|
+
* @param serializable
|
|
263
|
+
* @param server
|
|
264
|
+
*/
|
|
265
|
+
static fromSerializable(serializable, server) {
|
|
266
|
+
const specificVaultInformation = {
|
|
267
|
+
credentials: [],
|
|
268
|
+
...serializable.serializableSpecificVaultInformation
|
|
269
|
+
};
|
|
270
|
+
const vault = new Vault(specificVaultInformation, server);
|
|
271
|
+
vault._credentials = serializable.encryptedSerializableCredentials.map(serializableCredential => Credential_1.default.fromSerializable(serializableCredential, vault, server));
|
|
272
|
+
return vault;
|
|
273
|
+
}
|
|
194
274
|
getServer() {
|
|
195
275
|
return this.server;
|
|
196
276
|
}
|
|
197
277
|
get vaultId() {
|
|
198
|
-
return this.
|
|
278
|
+
return this._specificVaultInformation.vault_id;
|
|
199
279
|
}
|
|
200
280
|
get vaultKey() {
|
|
201
281
|
return this._vaultKey;
|
|
@@ -204,49 +284,49 @@ class Vault {
|
|
|
204
284
|
this._vaultKey = value;
|
|
205
285
|
}
|
|
206
286
|
get guid() {
|
|
207
|
-
return this.
|
|
287
|
+
return this._specificVaultInformation.guid;
|
|
208
288
|
}
|
|
209
289
|
get name() {
|
|
210
|
-
return this.
|
|
290
|
+
return this._specificVaultInformation.name;
|
|
211
291
|
}
|
|
212
292
|
set name(value) {
|
|
213
|
-
this.
|
|
293
|
+
this._specificVaultInformation.name = value;
|
|
214
294
|
}
|
|
215
295
|
get created() {
|
|
216
|
-
return this.
|
|
296
|
+
return this._specificVaultInformation.created;
|
|
217
297
|
}
|
|
218
298
|
get public_sharing_key() {
|
|
219
|
-
return this.
|
|
299
|
+
return this._specificVaultInformation.public_sharing_key;
|
|
220
300
|
}
|
|
221
301
|
get private_sharing_key() {
|
|
222
|
-
return PassmanCrypto_1.PassmanCrypto.decryptString(this.
|
|
302
|
+
return PassmanCrypto_1.PassmanCrypto.decryptString(this._specificVaultInformation.private_sharing_key, this.vaultKey);
|
|
223
303
|
}
|
|
224
304
|
get sharing_keys_generated() {
|
|
225
|
-
return this.
|
|
305
|
+
return this._specificVaultInformation.sharing_keys_generated;
|
|
226
306
|
}
|
|
227
307
|
set sharing_keys_generated(value) {
|
|
228
|
-
this.
|
|
308
|
+
this._specificVaultInformation.sharing_keys_generated = value;
|
|
229
309
|
}
|
|
230
310
|
get last_access() {
|
|
231
|
-
return this.
|
|
311
|
+
return this._specificVaultInformation.last_access;
|
|
232
312
|
}
|
|
233
313
|
get challenge_password() {
|
|
234
|
-
return this.
|
|
314
|
+
return this._specificVaultInformation.challenge_password;
|
|
235
315
|
}
|
|
236
316
|
get delete_request_pending() {
|
|
237
|
-
return this.
|
|
317
|
+
return this._specificVaultInformation.delete_request_pending;
|
|
238
318
|
}
|
|
239
319
|
set delete_request_pending(value) {
|
|
240
|
-
this.
|
|
320
|
+
this._specificVaultInformation.delete_request_pending = value;
|
|
241
321
|
}
|
|
242
322
|
get vault_settings() {
|
|
243
|
-
return this.
|
|
323
|
+
return this._specificVaultInformation.vault_settings;
|
|
244
324
|
}
|
|
245
325
|
set vault_settings(value) {
|
|
246
|
-
this.
|
|
326
|
+
this._specificVaultInformation.vault_settings = value;
|
|
247
327
|
}
|
|
248
328
|
get credentials() {
|
|
249
|
-
return this.
|
|
329
|
+
return this._credentials;
|
|
250
330
|
}
|
|
251
331
|
}
|
|
252
332
|
exports.default = Vault;
|
package/lib/PassmanClient.d.ts
CHANGED
|
@@ -2,33 +2,74 @@ import Vault from "./Model/Vault";
|
|
|
2
2
|
import type { LoggingHandlerInterface } from "./Interfaces/LoggingHandlerInterface";
|
|
3
3
|
import { NextcloudServerInfoInterface } from "./Interfaces/NextcloudServer/NextcloudServerInfoInterface";
|
|
4
4
|
import type { NextcloudServerInterface } from "./Interfaces/NextcloudServer/NextcloudServerInterface";
|
|
5
|
+
import PreloadedVault from "./Model/PreloadedVault";
|
|
6
|
+
import { PersistenceInterface } from "./Interfaces/PersistenceInterface";
|
|
7
|
+
import { RequestCachingHandlerInterface } from "./Interfaces/RequestCachingHandlerInterface";
|
|
5
8
|
export declare class PassmanClient {
|
|
6
9
|
readonly server: NextcloudServerInterface;
|
|
7
|
-
private logger;
|
|
8
|
-
vaults: Vault[];
|
|
10
|
+
private readonly logger;
|
|
9
11
|
/**
|
|
10
|
-
*
|
|
12
|
+
* Non-serializable in-memory object cache, useful for long-living instances.
|
|
13
|
+
* todo: may add a constructor option to disable this one to save memory (for short-living instances like webextensions)
|
|
14
|
+
*/
|
|
15
|
+
protected readonly _fullFeaturedVaultObjectCache: Vault[];
|
|
16
|
+
/**
|
|
17
|
+
* Array of available vaults, with just enough metadata for a vault listing and to run testVaultKey('...').
|
|
18
|
+
*/
|
|
19
|
+
protected _preloadedVaults: PreloadedVault[];
|
|
20
|
+
/**
|
|
21
|
+
* Create PassmanClient instance. Deprecated!
|
|
11
22
|
* @param serverData
|
|
12
23
|
* @param nextcloudServer
|
|
13
24
|
* @param logger
|
|
25
|
+
* @param persistence
|
|
14
26
|
* @throws ConfigurationError from nextcloud server configuration data
|
|
27
|
+
* @deprecated use PassmanClient.createInstance() instead in external/public libs
|
|
28
|
+
*/
|
|
29
|
+
constructor(serverData: NextcloudServerInfoInterface, nextcloudServer?: NextcloudServerInterface, logger?: LoggingHandlerInterface, persistence?: PersistenceInterface);
|
|
30
|
+
/**
|
|
31
|
+
* Create PassmanClient instance.
|
|
32
|
+
* To use "auto restore on reconstruction" provide a custom persistence instance. This will never auto-unlock vaults.
|
|
33
|
+
* @param serverData
|
|
34
|
+
* @param nextcloudServer
|
|
35
|
+
* @param logger
|
|
36
|
+
* @param persistence
|
|
15
37
|
*/
|
|
16
|
-
|
|
38
|
+
static createInstance(serverData: NextcloudServerInfoInterface, nextcloudServer?: NextcloudServerInterface, logger?: LoggingHandlerInterface, persistence?: PersistenceInterface): Promise<PassmanClient>;
|
|
39
|
+
protected restoreFromCacheHandler(cache: RequestCachingHandlerInterface): Promise<void>;
|
|
17
40
|
/**
|
|
18
|
-
*
|
|
41
|
+
* Preloads vaults or refreshes the preloaded vaults array, if getCachedIfPossible = false.
|
|
19
42
|
* @param throwError
|
|
20
|
-
* @param preserveInMemoryVaultKeys
|
|
21
43
|
* @param getCachedIfPossible
|
|
22
|
-
* @throws Error if throwError = true and the api request fails with an error
|
|
23
44
|
*/
|
|
24
|
-
|
|
45
|
+
preloadVaults(throwError?: boolean, getCachedIfPossible?: boolean): Promise<boolean>;
|
|
25
46
|
createVault(vaultName: string, vaultPassword: string): Promise<void | Vault>;
|
|
47
|
+
/**
|
|
48
|
+
* @deprecated use getFullVaultByGuid instead
|
|
49
|
+
* @param guid
|
|
50
|
+
* @param getCachedIfPossible
|
|
51
|
+
*/
|
|
26
52
|
getVaultByGuid(guid: string, getCachedIfPossible?: boolean): Promise<Vault>;
|
|
27
53
|
/**
|
|
28
|
-
*
|
|
54
|
+
* Returns full vault from cache, or fetches it from the server (and updates the full-featured vault cache afterward).
|
|
55
|
+
* If a vault key is provided, it tries not only to unlock, also to restore decrypted data if getCachedIfPossible is set.
|
|
29
56
|
* @param guid
|
|
57
|
+
* @param getCachedIfPossible
|
|
58
|
+
*/
|
|
59
|
+
getFullVaultByGuid(guid: string, getCachedIfPossible?: boolean, vaultKey?: string): Promise<Vault>;
|
|
60
|
+
get preloadedVaults(): PreloadedVault[];
|
|
61
|
+
set preloadedVaults(preloadedVaults: PreloadedVault[]);
|
|
62
|
+
/**
|
|
63
|
+
* Returns the full-featured vault instance from the in-memory object cache, if possible.
|
|
64
|
+
* @param guid
|
|
65
|
+
* @private
|
|
66
|
+
*/
|
|
67
|
+
protected _getFullFeaturedVaultFromObjectCacheByGuid(guid: string): Vault | void;
|
|
68
|
+
/**
|
|
69
|
+
* Add or update a full-featured vault instance in the in-memory object cache.
|
|
70
|
+
* @param vault
|
|
30
71
|
* @private
|
|
31
72
|
*/
|
|
32
|
-
|
|
73
|
+
protected _updateFullFeaturedVaultInObjectCache(vault: Vault): void;
|
|
33
74
|
getTranslation(lang?: string): Promise<void | object>;
|
|
34
75
|
}
|
package/lib/PassmanClient.js
CHANGED
|
@@ -7,48 +7,78 @@ exports.PassmanClient = void 0;
|
|
|
7
7
|
const NextcloudServer_1 = require("./Model/NextcloudServer");
|
|
8
8
|
const Vault_1 = __importDefault(require("./Model/Vault"));
|
|
9
9
|
const DefaultLoggingService_1 = require("./Service/DefaultLoggingService");
|
|
10
|
+
const PreloadedVault_1 = __importDefault(require("./Model/PreloadedVault"));
|
|
11
|
+
const DefaultPersistenceService_1 = require("./Service/DefaultPersistenceService");
|
|
10
12
|
class PassmanClient {
|
|
11
13
|
server;
|
|
12
14
|
logger;
|
|
13
|
-
vaults;
|
|
14
15
|
/**
|
|
15
|
-
*
|
|
16
|
+
* Non-serializable in-memory object cache, useful for long-living instances.
|
|
17
|
+
* todo: may add a constructor option to disable this one to save memory (for short-living instances like webextensions)
|
|
18
|
+
*/
|
|
19
|
+
_fullFeaturedVaultObjectCache;
|
|
20
|
+
/**
|
|
21
|
+
* Array of available vaults, with just enough metadata for a vault listing and to run testVaultKey('...').
|
|
22
|
+
*/
|
|
23
|
+
_preloadedVaults;
|
|
24
|
+
/**
|
|
25
|
+
* Create PassmanClient instance. Deprecated!
|
|
16
26
|
* @param serverData
|
|
17
27
|
* @param nextcloudServer
|
|
18
28
|
* @param logger
|
|
29
|
+
* @param persistence
|
|
19
30
|
* @throws ConfigurationError from nextcloud server configuration data
|
|
31
|
+
* @deprecated use PassmanClient.createInstance() instead in external/public libs
|
|
20
32
|
*/
|
|
21
|
-
constructor(serverData, nextcloudServer, logger) {
|
|
33
|
+
constructor(serverData, nextcloudServer, logger, persistence) {
|
|
22
34
|
this.logger = logger ?? new DefaultLoggingService_1.DefaultLoggingService();
|
|
23
|
-
this.server = nextcloudServer ?? new NextcloudServer_1.NextcloudServer(serverData, this.logger);
|
|
35
|
+
this.server = nextcloudServer ?? new NextcloudServer_1.NextcloudServer(serverData, this.logger, persistence ?? new DefaultPersistenceService_1.DefaultPersistenceService());
|
|
36
|
+
this._fullFeaturedVaultObjectCache = [];
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Create PassmanClient instance.
|
|
40
|
+
* To use "auto restore on reconstruction" provide a custom persistence instance. This will never auto-unlock vaults.
|
|
41
|
+
* @param serverData
|
|
42
|
+
* @param nextcloudServer
|
|
43
|
+
* @param logger
|
|
44
|
+
* @param persistence
|
|
45
|
+
*/
|
|
46
|
+
static async createInstance(serverData, nextcloudServer, logger, persistence) {
|
|
47
|
+
if (persistence?.autoRestoreOnReconstruction()) {
|
|
48
|
+
let passmanClient = new this(serverData, nextcloudServer ?? new NextcloudServer_1.NextcloudServer(serverData, logger, persistence), logger, persistence);
|
|
49
|
+
await passmanClient.restoreFromCacheHandler(persistence.getRequestCacheHandler());
|
|
50
|
+
return passmanClient;
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
return new this(serverData, nextcloudServer, logger, persistence);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
async restoreFromCacheHandler(cache) {
|
|
57
|
+
await this.preloadVaults(false, true);
|
|
58
|
+
// test for which vaults we have full-feature loading requests cached and hint-load them to be recreated from request cache
|
|
59
|
+
const cachePrefix = 'cache-getJson-';
|
|
60
|
+
for (const preloadedVault of this.preloadedVaults) {
|
|
61
|
+
const cachedValue = await cache.get(cachePrefix + '/vaults/' + preloadedVault.guid);
|
|
62
|
+
if (cachedValue && cachedValue !== '') {
|
|
63
|
+
await this.getFullVaultByGuid(preloadedVault.guid, true);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
24
66
|
}
|
|
25
67
|
/**
|
|
26
|
-
*
|
|
68
|
+
* Preloads vaults or refreshes the preloaded vaults array, if getCachedIfPossible = false.
|
|
27
69
|
* @param throwError
|
|
28
|
-
* @param preserveInMemoryVaultKeys
|
|
29
70
|
* @param getCachedIfPossible
|
|
30
|
-
* @throws Error if throwError = true and the api request fails with an error
|
|
31
71
|
*/
|
|
32
|
-
async
|
|
33
|
-
|
|
34
|
-
const vaults = await this.server.getJson('/vaults', (error) => {
|
|
72
|
+
async preloadVaults(throwError = false, getCachedIfPossible = false) {
|
|
73
|
+
const vaultsResponse = await this.server.getJson('/vaults', (error) => {
|
|
35
74
|
console.error(error);
|
|
36
75
|
if (throwError) {
|
|
37
76
|
this.logger.onThrow(error);
|
|
38
77
|
}
|
|
39
78
|
}, getCachedIfPossible);
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if (this.vaults !== undefined && preserveInMemoryVaultKeys) {
|
|
44
|
-
const oldVaultInstance = this._getVaultByGuid(vault.guid);
|
|
45
|
-
if (oldVaultInstance && oldVaultInstance.vaultKey && vault.testVaultKey(oldVaultInstance.vaultKey)) {
|
|
46
|
-
vault.vaultKey = oldVaultInstance.vaultKey;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
newVaults.push(vault);
|
|
50
|
-
});
|
|
51
|
-
this.vaults = newVaults;
|
|
79
|
+
let newPreloadedVaults = PreloadedVault_1.default.parseResponse(vaultsResponse, this.server);
|
|
80
|
+
if (newPreloadedVaults) {
|
|
81
|
+
this.preloadedVaults = newPreloadedVaults;
|
|
52
82
|
return true;
|
|
53
83
|
}
|
|
54
84
|
return false;
|
|
@@ -56,34 +86,70 @@ class PassmanClient {
|
|
|
56
86
|
async createVault(vaultName, vaultPassword) {
|
|
57
87
|
let newVault = await Vault_1.default.create(vaultName, vaultPassword, this.server);
|
|
58
88
|
if (newVault) {
|
|
59
|
-
this.
|
|
89
|
+
this._updateFullFeaturedVaultInObjectCache(newVault);
|
|
60
90
|
return newVault;
|
|
61
91
|
}
|
|
62
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* @deprecated use getFullVaultByGuid instead
|
|
95
|
+
* @param guid
|
|
96
|
+
* @param getCachedIfPossible
|
|
97
|
+
*/
|
|
63
98
|
async getVaultByGuid(guid, getCachedIfPossible = false) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
99
|
+
return this.getFullVaultByGuid(guid, getCachedIfPossible);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Returns full vault from cache, or fetches it from the server (and updates the full-featured vault cache afterward).
|
|
103
|
+
* If a vault key is provided, it tries not only to unlock, also to restore decrypted data if getCachedIfPossible is set.
|
|
104
|
+
* @param guid
|
|
105
|
+
* @param getCachedIfPossible
|
|
106
|
+
*/
|
|
107
|
+
async getFullVaultByGuid(guid, getCachedIfPossible = false, vaultKey) {
|
|
108
|
+
if (getCachedIfPossible) {
|
|
109
|
+
const cachedVault = this._getFullFeaturedVaultFromObjectCacheByGuid(guid);
|
|
110
|
+
if (cachedVault) {
|
|
111
|
+
return cachedVault;
|
|
68
112
|
}
|
|
69
113
|
}
|
|
70
|
-
const
|
|
71
|
-
if (
|
|
72
|
-
|
|
114
|
+
const freshVault = await Vault_1.default.fetchFullVaultFromServer(this.server, guid, vaultKey, getCachedIfPossible);
|
|
115
|
+
if (freshVault) {
|
|
116
|
+
this._updateFullFeaturedVaultInObjectCache(freshVault);
|
|
117
|
+
return freshVault;
|
|
73
118
|
}
|
|
74
119
|
this.logger.onError(`vault with guid ${guid} not found`);
|
|
75
120
|
}
|
|
121
|
+
get preloadedVaults() {
|
|
122
|
+
return this._preloadedVaults ?? [];
|
|
123
|
+
}
|
|
124
|
+
set preloadedVaults(preloadedVaults) {
|
|
125
|
+
this._preloadedVaults = preloadedVaults;
|
|
126
|
+
}
|
|
76
127
|
/**
|
|
77
|
-
*
|
|
128
|
+
* Returns the full-featured vault instance from the in-memory object cache, if possible.
|
|
78
129
|
* @param guid
|
|
79
130
|
* @private
|
|
80
131
|
*/
|
|
81
|
-
|
|
82
|
-
for (const
|
|
83
|
-
if (
|
|
84
|
-
return
|
|
132
|
+
_getFullFeaturedVaultFromObjectCacheByGuid(guid) {
|
|
133
|
+
for (const vault of this._fullFeaturedVaultObjectCache) {
|
|
134
|
+
if (vault.guid === guid) {
|
|
135
|
+
return vault;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Add or update a full-featured vault instance in the in-memory object cache.
|
|
141
|
+
* @param vault
|
|
142
|
+
* @private
|
|
143
|
+
*/
|
|
144
|
+
_updateFullFeaturedVaultInObjectCache(vault) {
|
|
145
|
+
for (let i = 0; i < this._fullFeaturedVaultObjectCache.length; i++) {
|
|
146
|
+
if (this._fullFeaturedVaultObjectCache[i].guid === vault.guid) {
|
|
147
|
+
this._fullFeaturedVaultObjectCache[i] = vault;
|
|
148
|
+
return;
|
|
85
149
|
}
|
|
86
150
|
}
|
|
151
|
+
// add if vault was not found in the cache loop above
|
|
152
|
+
this._fullFeaturedVaultObjectCache.push(vault);
|
|
87
153
|
}
|
|
88
154
|
async getTranslation(lang = 'en') {
|
|
89
155
|
return await this.server.getJson('/language?lang=' + lang, (response) => {
|