@dxos/credentials 0.4.10-main.fd8ea31 → 0.4.10-next.71cec10
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/index.mjs +28 -142
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +23 -137
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/types/src/credentials/credential-generator.d.ts +1 -3
- package/dist/types/src/credentials/credential-generator.d.ts.map +1 -1
- package/dist/types/src/state-machine/index.d.ts +0 -1
- package/dist/types/src/state-machine/index.d.ts.map +1 -1
- package/dist/types/src/state-machine/space-state-machine.d.ts +0 -6
- package/dist/types/src/state-machine/space-state-machine.d.ts.map +1 -1
- package/package.json +16 -16
- package/src/credentials/credential-generator.ts +0 -24
- package/src/state-machine/index.ts +0 -1
- package/src/state-machine/space-state-machine.ts +3 -22
- package/dist/types/src/state-machine/invitation-state-machine.d.ts +0 -22
- package/dist/types/src/state-machine/invitation-state-machine.d.ts.map +0 -1
- package/dist/types/src/state-machine/invitation-state-machine.test.d.ts +0 -2
- package/dist/types/src/state-machine/invitation-state-machine.test.d.ts.map +0 -1
- package/src/state-machine/invitation-state-machine.test.ts +0 -166
- package/src/state-machine/invitation-state-machine.ts +0 -85
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/credentials",
|
|
3
|
-
"version": "0.4.10-
|
|
3
|
+
"version": "0.4.10-next.71cec10",
|
|
4
4
|
"description": "DXOS Credentials",
|
|
5
5
|
"homepage": "https://github.com/dxos/dxos/tree/main/packages/core/halo/credentials#readme",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -25,20 +25,20 @@
|
|
|
25
25
|
"performance-now": "^2.1.0",
|
|
26
26
|
"pump": "^3.0.0",
|
|
27
27
|
"stream-to-array": "^2.3.0",
|
|
28
|
-
"@dxos/async": "0.4.10-
|
|
29
|
-
"@dxos/codec-protobuf": "0.4.10-
|
|
30
|
-
"@dxos/
|
|
31
|
-
"@dxos/crypto": "0.4.10-
|
|
32
|
-
"@dxos/
|
|
33
|
-
"@dxos/
|
|
34
|
-
"@dxos/
|
|
35
|
-
"@dxos/
|
|
36
|
-
"@dxos/
|
|
37
|
-
"@dxos/node-std": "0.4.10-
|
|
38
|
-
"@dxos/
|
|
39
|
-
"@dxos/
|
|
40
|
-
"@dxos/
|
|
41
|
-
"@dxos/
|
|
28
|
+
"@dxos/async": "0.4.10-next.71cec10",
|
|
29
|
+
"@dxos/codec-protobuf": "0.4.10-next.71cec10",
|
|
30
|
+
"@dxos/context": "0.4.10-next.71cec10",
|
|
31
|
+
"@dxos/crypto": "0.4.10-next.71cec10",
|
|
32
|
+
"@dxos/feed-store": "0.4.10-next.71cec10",
|
|
33
|
+
"@dxos/debug": "0.4.10-next.71cec10",
|
|
34
|
+
"@dxos/invariant": "0.4.10-next.71cec10",
|
|
35
|
+
"@dxos/keyring": "0.4.10-next.71cec10",
|
|
36
|
+
"@dxos/keys": "0.4.10-next.71cec10",
|
|
37
|
+
"@dxos/node-std": "0.4.10-next.71cec10",
|
|
38
|
+
"@dxos/timeframe": "0.4.10-next.71cec10",
|
|
39
|
+
"@dxos/log": "0.4.10-next.71cec10",
|
|
40
|
+
"@dxos/util": "0.4.10-next.71cec10",
|
|
41
|
+
"@dxos/protocols": "0.4.10-next.71cec10"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@types/encoding-down": "^5.0.0",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"@types/pify": "^3.0.2",
|
|
51
51
|
"@types/pump": "^1.1.0",
|
|
52
52
|
"@types/stream-to-array": "^2.3.0",
|
|
53
|
-
"@dxos/random-access-storage": "0.4.10-
|
|
53
|
+
"@dxos/random-access-storage": "0.4.10-next.71cec10"
|
|
54
54
|
},
|
|
55
55
|
"publishConfig": {
|
|
56
56
|
"access": "public"
|
|
@@ -13,7 +13,6 @@ import {
|
|
|
13
13
|
type ProfileDocument,
|
|
14
14
|
SpaceMember,
|
|
15
15
|
} from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
16
|
-
import { type DelegateSpaceInvitation } from '@dxos/protocols/proto/dxos/halo/invitations';
|
|
17
16
|
import { Timeframe } from '@dxos/timeframe';
|
|
18
17
|
|
|
19
18
|
import { createCredential, type CredentialSigner } from './credential-factory';
|
|
@@ -208,7 +207,6 @@ export const createAdmissionCredentials = async (
|
|
|
208
207
|
spaceKey: PublicKey,
|
|
209
208
|
genesisFeedKey: PublicKey,
|
|
210
209
|
profile?: ProfileDocument,
|
|
211
|
-
invitationCredentialId?: PublicKey,
|
|
212
210
|
): Promise<FeedMessage.Payload[]> => {
|
|
213
211
|
const credentials = await Promise.all([
|
|
214
212
|
await signer.createCredential({
|
|
@@ -219,7 +217,6 @@ export const createAdmissionCredentials = async (
|
|
|
219
217
|
role: SpaceMember.Role.ADMIN, // TODO(burdon): Configure.
|
|
220
218
|
profile,
|
|
221
219
|
genesisFeedKey,
|
|
222
|
-
invitationCredentialId,
|
|
223
220
|
},
|
|
224
221
|
}),
|
|
225
222
|
]);
|
|
@@ -228,24 +225,3 @@ export const createAdmissionCredentials = async (
|
|
|
228
225
|
credential: { credential },
|
|
229
226
|
}));
|
|
230
227
|
};
|
|
231
|
-
|
|
232
|
-
export const createDelegatedSpaceInvitationCredential = async (
|
|
233
|
-
signer: CredentialSigner,
|
|
234
|
-
subject: PublicKey,
|
|
235
|
-
invitation: DelegateSpaceInvitation,
|
|
236
|
-
): Promise<FeedMessage.Payload> => {
|
|
237
|
-
const credential = await signer.createCredential({
|
|
238
|
-
subject,
|
|
239
|
-
assertion: {
|
|
240
|
-
'@type': 'dxos.halo.invitations.DelegateSpaceInvitation',
|
|
241
|
-
invitationId: invitation.invitationId,
|
|
242
|
-
authMethod: invitation.authMethod,
|
|
243
|
-
swarmKey: invitation.swarmKey,
|
|
244
|
-
role: invitation.role,
|
|
245
|
-
guestKey: invitation.guestKey,
|
|
246
|
-
expiresOn: invitation.expiresOn,
|
|
247
|
-
multiUse: invitation.multiUse,
|
|
248
|
-
},
|
|
249
|
-
});
|
|
250
|
-
return { credential: { credential } };
|
|
251
|
-
};
|
|
@@ -8,11 +8,9 @@ import { PublicKey } from '@dxos/keys';
|
|
|
8
8
|
import { log } from '@dxos/log';
|
|
9
9
|
import { type TypedMessage } from '@dxos/protocols';
|
|
10
10
|
import { type Credential, SpaceMember } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
11
|
-
import { type DelegateSpaceInvitation } from '@dxos/protocols/proto/dxos/halo/invitations';
|
|
12
11
|
import { type AsyncCallback, Callback, ComplexMap, ComplexSet } from '@dxos/util';
|
|
13
12
|
|
|
14
13
|
import { type FeedInfo, FeedStateMachine } from './feed-state-machine';
|
|
15
|
-
import { InvitationStateMachine } from './invitation-state-machine';
|
|
16
14
|
import { MemberStateMachine, type MemberInfo } from './member-state-machine';
|
|
17
15
|
import { getCredentialAssertion, verifyCredential } from '../credentials';
|
|
18
16
|
import { type CredentialProcessor } from '../processor/credential-processor';
|
|
@@ -23,7 +21,6 @@ export interface SpaceState {
|
|
|
23
21
|
readonly credentials: Credential[];
|
|
24
22
|
readonly genesisCredential: Credential | undefined;
|
|
25
23
|
readonly creator: MemberInfo | undefined;
|
|
26
|
-
readonly invitations: ReadonlyMap<PublicKey, DelegateSpaceInvitation>;
|
|
27
24
|
|
|
28
25
|
addCredentialProcessor(processor: CredentialProcessor): Promise<void>;
|
|
29
26
|
removeCredentialProcessor(processor: CredentialProcessor): Promise<void>;
|
|
@@ -42,7 +39,7 @@ export type CredentialEntry = {
|
|
|
42
39
|
revoked: boolean;
|
|
43
40
|
};
|
|
44
41
|
|
|
45
|
-
const
|
|
42
|
+
const REVOKABLE_CREDENTIALS = ['dxos.halo.credentials.SpaceMember'];
|
|
46
43
|
|
|
47
44
|
/**
|
|
48
45
|
* Validates and processes credentials for a single space.
|
|
@@ -52,7 +49,6 @@ const REVOCABLE_CREDENTIALS = ['dxos.halo.credentials.SpaceMember'];
|
|
|
52
49
|
export class SpaceStateMachine implements SpaceState {
|
|
53
50
|
private readonly _members = new MemberStateMachine(this._spaceKey);
|
|
54
51
|
private readonly _feeds = new FeedStateMachine(this._spaceKey);
|
|
55
|
-
private readonly _invitations = new InvitationStateMachine();
|
|
56
52
|
private readonly _credentials: CredentialEntry[] = [];
|
|
57
53
|
private readonly _credentialsById = new ComplexMap<PublicKey, CredentialEntry>(PublicKey.hash);
|
|
58
54
|
private readonly _processedCredentials = new ComplexSet<PublicKey>(PublicKey.hash);
|
|
@@ -63,8 +59,6 @@ export class SpaceStateMachine implements SpaceState {
|
|
|
63
59
|
readonly onCredentialProcessed = new Callback<AsyncCallback<Credential>>();
|
|
64
60
|
readonly onMemberAdmitted = this._members.onMemberAdmitted;
|
|
65
61
|
readonly onFeedAdmitted = this._feeds.onFeedAdmitted;
|
|
66
|
-
readonly onDelegatedInvitation = this._invitations.onDelegatedInvitation;
|
|
67
|
-
readonly onDelegatedInvitationRemoved = this._invitations.onDelegatedInvitationRemoved;
|
|
68
62
|
|
|
69
63
|
constructor(private readonly _spaceKey: PublicKey) {}
|
|
70
64
|
|
|
@@ -92,10 +86,6 @@ export class SpaceStateMachine implements SpaceState {
|
|
|
92
86
|
return this._genesisCredential;
|
|
93
87
|
}
|
|
94
88
|
|
|
95
|
-
get invitations(): ReadonlyMap<PublicKey, DelegateSpaceInvitation> {
|
|
96
|
-
return this._invitations.invitations;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
89
|
async addCredentialProcessor(processor: CredentialProcessor) {
|
|
100
90
|
if (this._credentialProcessors.find((p) => p.processor === processor)) {
|
|
101
91
|
throw new Error('Credential processor already added.');
|
|
@@ -169,6 +159,7 @@ export class SpaceStateMachine implements SpaceState {
|
|
|
169
159
|
|
|
170
160
|
toBeRevoked.revoked = true;
|
|
171
161
|
await this._members.onRevoked(toBeRevoked.credential, credential);
|
|
162
|
+
|
|
172
163
|
break;
|
|
173
164
|
}
|
|
174
165
|
case 'dxos.halo.credentials.SpaceGenesis': {
|
|
@@ -204,7 +195,6 @@ export class SpaceStateMachine implements SpaceState {
|
|
|
204
195
|
}
|
|
205
196
|
|
|
206
197
|
await this._members.process(credential);
|
|
207
|
-
await this._invitations.process(credential);
|
|
208
198
|
break;
|
|
209
199
|
}
|
|
210
200
|
|
|
@@ -232,15 +222,6 @@ export class SpaceStateMachine implements SpaceState {
|
|
|
232
222
|
await this._feeds.process(credential, sourceFeed);
|
|
233
223
|
break;
|
|
234
224
|
}
|
|
235
|
-
case 'dxos.halo.invitations.CancelDelegatedInvitation':
|
|
236
|
-
case 'dxos.halo.invitations.DelegateSpaceInvitation': {
|
|
237
|
-
if (!this._canInviteNewMembers(credential.issuer)) {
|
|
238
|
-
log.warn(`Invalid invitation, space member is not authorized to invite new members: ${credential.issuer}`);
|
|
239
|
-
return false;
|
|
240
|
-
}
|
|
241
|
-
await this._invitations.process(credential);
|
|
242
|
-
break;
|
|
243
|
-
}
|
|
244
225
|
}
|
|
245
226
|
|
|
246
227
|
const newEntry: CredentialEntry = { credential, sourceFeed, revoked: false };
|
|
@@ -276,7 +257,7 @@ export class SpaceStateMachine implements SpaceState {
|
|
|
276
257
|
|
|
277
258
|
private _credentialCanBeRevoked(entry: CredentialEntry) {
|
|
278
259
|
// TODO(dmaretskyi): Prohibit from removing space's creator member credential.
|
|
279
|
-
return
|
|
260
|
+
return REVOKABLE_CREDENTIALS.includes(entry.credential.subject.assertion['@type']);
|
|
280
261
|
}
|
|
281
262
|
}
|
|
282
263
|
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { PublicKey } from '@dxos/keys';
|
|
2
|
-
import { type Credential } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
3
|
-
import { type DelegateSpaceInvitation } from '@dxos/protocols/proto/dxos/halo/invitations';
|
|
4
|
-
import { type AsyncCallback, Callback } from '@dxos/util';
|
|
5
|
-
export interface DelegateInvitationCredential {
|
|
6
|
-
credentialId: PublicKey;
|
|
7
|
-
invitation: DelegateSpaceInvitation;
|
|
8
|
-
}
|
|
9
|
-
/**
|
|
10
|
-
* Tracks the feed tree for a space.
|
|
11
|
-
* Provides a list of admitted feeds.
|
|
12
|
-
*/
|
|
13
|
-
export declare class InvitationStateMachine {
|
|
14
|
-
private readonly _invitations;
|
|
15
|
-
private readonly _redeemedInvitationCredentialIds;
|
|
16
|
-
private readonly _cancelledInvitationCredentialIds;
|
|
17
|
-
readonly onDelegatedInvitation: Callback<AsyncCallback<DelegateInvitationCredential>>;
|
|
18
|
-
readonly onDelegatedInvitationRemoved: Callback<AsyncCallback<DelegateInvitationCredential>>;
|
|
19
|
-
get invitations(): ReadonlyMap<PublicKey, DelegateSpaceInvitation>;
|
|
20
|
-
process(credential: Credential): Promise<void>;
|
|
21
|
-
}
|
|
22
|
-
//# sourceMappingURL=invitation-state-machine.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"invitation-state-machine.d.ts","sourceRoot":"","sources":["../../../../src/state-machine/invitation-state-machine.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,6CAA6C,CAAC;AAC9E,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,6CAA6C,CAAC;AAC3F,OAAO,EAAE,KAAK,aAAa,EAAE,QAAQ,EAA0B,MAAM,YAAY,CAAC;AAIlF,MAAM,WAAW,4BAA4B;IAC3C,YAAY,EAAE,SAAS,CAAC;IACxB,UAAU,EAAE,uBAAuB,CAAC;CACrC;AAED;;;GAGG;AACH,qBAAa,sBAAsB;IACjC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAsE;IACnG,OAAO,CAAC,QAAQ,CAAC,gCAAgC,CAAkC;IACnF,OAAO,CAAC,QAAQ,CAAC,iCAAiC,CAAkC;IAEpF,QAAQ,CAAC,qBAAqB,wDAA+D;IAC7F,QAAQ,CAAC,4BAA4B,wDAA+D;IAEpG,IAAI,WAAW,IAAI,WAAW,CAAC,SAAS,EAAE,uBAAuB,CAAC,CAEjE;IAEK,OAAO,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;CAoDrD"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"invitation-state-machine.test.d.ts","sourceRoot":"","sources":["../../../../src/state-machine/invitation-state-machine.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2022 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { expect } from 'chai';
|
|
6
|
-
|
|
7
|
-
import { Keyring } from '@dxos/keyring';
|
|
8
|
-
import { PublicKey } from '@dxos/keys';
|
|
9
|
-
import { Invitation } from '@dxos/protocols/proto/dxos/client/services';
|
|
10
|
-
import { SpaceMember, type Credential } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
11
|
-
import { type DelegateSpaceInvitation } from '@dxos/protocols/proto/dxos/halo/invitations';
|
|
12
|
-
import { describe, test } from '@dxos/test';
|
|
13
|
-
import { range } from '@dxos/util';
|
|
14
|
-
|
|
15
|
-
import { InvitationStateMachine } from './invitation-state-machine';
|
|
16
|
-
import {
|
|
17
|
-
createCredential,
|
|
18
|
-
createCredentialSignerWithKey,
|
|
19
|
-
createDelegatedSpaceInvitationCredential,
|
|
20
|
-
} from '../credentials';
|
|
21
|
-
|
|
22
|
-
describe('InvitationStateMachine', () => {
|
|
23
|
-
const keyring = new Keyring();
|
|
24
|
-
let space: PublicKey;
|
|
25
|
-
let identity: PublicKey;
|
|
26
|
-
const baseInvitation: DelegateSpaceInvitation = {
|
|
27
|
-
invitationId: PublicKey.random().toHex(),
|
|
28
|
-
swarmKey: PublicKey.random(),
|
|
29
|
-
role: SpaceMember.Role.ADMIN,
|
|
30
|
-
authMethod: Invitation.AuthMethod.KNOWN_PUBLIC_KEY,
|
|
31
|
-
multiUse: false,
|
|
32
|
-
};
|
|
33
|
-
beforeEach(async () => {
|
|
34
|
-
space = await keyring.createKey();
|
|
35
|
-
identity = await keyring.createKey();
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
test('invitation delegated', async () => {
|
|
39
|
-
const stateMachine = createStateMachine();
|
|
40
|
-
await stateMachine.process(await delegateInvitation(baseInvitation));
|
|
41
|
-
expectHasInvitation(stateMachine, baseInvitation);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
test('multiple invitations', async () => {
|
|
45
|
-
const stateMachine = createStateMachine();
|
|
46
|
-
const invitations = range(3).map(() => ({ ...baseInvitation, invitationId: PublicKey.random().toHex() }));
|
|
47
|
-
await Promise.all(
|
|
48
|
-
invitations.map(async (invitation) => {
|
|
49
|
-
return stateMachine.process(await delegateInvitation(invitation));
|
|
50
|
-
}),
|
|
51
|
-
);
|
|
52
|
-
invitations.forEach((invitation) => expectHasInvitation(stateMachine, invitation));
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
test('expired invitations are ignored', async () => {
|
|
56
|
-
const stateMachine = createStateMachine();
|
|
57
|
-
await stateMachine.process(
|
|
58
|
-
await delegateInvitation({
|
|
59
|
-
...baseInvitation,
|
|
60
|
-
expiresOn: new Date(Date.now() - 1),
|
|
61
|
-
}),
|
|
62
|
-
);
|
|
63
|
-
expectNoInvitation(stateMachine, baseInvitation);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
test('single use invitation redeemed when a member joins', async () => {
|
|
67
|
-
const stateMachine = createStateMachine();
|
|
68
|
-
const delegatedInvitation = await delegateInvitation(baseInvitation);
|
|
69
|
-
await stateMachine.process(delegatedInvitation);
|
|
70
|
-
await stateMachine.process(await admitMember(delegatedInvitation));
|
|
71
|
-
expectNoInvitation(stateMachine, baseInvitation);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
test('invitation stays if unrelated admission credential', async () => {
|
|
75
|
-
const stateMachine = createStateMachine();
|
|
76
|
-
const unrelatedInvitation = await delegateInvitation({
|
|
77
|
-
...baseInvitation,
|
|
78
|
-
invitationId: PublicKey.random().toHex(),
|
|
79
|
-
});
|
|
80
|
-
await stateMachine.process(await delegateInvitation(baseInvitation));
|
|
81
|
-
await stateMachine.process(await admitMember(unrelatedInvitation));
|
|
82
|
-
expectHasInvitation(stateMachine, baseInvitation);
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
test('multi-use invitations', async () => {
|
|
86
|
-
const stateMachine = createStateMachine();
|
|
87
|
-
const multiUseInvitation = { ...baseInvitation, multiUse: true };
|
|
88
|
-
const delegatedInvitation = await delegateInvitation(multiUseInvitation);
|
|
89
|
-
await stateMachine.process(delegatedInvitation);
|
|
90
|
-
await stateMachine.process(await admitMember(delegatedInvitation));
|
|
91
|
-
await stateMachine.process(await admitMember(delegatedInvitation));
|
|
92
|
-
expectHasInvitation(stateMachine, multiUseInvitation);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
test('explicit cancellation', async () => {
|
|
96
|
-
const stateMachine = createStateMachine();
|
|
97
|
-
const delegatedInvitation = await delegateInvitation(baseInvitation);
|
|
98
|
-
await stateMachine.process(delegatedInvitation);
|
|
99
|
-
await stateMachine.process(await cancelInvitation(delegatedInvitation));
|
|
100
|
-
expectNoInvitation(stateMachine, baseInvitation);
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
test('out of order joining', async () => {
|
|
104
|
-
const stateMachine = createStateMachine();
|
|
105
|
-
const delegatedInvitation = await delegateInvitation(baseInvitation);
|
|
106
|
-
await stateMachine.process(await admitMember(delegatedInvitation));
|
|
107
|
-
await stateMachine.process(delegatedInvitation);
|
|
108
|
-
expectNoInvitation(stateMachine, baseInvitation);
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
test('out of order cancellation', async () => {
|
|
112
|
-
const stateMachine = createStateMachine();
|
|
113
|
-
const delegatedInvitation = await delegateInvitation(baseInvitation);
|
|
114
|
-
await stateMachine.process(await cancelInvitation(delegatedInvitation));
|
|
115
|
-
await stateMachine.process(delegatedInvitation);
|
|
116
|
-
expectNoInvitation(stateMachine, baseInvitation);
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
const expectHasInvitation = (state: InvitationStateMachine, expected: DelegateSpaceInvitation) => {
|
|
120
|
-
const actual = [...state.invitations.values()].find((i) => i.invitationId === expected.invitationId);
|
|
121
|
-
expect(actual).to.deep.contain(expected);
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
const expectNoInvitation = (state: InvitationStateMachine, expected: DelegateSpaceInvitation) => {
|
|
125
|
-
const actual = [...state.invitations.values()].find((i) => i.invitationId === expected.invitationId);
|
|
126
|
-
expect(actual).to.be.undefined;
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
const delegateInvitation = async (invitation: DelegateSpaceInvitation): Promise<Credential> => {
|
|
130
|
-
const message = await createDelegatedSpaceInvitationCredential(
|
|
131
|
-
createCredentialSignerWithKey(keyring, identity),
|
|
132
|
-
space,
|
|
133
|
-
invitation,
|
|
134
|
-
);
|
|
135
|
-
return message.credential!.credential;
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
const cancelInvitation = async (invitation: Credential): Promise<Credential> => {
|
|
139
|
-
return createCredential({
|
|
140
|
-
issuer: space,
|
|
141
|
-
subject: identity,
|
|
142
|
-
assertion: {
|
|
143
|
-
'@type': 'dxos.halo.invitations.CancelDelegatedInvitation',
|
|
144
|
-
credentialId: invitation.id!,
|
|
145
|
-
},
|
|
146
|
-
signer: keyring,
|
|
147
|
-
});
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
const admitMember = (invitation: Credential) => {
|
|
151
|
-
return createCredential({
|
|
152
|
-
issuer: space,
|
|
153
|
-
subject: identity,
|
|
154
|
-
assertion: {
|
|
155
|
-
'@type': 'dxos.halo.credentials.SpaceMember',
|
|
156
|
-
spaceKey: space,
|
|
157
|
-
role: SpaceMember.Role.ADMIN,
|
|
158
|
-
genesisFeedKey: PublicKey.random(),
|
|
159
|
-
invitationCredentialId: invitation.id,
|
|
160
|
-
},
|
|
161
|
-
signer: keyring,
|
|
162
|
-
});
|
|
163
|
-
};
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
const createStateMachine = () => new InvitationStateMachine();
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2022 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { PublicKey } from '@dxos/keys';
|
|
6
|
-
import { type Credential } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
7
|
-
import { type DelegateSpaceInvitation } from '@dxos/protocols/proto/dxos/halo/invitations';
|
|
8
|
-
import { type AsyncCallback, Callback, ComplexMap, ComplexSet } from '@dxos/util';
|
|
9
|
-
|
|
10
|
-
import { getCredentialAssertion } from '../credentials';
|
|
11
|
-
|
|
12
|
-
export interface DelegateInvitationCredential {
|
|
13
|
-
credentialId: PublicKey;
|
|
14
|
-
invitation: DelegateSpaceInvitation;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Tracks the feed tree for a space.
|
|
19
|
-
* Provides a list of admitted feeds.
|
|
20
|
-
*/
|
|
21
|
-
export class InvitationStateMachine {
|
|
22
|
-
private readonly _invitations = new ComplexMap<PublicKey, DelegateSpaceInvitation>(PublicKey.hash);
|
|
23
|
-
private readonly _redeemedInvitationCredentialIds = new ComplexSet(PublicKey.hash);
|
|
24
|
-
private readonly _cancelledInvitationCredentialIds = new ComplexSet(PublicKey.hash);
|
|
25
|
-
|
|
26
|
-
readonly onDelegatedInvitation = new Callback<AsyncCallback<DelegateInvitationCredential>>();
|
|
27
|
-
readonly onDelegatedInvitationRemoved = new Callback<AsyncCallback<DelegateInvitationCredential>>();
|
|
28
|
-
|
|
29
|
-
get invitations(): ReadonlyMap<PublicKey, DelegateSpaceInvitation> {
|
|
30
|
-
return this._invitations;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
async process(credential: Credential): Promise<void> {
|
|
34
|
-
const credentialId = credential.id;
|
|
35
|
-
if (credentialId == null) {
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
const assertion = getCredentialAssertion(credential);
|
|
39
|
-
switch (assertion['@type']) {
|
|
40
|
-
case 'dxos.halo.invitations.CancelDelegatedInvitation': {
|
|
41
|
-
this._cancelledInvitationCredentialIds.add(assertion.credentialId);
|
|
42
|
-
const existingInvitation = this._invitations.get(assertion.credentialId);
|
|
43
|
-
if (existingInvitation != null) {
|
|
44
|
-
this._invitations.delete(assertion.credentialId);
|
|
45
|
-
await this.onDelegatedInvitationRemoved.callIfSet({
|
|
46
|
-
credentialId: assertion.credentialId,
|
|
47
|
-
invitation: existingInvitation,
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
break;
|
|
51
|
-
}
|
|
52
|
-
case 'dxos.halo.invitations.DelegateSpaceInvitation': {
|
|
53
|
-
if (credential.id) {
|
|
54
|
-
const isExpired = assertion.expiresOn && assertion.expiresOn.getTime() < Date.now();
|
|
55
|
-
const wasUsed = this._redeemedInvitationCredentialIds.has(credential.id) && !assertion.multiUse;
|
|
56
|
-
const wasCancelled = this._cancelledInvitationCredentialIds.has(credential.id);
|
|
57
|
-
if (isExpired || wasCancelled || wasUsed) {
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
const invitation: DelegateSpaceInvitation = { ...assertion };
|
|
61
|
-
this._invitations.set(credential.id, invitation);
|
|
62
|
-
await this.onDelegatedInvitation.callIfSet({
|
|
63
|
-
credentialId: credential.id,
|
|
64
|
-
invitation,
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
break;
|
|
68
|
-
}
|
|
69
|
-
case 'dxos.halo.credentials.SpaceMember': {
|
|
70
|
-
if (assertion.invitationCredentialId != null) {
|
|
71
|
-
this._redeemedInvitationCredentialIds.add(assertion.invitationCredentialId);
|
|
72
|
-
const existingInvitation = this._invitations.get(assertion.invitationCredentialId);
|
|
73
|
-
if (existingInvitation != null && !existingInvitation.multiUse) {
|
|
74
|
-
this._invitations.delete(assertion.invitationCredentialId);
|
|
75
|
-
await this.onDelegatedInvitationRemoved.callIfSet({
|
|
76
|
-
credentialId: assertion.invitationCredentialId,
|
|
77
|
-
invitation: existingInvitation,
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
break;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|