@dxos/client-services 0.6.12 → 0.6.13-main.548ca8d
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/lib/browser/{chunk-TOAILL4T.mjs → chunk-UEQIHAL2.mjs} +5838 -5151
- package/dist/lib/browser/chunk-UEQIHAL2.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +3 -3
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +8 -7
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/node/{chunk-H6C4XY6B.cjs → chunk-MA5EWTRH.cjs} +5858 -5175
- package/dist/lib/node/chunk-MA5EWTRH.cjs.map +7 -0
- package/dist/lib/node/index.cjs +46 -46
- package/dist/lib/node/index.cjs.map +3 -3
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/testing/index.cjs +14 -13
- package/dist/lib/node/testing/index.cjs.map +3 -3
- package/dist/lib/node-esm/chunk-AIBLDI4U.mjs +8403 -0
- package/dist/lib/node-esm/chunk-AIBLDI4U.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +416 -0
- package/dist/lib/node-esm/index.mjs.map +7 -0
- package/dist/lib/node-esm/meta.json +1 -0
- package/dist/lib/node-esm/testing/index.mjs +420 -0
- package/dist/lib/node-esm/testing/index.mjs.map +7 -0
- package/dist/types/src/packlets/diagnostics/diagnostics-broadcast.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/authenticator.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/authenticator.node.test.d.ts +2 -0
- package/dist/types/src/packlets/identity/authenticator.node.test.d.ts.map +1 -0
- package/dist/types/src/packlets/identity/contacts-service.d.ts +1 -1
- package/dist/types/src/packlets/identity/contacts-service.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity-manager.d.ts +19 -7
- package/dist/types/src/packlets/identity/identity-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity.d.ts +8 -1
- package/dist/types/src/packlets/identity/identity.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/edge-invitation-handler.d.ts +30 -0
- package/dist/types/src/packlets/invitations/edge-invitation-handler.d.ts.map +1 -0
- package/dist/types/src/packlets/invitations/invitation-guest-extenstion.d.ts +2 -1
- package/dist/types/src/packlets/invitations/invitation-guest-extenstion.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitation-host-extension.d.ts +2 -1
- package/dist/types/src/packlets/invitations/invitation-host-extension.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitation-state.d.ts +19 -0
- package/dist/types/src/packlets/invitations/invitation-state.d.ts.map +1 -0
- package/dist/types/src/packlets/invitations/invitations-handler.d.ts +8 -8
- package/dist/types/src/packlets/invitations/invitations-handler.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-context.d.ts +9 -9
- package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-host.d.ts +1 -0
- package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/data-space-manager.d.ts +6 -3
- package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/data-space.d.ts +4 -3
- package/dist/types/src/packlets/spaces/data-space.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/edge-feed-replicator.d.ts +3 -0
- package/dist/types/src/packlets/spaces/edge-feed-replicator.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/edge-feed-replicator.test.d.ts +2 -0
- package/dist/types/src/packlets/spaces/edge-feed-replicator.test.d.ts.map +1 -0
- package/dist/types/src/packlets/spaces/epoch-migrations.d.ts +1 -1
- package/dist/types/src/packlets/spaces/epoch-migrations.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/notarization-plugin.d.ts +31 -6
- package/dist/types/src/packlets/spaces/notarization-plugin.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/spaces-service.d.ts +1 -1
- package/dist/types/src/packlets/spaces/spaces-service.d.ts.map +1 -1
- package/dist/types/src/packlets/storage/storage.d.ts.map +1 -1
- package/dist/types/src/packlets/testing/test-builder.d.ts +1 -2
- package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
- package/dist/types/src/packlets/worker/worker-runtime.d.ts.map +1 -1
- package/dist/types/src/testing/setup.d.ts +3 -0
- package/dist/types/src/testing/setup.d.ts.map +1 -0
- package/dist/types/src/version.d.ts +1 -1
- package/dist/types/src/version.d.ts.map +1 -1
- package/package.json +43 -39
- package/src/packlets/devices/devices-service.test.ts +4 -5
- package/src/packlets/diagnostics/diagnostics-broadcast.ts +1 -0
- package/src/packlets/identity/{authenticator.test.ts → authenticator.node.test.ts} +2 -3
- package/src/packlets/identity/authenticator.ts +5 -2
- package/src/packlets/identity/contacts-service.ts +1 -1
- package/src/packlets/identity/identity-manager.test.ts +5 -6
- package/src/packlets/identity/identity-manager.ts +35 -19
- package/src/packlets/identity/identity-service.test.ts +4 -8
- package/src/packlets/identity/identity.test.ts +128 -239
- package/src/packlets/identity/identity.ts +42 -8
- package/src/packlets/invitations/device-invitation-protocol.test.ts +7 -4
- package/src/packlets/invitations/edge-invitation-handler.ts +184 -0
- package/src/packlets/invitations/invitation-guest-extenstion.ts +8 -4
- package/src/packlets/invitations/invitation-host-extension.ts +8 -7
- package/src/packlets/invitations/invitation-state.ts +111 -0
- package/src/packlets/invitations/invitations-handler.test.ts +16 -9
- package/src/packlets/invitations/invitations-handler.ts +23 -92
- package/src/packlets/invitations/space-invitation-protocol.test.ts +4 -3
- package/src/packlets/invitations/space-invitation-protocol.ts +4 -0
- package/src/packlets/logging/logging.test.ts +1 -2
- package/src/packlets/network/network-service.test.ts +2 -3
- package/src/packlets/services/service-context.test.ts +3 -1
- package/src/packlets/services/service-context.ts +68 -31
- package/src/packlets/services/service-host.test.ts +8 -12
- package/src/packlets/services/service-host.ts +8 -6
- package/src/packlets/services/service-registry.test.ts +1 -2
- package/src/packlets/spaces/data-space-manager.test.ts +2 -2
- package/src/packlets/spaces/data-space-manager.ts +40 -5
- package/src/packlets/spaces/data-space.ts +34 -6
- package/src/packlets/spaces/edge-feed-replicator.test.ts +253 -0
- package/src/packlets/spaces/edge-feed-replicator.ts +80 -22
- package/src/packlets/spaces/epoch-migrations.ts +2 -2
- package/src/packlets/spaces/notarization-plugin.test.ts +10 -7
- package/src/packlets/spaces/notarization-plugin.ts +169 -29
- package/src/packlets/spaces/spaces-service.test.ts +5 -9
- package/src/packlets/spaces/spaces-service.ts +6 -1
- package/src/packlets/storage/storage.ts +0 -1
- package/src/packlets/system/system-service.test.ts +1 -2
- package/src/packlets/testing/test-builder.ts +3 -4
- package/src/packlets/worker/worker-runtime.ts +2 -2
- package/src/testing/setup.ts +11 -0
- package/src/version.ts +1 -5
- package/dist/lib/browser/chunk-TOAILL4T.mjs.map +0 -7
- package/dist/lib/node/chunk-H6C4XY6B.cjs.map +0 -7
- package/dist/types/src/packlets/identity/authenticator.test.d.ts +0 -2
- package/dist/types/src/packlets/identity/authenticator.test.d.ts.map +0 -1
- package/dist/types/src/packlets/services/automerge-host.test.d.ts +0 -2
- package/dist/types/src/packlets/services/automerge-host.test.d.ts.map +0 -1
- package/src/packlets/services/automerge-host.test.ts +0 -60
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
// Copyright 2022 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import expect from '
|
|
5
|
+
import { onTestFinished, describe, expect, test } from 'vitest';
|
|
6
6
|
|
|
7
|
+
import { Event } from '@dxos/async';
|
|
7
8
|
import { Context } from '@dxos/context';
|
|
8
9
|
import { CredentialGenerator, verifyCredential } from '@dxos/credentials';
|
|
9
10
|
import {
|
|
@@ -15,7 +16,9 @@ import {
|
|
|
15
16
|
SpaceProtocol,
|
|
16
17
|
valueEncoding,
|
|
17
18
|
} from '@dxos/echo-pipeline';
|
|
19
|
+
import { type EdgeConnection, type MessageListener } from '@dxos/edge-client';
|
|
18
20
|
import { FeedFactory, FeedStore } from '@dxos/feed-store';
|
|
21
|
+
import { type FeedWrapper } from '@dxos/feed-store';
|
|
19
22
|
import { Keyring } from '@dxos/keyring';
|
|
20
23
|
import { type PublicKey } from '@dxos/keys';
|
|
21
24
|
import { MemorySignalManager, MemorySignalManagerContext } from '@dxos/messaging';
|
|
@@ -24,7 +27,6 @@ import { type FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
|
|
|
24
27
|
import { AdmittedFeed } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
25
28
|
import { createStorage, StorageType } from '@dxos/random-access-storage';
|
|
26
29
|
import { BlobStore } from '@dxos/teleport-extension-object-sync';
|
|
27
|
-
import { afterTest, describe, test } from '@dxos/test';
|
|
28
30
|
|
|
29
31
|
import { Identity } from './identity';
|
|
30
32
|
|
|
@@ -42,16 +44,108 @@ const createStores = () => {
|
|
|
42
44
|
|
|
43
45
|
describe('identity/identity', () => {
|
|
44
46
|
test('create', async () => {
|
|
47
|
+
const setup = await setupIdentity();
|
|
48
|
+
|
|
49
|
+
await writeGenesisCredential(setup);
|
|
50
|
+
|
|
51
|
+
// Wait for identity to be ready.
|
|
52
|
+
await setup.identity.ready();
|
|
53
|
+
const identitySigner = setup.identity.getIdentityCredentialSigner();
|
|
54
|
+
const credential = await identitySigner.createCredential({
|
|
55
|
+
subject: setup.identityKey,
|
|
56
|
+
assertion: {
|
|
57
|
+
'@type': 'dxos.halo.credentials.IdentityProfile',
|
|
58
|
+
profile: {
|
|
59
|
+
displayName: 'Alice',
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
expect(credential.issuer).toEqual(setup.identityKey);
|
|
65
|
+
expect(await verifyCredential(credential)).toEqual({ kind: 'pass' });
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test('two devices', async () => {
|
|
69
|
+
const signalContext = new MemorySignalManagerContext();
|
|
70
|
+
|
|
71
|
+
const owner = await setupIdentity({ signalContext });
|
|
72
|
+
await writeGenesisCredential(owner);
|
|
73
|
+
await owner.identity.ready();
|
|
74
|
+
|
|
75
|
+
const secondDevice = (
|
|
76
|
+
await setupIdentity({
|
|
77
|
+
signalContext,
|
|
78
|
+
spaceKey: owner.spaceKey,
|
|
79
|
+
identityKey: owner.identityKey,
|
|
80
|
+
genesisFeedKey: owner.controlFeed.key,
|
|
81
|
+
})
|
|
82
|
+
).identity;
|
|
83
|
+
|
|
84
|
+
//
|
|
85
|
+
// Second device admission
|
|
86
|
+
//
|
|
87
|
+
{
|
|
88
|
+
const signer = owner.identity.getIdentityCredentialSigner();
|
|
89
|
+
void owner.identity.controlPipeline.writer.write({
|
|
90
|
+
credential: {
|
|
91
|
+
credential: await signer.createCredential({
|
|
92
|
+
subject: secondDevice.deviceKey,
|
|
93
|
+
assertion: {
|
|
94
|
+
'@type': 'dxos.halo.credentials.AuthorizedDevice',
|
|
95
|
+
identityKey: owner.identityKey,
|
|
96
|
+
deviceKey: secondDevice.deviceKey,
|
|
97
|
+
},
|
|
98
|
+
}),
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
await secondDevice.ready();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
expect(Array.from(owner.identity.authorizedDeviceKeys.keys())).toEqual([owner.deviceKey, secondDevice.deviceKey]);
|
|
106
|
+
expect(Array.from(secondDevice.authorizedDeviceKeys.keys())).toEqual([owner.deviceKey, secondDevice.deviceKey]);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test('edge feed replicator', async () => {
|
|
110
|
+
let replicationStarted = false;
|
|
111
|
+
const connectedEvent = new Event();
|
|
112
|
+
const setup = await setupIdentity({
|
|
113
|
+
edgeConnection: {
|
|
114
|
+
connected: connectedEvent,
|
|
115
|
+
open: async () => {},
|
|
116
|
+
close: async () => {},
|
|
117
|
+
addListener: (_: MessageListener): (() => void) => {
|
|
118
|
+
return () => {};
|
|
119
|
+
},
|
|
120
|
+
send: async (_) => {
|
|
121
|
+
replicationStarted = true;
|
|
122
|
+
},
|
|
123
|
+
} as EdgeConnection,
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
await writeGenesisCredential(setup);
|
|
127
|
+
connectedEvent.emit();
|
|
128
|
+
|
|
129
|
+
await expect.poll(() => replicationStarted).toBeTruthy();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const setupIdentity = async (args?: {
|
|
133
|
+
signalContext?: MemorySignalManagerContext;
|
|
134
|
+
spaceKey?: PublicKey;
|
|
135
|
+
identityKey?: PublicKey;
|
|
136
|
+
genesisFeedKey?: PublicKey;
|
|
137
|
+
edgeConnection?: EdgeConnection;
|
|
138
|
+
}): Promise<TestIdentitySetup> => {
|
|
45
139
|
const { storage, metadataStore, blobStore } = createStores();
|
|
46
140
|
|
|
47
141
|
const keyring = new Keyring();
|
|
48
|
-
const identityKey = await keyring.createKey();
|
|
49
142
|
const deviceKey = await keyring.createKey();
|
|
50
|
-
const
|
|
143
|
+
const identityKey = args?.identityKey ?? (await keyring.createKey());
|
|
144
|
+
const spaceKey = args?.spaceKey ?? (await keyring.createKey());
|
|
51
145
|
|
|
52
146
|
const feedStore = new FeedStore<FeedMessage>({
|
|
53
147
|
factory: new FeedFactory<FeedMessage>({
|
|
54
|
-
root: storage.createDirectory(
|
|
148
|
+
root: storage.createDirectory(),
|
|
55
149
|
signer: keyring,
|
|
56
150
|
hypercore: {
|
|
57
151
|
valueEncoding,
|
|
@@ -77,7 +171,7 @@ describe('identity/identity', () => {
|
|
|
77
171
|
},
|
|
78
172
|
blobStore,
|
|
79
173
|
networkManager: new SwarmNetworkManager({
|
|
80
|
-
signalManager: new MemorySignalManager(new MemorySignalManagerContext()),
|
|
174
|
+
signalManager: new MemorySignalManager(args?.signalContext ?? new MemorySignalManagerContext()),
|
|
81
175
|
transportFactory: MemoryTransportFactory,
|
|
82
176
|
}),
|
|
83
177
|
});
|
|
@@ -87,7 +181,7 @@ describe('identity/identity', () => {
|
|
|
87
181
|
id: await createIdFromSpaceKey(spaceKey),
|
|
88
182
|
spaceKey,
|
|
89
183
|
protocol,
|
|
90
|
-
genesisFeed: controlFeed,
|
|
184
|
+
genesisFeed: args?.genesisFeedKey ? await feedStore.openFeed(args.genesisFeedKey) : controlFeed,
|
|
91
185
|
feedProvider: (feedKey) => feedStore.openFeed(feedKey),
|
|
92
186
|
memberKey: identityKey,
|
|
93
187
|
metadataStore,
|
|
@@ -103,242 +197,37 @@ describe('identity/identity', () => {
|
|
|
103
197
|
identityKey,
|
|
104
198
|
deviceKey,
|
|
105
199
|
space,
|
|
200
|
+
edgeFeatures: args?.edgeConnection && { feedReplicator: true },
|
|
201
|
+
edgeConnection: args?.edgeConnection,
|
|
106
202
|
});
|
|
107
203
|
|
|
108
204
|
await identity.open(new Context());
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
// Identity genesis
|
|
113
|
-
//
|
|
114
|
-
{
|
|
115
|
-
const generator = new CredentialGenerator(keyring, identityKey, deviceKey);
|
|
116
|
-
const credentials = [
|
|
117
|
-
...(await generator.createSpaceGenesis(spaceKey, controlFeed.key)),
|
|
118
|
-
await generator.createDeviceAuthorization(deviceKey),
|
|
119
|
-
await generator.createFeedAdmission(spaceKey, dataFeed.key, AdmittedFeed.Designation.DATA),
|
|
120
|
-
];
|
|
121
|
-
|
|
122
|
-
for (const credential of credentials) {
|
|
123
|
-
await identity.controlPipeline.writer.write({
|
|
124
|
-
credential: { credential },
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Wait for identity to be ready.
|
|
130
|
-
await identity.ready();
|
|
131
|
-
const identitySigner = identity.getIdentityCredentialSigner();
|
|
132
|
-
const credential = await identitySigner.createCredential({
|
|
133
|
-
subject: identityKey,
|
|
134
|
-
assertion: {
|
|
135
|
-
'@type': 'dxos.halo.credentials.IdentityProfile',
|
|
136
|
-
profile: {
|
|
137
|
-
displayName: 'Alice',
|
|
138
|
-
},
|
|
139
|
-
},
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
expect(credential.issuer).toEqual(identityKey);
|
|
143
|
-
expect(await verifyCredential(credential)).toEqual({ kind: 'pass' });
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
test('two devices', async () => {
|
|
147
|
-
const signalContext = new MemorySignalManagerContext();
|
|
148
|
-
|
|
149
|
-
let spaceKey: PublicKey;
|
|
150
|
-
let identityKey: PublicKey;
|
|
151
|
-
let genesisFeedKey: PublicKey;
|
|
152
|
-
let identity1: Identity;
|
|
153
|
-
let identity2: Identity;
|
|
154
|
-
|
|
155
|
-
//
|
|
156
|
-
// First device
|
|
157
|
-
//
|
|
158
|
-
{
|
|
159
|
-
const { storage, metadataStore, blobStore } = createStores();
|
|
160
|
-
|
|
161
|
-
const keyring = new Keyring();
|
|
162
|
-
identityKey = await keyring.createKey();
|
|
163
|
-
const deviceKey = await keyring.createKey();
|
|
164
|
-
spaceKey = await keyring.createKey();
|
|
165
|
-
|
|
166
|
-
const feedStore = new FeedStore<FeedMessage>({
|
|
167
|
-
factory: new FeedFactory<FeedMessage>({
|
|
168
|
-
root: storage.createDirectory(),
|
|
169
|
-
signer: keyring,
|
|
170
|
-
hypercore: {
|
|
171
|
-
valueEncoding,
|
|
172
|
-
},
|
|
173
|
-
}),
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
const createFeed = async () => {
|
|
177
|
-
const feedKey = await keyring.createKey();
|
|
178
|
-
return feedStore.openFeed(feedKey, { writable: true });
|
|
179
|
-
};
|
|
180
|
-
|
|
181
|
-
const controlFeed = await createFeed();
|
|
182
|
-
genesisFeedKey = controlFeed.key;
|
|
183
|
-
|
|
184
|
-
const dataFeed = await createFeed();
|
|
185
|
-
|
|
186
|
-
const protocol = new SpaceProtocol({
|
|
187
|
-
topic: spaceKey,
|
|
188
|
-
swarmIdentity: {
|
|
189
|
-
peerKey: deviceKey,
|
|
190
|
-
identityKey,
|
|
191
|
-
credentialProvider: MOCK_AUTH_PROVIDER, // createHaloAuthProvider(createCredentialSignerWithKey(keyring, device_key)),
|
|
192
|
-
credentialAuthenticator: MOCK_AUTH_VERIFIER, // createHaloAuthVerifier(() => identity.authorizedDeviceKeys),
|
|
193
|
-
},
|
|
194
|
-
blobStore,
|
|
195
|
-
networkManager: new SwarmNetworkManager({
|
|
196
|
-
signalManager: new MemorySignalManager(signalContext),
|
|
197
|
-
transportFactory: MemoryTransportFactory,
|
|
198
|
-
}),
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
await metadataStore.setIdentityRecord({ haloSpace: { key: spaceKey }, identityKey, deviceKey });
|
|
202
|
-
const space = new Space({
|
|
203
|
-
id: await createIdFromSpaceKey(spaceKey),
|
|
204
|
-
spaceKey,
|
|
205
|
-
protocol,
|
|
206
|
-
genesisFeed: controlFeed,
|
|
207
|
-
feedProvider: (feedKey) => feedStore.openFeed(feedKey),
|
|
208
|
-
memberKey: identityKey,
|
|
209
|
-
metadataStore,
|
|
210
|
-
onDelegatedInvitationStatusChange: async () => {},
|
|
211
|
-
onMemberRolesChanged: async () => {},
|
|
212
|
-
});
|
|
213
|
-
await space.setControlFeed(controlFeed);
|
|
214
|
-
await space.setDataFeed(dataFeed);
|
|
215
|
-
|
|
216
|
-
const identity = (identity1 = new Identity({
|
|
217
|
-
signer: keyring,
|
|
218
|
-
identityKey,
|
|
219
|
-
deviceKey,
|
|
220
|
-
space,
|
|
221
|
-
}));
|
|
222
|
-
|
|
223
|
-
await identity.open(new Context());
|
|
224
|
-
afterTest(() => identity.close(new Context()));
|
|
225
|
-
|
|
226
|
-
//
|
|
227
|
-
// Identity genesis
|
|
228
|
-
//
|
|
229
|
-
{
|
|
230
|
-
const generator = new CredentialGenerator(keyring, identityKey, deviceKey);
|
|
231
|
-
const credentials = [
|
|
232
|
-
...(await generator.createSpaceGenesis(spaceKey, controlFeed.key)),
|
|
233
|
-
await generator.createDeviceAuthorization(deviceKey),
|
|
234
|
-
await generator.createFeedAdmission(spaceKey, dataFeed.key, AdmittedFeed.Designation.DATA),
|
|
235
|
-
];
|
|
236
|
-
|
|
237
|
-
for (const credential of credentials) {
|
|
238
|
-
await identity.controlPipeline.writer.write({
|
|
239
|
-
credential: { credential },
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// Wait for identity to be ready.
|
|
245
|
-
await identity.ready();
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
//
|
|
249
|
-
// Second device
|
|
250
|
-
//
|
|
251
|
-
{
|
|
252
|
-
const { storage, metadataStore, blobStore } = createStores();
|
|
253
|
-
|
|
254
|
-
const keyring = new Keyring();
|
|
255
|
-
const deviceKey = await keyring.createKey();
|
|
256
|
-
|
|
257
|
-
const feedStore = new FeedStore<FeedMessage>({
|
|
258
|
-
factory: new FeedFactory<FeedMessage>({
|
|
259
|
-
root: storage.createDirectory(),
|
|
260
|
-
signer: keyring,
|
|
261
|
-
hypercore: {
|
|
262
|
-
valueEncoding,
|
|
263
|
-
},
|
|
264
|
-
}),
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
const createFeed = async () => {
|
|
268
|
-
const feedKey = await keyring.createKey();
|
|
269
|
-
return feedStore.openFeed(feedKey, { writable: true });
|
|
270
|
-
};
|
|
271
|
-
|
|
272
|
-
const controlFeed = await createFeed();
|
|
273
|
-
const dataFeed = await createFeed();
|
|
274
|
-
|
|
275
|
-
const protocol = new SpaceProtocol({
|
|
276
|
-
topic: spaceKey,
|
|
277
|
-
swarmIdentity: {
|
|
278
|
-
peerKey: deviceKey,
|
|
279
|
-
identityKey,
|
|
280
|
-
credentialProvider: MOCK_AUTH_PROVIDER, // createHaloAuthProvider(createCredentialSignerWithKey(keyring, device_key)),
|
|
281
|
-
credentialAuthenticator: MOCK_AUTH_VERIFIER, // createHaloAuthVerifier(() => identity.authorizedDeviceKeys),
|
|
282
|
-
},
|
|
283
|
-
blobStore,
|
|
284
|
-
networkManager: new SwarmNetworkManager({
|
|
285
|
-
signalManager: new MemorySignalManager(signalContext),
|
|
286
|
-
transportFactory: MemoryTransportFactory,
|
|
287
|
-
}),
|
|
288
|
-
});
|
|
205
|
+
onTestFinished(() => identity.close(new Context()));
|
|
206
|
+
return { identity, identityKey, keyring, deviceKey, controlFeed, spaceKey, dataFeed };
|
|
207
|
+
};
|
|
289
208
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
memberKey: identityKey,
|
|
302
|
-
metadataStore,
|
|
303
|
-
onDelegatedInvitationStatusChange: async () => {},
|
|
304
|
-
onMemberRolesChanged: async () => {},
|
|
209
|
+
const writeGenesisCredential = async (setup: TestIdentitySetup) => {
|
|
210
|
+
const generator = new CredentialGenerator(setup.keyring, setup.identityKey, setup.deviceKey);
|
|
211
|
+
const credentials = [
|
|
212
|
+
...(await generator.createSpaceGenesis(setup.spaceKey, setup.controlFeed.key)),
|
|
213
|
+
await generator.createDeviceAuthorization(setup.deviceKey),
|
|
214
|
+
await generator.createFeedAdmission(setup.spaceKey, setup.dataFeed.key, AdmittedFeed.Designation.DATA),
|
|
215
|
+
];
|
|
216
|
+
|
|
217
|
+
for (const credential of credentials) {
|
|
218
|
+
await setup.identity.controlPipeline.writer.write({
|
|
219
|
+
credential: { credential },
|
|
305
220
|
});
|
|
306
|
-
await space.setControlFeed(controlFeed);
|
|
307
|
-
await space.setDataFeed(dataFeed);
|
|
308
|
-
|
|
309
|
-
const identity = (identity2 = new Identity({
|
|
310
|
-
signer: keyring,
|
|
311
|
-
identityKey: identity1.identityKey,
|
|
312
|
-
deviceKey,
|
|
313
|
-
space,
|
|
314
|
-
}));
|
|
315
|
-
|
|
316
|
-
await identity.open(new Context());
|
|
317
|
-
afterTest(() => identity.close(new Context()));
|
|
318
221
|
}
|
|
319
|
-
|
|
320
|
-
//
|
|
321
|
-
// Second device admission
|
|
322
|
-
//
|
|
323
|
-
{
|
|
324
|
-
const signer = identity1.getIdentityCredentialSigner();
|
|
325
|
-
void identity1.controlPipeline.writer.write({
|
|
326
|
-
credential: {
|
|
327
|
-
credential: await signer.createCredential({
|
|
328
|
-
subject: identity2.deviceKey,
|
|
329
|
-
assertion: {
|
|
330
|
-
'@type': 'dxos.halo.credentials.AuthorizedDevice',
|
|
331
|
-
identityKey: identity1.identityKey,
|
|
332
|
-
deviceKey: identity2.deviceKey,
|
|
333
|
-
},
|
|
334
|
-
}),
|
|
335
|
-
},
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
await identity2.ready();
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
expect(Array.from(identity1.authorizedDeviceKeys.keys())).toEqual([identity1.deviceKey, identity2.deviceKey]);
|
|
342
|
-
expect(Array.from(identity2.authorizedDeviceKeys.keys())).toEqual([identity1.deviceKey, identity2.deviceKey]);
|
|
343
|
-
});
|
|
222
|
+
};
|
|
344
223
|
});
|
|
224
|
+
|
|
225
|
+
type TestIdentitySetup = {
|
|
226
|
+
identity: Identity;
|
|
227
|
+
keyring: Keyring;
|
|
228
|
+
identityKey: PublicKey;
|
|
229
|
+
deviceKey: PublicKey;
|
|
230
|
+
spaceKey: PublicKey;
|
|
231
|
+
controlFeed: FeedWrapper<FeedMessage>;
|
|
232
|
+
dataFeed: FeedWrapper<FeedMessage>;
|
|
233
|
+
};
|
|
@@ -14,10 +14,12 @@ import {
|
|
|
14
14
|
} from '@dxos/credentials';
|
|
15
15
|
import { type Signer } from '@dxos/crypto';
|
|
16
16
|
import { type Space } from '@dxos/echo-pipeline';
|
|
17
|
-
import {
|
|
17
|
+
import { type EdgeConnection } from '@dxos/edge-client';
|
|
18
|
+
import { writeMessages, type FeedWrapper } from '@dxos/feed-store';
|
|
18
19
|
import { invariant } from '@dxos/invariant';
|
|
19
20
|
import { PublicKey, type SpaceId } from '@dxos/keys';
|
|
20
21
|
import { log } from '@dxos/log';
|
|
22
|
+
import { type Runtime } from '@dxos/protocols/proto/dxos/config';
|
|
21
23
|
import { type FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
|
|
22
24
|
import {
|
|
23
25
|
AdmittedFeed,
|
|
@@ -32,6 +34,7 @@ import { type ComplexMap, ComplexSet } from '@dxos/util';
|
|
|
32
34
|
|
|
33
35
|
import { TrustedKeySetAuthVerifier } from './authenticator';
|
|
34
36
|
import { DefaultSpaceStateMachine } from './default-space-state-machine';
|
|
37
|
+
import { EdgeFeedReplicator } from '../spaces';
|
|
35
38
|
|
|
36
39
|
export type IdentityParams = {
|
|
37
40
|
identityKey: PublicKey;
|
|
@@ -39,6 +42,9 @@ export type IdentityParams = {
|
|
|
39
42
|
signer: Signer;
|
|
40
43
|
space: Space;
|
|
41
44
|
presence?: Presence;
|
|
45
|
+
|
|
46
|
+
edgeConnection?: EdgeConnection;
|
|
47
|
+
edgeFeatures?: Runtime.Client.EdgeFeatures;
|
|
42
48
|
};
|
|
43
49
|
|
|
44
50
|
/**
|
|
@@ -52,6 +58,8 @@ export class Identity {
|
|
|
52
58
|
private readonly _deviceStateMachine: DeviceStateMachine;
|
|
53
59
|
private readonly _profileStateMachine: ProfileStateMachine;
|
|
54
60
|
private readonly _defaultSpaceStateMachine: DefaultSpaceStateMachine;
|
|
61
|
+
private readonly _edgeFeedReplicator?: EdgeFeedReplicator = undefined;
|
|
62
|
+
|
|
55
63
|
public readonly authVerifier: TrustedKeySetAuthVerifier;
|
|
56
64
|
|
|
57
65
|
public readonly identityKey: PublicKey;
|
|
@@ -59,15 +67,15 @@ export class Identity {
|
|
|
59
67
|
|
|
60
68
|
public readonly stateUpdate = new Event();
|
|
61
69
|
|
|
62
|
-
constructor(
|
|
63
|
-
this.space = space;
|
|
64
|
-
this._signer = signer;
|
|
65
|
-
this._presence = presence;
|
|
70
|
+
constructor(params: IdentityParams) {
|
|
71
|
+
this.space = params.space;
|
|
72
|
+
this._signer = params.signer;
|
|
73
|
+
this._presence = params.presence;
|
|
66
74
|
|
|
67
|
-
this.identityKey = identityKey;
|
|
68
|
-
this.deviceKey = deviceKey;
|
|
75
|
+
this.identityKey = params.identityKey;
|
|
76
|
+
this.deviceKey = params.deviceKey;
|
|
69
77
|
|
|
70
|
-
log.trace('dxos.halo.device', { deviceKey });
|
|
78
|
+
log.trace('dxos.halo.device', { deviceKey: params.deviceKey });
|
|
71
79
|
|
|
72
80
|
this._deviceStateMachine = new DeviceStateMachine({
|
|
73
81
|
identityKey: this.identityKey,
|
|
@@ -88,6 +96,10 @@ export class Identity {
|
|
|
88
96
|
update: this.stateUpdate,
|
|
89
97
|
authTimeout: AUTH_TIMEOUT,
|
|
90
98
|
});
|
|
99
|
+
|
|
100
|
+
if (params.edgeConnection && params.edgeFeatures?.feedReplicator) {
|
|
101
|
+
this._edgeFeedReplicator = new EdgeFeedReplicator({ messenger: params.edgeConnection, spaceId: this.space.id });
|
|
102
|
+
}
|
|
91
103
|
}
|
|
92
104
|
|
|
93
105
|
// TODO(burdon): Expose state object?
|
|
@@ -105,7 +117,14 @@ export class Identity {
|
|
|
105
117
|
await this.space.spaceState.addCredentialProcessor(this._deviceStateMachine);
|
|
106
118
|
await this.space.spaceState.addCredentialProcessor(this._profileStateMachine);
|
|
107
119
|
await this.space.spaceState.addCredentialProcessor(this._defaultSpaceStateMachine);
|
|
120
|
+
|
|
121
|
+
if (this._edgeFeedReplicator) {
|
|
122
|
+
this.space.protocol.feedAdded.append(this._onFeedAdded);
|
|
123
|
+
}
|
|
124
|
+
|
|
108
125
|
await this.space.open(ctx);
|
|
126
|
+
|
|
127
|
+
await this._edgeFeedReplicator?.open();
|
|
109
128
|
}
|
|
110
129
|
|
|
111
130
|
@trace.span()
|
|
@@ -115,6 +134,13 @@ export class Identity {
|
|
|
115
134
|
await this.space.spaceState.removeCredentialProcessor(this._defaultSpaceStateMachine);
|
|
116
135
|
await this.space.spaceState.removeCredentialProcessor(this._profileStateMachine);
|
|
117
136
|
await this.space.spaceState.removeCredentialProcessor(this._deviceStateMachine);
|
|
137
|
+
|
|
138
|
+
if (this._edgeFeedReplicator) {
|
|
139
|
+
this.space.protocol.feedAdded.remove(this._onFeedAdded);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
await this._edgeFeedReplicator?.close();
|
|
143
|
+
|
|
118
144
|
await this.space.close();
|
|
119
145
|
}
|
|
120
146
|
|
|
@@ -151,6 +177,10 @@ export class Identity {
|
|
|
151
177
|
return this._presence;
|
|
152
178
|
}
|
|
153
179
|
|
|
180
|
+
get signer() {
|
|
181
|
+
return this._signer;
|
|
182
|
+
}
|
|
183
|
+
|
|
154
184
|
/**
|
|
155
185
|
* Issues credentials as identity.
|
|
156
186
|
* Requires identity to be ready.
|
|
@@ -223,4 +253,8 @@ export class Identity {
|
|
|
223
253
|
].map((credential): FeedMessage.Payload => ({ credential: { credential } })),
|
|
224
254
|
);
|
|
225
255
|
}
|
|
256
|
+
|
|
257
|
+
private _onFeedAdded = async (feed: FeedWrapper<any>) => {
|
|
258
|
+
await this._edgeFeedReplicator!.addFeed(feed);
|
|
259
|
+
};
|
|
226
260
|
}
|
|
@@ -2,19 +2,20 @@
|
|
|
2
2
|
// Copyright 2022 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { expect } from '
|
|
5
|
+
import { describe, expect, test, onTestFinished } from 'vitest';
|
|
6
6
|
|
|
7
7
|
import { asyncChain } from '@dxos/async';
|
|
8
8
|
import { Context } from '@dxos/context';
|
|
9
9
|
import { AlreadyJoinedError } from '@dxos/protocols';
|
|
10
10
|
import { Invitation } from '@dxos/protocols/proto/dxos/client/services';
|
|
11
|
-
import { describe, test, afterTest } from '@dxos/test';
|
|
12
11
|
|
|
13
12
|
import { type ServiceContext } from '../services';
|
|
14
13
|
import { createPeers, createServiceContext, performInvitation } from '../testing';
|
|
15
14
|
|
|
16
15
|
const closeAfterTest = async (peer: ServiceContext) => {
|
|
17
|
-
|
|
16
|
+
onTestFinished(async () => {
|
|
17
|
+
await peer.close();
|
|
18
|
+
});
|
|
18
19
|
return peer;
|
|
19
20
|
};
|
|
20
21
|
|
|
@@ -22,7 +23,9 @@ describe('services/device', () => {
|
|
|
22
23
|
test('creates identity', async () => {
|
|
23
24
|
const peer = await createServiceContext();
|
|
24
25
|
await peer.open(new Context());
|
|
25
|
-
|
|
26
|
+
onTestFinished(async () => {
|
|
27
|
+
await peer.close();
|
|
28
|
+
});
|
|
26
29
|
|
|
27
30
|
const identity = await peer.createIdentity();
|
|
28
31
|
expect(identity).not.to.be.undefined;
|