@dxos/echo-db 2.33.2 → 2.33.3-dev.08e106c3
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/dist/src/echo.js +2 -2
- package/dist/src/echo.js.map +1 -1
- package/dist/src/echo.test.js +10 -10
- package/dist/src/echo.test.js.map +1 -1
- package/dist/src/halo/halo-party.d.ts.map +1 -1
- package/dist/src/halo/halo-party.js +1 -1
- package/dist/src/halo/halo-party.js.map +1 -1
- package/dist/src/halo/halo.d.ts +1 -0
- package/dist/src/halo/halo.d.ts.map +1 -1
- package/dist/src/halo/halo.js +10 -1
- package/dist/src/halo/halo.js.map +1 -1
- package/dist/src/halo/identity-manager.d.ts +0 -1
- package/dist/src/halo/identity-manager.d.ts.map +1 -1
- package/dist/src/halo/identity-manager.js +11 -11
- package/dist/src/halo/identity-manager.js.map +1 -1
- package/dist/src/halo/identity.d.ts +18 -13
- package/dist/src/halo/identity.d.ts.map +1 -1
- package/dist/src/halo/identity.js +24 -27
- package/dist/src/halo/identity.js.map +1 -1
- package/dist/src/invitations/offline-invitation-claimer.d.ts +2 -2
- package/dist/src/invitations/offline-invitation-claimer.d.ts.map +1 -1
- package/dist/src/invitations/offline-invitation-claimer.js +2 -4
- package/dist/src/invitations/offline-invitation-claimer.js.map +1 -1
- package/dist/src/parties/data-party.d.ts.map +1 -1
- package/dist/src/parties/data-party.js +1 -1
- package/dist/src/parties/data-party.js.map +1 -1
- package/dist/src/parties/data-party.test.d.ts +2 -0
- package/dist/src/parties/data-party.test.d.ts.map +1 -0
- package/dist/src/parties/data-party.test.js +129 -0
- package/dist/src/parties/data-party.test.js.map +1 -0
- package/dist/src/parties/party-factory.d.ts +2 -2
- package/dist/src/parties/party-factory.d.ts.map +1 -1
- package/dist/src/parties/party-factory.js +12 -20
- package/dist/src/parties/party-factory.js.map +1 -1
- package/dist/src/parties/party-manager.d.ts +2 -2
- package/dist/src/parties/party-manager.d.ts.map +1 -1
- package/dist/src/parties/party-manager.js +2 -1
- package/dist/src/parties/party-manager.js.map +1 -1
- package/dist/src/parties/party-manager.test.js +7 -22
- package/dist/src/parties/party-manager.test.js.map +1 -1
- package/dist/src/pipeline/metadata-store.d.ts.map +1 -1
- package/dist/src/pipeline/metadata-store.js +5 -6
- package/dist/src/pipeline/metadata-store.js.map +1 -1
- package/dist/src/pipeline/party-core.test.js +1 -1
- package/dist/src/pipeline/party-core.test.js.map +1 -1
- package/dist/src/pipeline/party-processor.d.ts +0 -5
- package/dist/src/pipeline/party-processor.d.ts.map +1 -1
- package/dist/src/pipeline/party-processor.js +0 -7
- package/dist/src/pipeline/party-processor.js.map +1 -1
- package/dist/src/protocol/credentials-signer.d.ts +4 -4
- package/dist/src/protocol/credentials-signer.d.ts.map +1 -1
- package/dist/src/protocol/credentials-signer.js +8 -8
- package/dist/src/protocol/credentials-signer.js.map +1 -1
- package/dist/src/protocol/identity-credentials.d.ts +22 -0
- package/dist/src/protocol/identity-credentials.d.ts.map +1 -0
- package/dist/src/protocol/identity-credentials.js +50 -0
- package/dist/src/protocol/identity-credentials.js.map +1 -0
- package/dist/src/protocol/party-protocol-factory.d.ts +3 -4
- package/dist/src/protocol/party-protocol-factory.d.ts.map +1 -1
- package/dist/src/protocol/party-protocol-factory.js +12 -15
- package/dist/src/protocol/party-protocol-factory.js.map +1 -1
- package/dist/src/snapshots/snapshot-store.d.ts.map +1 -1
- package/dist/src/snapshots/snapshot-store.js +5 -6
- package/dist/src/snapshots/snapshot-store.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +17 -17
- package/src/echo.test.ts +10 -10
- package/src/echo.ts +1 -1
- package/src/halo/halo-party.ts +1 -2
- package/src/halo/halo.ts +8 -1
- package/src/halo/identity-manager.ts +13 -14
- package/src/halo/identity.ts +39 -41
- package/src/invitations/offline-invitation-claimer.ts +6 -7
- package/src/parties/data-party.test.ts +212 -0
- package/src/parties/data-party.ts +1 -2
- package/src/parties/party-factory.ts +14 -20
- package/src/parties/party-manager.test.ts +8 -42
- package/src/parties/party-manager.ts +4 -3
- package/src/pipeline/metadata-store.ts +5 -6
- package/src/pipeline/party-core.test.ts +2 -4
- package/src/pipeline/party-processor.ts +0 -13
- package/src/protocol/credentials-signer.ts +9 -9
- package/src/protocol/identity-credentials.ts +78 -0
- package/src/protocol/party-protocol-factory.ts +13 -17
- package/src/snapshots/snapshot-store.ts +5 -6
package/src/halo/halo.ts
CHANGED
|
@@ -46,6 +46,8 @@ export class HALO {
|
|
|
46
46
|
private readonly _keyring: Keyring;
|
|
47
47
|
private readonly _identityManager: IdentityManager;
|
|
48
48
|
|
|
49
|
+
private _isOpen = false;
|
|
50
|
+
|
|
49
51
|
constructor ({
|
|
50
52
|
keyring,
|
|
51
53
|
networkManager,
|
|
@@ -85,7 +87,7 @@ export class HALO {
|
|
|
85
87
|
* Whether the current identity manager has been initialized.
|
|
86
88
|
*/
|
|
87
89
|
get isInitialized (): boolean {
|
|
88
|
-
return this.
|
|
90
|
+
return this._isOpen;
|
|
89
91
|
}
|
|
90
92
|
|
|
91
93
|
/**
|
|
@@ -129,6 +131,7 @@ export class HALO {
|
|
|
129
131
|
*
|
|
130
132
|
* Loads the saved identity from disk. Is called by client.
|
|
131
133
|
*/
|
|
134
|
+
@synchronized
|
|
132
135
|
async open (onProgressCallback?: ((progress: OpenProgress) => void) | undefined) {
|
|
133
136
|
// TODO(burdon): Replace with events.
|
|
134
137
|
onProgressCallback?.({ haloOpened: false });
|
|
@@ -137,12 +140,16 @@ export class HALO {
|
|
|
137
140
|
await this._identityManager.loadFromStorage();
|
|
138
141
|
|
|
139
142
|
onProgressCallback?.({ haloOpened: true });
|
|
143
|
+
|
|
144
|
+
this._isOpen = true;
|
|
140
145
|
}
|
|
141
146
|
|
|
142
147
|
/**
|
|
143
148
|
* Closes HALO. Automatically called when client is destroyed.
|
|
144
149
|
*/
|
|
150
|
+
@synchronized
|
|
145
151
|
async close () {
|
|
152
|
+
this._isOpen = false;
|
|
146
153
|
await this._identityManager.close();
|
|
147
154
|
}
|
|
148
155
|
|
|
@@ -7,6 +7,7 @@ import debug from 'debug';
|
|
|
7
7
|
|
|
8
8
|
import { Event, synchronized, waitForCondition } from '@dxos/async';
|
|
9
9
|
import { Filter, KeyRecord, Keyring, KeyType, SecretProvider } from '@dxos/credentials';
|
|
10
|
+
import { failUndefined } from '@dxos/debug';
|
|
10
11
|
|
|
11
12
|
import { InvitationDescriptor } from '../invitations';
|
|
12
13
|
import { MetadataStore } from '../pipeline';
|
|
@@ -34,25 +35,23 @@ export class IdentityManager {
|
|
|
34
35
|
return this._identity;
|
|
35
36
|
}
|
|
36
37
|
|
|
37
|
-
get initialized (): boolean {
|
|
38
|
-
return this._identity !== undefined &&
|
|
39
|
-
!!this._identity.halo &&
|
|
40
|
-
this._identity.halo.isOpen &&
|
|
41
|
-
!!this._identity.halo!.memberKeys.length &&
|
|
42
|
-
!!this._identity.halo!.identityGenesis &&
|
|
43
|
-
!!this._identity.deviceKeyChain;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
38
|
private async _initialize (halo: HaloParty) {
|
|
47
39
|
assert(halo.isOpen, 'HALO must be open.');
|
|
48
40
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
//
|
|
52
|
-
|
|
41
|
+
// Wait for the minimum set of keys and messages we need for proper function:
|
|
42
|
+
//
|
|
43
|
+
// - KeyAdmit message for the current device so we can build the device KeyChain.
|
|
44
|
+
// - Identity genesis so it can be copied into newly joined parties.
|
|
45
|
+
//
|
|
46
|
+
const deviceKey = this._keyring.findKey(Keyring.signingFilter({ type: KeyType.DEVICE })) ?? failUndefined();
|
|
47
|
+
await waitForCondition(() =>
|
|
48
|
+
halo.processor.isMemberKey(deviceKey.publicKey) &&
|
|
49
|
+
halo.identityGenesis
|
|
50
|
+
);
|
|
53
51
|
|
|
54
|
-
|
|
52
|
+
this._identity = new Identity(this._keyring, halo);
|
|
55
53
|
this.ready.emit();
|
|
54
|
+
log('HALO initialized.');
|
|
56
55
|
}
|
|
57
56
|
|
|
58
57
|
async close () {
|
package/src/halo/identity.ts
CHANGED
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
// Copyright 2021 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import assert from 'assert';
|
|
5
6
|
import debug from 'debug';
|
|
6
7
|
|
|
7
|
-
import { Filter, KeyChain, KeyRecord, Keyring, KeyType, Signer } from '@dxos/credentials';
|
|
8
|
-
import {
|
|
8
|
+
import { Filter, KeyChain, KeyRecord, Keyring, KeyType, SignedMessage, Signer } from '@dxos/credentials';
|
|
9
|
+
import { failUndefined } from '@dxos/debug';
|
|
9
10
|
|
|
10
|
-
import { IdentityNotInitializedError } from '../errors';
|
|
11
11
|
import { CredentialsSigner } from '../protocol/credentials-signer';
|
|
12
|
+
import { IdentityCredentials } from '../protocol/identity-credentials';
|
|
12
13
|
import { ContactManager } from './contact-manager';
|
|
13
14
|
import { HaloParty } from './halo-party';
|
|
14
15
|
import { Preferences } from './preferences';
|
|
@@ -20,10 +21,10 @@ const log = debug('dxos:echo-db:identity');
|
|
|
20
21
|
*
|
|
21
22
|
* Acts as a read-only view into IdentityManager.
|
|
22
23
|
*/
|
|
23
|
-
export class Identity {
|
|
24
|
-
private _identityKey
|
|
25
|
-
private _deviceKey
|
|
26
|
-
private _deviceKeyChain
|
|
24
|
+
export class Identity implements IdentityCredentials {
|
|
25
|
+
private readonly _identityKey: KeyRecord;
|
|
26
|
+
private readonly _deviceKey: KeyRecord;
|
|
27
|
+
private readonly _deviceKeyChain: KeyChain;
|
|
27
28
|
|
|
28
29
|
/**
|
|
29
30
|
* @param _halo HALO party. Must be open.
|
|
@@ -31,54 +32,55 @@ export class Identity {
|
|
|
31
32
|
constructor (
|
|
32
33
|
private readonly _keyring: Keyring,
|
|
33
34
|
private readonly _halo: HaloParty
|
|
34
|
-
) {
|
|
35
|
+
) {
|
|
36
|
+
this._identityKey = this._keyring.findKey(Filter.matches({ type: KeyType.IDENTITY, own: true, trusted: true })) ?? failUndefined();
|
|
37
|
+
this._deviceKey = this._keyring.findKey(Keyring.signingFilter({ type: KeyType.DEVICE })) ?? failUndefined();
|
|
38
|
+
this._deviceKeyChain = getDeviceKeyChainFromHalo(this._halo, this.deviceKey);
|
|
39
|
+
assert(this._halo.identityGenesis);
|
|
40
|
+
}
|
|
35
41
|
|
|
36
42
|
get signer (): Signer {
|
|
37
43
|
return this._keyring;
|
|
38
44
|
}
|
|
39
45
|
|
|
40
|
-
get
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
46
|
+
get keyring (): Keyring {
|
|
47
|
+
return this._keyring;
|
|
48
|
+
}
|
|
44
49
|
|
|
50
|
+
get identityKey (): KeyRecord {
|
|
45
51
|
return this._identityKey;
|
|
46
52
|
}
|
|
47
53
|
|
|
48
|
-
get deviceKey (): KeyRecord
|
|
49
|
-
if (!this._deviceKey) {
|
|
50
|
-
this._deviceKey = this._keyring.findKey(Keyring.signingFilter({ type: KeyType.DEVICE }));
|
|
51
|
-
}
|
|
52
|
-
|
|
54
|
+
get deviceKey (): KeyRecord {
|
|
53
55
|
return this._deviceKey;
|
|
54
56
|
}
|
|
55
57
|
|
|
56
|
-
get deviceKeyChain (): KeyChain
|
|
57
|
-
if (!this._deviceKeyChain) {
|
|
58
|
-
this._deviceKeyChain = this.deviceKey && this._halo ? getDeviceKeyChainFromHalo(this._halo, this.deviceKey) : undefined;
|
|
59
|
-
}
|
|
60
|
-
|
|
58
|
+
get deviceKeyChain (): KeyChain {
|
|
61
59
|
return this._deviceKeyChain;
|
|
62
60
|
}
|
|
63
61
|
|
|
64
|
-
get
|
|
65
|
-
return this.
|
|
62
|
+
get displayName (): string | undefined {
|
|
63
|
+
return this.identityInfo?.signed.payload.displayName;
|
|
66
64
|
}
|
|
67
65
|
|
|
68
|
-
|
|
69
|
-
|
|
66
|
+
/**
|
|
67
|
+
* Contains profile username.
|
|
68
|
+
* Can be missing if the username wasn't provided when profile was created.
|
|
69
|
+
*/
|
|
70
|
+
get identityInfo (): SignedMessage | undefined {
|
|
71
|
+
return this._halo.identityInfo;
|
|
70
72
|
}
|
|
71
73
|
|
|
72
|
-
get
|
|
73
|
-
return this.
|
|
74
|
+
get identityGenesis (): SignedMessage {
|
|
75
|
+
return this._halo.identityGenesis ?? failUndefined();
|
|
74
76
|
}
|
|
75
77
|
|
|
76
|
-
get
|
|
77
|
-
return this._halo
|
|
78
|
+
get preferences (): Preferences {
|
|
79
|
+
return this._halo.preferences;
|
|
78
80
|
}
|
|
79
81
|
|
|
80
|
-
get
|
|
81
|
-
return this._halo
|
|
82
|
+
get contacts (): ContactManager {
|
|
83
|
+
return this._halo.contacts;
|
|
82
84
|
}
|
|
83
85
|
|
|
84
86
|
/**
|
|
@@ -88,16 +90,12 @@ export class Identity {
|
|
|
88
90
|
return this._halo;
|
|
89
91
|
}
|
|
90
92
|
|
|
91
|
-
get keyring () {
|
|
92
|
-
return this._keyring;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
93
|
createCredentialsSigner (): CredentialsSigner {
|
|
96
94
|
return new CredentialsSigner(
|
|
97
95
|
this._keyring,
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
96
|
+
this.identityKey,
|
|
97
|
+
this.deviceKey,
|
|
98
|
+
this.deviceKeyChain
|
|
101
99
|
);
|
|
102
100
|
}
|
|
103
101
|
}
|
|
@@ -112,7 +110,7 @@ function getDeviceKeyChainFromHalo (halo: HaloParty, deviceKey: KeyRecord) {
|
|
|
112
110
|
halo.feedKeys
|
|
113
111
|
);
|
|
114
112
|
} catch (err: any) {
|
|
115
|
-
log('Unable to locate device KeyChain:', err);
|
|
116
|
-
|
|
113
|
+
log('Unable to locate device KeyChain:', err);
|
|
114
|
+
throw err;
|
|
117
115
|
}
|
|
118
116
|
}
|
|
@@ -21,11 +21,10 @@ import {
|
|
|
21
21
|
codec
|
|
22
22
|
} from '@dxos/credentials';
|
|
23
23
|
import { keyToBuffer, keyToString, PublicKey, randomBytes } from '@dxos/crypto';
|
|
24
|
-
import { raise } from '@dxos/debug';
|
|
25
24
|
import { FullyConnectedTopology, NetworkManager } from '@dxos/network-manager';
|
|
26
25
|
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
26
|
+
import { InvalidInvitationError } from '../errors';
|
|
27
|
+
import { CredentialsSigner } from '../protocol';
|
|
29
28
|
import { greetingProtocolProvider } from './greeting-protocol-provider';
|
|
30
29
|
import { GreetingState } from './greeting-responder';
|
|
31
30
|
import { InvitationDescriptor, InvitationDescriptorType } from './invitation-descriptor';
|
|
@@ -170,17 +169,17 @@ export class OfflineInvitationClaimer {
|
|
|
170
169
|
}
|
|
171
170
|
|
|
172
171
|
// The secretProvider should provide an `Auth` message signed directly by the Identity key.
|
|
173
|
-
static createSecretProvider (
|
|
172
|
+
static createSecretProvider (credentials: CredentialsSigner): SecretProvider {
|
|
174
173
|
return async (info?: SecretInfo) => {
|
|
175
174
|
return Buffer.from(codec.encode(
|
|
176
175
|
/* The signed portion of the Auth message includes the ID and authNonce provided
|
|
177
176
|
* by the `info` object. These values will be validated on the other end.
|
|
178
177
|
*/
|
|
179
178
|
createAuthMessage(
|
|
180
|
-
|
|
179
|
+
credentials.signer,
|
|
181
180
|
info!.id.value,
|
|
182
|
-
|
|
183
|
-
|
|
181
|
+
credentials.getIdentityKey(),
|
|
182
|
+
credentials.getDeviceSigningKeys(),
|
|
184
183
|
undefined,
|
|
185
184
|
info!.authNonce.value)
|
|
186
185
|
));
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2022 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import expect from 'expect';
|
|
6
|
+
import { it as test } from 'mocha';
|
|
7
|
+
|
|
8
|
+
import { createKeyAdmitMessage, createPartyGenesisMessage, defaultSecretProvider, KeyHint, Keyring, KeyType, codec as haloCodec } from '@dxos/credentials';
|
|
9
|
+
import { PublicKey } from '@dxos/crypto';
|
|
10
|
+
import { codec } from '@dxos/echo-protocol';
|
|
11
|
+
import { FeedStore } from '@dxos/feed-store';
|
|
12
|
+
import { ModelFactory } from '@dxos/model-factory';
|
|
13
|
+
import { NetworkManager } from '@dxos/network-manager';
|
|
14
|
+
import { ObjectModel } from '@dxos/object-model';
|
|
15
|
+
import { createStorage, StorageType } from '@dxos/random-access-multi-storage';
|
|
16
|
+
|
|
17
|
+
import { createDataPartyAdmissionMessages, defaultInvitationAuthenticator, GreetingInitiator } from '../invitations';
|
|
18
|
+
import { MetadataStore, PartyFeedProvider } from '../pipeline';
|
|
19
|
+
import { createAuthenticator, createCredentialsProvider } from '../protocol';
|
|
20
|
+
import { createTestIdentityCredentials, deriveTestDeviceCredentials, IdentityCredentials } from '../protocol/identity-credentials';
|
|
21
|
+
import { SnapshotStore } from '../snapshots';
|
|
22
|
+
import { createRamStorage } from '../util';
|
|
23
|
+
import { DataParty } from './data-party';
|
|
24
|
+
|
|
25
|
+
describe('DataParty', () => {
|
|
26
|
+
const createParty = async (identity: IdentityCredentials, partyKey: PublicKey, hints: KeyHint[]) => {
|
|
27
|
+
const metadataStore = new MetadataStore(createRamStorage());
|
|
28
|
+
const feedStore = new FeedStore(createStorage('', StorageType.RAM), { valueEncoding: codec });
|
|
29
|
+
const snapshotStore = new SnapshotStore(createStorage('', StorageType.RAM));
|
|
30
|
+
const modelFactory = new ModelFactory().registerModel(ObjectModel);
|
|
31
|
+
const networkManager = new NetworkManager();
|
|
32
|
+
const partyFeedProvider = new PartyFeedProvider(metadataStore, identity.keyring, feedStore, partyKey);
|
|
33
|
+
|
|
34
|
+
return new DataParty(
|
|
35
|
+
partyKey,
|
|
36
|
+
modelFactory,
|
|
37
|
+
snapshotStore,
|
|
38
|
+
partyFeedProvider,
|
|
39
|
+
identity.createCredentialsSigner(),
|
|
40
|
+
identity.preferences,
|
|
41
|
+
networkManager,
|
|
42
|
+
hints
|
|
43
|
+
);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
test('open & close', async () => {
|
|
47
|
+
const keyring = new Keyring();
|
|
48
|
+
const identity = await createTestIdentityCredentials(keyring);
|
|
49
|
+
const partyKey = await keyring.createKeyRecord({ type: KeyType.PARTY });
|
|
50
|
+
const party = await createParty(identity, partyKey.publicKey, []);
|
|
51
|
+
|
|
52
|
+
await party.open();
|
|
53
|
+
await party.close();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('edit data', async () => {
|
|
57
|
+
const keyring = new Keyring();
|
|
58
|
+
const identity = await createTestIdentityCredentials(keyring);
|
|
59
|
+
const partyKey = await keyring.createKeyRecord({ type: KeyType.PARTY });
|
|
60
|
+
const party = await createParty(identity, partyKey.publicKey, []);
|
|
61
|
+
await party.open();
|
|
62
|
+
|
|
63
|
+
const feed = await party.feedProvider.createOrOpenWritableFeed();
|
|
64
|
+
await party.processor.writeHaloMessage(createPartyGenesisMessage(
|
|
65
|
+
keyring,
|
|
66
|
+
partyKey,
|
|
67
|
+
feed.key,
|
|
68
|
+
partyKey
|
|
69
|
+
));
|
|
70
|
+
|
|
71
|
+
await party.database.createItem({ type: 'test:item' });
|
|
72
|
+
|
|
73
|
+
await party.close();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('authenticates its own credentials', async () => {
|
|
77
|
+
const keyring = new Keyring();
|
|
78
|
+
const identity = await createTestIdentityCredentials(keyring);
|
|
79
|
+
const partyKey = await keyring.createKeyRecord({ type: KeyType.PARTY });
|
|
80
|
+
|
|
81
|
+
const party = await createParty(identity, partyKey.publicKey, []);
|
|
82
|
+
await party.open();
|
|
83
|
+
const feed = await party.feedProvider.createOrOpenWritableFeed();
|
|
84
|
+
await party.processor.writeHaloMessage(createPartyGenesisMessage(
|
|
85
|
+
keyring,
|
|
86
|
+
partyKey,
|
|
87
|
+
feed.key,
|
|
88
|
+
partyKey
|
|
89
|
+
));
|
|
90
|
+
|
|
91
|
+
const authenticator = createAuthenticator(party.processor, identity.createCredentialsSigner());
|
|
92
|
+
const credentialsProvider = createCredentialsProvider(identity.createCredentialsSigner(), party.key, feed.key);
|
|
93
|
+
|
|
94
|
+
const wrappedCredentials = haloCodec.decode(credentialsProvider.get());
|
|
95
|
+
expect(await authenticator.authenticate(wrappedCredentials.payload)).toEqual(true);
|
|
96
|
+
|
|
97
|
+
await party.close();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test('authenticates another device', async () => {
|
|
101
|
+
const keyring = new Keyring();
|
|
102
|
+
const identityA = await createTestIdentityCredentials(keyring);
|
|
103
|
+
const partyKey = await keyring.createKeyRecord({ type: KeyType.PARTY });
|
|
104
|
+
|
|
105
|
+
const party = await createParty(identityA, partyKey.publicKey, []);
|
|
106
|
+
await party.open();
|
|
107
|
+
const feed = await party.feedProvider.createOrOpenWritableFeed();
|
|
108
|
+
await party.processor.writeHaloMessage(createPartyGenesisMessage(
|
|
109
|
+
keyring,
|
|
110
|
+
partyKey,
|
|
111
|
+
feed.key,
|
|
112
|
+
partyKey
|
|
113
|
+
));
|
|
114
|
+
const authenticator = createAuthenticator(party.processor, identityA.createCredentialsSigner());
|
|
115
|
+
|
|
116
|
+
const identityB = await deriveTestDeviceCredentials(identityA);
|
|
117
|
+
const credentialsProvider = createCredentialsProvider(identityB.createCredentialsSigner(), party.key, feed.key);
|
|
118
|
+
|
|
119
|
+
const wrappedCredentials = haloCodec.decode(credentialsProvider.get());
|
|
120
|
+
expect(await authenticator.authenticate(wrappedCredentials.payload)).toEqual(true);
|
|
121
|
+
|
|
122
|
+
await party.close();
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test('two instances replicating', async () => {
|
|
126
|
+
const keyring = new Keyring();
|
|
127
|
+
const identityA = await createTestIdentityCredentials(keyring);
|
|
128
|
+
const partyKey = await keyring.createKeyRecord({ type: KeyType.PARTY });
|
|
129
|
+
|
|
130
|
+
const partyA = await createParty(identityA, partyKey.publicKey, []);
|
|
131
|
+
await partyA.open();
|
|
132
|
+
const feedA = await partyA.feedProvider.createOrOpenWritableFeed();
|
|
133
|
+
await partyA.processor.writeHaloMessage(createPartyGenesisMessage(
|
|
134
|
+
keyring,
|
|
135
|
+
partyKey,
|
|
136
|
+
feedA.key,
|
|
137
|
+
partyKey
|
|
138
|
+
));
|
|
139
|
+
await partyA.processor.writeHaloMessage(createKeyAdmitMessage(
|
|
140
|
+
keyring,
|
|
141
|
+
partyKey.publicKey,
|
|
142
|
+
identityA.identityKey,
|
|
143
|
+
[partyKey]
|
|
144
|
+
));
|
|
145
|
+
|
|
146
|
+
const identityB = await deriveTestDeviceCredentials(identityA);
|
|
147
|
+
const partyB = await createParty(identityB, partyKey.publicKey, [
|
|
148
|
+
{ type: KeyType.FEED, publicKey: feedA.key }
|
|
149
|
+
]);
|
|
150
|
+
await partyB.open();
|
|
151
|
+
|
|
152
|
+
await partyA.database.createItem({ type: 'test:item-a' });
|
|
153
|
+
await partyB.database.waitForItem({ type: 'test:item-a' });
|
|
154
|
+
|
|
155
|
+
await partyB.database.createItem({ type: 'test:item-b' });
|
|
156
|
+
await partyA.database.waitForItem({ type: 'test:item-b' });
|
|
157
|
+
|
|
158
|
+
await partyA.close();
|
|
159
|
+
await partyB.close();
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test('invitations', async () => {
|
|
163
|
+
const identityA = await createTestIdentityCredentials(new Keyring());
|
|
164
|
+
const partyKeyA = await identityA.keyring.createKeyRecord({ type: KeyType.PARTY });
|
|
165
|
+
|
|
166
|
+
const partyA = await createParty(identityA, partyKeyA.publicKey, []);
|
|
167
|
+
await partyA.open();
|
|
168
|
+
const feedA = await partyA.feedProvider.createOrOpenWritableFeed();
|
|
169
|
+
await partyA.processor.writeHaloMessage(createPartyGenesisMessage(
|
|
170
|
+
identityA.keyring,
|
|
171
|
+
partyKeyA,
|
|
172
|
+
feedA.key,
|
|
173
|
+
partyKeyA
|
|
174
|
+
));
|
|
175
|
+
await partyA.processor.writeHaloMessage(createKeyAdmitMessage(
|
|
176
|
+
identityA.keyring,
|
|
177
|
+
partyKeyA.publicKey,
|
|
178
|
+
identityA.identityKey,
|
|
179
|
+
[partyKeyA]
|
|
180
|
+
));
|
|
181
|
+
|
|
182
|
+
const invitation = await partyA.invitationManager.createInvitation(defaultInvitationAuthenticator);
|
|
183
|
+
|
|
184
|
+
const identityB = await createTestIdentityCredentials(new Keyring());
|
|
185
|
+
const initiator = new GreetingInitiator(
|
|
186
|
+
new NetworkManager(),
|
|
187
|
+
invitation,
|
|
188
|
+
async (partyKey, nonce) => [createDataPartyAdmissionMessages(
|
|
189
|
+
identityB.createCredentialsSigner(),
|
|
190
|
+
partyKey,
|
|
191
|
+
identityB.identityGenesis,
|
|
192
|
+
nonce
|
|
193
|
+
)]
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
await initiator.connect();
|
|
197
|
+
const { partyKey: partyKeyB, hints: hintsB } = await initiator.redeemInvitation(defaultSecretProvider);
|
|
198
|
+
expect(partyKeyB.equals(partyKeyA.publicKey));
|
|
199
|
+
const partyB = await createParty(identityB, partyKeyB, hintsB);
|
|
200
|
+
await partyB.open();
|
|
201
|
+
await initiator.destroy();
|
|
202
|
+
|
|
203
|
+
await partyA.database.createItem({ type: 'test:item-a' });
|
|
204
|
+
await partyB.database.waitForItem({ type: 'test:item-a' });
|
|
205
|
+
|
|
206
|
+
await partyB.database.createItem({ type: 'test:item-b' });
|
|
207
|
+
await partyA.database.waitForItem({ type: 'test:item-b' });
|
|
208
|
+
|
|
209
|
+
await partyA.close();
|
|
210
|
+
await partyB.close();
|
|
211
|
+
});
|
|
212
|
+
});
|
|
@@ -175,8 +175,7 @@ export class DataParty {
|
|
|
175
175
|
this._networkManager,
|
|
176
176
|
this._feedProvider,
|
|
177
177
|
deviceKey.publicKey,
|
|
178
|
-
createCredentialsProvider(this._credentialsSigner, this._partyCore.key, writeFeed.key)
|
|
179
|
-
this._partyCore.processor.getActiveFeedSet()
|
|
178
|
+
createCredentialsProvider(this._credentialsSigner, this._partyCore.key, writeFeed.key)
|
|
180
179
|
);
|
|
181
180
|
|
|
182
181
|
await this._protocol.start([
|
|
@@ -22,12 +22,12 @@ import { NetworkManager } from '@dxos/network-manager';
|
|
|
22
22
|
import { ObjectModel } from '@dxos/object-model';
|
|
23
23
|
|
|
24
24
|
import { IdentityNotInitializedError } from '../errors';
|
|
25
|
-
import { IdentityProvider } from '../halo';
|
|
26
25
|
import {
|
|
27
26
|
createDataPartyAdmissionMessages,
|
|
28
27
|
GreetingInitiator, InvitationDescriptor, InvitationDescriptorType, OfflineInvitationClaimer
|
|
29
28
|
} from '../invitations';
|
|
30
29
|
import { PartyFeedProvider, PartyOptions } from '../pipeline';
|
|
30
|
+
import { IdentityCredentialsProvider } from '../protocol/identity-credentials';
|
|
31
31
|
import { SnapshotStore } from '../snapshots';
|
|
32
32
|
import { DataParty, PARTY_ITEM_TYPE } from './data-party';
|
|
33
33
|
|
|
@@ -38,7 +38,7 @@ const log = debug('dxos:echo-db:party-factory');
|
|
|
38
38
|
*/
|
|
39
39
|
export class PartyFactory {
|
|
40
40
|
constructor (
|
|
41
|
-
private readonly _identityProvider:
|
|
41
|
+
private readonly _identityProvider: IdentityCredentialsProvider,
|
|
42
42
|
private readonly _networkManager: NetworkManager,
|
|
43
43
|
private readonly _modelFactory: ModelFactory,
|
|
44
44
|
private readonly _snapshotStore: SnapshotStore,
|
|
@@ -52,8 +52,6 @@ export class PartyFactory {
|
|
|
52
52
|
@timed(5_000)
|
|
53
53
|
async createParty (): Promise<DataParty> {
|
|
54
54
|
const identity = this._identityProvider() ?? raise(new IdentityNotInitializedError());
|
|
55
|
-
assert(identity.identityGenesis, 'HALO not initialized.');
|
|
56
|
-
assert(identity.deviceKeyChain, 'Device KeyChain not initialized.');
|
|
57
55
|
assert(!this._options.readOnly, 'PartyFactory is read-only.');
|
|
58
56
|
|
|
59
57
|
const partyKey = await identity.keyring.createKeyRecord({ type: KeyType.PARTY });
|
|
@@ -66,7 +64,7 @@ export class PartyFactory {
|
|
|
66
64
|
|
|
67
65
|
// PartyGenesis (self-signed by Party).
|
|
68
66
|
await party.processor.writeHaloMessage(createPartyGenesisMessage(
|
|
69
|
-
identity.
|
|
67
|
+
identity.keyring,
|
|
70
68
|
partyKey,
|
|
71
69
|
writableFeed.key,
|
|
72
70
|
partyKey)
|
|
@@ -74,15 +72,16 @@ export class PartyFactory {
|
|
|
74
72
|
|
|
75
73
|
// KeyAdmit (IdentityGenesis in an Envelope signed by Party).
|
|
76
74
|
await party.processor.writeHaloMessage(createEnvelopeMessage(
|
|
77
|
-
identity.
|
|
75
|
+
identity.keyring,
|
|
78
76
|
partyKey.publicKey,
|
|
79
77
|
wrapMessage(identity.identityGenesis),
|
|
80
78
|
[partyKey])
|
|
81
79
|
);
|
|
82
80
|
|
|
83
81
|
// FeedAdmit (signed by the Device KeyChain).
|
|
82
|
+
// TODO(dmaretskyi): Is this really needed since a feed is already admitted by party genesis message.
|
|
84
83
|
await party.processor.writeHaloMessage(createFeedAdmitMessage(
|
|
85
|
-
identity.
|
|
84
|
+
identity.keyring,
|
|
86
85
|
partyKey.publicKey,
|
|
87
86
|
writableFeed.key,
|
|
88
87
|
[identity.deviceKeyChain]
|
|
@@ -91,7 +90,7 @@ export class PartyFactory {
|
|
|
91
90
|
// IdentityInfo in an Envelope signed by the Device KeyChain.
|
|
92
91
|
if (identity.identityInfo) {
|
|
93
92
|
await party.processor.writeHaloMessage(createEnvelopeMessage(
|
|
94
|
-
identity.
|
|
93
|
+
identity.keyring,
|
|
95
94
|
partyKey.publicKey,
|
|
96
95
|
wrapMessage(identity.identityInfo),
|
|
97
96
|
[identity.deviceKeyChain]
|
|
@@ -138,13 +137,12 @@ export class PartyFactory {
|
|
|
138
137
|
);
|
|
139
138
|
|
|
140
139
|
await party.open();
|
|
141
|
-
assert(identity.identityKey, 'No identity key.');
|
|
142
140
|
const isHalo = identity.identityKey.publicKey.equals(partyKey);
|
|
143
141
|
const signingKey = isHalo ? identity.deviceKey : identity.deviceKeyChain;
|
|
144
142
|
assert(signingKey, 'No device key or keychain.');
|
|
145
143
|
// Write the Feed genesis message.
|
|
146
144
|
await party.processor.writeHaloMessage(createFeedAdmitMessage(
|
|
147
|
-
identity.
|
|
145
|
+
identity.keyring,
|
|
148
146
|
partyKey,
|
|
149
147
|
feedKeyPair.publicKey,
|
|
150
148
|
[signingKey]
|
|
@@ -213,7 +211,7 @@ export class PartyFactory {
|
|
|
213
211
|
async (partyKey, nonce) => [createDataPartyAdmissionMessages(
|
|
214
212
|
identity.createCredentialsSigner(),
|
|
215
213
|
partyKey,
|
|
216
|
-
identity.identityGenesis
|
|
214
|
+
identity.identityGenesis,
|
|
217
215
|
nonce
|
|
218
216
|
)]
|
|
219
217
|
);
|
|
@@ -223,13 +221,11 @@ export class PartyFactory {
|
|
|
223
221
|
const party = await this.addParty(partyKey, hints);
|
|
224
222
|
await initiator.destroy();
|
|
225
223
|
if (!haloInvitation) {
|
|
226
|
-
assert(identity.deviceKeyChain);
|
|
227
|
-
|
|
228
224
|
// Copy our signed IdentityInfo into the new Party.
|
|
229
225
|
const infoMessage = identity.identityInfo;
|
|
230
226
|
if (infoMessage) {
|
|
231
227
|
await party.processor.writeHaloMessage(createEnvelopeMessage(
|
|
232
|
-
identity.
|
|
228
|
+
identity.keyring,
|
|
233
229
|
partyKey,
|
|
234
230
|
wrapMessage(infoMessage),
|
|
235
231
|
[identity.deviceKeyChain]
|
|
@@ -244,8 +240,6 @@ export class PartyFactory {
|
|
|
244
240
|
const identity = this._identityProvider() ?? raise(new IdentityNotInitializedError());
|
|
245
241
|
|
|
246
242
|
assert(!this._options.readOnly, 'PartyFactory is read-only');
|
|
247
|
-
assert(identity.identityGenesis, 'IdentityGenesis must exist');
|
|
248
|
-
assert(identity.deviceKeyChain, 'Device KeyChain must exist');
|
|
249
243
|
|
|
250
244
|
const partyKey = await identity.keyring.createKeyRecord({ type: KeyType.PARTY });
|
|
251
245
|
const party = await this.constructParty(partyKey.publicKey);
|
|
@@ -257,7 +251,7 @@ export class PartyFactory {
|
|
|
257
251
|
|
|
258
252
|
// PartyGenesis (self-signed by Party).
|
|
259
253
|
await party.processor.writeHaloMessage(createPartyGenesisMessage(
|
|
260
|
-
identity.
|
|
254
|
+
identity.keyring,
|
|
261
255
|
partyKey,
|
|
262
256
|
writableFeed.key,
|
|
263
257
|
partyKey)
|
|
@@ -265,7 +259,7 @@ export class PartyFactory {
|
|
|
265
259
|
|
|
266
260
|
// KeyAdmit (IdentityGenesis in an Envelope signed by Party).
|
|
267
261
|
await party.processor.writeHaloMessage(createEnvelopeMessage(
|
|
268
|
-
identity.
|
|
262
|
+
identity.keyring,
|
|
269
263
|
partyKey.publicKey,
|
|
270
264
|
wrapMessage(identity.identityGenesis),
|
|
271
265
|
[partyKey]
|
|
@@ -273,7 +267,7 @@ export class PartyFactory {
|
|
|
273
267
|
|
|
274
268
|
// FeedAdmit (signed by the Device KeyChain).
|
|
275
269
|
await party.processor.writeHaloMessage(createFeedAdmitMessage(
|
|
276
|
-
identity.
|
|
270
|
+
identity.keyring,
|
|
277
271
|
partyKey.publicKey,
|
|
278
272
|
writableFeed.key,
|
|
279
273
|
[identity.deviceKeyChain]
|
|
@@ -282,7 +276,7 @@ export class PartyFactory {
|
|
|
282
276
|
// IdentityInfo in an Envelope signed by the Device KeyChain.
|
|
283
277
|
if (identity.identityInfo) {
|
|
284
278
|
await party.processor.writeHaloMessage(createEnvelopeMessage(
|
|
285
|
-
identity.
|
|
279
|
+
identity.keyring,
|
|
286
280
|
partyKey.publicKey,
|
|
287
281
|
wrapMessage(identity.identityInfo),
|
|
288
282
|
[identity.deviceKeyChain]
|