favalib 0.0.4 → 0.0.6
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/build/Command/BaseCommand.d.mts +2 -2
- package/build/Command/commandConstructors.d.mts +2 -0
- package/build/Command/commandConstructors.mjs +2 -0
- package/build/Command/commands/AddSyncDeviceCommand.d.mts +3 -4
- package/build/Command/commands/AddSyncDeviceCommand.mjs +2 -2
- package/build/Command/commands/ChangeSyncDeviceMetaCommand.d.mts +32 -0
- package/build/Command/commands/ChangeSyncDeviceMetaCommand.mjs +37 -0
- package/build/TwoFaLib.d.mts +3 -2
- package/build/TwoFaLib.mjs +7 -4
- package/build/interfaces/CommandTypes.d.mts +5 -1
- package/build/interfaces/SyncTypes.d.mts +7 -5
- package/build/main.d.mts +2 -2
- package/build/subclasses/CommandManager.mjs +9 -2
- package/build/subclasses/PersistentStorageManager.mjs +2 -1
- package/build/subclasses/SyncManager.d.mts +20 -8
- package/build/subclasses/SyncManager.mjs +100 -54
- package/build/subclasses/VaultDataManager.d.mts +3 -2
- package/build/subclasses/VaultDataManager.mjs +10 -3
- package/package.json +14 -14
|
@@ -30,9 +30,9 @@ declare abstract class BaseCommand<T extends CommandData = CommandData> {
|
|
|
30
30
|
/**
|
|
31
31
|
* Creates an undo command that, when executed, reverses the effects of this command.
|
|
32
32
|
* @param VaultDataManager - The TwoFaLibMediator instance to use for creating the undo command.
|
|
33
|
-
* @returns A BaseCommand instance that undoes this command.
|
|
33
|
+
* @returns A BaseCommand instance that undoes this command or false if this command has no undo.
|
|
34
34
|
*/
|
|
35
|
-
abstract createUndoCommand(TwoFaLibMediator: TwoFaLibMediator): BaseCommand;
|
|
35
|
+
abstract createUndoCommand(TwoFaLibMediator: TwoFaLibMediator): BaseCommand | false;
|
|
36
36
|
/**
|
|
37
37
|
* Creates a new instance of the command with the provided data.
|
|
38
38
|
* @param data - The data to use for creating the new command instance.
|
|
@@ -2,10 +2,12 @@ import AddEntryCommand from './commands/AddEntryCommand.mjs';
|
|
|
2
2
|
import AddSyncDeviceCommand from './commands/AddSyncDeviceCommand.mjs';
|
|
3
3
|
import DeleteEntryCommand from './commands/DeleteEntryCommand.mjs';
|
|
4
4
|
import UpdateEntryCommand from './commands/UpdateEntryCommand.mjs';
|
|
5
|
+
import ChangeSyncDeviceMetaCommand from './commands/ChangeSyncDeviceMetaCommand.mjs';
|
|
5
6
|
declare const commandConstructors: {
|
|
6
7
|
AddEntry: typeof AddEntryCommand;
|
|
7
8
|
DeleteEntry: typeof DeleteEntryCommand;
|
|
8
9
|
UpdateEntry: typeof UpdateEntryCommand;
|
|
9
10
|
AddSyncDevice: typeof AddSyncDeviceCommand;
|
|
11
|
+
ChangeSyncDeviceMeta: typeof ChangeSyncDeviceMetaCommand;
|
|
10
12
|
};
|
|
11
13
|
export default commandConstructors;
|
|
@@ -2,10 +2,12 @@ import AddEntryCommand from './commands/AddEntryCommand.mjs';
|
|
|
2
2
|
import AddSyncDeviceCommand from './commands/AddSyncDeviceCommand.mjs';
|
|
3
3
|
import DeleteEntryCommand from './commands/DeleteEntryCommand.mjs';
|
|
4
4
|
import UpdateEntryCommand from './commands/UpdateEntryCommand.mjs';
|
|
5
|
+
import ChangeSyncDeviceMetaCommand from './commands/ChangeSyncDeviceMetaCommand.mjs';
|
|
5
6
|
const commandConstructors = {
|
|
6
7
|
AddEntry: AddEntryCommand,
|
|
7
8
|
DeleteEntry: DeleteEntryCommand,
|
|
8
9
|
UpdateEntry: UpdateEntryCommand,
|
|
9
10
|
AddSyncDevice: AddSyncDeviceCommand,
|
|
11
|
+
ChangeSyncDeviceMeta: ChangeSyncDeviceMetaCommand,
|
|
10
12
|
};
|
|
11
13
|
export default commandConstructors;
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import type TwoFaLibMediator from '../../TwoFaLibMediator.mjs';
|
|
2
2
|
import Command from '../BaseCommand.mjs';
|
|
3
|
-
import type { DeviceId
|
|
3
|
+
import type { DeviceId } from '../../interfaces/SyncTypes.mjs';
|
|
4
4
|
import type { PublicKey } from '../../interfaces/CryptoLib.mjs';
|
|
5
5
|
export interface AddSyncDeviceData {
|
|
6
6
|
deviceId: DeviceId;
|
|
7
|
-
deviceType: DeviceType;
|
|
8
7
|
publicKey: PublicKey;
|
|
9
8
|
}
|
|
10
9
|
/**
|
|
@@ -12,13 +11,13 @@ export interface AddSyncDeviceData {
|
|
|
12
11
|
*/
|
|
13
12
|
declare class AddSyncDeviceCommand extends Command<AddSyncDeviceData> {
|
|
14
13
|
/**
|
|
15
|
-
* Creates a new
|
|
14
|
+
* Creates a new AddSyncDeviceCommand instance.
|
|
16
15
|
* @inheritdoc
|
|
17
16
|
* @param data - The data of the entry to be added.
|
|
18
17
|
*/
|
|
19
18
|
constructor(data: AddSyncDeviceData, id?: string, timestamp?: number, version?: string, fromRemote?: boolean);
|
|
20
19
|
/**
|
|
21
|
-
* Executes the command to add
|
|
20
|
+
* Executes the command to add a sync device
|
|
22
21
|
* @inheritdoc
|
|
23
22
|
* @throws {InvalidCommandError} If the command data is invalid.
|
|
24
23
|
*/
|
|
@@ -5,7 +5,7 @@ import Command from '../BaseCommand.mjs';
|
|
|
5
5
|
*/
|
|
6
6
|
class AddSyncDeviceCommand extends Command {
|
|
7
7
|
/**
|
|
8
|
-
* Creates a new
|
|
8
|
+
* Creates a new AddSyncDeviceCommand instance.
|
|
9
9
|
* @inheritdoc
|
|
10
10
|
* @param data - The data of the entry to be added.
|
|
11
11
|
*/
|
|
@@ -13,7 +13,7 @@ class AddSyncDeviceCommand extends Command {
|
|
|
13
13
|
super('AddSyncDevice', data, id, timestamp, version, fromRemote);
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
|
-
* Executes the command to add
|
|
16
|
+
* Executes the command to add a sync device
|
|
17
17
|
* @inheritdoc
|
|
18
18
|
* @throws {InvalidCommandError} If the command data is invalid.
|
|
19
19
|
*/
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type TwoFaLibMediator from '../../TwoFaLibMediator.mjs';
|
|
2
|
+
import Command from '../BaseCommand.mjs';
|
|
3
|
+
import type { DeviceFriendlyName, DeviceId, DeviceType } from '../../interfaces/SyncTypes.mjs';
|
|
4
|
+
export interface ChangeSyncDeviceMetaData {
|
|
5
|
+
deviceId: DeviceId;
|
|
6
|
+
newMeta: {
|
|
7
|
+
deviceFriendlyName: DeviceFriendlyName;
|
|
8
|
+
deviceType: DeviceType;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Represents a command that when executed changes the meta info of a sync device
|
|
13
|
+
*/
|
|
14
|
+
declare class ChangeSyncDeviceMetaCommand extends Command<ChangeSyncDeviceMetaData> {
|
|
15
|
+
/**
|
|
16
|
+
* Creates a new ChangeSyncDeviceMetaCommand instance.
|
|
17
|
+
* @inheritdoc
|
|
18
|
+
* @param data - The id of the device to change and the new meta info
|
|
19
|
+
*/
|
|
20
|
+
constructor(data: ChangeSyncDeviceMetaData, id?: string, timestamp?: number, version?: string, fromRemote?: boolean);
|
|
21
|
+
/**
|
|
22
|
+
* Executes the command to change the sync device metadata
|
|
23
|
+
* @inheritdoc
|
|
24
|
+
* @throws {InvalidCommandError} If the referenced sync device cannot be found
|
|
25
|
+
*/
|
|
26
|
+
execute(mediator: TwoFaLibMediator): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* @inheritdoc
|
|
29
|
+
*/
|
|
30
|
+
createUndoCommand(): Command;
|
|
31
|
+
}
|
|
32
|
+
export default ChangeSyncDeviceMetaCommand;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { InvalidCommandError, TwoFALibError } from '../../TwoFALibError.mjs';
|
|
2
|
+
import Command from '../BaseCommand.mjs';
|
|
3
|
+
/**
|
|
4
|
+
* Represents a command that when executed changes the meta info of a sync device
|
|
5
|
+
*/
|
|
6
|
+
class ChangeSyncDeviceMetaCommand extends Command {
|
|
7
|
+
/**
|
|
8
|
+
* Creates a new ChangeSyncDeviceMetaCommand instance.
|
|
9
|
+
* @inheritdoc
|
|
10
|
+
* @param data - The id of the device to change and the new meta info
|
|
11
|
+
*/
|
|
12
|
+
constructor(data, id, timestamp, version, fromRemote = false) {
|
|
13
|
+
super('ChangeSyncDeviceMeta', data, id, timestamp, version, fromRemote);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Executes the command to change the sync device metadata
|
|
17
|
+
* @inheritdoc
|
|
18
|
+
* @throws {InvalidCommandError} If the referenced sync device cannot be found
|
|
19
|
+
*/
|
|
20
|
+
execute(mediator) {
|
|
21
|
+
const syncManager = mediator.getComponent('syncManager');
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/dot-notation
|
|
23
|
+
const device = syncManager['syncDevices'].find((d) => d.deviceId === this.data.deviceId);
|
|
24
|
+
if (!device) {
|
|
25
|
+
throw new InvalidCommandError('Trying to change meta of device that is not found');
|
|
26
|
+
}
|
|
27
|
+
device.meta = this.data.newMeta;
|
|
28
|
+
return Promise.resolve();
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* @inheritdoc
|
|
32
|
+
*/
|
|
33
|
+
createUndoCommand() {
|
|
34
|
+
throw new TwoFALibError('Not implemented yet');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export default ChangeSyncDeviceMetaCommand;
|
package/build/TwoFaLib.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { TypedEventTarget } from 'typescript-event-target';
|
|
2
2
|
import type CryptoLib from './interfaces/CryptoLib.mjs';
|
|
3
3
|
import type { EncryptedPrivateKey, EncryptedSymmetricKey, Passphrase, PrivateKey, PublicKey, Salt, SymmetricKey } from './interfaces/CryptoLib.mjs';
|
|
4
|
-
import type { DeviceId, DeviceType } from './interfaces/SyncTypes.mjs';
|
|
4
|
+
import type { DeviceFriendlyName, DeviceId, DeviceType } from './interfaces/SyncTypes.mjs';
|
|
5
5
|
import type { TwoFaLibEventMapEvents } from './interfaces/Events.mjs';
|
|
6
6
|
import type { PassphraseExtraDict } from './interfaces/PassphraseExtraDict.js';
|
|
7
7
|
import type { Vault, VaultSyncState } from './interfaces/Vault.mjs';
|
|
@@ -15,13 +15,14 @@ declare class TwoFaLib extends TypedEventTarget<TwoFaLibEventMapEvents> {
|
|
|
15
15
|
static readonly version = "0.0.1";
|
|
16
16
|
readonly deviceId: DeviceId;
|
|
17
17
|
readonly deviceType: DeviceType;
|
|
18
|
+
deviceFriendlyName: DeviceFriendlyName;
|
|
18
19
|
private mediator;
|
|
19
20
|
private readonly publicKey;
|
|
20
21
|
private readonly privateKey;
|
|
21
22
|
readonly ready: Promise<unknown>;
|
|
22
23
|
/**
|
|
23
24
|
* Constructs a new instance of TwoFaLib. If a serverUrl is provided, the library will use it for its sync operations.
|
|
24
|
-
* @param deviceType -
|
|
25
|
+
* @param deviceType - The identifier for this device type (e.g. 2fa-cli).
|
|
25
26
|
* @param cryptoLib - An instance of CryptoLib that is compatible with the environment.
|
|
26
27
|
* @param passphraseExtraDict - Additional words to be used for passphrase strength evaluation.
|
|
27
28
|
* @param privateKey - The private key used for cryptographic operations.
|
package/build/TwoFaLib.mjs
CHANGED
|
@@ -17,7 +17,7 @@ class TwoFaLib extends TypedEventTarget {
|
|
|
17
17
|
static { this.version = '0.0.1'; }
|
|
18
18
|
/**
|
|
19
19
|
* Constructs a new instance of TwoFaLib. If a serverUrl is provided, the library will use it for its sync operations.
|
|
20
|
-
* @param deviceType -
|
|
20
|
+
* @param deviceType - The identifier for this device type (e.g. 2fa-cli).
|
|
21
21
|
* @param cryptoLib - An instance of CryptoLib that is compatible with the environment.
|
|
22
22
|
* @param passphraseExtraDict - Additional words to be used for passphrase strength evaluation.
|
|
23
23
|
* @param privateKey - The private key used for cryptographic operations.
|
|
@@ -35,6 +35,7 @@ class TwoFaLib extends TypedEventTarget {
|
|
|
35
35
|
*/
|
|
36
36
|
constructor(deviceType, cryptoLib, passphraseExtraDict, privateKey, symmetricKey, encryptedPrivateKey, encryptedSymmetricKey, salt, publicKey, deviceId, vault, syncState) {
|
|
37
37
|
super();
|
|
38
|
+
this.deviceFriendlyName = '';
|
|
38
39
|
if (!deviceType) {
|
|
39
40
|
throw new InitializationError('Device type is required');
|
|
40
41
|
}
|
|
@@ -73,7 +74,7 @@ class TwoFaLib extends TypedEventTarget {
|
|
|
73
74
|
}
|
|
74
75
|
if (syncState?.serverUrl) {
|
|
75
76
|
// Initiate the syncManager
|
|
76
|
-
this.mediator.registerComponent('syncManager', new SyncManager(this.mediator, this.
|
|
77
|
+
this.mediator.registerComponent('syncManager', new SyncManager(this.mediator, this.publicKey, this.privateKey, syncState, this.deviceId));
|
|
77
78
|
}
|
|
78
79
|
else {
|
|
79
80
|
// If no syncmanager we're ready now, otherwise the syncmanager is responsible for emitting the ready event
|
|
@@ -152,7 +153,7 @@ class TwoFaLib extends TypedEventTarget {
|
|
|
152
153
|
devices: [],
|
|
153
154
|
commandSendQueue: [],
|
|
154
155
|
};
|
|
155
|
-
const newSyncManager = new SyncManager(this.mediator, this.
|
|
156
|
+
const newSyncManager = new SyncManager(this.mediator, this.publicKey, this.privateKey, newSyncState, this.deviceId);
|
|
156
157
|
const success = await new Promise((resolve) => {
|
|
157
158
|
this.addEventListener(TwoFaLibEvent.ConnectionToSyncServerStatusChanged, (event) => {
|
|
158
159
|
if (event.detail.newStatus === ConnectionStatus.CONNECTED) {
|
|
@@ -176,9 +177,11 @@ class TwoFaLib extends TypedEventTarget {
|
|
|
176
177
|
throw new SyncError(`Failed to connect to server at ${serverUrl}, not setting`);
|
|
177
178
|
}
|
|
178
179
|
}
|
|
179
|
-
// connection succeeded (or force=true)
|
|
180
|
+
// connection succeeded (or force=true)
|
|
181
|
+
// switch to the new syncManager
|
|
180
182
|
this.mediator.unRegisterComponent('syncManager');
|
|
181
183
|
this.mediator.registerComponent('syncManager', newSyncManager);
|
|
184
|
+
// save
|
|
182
185
|
await this.forceSave();
|
|
183
186
|
}
|
|
184
187
|
/**
|
|
@@ -2,6 +2,7 @@ import type { AddEntryData } from '../Command/commands/AddEntryCommand.mjs';
|
|
|
2
2
|
import type { DeleteEntryData } from '../Command/commands/DeleteEntryCommand.mjs';
|
|
3
3
|
import type { UpdateEntryData } from '../Command/commands/UpdateEntryCommand.mjs';
|
|
4
4
|
import type { AddSyncDeviceData } from '../Command/commands/AddSyncDeviceCommand.mjs';
|
|
5
|
+
import type { ChangeSyncDeviceMetaData } from '../Command/commands/ChangeSyncDeviceMetaCommand.mjs';
|
|
5
6
|
export type SyncCommand = ({
|
|
6
7
|
type: 'AddEntry';
|
|
7
8
|
data: AddEntryData;
|
|
@@ -14,7 +15,10 @@ export type SyncCommand = ({
|
|
|
14
15
|
} | {
|
|
15
16
|
type: 'AddSyncDevice';
|
|
16
17
|
data: AddSyncDeviceData;
|
|
18
|
+
} | {
|
|
19
|
+
type: 'ChangeSyncDeviceMeta';
|
|
20
|
+
data: ChangeSyncDeviceMetaData;
|
|
17
21
|
}) & {
|
|
18
22
|
id: string;
|
|
19
23
|
};
|
|
20
|
-
export type CommandData = AddEntryData | DeleteEntryData | UpdateEntryData | AddSyncDeviceData;
|
|
24
|
+
export type CommandData = AddEntryData | DeleteEntryData | UpdateEntryData | AddSyncDeviceData | ChangeSyncDeviceMetaData;
|
|
@@ -3,11 +3,16 @@ import type { JPakeThreePass, Round1Result } from 'jpake-ts';
|
|
|
3
3
|
import { PublicKey, SyncKey } from './CryptoLib.mjs';
|
|
4
4
|
export type DeviceId = Tagged<string, 'DeviceId'>;
|
|
5
5
|
export type DeviceType = Tagged<string, 'DeviceType'>;
|
|
6
|
+
export type DeviceFriendlyName = Tagged<string, 'DeviceFriendlyName'>;
|
|
6
7
|
export interface SyncDevice {
|
|
7
8
|
deviceId: DeviceId;
|
|
8
|
-
deviceType: DeviceType;
|
|
9
9
|
publicKey: PublicKey;
|
|
10
|
+
meta?: {
|
|
11
|
+
deviceType: DeviceType;
|
|
12
|
+
deviceFriendlyName: DeviceFriendlyName;
|
|
13
|
+
};
|
|
10
14
|
}
|
|
15
|
+
export type PublicSyncDevice = Omit<SyncDevice, 'publicKey'>;
|
|
11
16
|
export interface BaseAddDeviceFlow {
|
|
12
17
|
jpak: JPakeThreePass;
|
|
13
18
|
addDevicePassword: Uint8Array;
|
|
@@ -22,14 +27,12 @@ export interface AddDeviceFlowInitiator_Initiated extends BaseAddDeviceFlow {
|
|
|
22
27
|
export interface AddDeviceFlowInitiator_SyncKeyCreated extends Omit<AddDeviceFlowInitiator_Initiated, 'state' | 'resolveContinuePromise'> {
|
|
23
28
|
state: 'initiator:syncKeyCreated';
|
|
24
29
|
responderDeviceId: DeviceId;
|
|
25
|
-
responderDeviceType: DeviceType;
|
|
26
30
|
syncKey: SyncKey;
|
|
27
31
|
}
|
|
28
32
|
export interface AddDeviceFlowResponder_Initiated extends BaseAddDeviceFlow {
|
|
29
33
|
state: 'responder:initated';
|
|
30
|
-
initiatorDeviceId: DeviceId;
|
|
31
34
|
responderDeviceId: DeviceId;
|
|
32
|
-
|
|
35
|
+
initiatorDeviceId: DeviceId;
|
|
33
36
|
}
|
|
34
37
|
export interface AddDeviceFlowResponder_SyncKeyCreated extends Omit<AddDeviceFlowResponder_Initiated, 'state'> {
|
|
35
38
|
state: 'responder:syncKeyCreated';
|
|
@@ -39,7 +42,6 @@ export type ActiveAddDeviceFlow = AddDeviceFlowInitiator_Initiated | AddDeviceFl
|
|
|
39
42
|
export interface InitiateAddDeviceFlowResult {
|
|
40
43
|
addDevicePassword: string;
|
|
41
44
|
initiatorDeviceId: DeviceId;
|
|
42
|
-
initiatorDeviceType: DeviceType;
|
|
43
45
|
timestamp: number;
|
|
44
46
|
pass1Result: Record<keyof Round1Result, string>;
|
|
45
47
|
}
|
package/build/main.d.mts
CHANGED
|
@@ -3,10 +3,10 @@ import type Entry from './interfaces/Entry.mjs';
|
|
|
3
3
|
import type { EntryId, NewEntry, EntryMeta, EntryType, TotpPayload, Token, EntryMetaWithToken } from './interfaces/Entry.mjs';
|
|
4
4
|
import type CryptoLib from './interfaces/CryptoLib.mjs';
|
|
5
5
|
import type { Encrypted, EncryptedPrivateKey, EncryptedSymmetricKey, EncryptedPublicKey, PrivateKey, SymmetricKey, PublicKey, Passphrase, Salt } from './interfaces/CryptoLib.mjs';
|
|
6
|
-
import type {
|
|
6
|
+
import type { PublicSyncDevice, DeviceId, DeviceType, DeviceFriendlyName } from './interfaces/SyncTypes.mjs';
|
|
7
7
|
import type { EncryptedVaultStateString, LockedRepresentationString } from './interfaces/Vault.mjs';
|
|
8
8
|
import { TwoFALibError, InitializationError, AuthenticationError, EntryNotFoundError, TokenGenerationError } from './TwoFALibError.mjs';
|
|
9
9
|
import { TwoFaLibEvent } from './TwoFaLibEvent.mjs';
|
|
10
10
|
import { getTwoFaLibVaultCreationUtils } from './utils/creationUtils.mjs';
|
|
11
11
|
export { TwoFaLib, TwoFALibError, getTwoFaLibVaultCreationUtils, InitializationError, AuthenticationError, EntryNotFoundError, TokenGenerationError, TwoFaLibEvent, };
|
|
12
|
-
export type { Entry, EntryId, NewEntry, EntryMeta, EntryMetaWithToken, EntryType, TotpPayload, Token, EncryptedVaultStateString, LockedRepresentationString, CryptoLib, Encrypted, EncryptedPrivateKey, EncryptedPublicKey, EncryptedSymmetricKey, PrivateKey, SymmetricKey, PublicKey, Passphrase, Salt, DeviceId, DeviceType,
|
|
12
|
+
export type { Entry, EntryId, NewEntry, EntryMeta, EntryMetaWithToken, EntryType, TotpPayload, Token, EncryptedVaultStateString, LockedRepresentationString, CryptoLib, Encrypted, EncryptedPrivateKey, EncryptedPublicKey, EncryptedSymmetricKey, PrivateKey, SymmetricKey, PublicKey, Passphrase, Salt, DeviceId, DeviceType, DeviceFriendlyName, PublicSyncDevice, };
|
|
@@ -49,8 +49,15 @@ class CommandManager {
|
|
|
49
49
|
const command = this.executedCommands.pop();
|
|
50
50
|
if (command) {
|
|
51
51
|
const undoCommand = command.createUndoCommand(this.mediator);
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
// check if the last command was undoable
|
|
53
|
+
if (undoCommand) {
|
|
54
|
+
await undoCommand.execute(this.mediator);
|
|
55
|
+
this.undoneCommands.push(command);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
// if it was not, skip it
|
|
59
|
+
await this.undo();
|
|
60
|
+
}
|
|
54
61
|
}
|
|
55
62
|
}
|
|
56
63
|
/**
|
|
@@ -55,7 +55,8 @@ class PersistentStorageManager {
|
|
|
55
55
|
vault,
|
|
56
56
|
deviceId: this.deviceId,
|
|
57
57
|
sync: {
|
|
58
|
-
|
|
58
|
+
// eslint-disable-next-line @typescript-eslint/dot-notation
|
|
59
|
+
devices: this.syncManager ? this.syncManager['syncDevices'] : [],
|
|
59
60
|
serverUrl: this.syncManager?.serverUrl,
|
|
60
61
|
commandSendQueue: this.syncManager?.getCommandSendQueue() ?? [],
|
|
61
62
|
},
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { SyncDevice, DeviceId,
|
|
1
|
+
import { SyncDevice, DeviceId, PublicSyncDevice } from '../interfaces/SyncTypes.mjs';
|
|
2
2
|
import type { PrivateKey, PublicKey } from '../interfaces/CryptoLib.mjs';
|
|
3
3
|
import type Command from '../Command/BaseCommand.mjs';
|
|
4
4
|
import type TwoFaLibMediator from '../TwoFaLibMediator.mjs';
|
|
5
5
|
import { VaultSyncStateWithServerUrl } from '../interfaces/Vault.mjs';
|
|
6
6
|
import { SyncCommandFromServer } from 'favaserver/ServerMessage';
|
|
7
7
|
import { SyncCommandFromClient } from 'favaserver/ClientMessage';
|
|
8
|
-
import type { AddSyncDeviceData } from '../Command/commands/AddSyncDeviceCommand.mjs';
|
|
9
8
|
export declare enum ConnectionStatus {
|
|
10
9
|
CONNECTING = 0,
|
|
11
10
|
CONNECTED = 1,
|
|
@@ -17,14 +16,13 @@ export declare enum ConnectionStatus {
|
|
|
17
16
|
*/
|
|
18
17
|
declare class SyncManager {
|
|
19
18
|
private readonly mediator;
|
|
20
|
-
private readonly deviceType;
|
|
21
19
|
private readonly publicKey;
|
|
22
20
|
private readonly privateKey;
|
|
23
21
|
private ws?;
|
|
24
22
|
private activeAddDeviceFlow?;
|
|
25
23
|
private readonly reconnectInterval;
|
|
26
24
|
readonly serverUrl: string;
|
|
27
|
-
syncDevices
|
|
25
|
+
private syncDevices;
|
|
28
26
|
deviceId: DeviceId;
|
|
29
27
|
private readyEventEmitted;
|
|
30
28
|
private commandSendQueue;
|
|
@@ -37,17 +35,21 @@ declare class SyncManager {
|
|
|
37
35
|
* @returns The command send queue.
|
|
38
36
|
*/
|
|
39
37
|
getCommandSendQueue(): SyncCommandFromClient[];
|
|
38
|
+
/**
|
|
39
|
+
* Public getter for the sync devices
|
|
40
|
+
* @returns The sync devices (without their public key)
|
|
41
|
+
*/
|
|
42
|
+
getSyncDevices(): PublicSyncDevice[];
|
|
40
43
|
/**
|
|
41
44
|
* Creates an instance of SyncManager.
|
|
42
45
|
* @param mediator - The mediator for accessing other components.
|
|
43
|
-
* @param deviceType - The type of the device.
|
|
44
46
|
* @param publicKey - The public key of the device.
|
|
45
47
|
* @param privateKey - The private key of the device.
|
|
46
48
|
* @param syncState - The state of the sync.
|
|
47
49
|
* @param deviceId - The unique identifier of the device.
|
|
48
50
|
* @throws {InitializationError} If initialization fails (e.g., if the server URL is invalid).
|
|
49
51
|
*/
|
|
50
|
-
constructor(mediator: TwoFaLibMediator,
|
|
52
|
+
constructor(mediator: TwoFaLibMediator, publicKey: PublicKey, privateKey: PrivateKey, syncState: VaultSyncStateWithServerUrl, deviceId: DeviceId);
|
|
51
53
|
private get libraryLoader();
|
|
52
54
|
private get cryptoLib();
|
|
53
55
|
private get persistentStorageManager();
|
|
@@ -130,8 +132,9 @@ declare class SyncManager {
|
|
|
130
132
|
respondToAddDeviceFlow(initiatorData: string | Uint8Array | File, initiatorDataType: 'text' | 'qr'): Promise<void>;
|
|
131
133
|
private finishAddDeviceFlowKeyExchangeInitiator;
|
|
132
134
|
private finishAddDeviceFlowKeyExchangeResponder;
|
|
133
|
-
private
|
|
135
|
+
private sendFullVaultData;
|
|
134
136
|
private importInitialVaultState;
|
|
137
|
+
private importVaultState;
|
|
135
138
|
/**
|
|
136
139
|
* Cancels the active add sync device flow.
|
|
137
140
|
* @throws {SyncNoServerConnectionError} If there is no server connection.
|
|
@@ -156,11 +159,20 @@ declare class SyncManager {
|
|
|
156
159
|
* @throws {CryptoError} If decryption fails.
|
|
157
160
|
*/
|
|
158
161
|
receiveCommands(encryptedCommands: SyncCommandFromServer[]): Promise<void>;
|
|
162
|
+
/**
|
|
163
|
+
* Sends vault data to the server for each sync device
|
|
164
|
+
*/
|
|
165
|
+
private resilver;
|
|
159
166
|
/**
|
|
160
167
|
* Add a sync device
|
|
161
168
|
* @param deviceInfo - The info about the device
|
|
169
|
+
* @param saveAfter - Whether to save the new vault after adding it (set to false when adding multiple devices)
|
|
170
|
+
*/
|
|
171
|
+
addSyncDevice(deviceInfo: SyncDevice, saveAfter?: boolean): Promise<void>;
|
|
172
|
+
/**
|
|
173
|
+
* Requests a resilver of the vault
|
|
162
174
|
*/
|
|
163
|
-
|
|
175
|
+
requestResilver(): void;
|
|
164
176
|
/**
|
|
165
177
|
* Function to call when the server connection should be closed
|
|
166
178
|
*/
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import WebSocket from 'isomorphic-ws';
|
|
2
1
|
import { base64ToUint8Array, hexToUint8Array, stringToBase64, uint8ArrayToBase64, uint8ArrayToHex, } from 'uint8array-extras';
|
|
3
2
|
import { deriveSFromPassword, JPakeThreePass, } from 'jpake-ts';
|
|
4
3
|
import { TwoFaLibEvent } from '../TwoFaLibEvent.mjs';
|
|
@@ -30,19 +29,29 @@ class SyncManager {
|
|
|
30
29
|
getCommandSendQueue() {
|
|
31
30
|
return this.commandSendQueue;
|
|
32
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Public getter for the sync devices
|
|
34
|
+
* @returns The sync devices (without their public key)
|
|
35
|
+
*/
|
|
36
|
+
getSyncDevices() {
|
|
37
|
+
return this.syncDevices
|
|
38
|
+
.filter((d) => d.deviceId !== this.deviceId)
|
|
39
|
+
.map((d) => ({
|
|
40
|
+
deviceId: d.deviceId,
|
|
41
|
+
meta: d.meta,
|
|
42
|
+
}));
|
|
43
|
+
}
|
|
33
44
|
/**
|
|
34
45
|
* Creates an instance of SyncManager.
|
|
35
46
|
* @param mediator - The mediator for accessing other components.
|
|
36
|
-
* @param deviceType - The type of the device.
|
|
37
47
|
* @param publicKey - The public key of the device.
|
|
38
48
|
* @param privateKey - The private key of the device.
|
|
39
49
|
* @param syncState - The state of the sync.
|
|
40
50
|
* @param deviceId - The unique identifier of the device.
|
|
41
51
|
* @throws {InitializationError} If initialization fails (e.g., if the server URL is invalid).
|
|
42
52
|
*/
|
|
43
|
-
constructor(mediator,
|
|
53
|
+
constructor(mediator, publicKey, privateKey, syncState, deviceId) {
|
|
44
54
|
this.mediator = mediator;
|
|
45
|
-
this.deviceType = deviceType;
|
|
46
55
|
this.publicKey = publicKey;
|
|
47
56
|
this.privateKey = privateKey;
|
|
48
57
|
this.reconnectInterval = IN_TESTING ? 100 : 5000; // 5 seconds
|
|
@@ -60,6 +69,11 @@ class SyncManager {
|
|
|
60
69
|
this.commandSendQueue = commandSendQueue;
|
|
61
70
|
this.serverUrl = serverUrl;
|
|
62
71
|
this.initServerConnection();
|
|
72
|
+
// add ourselves to the list of syncdevices if we're missing
|
|
73
|
+
void this.addSyncDevice({
|
|
74
|
+
deviceId: deviceId,
|
|
75
|
+
publicKey: this.publicKey,
|
|
76
|
+
}, false);
|
|
63
77
|
// if not yet connected after 2 tries, emit ready event so we can continue
|
|
64
78
|
this.connectionFailedTimeout = setTimeout(() => {
|
|
65
79
|
if (!this.readyEventEmitted && !this.webSocketConnected) {
|
|
@@ -118,7 +132,7 @@ class SyncManager {
|
|
|
118
132
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
119
133
|
const syncManager = this;
|
|
120
134
|
ws.addEventListener('error', (event) => {
|
|
121
|
-
syncManager.log('warning', `Error in websocket: ${event
|
|
135
|
+
syncManager.log('warning', `Error in websocket: ${event}`);
|
|
122
136
|
});
|
|
123
137
|
ws.addEventListener('message', function message(message) {
|
|
124
138
|
try {
|
|
@@ -186,7 +200,7 @@ class SyncManager {
|
|
|
186
200
|
round1Result: jsonToUint8Array(unconvertedPass2Result.round1Result),
|
|
187
201
|
round2Result: jsonToUint8Array(unconvertedPass2Result.round2Result),
|
|
188
202
|
};
|
|
189
|
-
void this.finishAddDeviceFlowKeyExchangeInitiator(pass2Result, data.responderDeviceId
|
|
203
|
+
void this.finishAddDeviceFlowKeyExchangeInitiator(pass2Result, data.responderDeviceId);
|
|
190
204
|
break;
|
|
191
205
|
}
|
|
192
206
|
case 'JPAKEPass3': {
|
|
@@ -198,13 +212,21 @@ class SyncManager {
|
|
|
198
212
|
case 'publicKey': {
|
|
199
213
|
const { data } = message;
|
|
200
214
|
const { responderEncryptedPublicKey } = data;
|
|
201
|
-
void this.
|
|
215
|
+
void this.sendFullVaultData(responderEncryptedPublicKey);
|
|
216
|
+
break;
|
|
217
|
+
}
|
|
218
|
+
case 'initialVault': {
|
|
219
|
+
const { data } = message;
|
|
220
|
+
const { encryptedVaultData } = data;
|
|
221
|
+
void this.importInitialVaultState(encryptedVaultData);
|
|
202
222
|
break;
|
|
203
223
|
}
|
|
204
224
|
case 'vault': {
|
|
205
225
|
const { data } = message;
|
|
206
|
-
const { encryptedVaultData,
|
|
207
|
-
void this.
|
|
226
|
+
const { encryptedVaultData, encryptedSymmetricKey, fromDeviceId } = data;
|
|
227
|
+
void this.cryptoLib
|
|
228
|
+
.decrypt(this.privateKey, encryptedSymmetricKey)
|
|
229
|
+
.then((symmetricKey) => this.importVaultState(encryptedVaultData, symmetricKey, fromDeviceId));
|
|
208
230
|
break;
|
|
209
231
|
}
|
|
210
232
|
case 'syncCommandsReceived': {
|
|
@@ -217,6 +239,12 @@ class SyncManager {
|
|
|
217
239
|
void this.receiveCommands(commands);
|
|
218
240
|
break;
|
|
219
241
|
}
|
|
242
|
+
case 'startResilver': {
|
|
243
|
+
// const { data } = message
|
|
244
|
+
// todo: check for missing deviceIds
|
|
245
|
+
void this.resilver();
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
220
248
|
}
|
|
221
249
|
}
|
|
222
250
|
attemptReconnect() {
|
|
@@ -259,7 +287,6 @@ class SyncManager {
|
|
|
259
287
|
});
|
|
260
288
|
// register this add device request at the server
|
|
261
289
|
this.sendToServer('addSyncDeviceInitialiseData', {
|
|
262
|
-
initiatorDeviceType: this.deviceType,
|
|
263
290
|
initiatorDeviceId: this.deviceId,
|
|
264
291
|
timestamp,
|
|
265
292
|
nonce: await this.getNonce(),
|
|
@@ -269,7 +296,6 @@ class SyncManager {
|
|
|
269
296
|
const returnData = {
|
|
270
297
|
addDevicePassword: uint8ArrayToBase64(addDevicePassword),
|
|
271
298
|
initiatorDeviceId: this.deviceId,
|
|
272
|
-
initiatorDeviceType: this.deviceType,
|
|
273
299
|
timestamp,
|
|
274
300
|
pass1Result: {
|
|
275
301
|
G1: uint8ArrayToHex(pass1Result.G1),
|
|
@@ -306,12 +332,11 @@ class SyncManager {
|
|
|
306
332
|
if (this.activeAddDeviceFlow) {
|
|
307
333
|
throw new SyncAddDeviceFlowConflictError();
|
|
308
334
|
}
|
|
309
|
-
const { addDevicePassword, initiatorDeviceId, timestamp, pass1Result
|
|
335
|
+
const { addDevicePassword, initiatorDeviceId, timestamp, pass1Result } = await decodeInitiatorData(initiatorData, initiatorDataType, await this.libraryLoader.getJsQrLib(), this.libraryLoader.getCanvasLib.bind(this));
|
|
310
336
|
if (!addDevicePassword ||
|
|
311
337
|
!initiatorDeviceId ||
|
|
312
338
|
!timestamp ||
|
|
313
|
-
!pass1Result
|
|
314
|
-
!initiatorDeviceIdentifier) {
|
|
339
|
+
!pass1Result) {
|
|
315
340
|
throw new SyncError('Missing required fields in initiator data');
|
|
316
341
|
}
|
|
317
342
|
// Decode the base64 password
|
|
@@ -337,7 +362,6 @@ class SyncManager {
|
|
|
337
362
|
addDevicePassword: decodedPassword,
|
|
338
363
|
responderDeviceId: this.deviceId,
|
|
339
364
|
initiatorDeviceId: initiatorDeviceId,
|
|
340
|
-
initiatorDeviceType: initiatorDeviceIdentifier,
|
|
341
365
|
timestamp: Date.now(),
|
|
342
366
|
};
|
|
343
367
|
// respond to this add device request at the server
|
|
@@ -347,10 +371,9 @@ class SyncManager {
|
|
|
347
371
|
pass2Result,
|
|
348
372
|
responderDeviceId: this.deviceId,
|
|
349
373
|
initiatorDeviceId: initiatorDeviceId,
|
|
350
|
-
responderDeviceType: this.deviceType,
|
|
351
374
|
});
|
|
352
375
|
}
|
|
353
|
-
async finishAddDeviceFlowKeyExchangeInitiator(pass2Result, responderDeviceId
|
|
376
|
+
async finishAddDeviceFlowKeyExchangeInitiator(pass2Result, responderDeviceId) {
|
|
354
377
|
if (!this.ws || !this.webSocketConnected) {
|
|
355
378
|
throw new SyncNoServerConnectionError();
|
|
356
379
|
}
|
|
@@ -370,7 +393,6 @@ class SyncManager {
|
|
|
370
393
|
...this.activeAddDeviceFlow,
|
|
371
394
|
state: 'initiator:syncKeyCreated',
|
|
372
395
|
responderDeviceId: responderDeviceId,
|
|
373
|
-
responderDeviceType: responderDeviceType,
|
|
374
396
|
syncKey,
|
|
375
397
|
};
|
|
376
398
|
}
|
|
@@ -400,7 +422,7 @@ class SyncManager {
|
|
|
400
422
|
initiatorDeviceId: this.activeAddDeviceFlow.initiatorDeviceId,
|
|
401
423
|
});
|
|
402
424
|
}
|
|
403
|
-
async
|
|
425
|
+
async sendFullVaultData(responderEncryptedPublicKey) {
|
|
404
426
|
if (!this.ws || !this.webSocketConnected) {
|
|
405
427
|
throw new SyncNoServerConnectionError();
|
|
406
428
|
}
|
|
@@ -415,51 +437,44 @@ class SyncManager {
|
|
|
415
437
|
const decryptedPublicKey = await this.cryptoLib.decryptSymmetric(syncKey, responderEncryptedPublicKey);
|
|
416
438
|
// get the vault data (encrypted with the sync key)
|
|
417
439
|
const encryptedVaultData = await this.persistentStorageManager.getEncryptedVaultState(syncKey);
|
|
418
|
-
const initiatorEncryptedPublicKey = await this.cryptoLib.encryptSymmetric(syncKey, this.publicKey);
|
|
419
440
|
// Send the encrypted vault data to the server
|
|
420
|
-
this.sendToServer('
|
|
441
|
+
this.sendToServer('initialVault', {
|
|
421
442
|
nonce: await this.getNonce(),
|
|
422
443
|
encryptedVaultData,
|
|
423
444
|
initiatorDeviceId: this.activeAddDeviceFlow.initiatorDeviceId,
|
|
424
|
-
initiatorEncryptedPublicKey,
|
|
425
445
|
});
|
|
426
|
-
// save the added the sync device, done via command so this is synced to
|
|
427
|
-
// all the other (already existing) sync devices
|
|
446
|
+
// save the added the sync device, done via command so this is synced to all sync devices
|
|
428
447
|
const command = AddSyncDeviceCommand.create({
|
|
429
448
|
deviceId: this.activeAddDeviceFlow.responderDeviceId,
|
|
430
|
-
deviceType: this.activeAddDeviceFlow.responderDeviceType,
|
|
431
449
|
publicKey: decryptedPublicKey,
|
|
432
450
|
});
|
|
433
451
|
await this.commandManager.execute(command);
|
|
434
452
|
// all done
|
|
435
453
|
this.activeAddDeviceFlow = undefined;
|
|
436
454
|
}
|
|
437
|
-
async importInitialVaultState(encryptedVaultState
|
|
455
|
+
async importInitialVaultState(encryptedVaultState) {
|
|
438
456
|
if (this.activeAddDeviceFlow?.state !== 'responder:syncKeyCreated') {
|
|
439
457
|
throw new SyncInWrongStateError(`Expected responder:syncKeyCreated, got ${this.activeAddDeviceFlow?.state}`);
|
|
440
458
|
}
|
|
441
|
-
|
|
442
|
-
// Decrypt the received public key
|
|
443
|
-
const decryptedPublicKey = await this.cryptoLib.decryptSymmetric(syncKey, encryptedPublicKey);
|
|
444
|
-
const vaultState = JSON.parse(await this.cryptoLib.decryptSymmetric(syncKey, encryptedVaultState));
|
|
445
|
-
if (vaultState.deviceId !== this.activeAddDeviceFlow.initiatorDeviceId) {
|
|
446
|
-
throw new SyncError(`DeviceId mismatch when importing, expected ${this.activeAddDeviceFlow.initiatorDeviceId} got ${vaultState.deviceId}`);
|
|
447
|
-
}
|
|
448
|
-
this.syncDevices = vaultState.sync.devices;
|
|
449
|
-
this.mediator
|
|
450
|
-
.getComponent('vaultDataManager')
|
|
451
|
-
.replaceVault(vaultState.vault);
|
|
452
|
-
// Update the sync devices list with the initiator's information
|
|
453
|
-
// Not done as a command as all other devices already have the senders info
|
|
454
|
-
await this.addSyncDevice({
|
|
455
|
-
deviceId: this.activeAddDeviceFlow.initiatorDeviceId,
|
|
456
|
-
deviceType: this.activeAddDeviceFlow.initiatorDeviceType,
|
|
457
|
-
publicKey: decryptedPublicKey,
|
|
458
|
-
});
|
|
459
|
+
await this.importVaultState(encryptedVaultState, this.activeAddDeviceFlow.syncKey, this.activeAddDeviceFlow.initiatorDeviceId);
|
|
459
460
|
// Reset the active add device flow
|
|
460
461
|
this.activeAddDeviceFlow = undefined;
|
|
461
462
|
this.dispatchLibEvent(TwoFaLibEvent.ConnectToExistingVaultFinished);
|
|
462
463
|
}
|
|
464
|
+
async importVaultState(encryptedVaultState, symmetricKey, expectedDeviceId) {
|
|
465
|
+
const vaultState = JSON.parse(await this.cryptoLib.decryptSymmetric(symmetricKey, encryptedVaultState));
|
|
466
|
+
if (vaultState.deviceId !== expectedDeviceId) {
|
|
467
|
+
throw new SyncError(`DeviceId mismatch when importing, expected ${expectedDeviceId} got ${vaultState.deviceId}`);
|
|
468
|
+
}
|
|
469
|
+
for (const device of vaultState.sync.devices) {
|
|
470
|
+
await this.addSyncDevice(device, false);
|
|
471
|
+
}
|
|
472
|
+
const vaultDataManager = this.mediator.getComponent('vaultDataManager');
|
|
473
|
+
for (const entry of vaultState.vault) {
|
|
474
|
+
await vaultDataManager.addEntry(entry, false);
|
|
475
|
+
}
|
|
476
|
+
await this.persistentStorageManager.save();
|
|
477
|
+
}
|
|
463
478
|
/**
|
|
464
479
|
* Cancels the active add sync device flow.
|
|
465
480
|
* @throws {SyncNoServerConnectionError} If there is no server connection.
|
|
@@ -487,6 +502,10 @@ class SyncManager {
|
|
|
487
502
|
async sendCommand(command) {
|
|
488
503
|
const commandJson = command.toJSON();
|
|
489
504
|
await Promise.all(this.syncDevices.map(async (device) => {
|
|
505
|
+
if (device.deviceId === this.deviceId) {
|
|
506
|
+
// skip ourselves
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
490
509
|
const symmetricKey = await this.cryptoLib.createSymmetricKey();
|
|
491
510
|
const encryptedSymmetricKey = await this.cryptoLib.encrypt(device.publicKey, symmetricKey);
|
|
492
511
|
const encryptedCommand = await this.cryptoLib.encryptSymmetric(symmetricKey, JSON.stringify({
|
|
@@ -558,20 +577,50 @@ class SyncManager {
|
|
|
558
577
|
});
|
|
559
578
|
}
|
|
560
579
|
}
|
|
580
|
+
/**
|
|
581
|
+
* Sends vault data to the server for each sync device
|
|
582
|
+
*/
|
|
583
|
+
async resilver() {
|
|
584
|
+
for (const device of this.syncDevices) {
|
|
585
|
+
if (device.deviceId === this.deviceId) {
|
|
586
|
+
continue;
|
|
587
|
+
}
|
|
588
|
+
const symmetricKey = await this.cryptoLib.createSymmetricKey();
|
|
589
|
+
const encryptedSymmetricKey = await this.cryptoLib.encrypt(device.publicKey, symmetricKey);
|
|
590
|
+
const encryptedVaultData = await this.persistentStorageManager.getEncryptedVaultState(symmetricKey);
|
|
591
|
+
this.sendToServer('vault', {
|
|
592
|
+
forDeviceId: device.deviceId,
|
|
593
|
+
nonce: await this.getNonce(),
|
|
594
|
+
encryptedVaultData,
|
|
595
|
+
encryptedSymmetricKey,
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
}
|
|
561
599
|
/**
|
|
562
600
|
* Add a sync device
|
|
563
601
|
* @param deviceInfo - The info about the device
|
|
602
|
+
* @param saveAfter - Whether to save the new vault after adding it (set to false when adding multiple devices)
|
|
564
603
|
*/
|
|
565
|
-
async addSyncDevice(deviceInfo) {
|
|
566
|
-
if (
|
|
567
|
-
//
|
|
568
|
-
// besides the sender after the device add flow. We don't want to
|
|
569
|
-
// add ourselves to our syncDevices
|
|
604
|
+
async addSyncDevice(deviceInfo, saveAfter = true) {
|
|
605
|
+
if (this.syncDevices.some((d) => d.deviceId === deviceInfo.deviceId)) {
|
|
606
|
+
// we already have this device
|
|
570
607
|
return;
|
|
571
608
|
}
|
|
572
609
|
this.log('info', `Adding syncdevice ${deviceInfo.deviceId} to ${this.deviceId}`);
|
|
573
|
-
this.syncDevices.push(
|
|
574
|
-
|
|
610
|
+
this.syncDevices.push({
|
|
611
|
+
...deviceInfo,
|
|
612
|
+
});
|
|
613
|
+
if (saveAfter) {
|
|
614
|
+
await this.persistentStorageManager.save();
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
/**
|
|
618
|
+
* Requests a resilver of the vault
|
|
619
|
+
*/
|
|
620
|
+
requestResilver() {
|
|
621
|
+
this.sendToServer('startResilver', {
|
|
622
|
+
deviceIds: this.syncDevices.map((d) => d.deviceId),
|
|
623
|
+
});
|
|
575
624
|
}
|
|
576
625
|
/**
|
|
577
626
|
* Function to call when the server connection should be closed
|
|
@@ -586,9 +635,6 @@ class SyncManager {
|
|
|
586
635
|
const ws = this.ws;
|
|
587
636
|
this.ws = undefined;
|
|
588
637
|
ws.close();
|
|
589
|
-
// force terminate the connection after 2 seconds
|
|
590
|
-
// ws.terminate is not defined in the test enviroment, which is the reason for the &&
|
|
591
|
-
this.terminateTimeout = setTimeout(() => ws.terminate && ws.terminate(), 2000);
|
|
592
638
|
}
|
|
593
639
|
}
|
|
594
640
|
}
|
|
@@ -42,10 +42,11 @@ declare class VaultDataManager {
|
|
|
42
42
|
generateTokenForEntry(id: EntryId, timestamp?: number): Token;
|
|
43
43
|
/**
|
|
44
44
|
* Add a new entry to the vault.
|
|
45
|
-
* @param entry - The entry data to add
|
|
45
|
+
* @param entry - The entry data to add
|
|
46
|
+
* @param saveAfter - Whether to save the new vault after adding it (set to false when adding multiple entries)
|
|
46
47
|
* @returns A promise that resolves when the entry is added.
|
|
47
48
|
*/
|
|
48
|
-
addEntry(entry: Entry): Promise<void>;
|
|
49
|
+
addEntry(entry: Entry, saveAfter?: boolean): Promise<void>;
|
|
49
50
|
/**
|
|
50
51
|
* Delete an entry from the vault.
|
|
51
52
|
* @param entryId - The identifier of the entry to delete.
|
|
@@ -70,12 +70,19 @@ class VaultDataManager {
|
|
|
70
70
|
}
|
|
71
71
|
/**
|
|
72
72
|
* Add a new entry to the vault.
|
|
73
|
-
* @param entry - The entry data to add
|
|
73
|
+
* @param entry - The entry data to add
|
|
74
|
+
* @param saveAfter - Whether to save the new vault after adding it (set to false when adding multiple entries)
|
|
74
75
|
* @returns A promise that resolves when the entry is added.
|
|
75
76
|
*/
|
|
76
|
-
async addEntry(entry) {
|
|
77
|
+
async addEntry(entry, saveAfter = true) {
|
|
78
|
+
if (this.vault.find((e) => e.id === entry.id)) {
|
|
79
|
+
// We already have this entry
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
77
82
|
this.vault.push(entry);
|
|
78
|
-
|
|
83
|
+
if (saveAfter) {
|
|
84
|
+
await this.persistentStorageManager.save();
|
|
85
|
+
}
|
|
79
86
|
}
|
|
80
87
|
/**
|
|
81
88
|
* Delete an entry from the vault.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "favalib",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -27,31 +27,31 @@
|
|
|
27
27
|
"@types/node-forge": "^1.3.11",
|
|
28
28
|
"@types/qrcode": "^1.5.5",
|
|
29
29
|
"@types/uuid": "^10.0.0",
|
|
30
|
-
"@types/whatwg-url": "^
|
|
31
|
-
"@vitest/coverage-v8": "^
|
|
32
|
-
"eslint": "^9.
|
|
33
|
-
"type-fest": "^4.
|
|
34
|
-
"vitest": "^
|
|
30
|
+
"@types/whatwg-url": "^13.0.0",
|
|
31
|
+
"@vitest/coverage-v8": "^3.1.1",
|
|
32
|
+
"eslint": "^9.24.0",
|
|
33
|
+
"type-fest": "^4.39.1",
|
|
34
|
+
"vitest": "^3.1.1",
|
|
35
|
+
"vitest-websocket-mock": "^0.5.0"
|
|
35
36
|
},
|
|
36
37
|
"dependencies": {
|
|
37
38
|
"@zxcvbn-ts/core": "^3.0.4",
|
|
38
39
|
"@zxcvbn-ts/language-common": "^3.0.4",
|
|
39
40
|
"@zxcvbn-ts/language-en": "^3.0.2",
|
|
40
|
-
"canvas": "^
|
|
41
|
-
"hash-wasm": "^4.
|
|
42
|
-
"isomorphic-ws": "^5.0.0",
|
|
41
|
+
"canvas": "^3.1.0",
|
|
42
|
+
"hash-wasm": "^4.12.0",
|
|
43
43
|
"jpake-ts": "^1.0.1",
|
|
44
44
|
"jsqr": "^1.4.0",
|
|
45
45
|
"node-forge": "^1.3.1",
|
|
46
|
-
"openpgp": "^
|
|
46
|
+
"openpgp": "^6.1.0",
|
|
47
47
|
"qrcode": "^1.5.4",
|
|
48
48
|
"totp-generator": "^1.0.0",
|
|
49
49
|
"typescript-event-target": "^1.1.1",
|
|
50
50
|
"uint8array-extras": "^1.4.0",
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"whatwg-url": "^14.
|
|
54
|
-
"ws": "^8.18.
|
|
51
|
+
"unws": "^0.3.1",
|
|
52
|
+
"uuid": "^11.1.0",
|
|
53
|
+
"whatwg-url": "^14.2.0",
|
|
54
|
+
"ws": "^8.18.1"
|
|
55
55
|
},
|
|
56
56
|
"engines": {
|
|
57
57
|
"node": ">=20"
|