@dxos/credentials 0.8.4-main.fffef41 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +102 -5
- package/README.md +1 -1
- package/dist/lib/{browser → neutral}/index.mjs +83 -311
- package/dist/lib/neutral/index.mjs.map +7 -0
- package/dist/lib/neutral/meta.json +1 -0
- package/dist/types/src/credentials/assertions.d.ts.map +1 -1
- package/dist/types/src/credentials/credential-factory.d.ts +4 -4
- package/dist/types/src/credentials/credential-factory.d.ts.map +1 -1
- package/dist/types/src/credentials/credential-generator.d.ts +3 -3
- package/dist/types/src/credentials/credential-generator.d.ts.map +1 -1
- package/dist/types/src/credentials/signing.d.ts.map +1 -1
- package/dist/types/src/credentials/verifier.d.ts.map +1 -1
- package/dist/types/src/did.d.ts.map +1 -1
- package/dist/types/src/graph/credential-graph.d.ts.map +1 -1
- package/dist/types/src/passcode.d.ts.map +1 -1
- package/dist/types/src/presentations/presentation.d.ts.map +1 -1
- package/dist/types/src/presentations/signing.d.ts.map +1 -1
- package/dist/types/src/presentations/verifier.d.ts.map +1 -1
- package/dist/types/src/processor/device-state-machine.d.ts +2 -2
- package/dist/types/src/processor/device-state-machine.d.ts.map +1 -1
- package/dist/types/src/processor/profile-state-machine.d.ts +2 -2
- package/dist/types/src/processor/profile-state-machine.d.ts.map +1 -1
- package/dist/types/src/seedphrase.d.ts.map +1 -1
- package/dist/types/src/state-machine/feed-state-machine.d.ts.map +1 -1
- package/dist/types/src/state-machine/invitation-state-machine.d.ts.map +1 -1
- package/dist/types/src/state-machine/member-state-machine.d.ts.map +1 -1
- package/dist/types/src/state-machine/space-state-machine.d.ts +7 -1
- package/dist/types/src/state-machine/space-state-machine.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/docs/README.md +17 -17
- package/moon.yml +2 -1
- package/package.json +19 -20
- package/src/credentials/credential-factory.ts +4 -4
- package/src/credentials/credential-generator.ts +5 -0
- package/src/credentials/signing.ts +26 -0
- package/src/presentations/json-encoding.test.ts +0 -2
- package/src/presentations/presentation.ts +0 -1
- package/src/presentations/verifier.test.ts +0 -1
- package/src/presentations/verifier.ts +0 -1
- package/src/processor/device-state-machine.ts +2 -3
- package/src/processor/profile-state-machine.ts +2 -3
- package/src/state-machine/invitation-state-machine.test.ts +0 -1
- package/src/state-machine/member-state-machine.test.ts +0 -1
- package/src/state-machine/space-state-machine.test.ts +243 -2
- package/src/state-machine/space-state-machine.ts +20 -2
- package/.swc/plugins/linux_x86_64_19.0.0/727453fb3a62f7f1d952a41e051ca8a6f88cadc45cee43c6a4d1aa45f9b75665.wasmer-v7 +0 -0
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/node-esm/index.mjs +0 -1912
- package/dist/lib/node-esm/index.mjs.map +0 -7
- package/dist/lib/node-esm/meta.json +0 -1
package/docs/README.md
CHANGED
|
@@ -84,20 +84,20 @@ dxos/mesh-protocol --> dxos/codec-protobuf
|
|
|
84
84
|
|
|
85
85
|
## Dependencies
|
|
86
86
|
|
|
87
|
-
| Module
|
|
88
|
-
|
|
89
|
-
| [`@dxos/async`](../../../../common/async/docs/README.md)
|
|
90
|
-
| [`@dxos/codec-protobuf`](../../../../common/codec-protobuf/docs/README.md)
|
|
91
|
-
| [`@dxos/context`](../../../../common/context/docs/README.md)
|
|
92
|
-
| [`@dxos/crypto`](../../../../common/crypto/docs/README.md)
|
|
93
|
-
| [`@dxos/debug`](../../../../common/debug/docs/README.md)
|
|
94
|
-
| [`@dxos/feed-store`](../../../../common/feed-store/docs/README.md)
|
|
95
|
-
| [`@dxos/hypercore`](../../../../common/hypercore/docs/README.md)
|
|
96
|
-
| [`@dxos/keyring`](../../keyring/docs/README.md)
|
|
97
|
-
| [`@dxos/keys`](../../../../common/keys/docs/README.md)
|
|
98
|
-
| [`@dxos/log`](../../../../common/log/docs/README.md)
|
|
99
|
-
| [`@dxos/mesh-protocol`](../../../mesh/mesh-protocol/docs/README.md)
|
|
100
|
-
| [`@dxos/protocols`](../../../protocols/docs/README.md)
|
|
101
|
-
| [`@dxos/random-access-storage`](../../../../common/random-access-storage/docs/README.md) |
|
|
102
|
-
| [`@dxos/timeframe`](../../../../common/timeframe/docs/README.md)
|
|
103
|
-
| [`@dxos/util`](../../../../common/util/docs/README.md)
|
|
87
|
+
| Module | Direct |
|
|
88
|
+
| ---------------------------------------------------------------------------------------- | ------- |
|
|
89
|
+
| [`@dxos/async`](../../../../common/async/docs/README.md) | ✓ |
|
|
90
|
+
| [`@dxos/codec-protobuf`](../../../../common/codec-protobuf/docs/README.md) | ✓ |
|
|
91
|
+
| [`@dxos/context`](../../../../common/context/docs/README.md) | |
|
|
92
|
+
| [`@dxos/crypto`](../../../../common/crypto/docs/README.md) | ✓ |
|
|
93
|
+
| [`@dxos/debug`](../../../../common/debug/docs/README.md) | ✓ |
|
|
94
|
+
| [`@dxos/feed-store`](../../../../common/feed-store/docs/README.md) | ✓ |
|
|
95
|
+
| [`@dxos/hypercore`](../../../../common/hypercore/docs/README.md) | |
|
|
96
|
+
| [`@dxos/keyring`](../../keyring/docs/README.md) | ✓ |
|
|
97
|
+
| [`@dxos/keys`](../../../../common/keys/docs/README.md) | ✓ |
|
|
98
|
+
| [`@dxos/log`](../../../../common/log/docs/README.md) | ✓ |
|
|
99
|
+
| [`@dxos/mesh-protocol`](../../../mesh/mesh-protocol/docs/README.md) | ✓ |
|
|
100
|
+
| [`@dxos/protocols`](../../../protocols/docs/README.md) | ✓ |
|
|
101
|
+
| [`@dxos/random-access-storage`](../../../../common/random-access-storage/docs/README.md) | |
|
|
102
|
+
| [`@dxos/timeframe`](../../../../common/timeframe/docs/README.md) | |
|
|
103
|
+
| [`@dxos/util`](../../../../common/util/docs/README.md) | ✓ |
|
package/moon.yml
CHANGED
package/package.json
CHANGED
|
@@ -1,40 +1,39 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/credentials",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
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",
|
|
7
|
-
"repository":
|
|
8
|
-
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/dxos/dxos"
|
|
10
|
+
},
|
|
11
|
+
"license": "FSL-1.1-Apache-2.0",
|
|
9
12
|
"author": "info@dxos.org",
|
|
10
|
-
"sideEffects":
|
|
13
|
+
"sideEffects": false,
|
|
11
14
|
"type": "module",
|
|
12
15
|
"exports": {
|
|
13
16
|
".": {
|
|
14
17
|
"source": "./src/index.ts",
|
|
15
18
|
"types": "./dist/types/src/index.d.ts",
|
|
16
|
-
"
|
|
17
|
-
"node": "./dist/lib/node-esm/index.mjs"
|
|
19
|
+
"default": "./dist/lib/neutral/index.mjs"
|
|
18
20
|
}
|
|
19
21
|
},
|
|
20
22
|
"types": "dist/types/src/index.d.ts",
|
|
21
|
-
"typesVersions": {
|
|
22
|
-
"*": {}
|
|
23
|
-
},
|
|
24
23
|
"dependencies": {
|
|
25
24
|
"bip39": "^3.1.0",
|
|
26
25
|
"json-stable-stringify": "^1.3.0",
|
|
27
|
-
"@dxos/
|
|
28
|
-
"@dxos/
|
|
29
|
-
"@dxos/crypto": "0.
|
|
30
|
-
"@dxos/invariant": "0.
|
|
31
|
-
"@dxos/keyring": "0.
|
|
32
|
-
"@dxos/
|
|
33
|
-
"@dxos/
|
|
34
|
-
"@dxos/
|
|
35
|
-
"@dxos/
|
|
36
|
-
"@dxos/
|
|
37
|
-
"@dxos/
|
|
26
|
+
"@dxos/context": "0.9.0",
|
|
27
|
+
"@dxos/async": "0.9.0",
|
|
28
|
+
"@dxos/crypto": "0.9.0",
|
|
29
|
+
"@dxos/invariant": "0.9.0",
|
|
30
|
+
"@dxos/keyring": "0.9.0",
|
|
31
|
+
"@dxos/keys": "0.9.0",
|
|
32
|
+
"@dxos/log": "0.9.0",
|
|
33
|
+
"@dxos/node-std": "0.9.0",
|
|
34
|
+
"@dxos/timeframe": "0.9.0",
|
|
35
|
+
"@dxos/protocols": "0.9.0",
|
|
36
|
+
"@dxos/util": "0.9.0"
|
|
38
37
|
},
|
|
39
38
|
"publishConfig": {
|
|
40
39
|
"access": "public"
|
|
@@ -11,14 +11,14 @@ import { type Chain, type Credential } from '@dxos/protocols/proto/dxos/halo/cre
|
|
|
11
11
|
import { getCredentialProofPayload } from './signing';
|
|
12
12
|
import { SIGNATURE_TYPE_ED25519, verifyChain } from './verifier';
|
|
13
13
|
|
|
14
|
-
export type
|
|
14
|
+
export type CreateCredentialSignerProps = {
|
|
15
15
|
subject: PublicKey;
|
|
16
16
|
assertion: TypedMessage;
|
|
17
17
|
nonce?: Uint8Array;
|
|
18
18
|
parentCredentialIds?: PublicKey[];
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
-
export type
|
|
21
|
+
export type CreateCredentialProps = {
|
|
22
22
|
signer: Signer;
|
|
23
23
|
issuer: PublicKey;
|
|
24
24
|
signingKey?: PublicKey;
|
|
@@ -44,7 +44,7 @@ export const createCredential = async ({
|
|
|
44
44
|
chain,
|
|
45
45
|
nonce,
|
|
46
46
|
parentCredentialIds,
|
|
47
|
-
}:
|
|
47
|
+
}: CreateCredentialProps): Promise<Credential> => {
|
|
48
48
|
invariant(assertion['@type'], 'Invalid assertion.');
|
|
49
49
|
invariant(!!signingKey === !!chain, 'Chain must be provided if and only if the signing key differs from the issuer.');
|
|
50
50
|
if (chain) {
|
|
@@ -93,7 +93,7 @@ export const createCredentialMessage = (credential: Credential) => {
|
|
|
93
93
|
// TODO(burdon): Vs. Signer.
|
|
94
94
|
export interface CredentialSigner {
|
|
95
95
|
getIssuer(): PublicKey;
|
|
96
|
-
createCredential: (params:
|
|
96
|
+
createCredential: (params: CreateCredentialSignerProps) => Promise<Credential>;
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
/**
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
AdmittedFeed,
|
|
11
11
|
type Credential,
|
|
12
12
|
type DeviceProfileDocument,
|
|
13
|
+
MembershipPolicy,
|
|
13
14
|
type ProfileDocument,
|
|
14
15
|
SpaceMember,
|
|
15
16
|
} from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
@@ -38,6 +39,7 @@ export class CredentialGenerator {
|
|
|
38
39
|
spaceKey: PublicKey,
|
|
39
40
|
controlKey: PublicKey,
|
|
40
41
|
creatorProfile?: ProfileDocument,
|
|
42
|
+
membershipPolicy: MembershipPolicy = MembershipPolicy.INVITE,
|
|
41
43
|
): Promise<Credential[]> {
|
|
42
44
|
return [
|
|
43
45
|
await createCredential({
|
|
@@ -47,6 +49,7 @@ export class CredentialGenerator {
|
|
|
47
49
|
assertion: {
|
|
48
50
|
'@type': 'dxos.halo.credentials.SpaceGenesis',
|
|
49
51
|
spaceKey,
|
|
52
|
+
membershipPolicy,
|
|
50
53
|
},
|
|
51
54
|
}),
|
|
52
55
|
|
|
@@ -221,6 +224,7 @@ export const createAdmissionCredentials = async (
|
|
|
221
224
|
membershipChainHeads: PublicKey[] = [],
|
|
222
225
|
profile?: ProfileDocument,
|
|
223
226
|
invitationCredentialId?: PublicKey,
|
|
227
|
+
tags?: string[],
|
|
224
228
|
): Promise<FeedMessage.Payload[]> => {
|
|
225
229
|
const credentials = await Promise.all([
|
|
226
230
|
await signer.createCredential({
|
|
@@ -233,6 +237,7 @@ export const createAdmissionCredentials = async (
|
|
|
233
237
|
profile,
|
|
234
238
|
genesisFeedKey,
|
|
235
239
|
invitationCredentialId,
|
|
240
|
+
tags: tags ?? [],
|
|
236
241
|
},
|
|
237
242
|
}),
|
|
238
243
|
]);
|
|
@@ -27,6 +27,32 @@ export const getCredentialProofPayload = (credential: Credential): Uint8Array =>
|
|
|
27
27
|
}
|
|
28
28
|
delete copy.id; // ID is not part of the signature payload.
|
|
29
29
|
|
|
30
|
+
// Normalize proto3-default values in the assertion to avoid serialization asymmetry.
|
|
31
|
+
// Proto3 omits fields equal to their default on the wire (empty arrays, 0, "", false),
|
|
32
|
+
// and our codec doesn't restore defaults on decode — so after a round-trip the field
|
|
33
|
+
// is absent. The signing payload must pre-strip these defaults (plus `null`/`undefined`,
|
|
34
|
+
// which the canonical stringify replacer also drops) so signer and verifier produce the
|
|
35
|
+
// same canonical bytes regardless of whether the field was explicitly set to its default.
|
|
36
|
+
// Clone subject + assertion so we don't mutate the caller's credential.
|
|
37
|
+
const originalAssertion = copy.subject?.assertion;
|
|
38
|
+
if (originalAssertion) {
|
|
39
|
+
const normalizedAssertion: Record<string, any> = { ...originalAssertion };
|
|
40
|
+
for (const key of Object.keys(normalizedAssertion)) {
|
|
41
|
+
const val = normalizedAssertion[key];
|
|
42
|
+
if (
|
|
43
|
+
val === undefined ||
|
|
44
|
+
val === null ||
|
|
45
|
+
val === 0 ||
|
|
46
|
+
val === '' ||
|
|
47
|
+
val === false ||
|
|
48
|
+
(Array.isArray(val) && val.length === 0)
|
|
49
|
+
) {
|
|
50
|
+
delete normalizedAssertion[key];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
copy.subject = { ...copy.subject, assertion: normalizedAssertion as typeof originalAssertion };
|
|
54
|
+
}
|
|
55
|
+
|
|
30
56
|
return Buffer.from(canonicalStringify(copy));
|
|
31
57
|
};
|
|
32
58
|
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { inspect } from 'util';
|
|
6
|
-
|
|
7
6
|
import { describe, test } from 'vitest';
|
|
8
7
|
|
|
9
8
|
import { randomBytes } from '@dxos/crypto';
|
|
@@ -13,7 +12,6 @@ import { PresentationSchema } from '@dxos/protocols/buf/dxos/halo/credentials_pb
|
|
|
13
12
|
import { schema } from '@dxos/protocols/proto';
|
|
14
13
|
|
|
15
14
|
import { createCredential } from '../credentials';
|
|
16
|
-
|
|
17
15
|
import { signPresentation } from './presentation';
|
|
18
16
|
|
|
19
17
|
describe('json encoding', () => {
|
|
@@ -7,7 +7,6 @@ import { type PublicKey } from '@dxos/keys';
|
|
|
7
7
|
import { type Chain, type Presentation, type Proof } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
8
8
|
|
|
9
9
|
import { SIGNATURE_TYPE_ED25519 } from '../credentials';
|
|
10
|
-
|
|
11
10
|
import { getPresentationProofPayload } from './signing';
|
|
12
11
|
|
|
13
12
|
// TODO(burdon): Rename createPresentation?
|
|
@@ -10,7 +10,6 @@ import { PublicKey } from '@dxos/keys';
|
|
|
10
10
|
import { type Chain, SpaceMember } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
11
11
|
|
|
12
12
|
import { createCredential } from '../credentials';
|
|
13
|
-
|
|
14
13
|
import { signPresentation } from './presentation';
|
|
15
14
|
import { verifyPresentation, verifyPresentationSignature } from './verifier';
|
|
16
15
|
|
|
@@ -6,7 +6,6 @@ import { verifySignature } from '@dxos/crypto';
|
|
|
6
6
|
import { type Presentation, type Proof } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
7
7
|
|
|
8
8
|
import { SIGNATURE_TYPE_ED25519, type VerificationResult, verifyChain, verifyCredential } from '../credentials';
|
|
9
|
-
|
|
10
9
|
import { getPresentationProofPayload } from './signing';
|
|
11
10
|
|
|
12
11
|
export const verifyPresentation = async (presentation: Presentation): Promise<VerificationResult> => {
|
|
@@ -10,10 +10,9 @@ import { type Chain, type Credential, type DeviceProfileDocument } from '@dxos/p
|
|
|
10
10
|
import { ComplexMap } from '@dxos/util';
|
|
11
11
|
|
|
12
12
|
import { getCredentialAssertion, isValidAuthorizedDeviceCredential } from '../credentials';
|
|
13
|
-
|
|
14
13
|
import { type CredentialProcessor } from './credential-processor';
|
|
15
14
|
|
|
16
|
-
export type
|
|
15
|
+
export type DeviceStateMachineProps = {
|
|
17
16
|
identityKey: PublicKey;
|
|
18
17
|
deviceKey: PublicKey;
|
|
19
18
|
onUpdate?: () => void;
|
|
@@ -30,7 +29,7 @@ export class DeviceStateMachine implements CredentialProcessor {
|
|
|
30
29
|
|
|
31
30
|
public deviceCredentialChain?: Chain;
|
|
32
31
|
|
|
33
|
-
constructor(private readonly _params:
|
|
32
|
+
constructor(private readonly _params: DeviceStateMachineProps) {}
|
|
34
33
|
|
|
35
34
|
async processCredential(credential: Credential): Promise<void> {
|
|
36
35
|
log('processing credential...', {
|
|
@@ -7,10 +7,9 @@ import { log } from '@dxos/log';
|
|
|
7
7
|
import { type Credential, type ProfileDocument } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
8
8
|
|
|
9
9
|
import { getCredentialAssertion } from '../credentials';
|
|
10
|
-
|
|
11
10
|
import { type CredentialProcessor } from './credential-processor';
|
|
12
11
|
|
|
13
|
-
export type
|
|
12
|
+
export type ProfileStateMachineProps = {
|
|
14
13
|
identityKey: PublicKey;
|
|
15
14
|
onUpdate?: () => void;
|
|
16
15
|
};
|
|
@@ -22,7 +21,7 @@ export class ProfileStateMachine implements CredentialProcessor {
|
|
|
22
21
|
// TODO(burdon): Return values via getter.
|
|
23
22
|
public profile?: ProfileDocument;
|
|
24
23
|
|
|
25
|
-
constructor(private readonly _params:
|
|
24
|
+
constructor(private readonly _params: ProfileStateMachineProps) {}
|
|
26
25
|
|
|
27
26
|
async processCredential(credential: Credential): Promise<void> {
|
|
28
27
|
const assertion = getCredentialAssertion(credential);
|
|
@@ -10,7 +10,6 @@ import { SpaceMember } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
|
10
10
|
import { range } from '@dxos/util';
|
|
11
11
|
|
|
12
12
|
import { createAdmissionCredentials, createCredentialSignerWithKey } from '../credentials';
|
|
13
|
-
|
|
14
13
|
import { MemberStateMachine } from './member-state-machine';
|
|
15
14
|
|
|
16
15
|
const keyring = new Keyring();
|
|
@@ -6,10 +6,9 @@ import { describe, expect, test } from 'vitest';
|
|
|
6
6
|
|
|
7
7
|
import { Keyring } from '@dxos/keyring';
|
|
8
8
|
import { type PublicKey } from '@dxos/keys';
|
|
9
|
-
import { AdmittedFeed, type Chain, SpaceMember } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
9
|
+
import { AdmittedFeed, type Chain, MembershipPolicy, SpaceMember } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
10
10
|
|
|
11
11
|
import { createCredential, verifyCredential } from '../credentials';
|
|
12
|
-
|
|
13
12
|
import { SpaceStateMachine } from './space-state-machine';
|
|
14
13
|
|
|
15
14
|
describe('SpaceStateMachine', () => {
|
|
@@ -30,6 +29,7 @@ describe('SpaceStateMachine', () => {
|
|
|
30
29
|
assertion: {
|
|
31
30
|
'@type': 'dxos.halo.credentials.SpaceGenesis',
|
|
32
31
|
spaceKey: space,
|
|
32
|
+
membershipPolicy: MembershipPolicy.INVITE,
|
|
33
33
|
},
|
|
34
34
|
signer: keyring,
|
|
35
35
|
}),
|
|
@@ -130,6 +130,7 @@ describe('SpaceStateMachine', () => {
|
|
|
130
130
|
assertion: {
|
|
131
131
|
'@type': 'dxos.halo.credentials.SpaceGenesis',
|
|
132
132
|
spaceKey: space,
|
|
133
|
+
membershipPolicy: MembershipPolicy.INVITE,
|
|
133
134
|
},
|
|
134
135
|
signer: keyring,
|
|
135
136
|
}),
|
|
@@ -211,6 +212,60 @@ describe('SpaceStateMachine', () => {
|
|
|
211
212
|
expect(spaceState.credentials).toHaveLength(3);
|
|
212
213
|
});
|
|
213
214
|
|
|
215
|
+
test('space genesis with tags', async () => {
|
|
216
|
+
const keyring = new Keyring();
|
|
217
|
+
const space = await keyring.createKey();
|
|
218
|
+
const identity = await keyring.createKey();
|
|
219
|
+
const feed = await keyring.createKey();
|
|
220
|
+
|
|
221
|
+
const spaceState = new SpaceStateMachine(space);
|
|
222
|
+
|
|
223
|
+
expect(spaceState.tags).toEqual([]);
|
|
224
|
+
|
|
225
|
+
expect(
|
|
226
|
+
await spaceState.process(
|
|
227
|
+
await createCredential({
|
|
228
|
+
issuer: space,
|
|
229
|
+
subject: space,
|
|
230
|
+
assertion: {
|
|
231
|
+
'@type': 'dxos.halo.credentials.SpaceGenesis',
|
|
232
|
+
spaceKey: space,
|
|
233
|
+
tags: ['personal', 'test'],
|
|
234
|
+
membershipPolicy: MembershipPolicy.INVITE,
|
|
235
|
+
},
|
|
236
|
+
signer: keyring,
|
|
237
|
+
}),
|
|
238
|
+
{ sourceFeed: feed },
|
|
239
|
+
),
|
|
240
|
+
).toEqual(true);
|
|
241
|
+
|
|
242
|
+
expect(spaceState.tags).toEqual(['personal', 'test']);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
test('space genesis without tags returns empty array', async () => {
|
|
246
|
+
const keyring = new Keyring();
|
|
247
|
+
const space = await keyring.createKey();
|
|
248
|
+
const feed = await keyring.createKey();
|
|
249
|
+
|
|
250
|
+
const spaceState = new SpaceStateMachine(space);
|
|
251
|
+
|
|
252
|
+
await spaceState.process(
|
|
253
|
+
await createCredential({
|
|
254
|
+
issuer: space,
|
|
255
|
+
subject: space,
|
|
256
|
+
assertion: {
|
|
257
|
+
'@type': 'dxos.halo.credentials.SpaceGenesis',
|
|
258
|
+
spaceKey: space,
|
|
259
|
+
membershipPolicy: MembershipPolicy.INVITE,
|
|
260
|
+
},
|
|
261
|
+
signer: keyring,
|
|
262
|
+
}),
|
|
263
|
+
{ sourceFeed: feed },
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
expect(spaceState.tags).toEqual([]);
|
|
267
|
+
});
|
|
268
|
+
|
|
214
269
|
test('storing device credentials and building a chain', async () => {
|
|
215
270
|
const keyring = new Keyring();
|
|
216
271
|
const haloSpace = await keyring.createKey();
|
|
@@ -230,6 +285,7 @@ describe('SpaceStateMachine', () => {
|
|
|
230
285
|
assertion: {
|
|
231
286
|
'@type': 'dxos.halo.credentials.SpaceGenesis',
|
|
232
287
|
spaceKey: haloSpace,
|
|
288
|
+
membershipPolicy: MembershipPolicy.INVITE,
|
|
233
289
|
},
|
|
234
290
|
signer: keyring,
|
|
235
291
|
}),
|
|
@@ -339,4 +395,189 @@ describe('SpaceStateMachine', () => {
|
|
|
339
395
|
|
|
340
396
|
expect(await verifyCredential(credential)).toEqual({ kind: 'pass' });
|
|
341
397
|
});
|
|
398
|
+
|
|
399
|
+
test('space genesis with membership policy', async () => {
|
|
400
|
+
const keyring = new Keyring();
|
|
401
|
+
const space = await keyring.createKey();
|
|
402
|
+
const feed = await keyring.createKey();
|
|
403
|
+
|
|
404
|
+
const spaceState = new SpaceStateMachine(space);
|
|
405
|
+
|
|
406
|
+
await spaceState.process(
|
|
407
|
+
await createCredential({
|
|
408
|
+
issuer: space,
|
|
409
|
+
subject: space,
|
|
410
|
+
assertion: {
|
|
411
|
+
'@type': 'dxos.halo.credentials.SpaceGenesis',
|
|
412
|
+
spaceKey: space,
|
|
413
|
+
membershipPolicy: MembershipPolicy.LOCKED,
|
|
414
|
+
},
|
|
415
|
+
signer: keyring,
|
|
416
|
+
}),
|
|
417
|
+
{ sourceFeed: feed },
|
|
418
|
+
);
|
|
419
|
+
|
|
420
|
+
expect(spaceState.membershipPolicy).toEqual(MembershipPolicy.LOCKED);
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
test('space genesis without membership policy defaults to INVITE', async () => {
|
|
424
|
+
const keyring = new Keyring();
|
|
425
|
+
const space = await keyring.createKey();
|
|
426
|
+
const feed = await keyring.createKey();
|
|
427
|
+
|
|
428
|
+
const spaceState = new SpaceStateMachine(space);
|
|
429
|
+
|
|
430
|
+
// MembershipPolicy.INVITE is the proto zero-value default.
|
|
431
|
+
// Existing spaces without the field will have this value.
|
|
432
|
+
await spaceState.process(
|
|
433
|
+
await createCredential({
|
|
434
|
+
issuer: space,
|
|
435
|
+
subject: space,
|
|
436
|
+
assertion: {
|
|
437
|
+
'@type': 'dxos.halo.credentials.SpaceGenesis',
|
|
438
|
+
spaceKey: space,
|
|
439
|
+
membershipPolicy: MembershipPolicy.INVITE,
|
|
440
|
+
},
|
|
441
|
+
signer: keyring,
|
|
442
|
+
}),
|
|
443
|
+
{ sourceFeed: feed },
|
|
444
|
+
);
|
|
445
|
+
|
|
446
|
+
expect(spaceState.membershipPolicy).toEqual(MembershipPolicy.INVITE);
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
test('locked space rejects new members', async ({ expect }) => {
|
|
450
|
+
const keyring = new Keyring();
|
|
451
|
+
const space = await keyring.createKey();
|
|
452
|
+
const identity = await keyring.createKey();
|
|
453
|
+
const identity2 = await keyring.createKey();
|
|
454
|
+
const feed = await keyring.createKey();
|
|
455
|
+
|
|
456
|
+
const spaceState = new SpaceStateMachine(space);
|
|
457
|
+
|
|
458
|
+
await spaceState.process(
|
|
459
|
+
await createCredential({
|
|
460
|
+
issuer: space,
|
|
461
|
+
subject: space,
|
|
462
|
+
assertion: {
|
|
463
|
+
'@type': 'dxos.halo.credentials.SpaceGenesis',
|
|
464
|
+
spaceKey: space,
|
|
465
|
+
membershipPolicy: MembershipPolicy.LOCKED,
|
|
466
|
+
},
|
|
467
|
+
signer: keyring,
|
|
468
|
+
}),
|
|
469
|
+
{ sourceFeed: feed },
|
|
470
|
+
);
|
|
471
|
+
|
|
472
|
+
await spaceState.process(
|
|
473
|
+
await createCredential({
|
|
474
|
+
issuer: space,
|
|
475
|
+
subject: identity,
|
|
476
|
+
assertion: {
|
|
477
|
+
'@type': 'dxos.halo.credentials.SpaceMember',
|
|
478
|
+
spaceKey: space,
|
|
479
|
+
role: SpaceMember.Role.OWNER,
|
|
480
|
+
genesisFeedKey: feed,
|
|
481
|
+
},
|
|
482
|
+
signer: keyring,
|
|
483
|
+
}),
|
|
484
|
+
{ sourceFeed: feed },
|
|
485
|
+
);
|
|
486
|
+
|
|
487
|
+
// Attempt to add a second member — should be rejected.
|
|
488
|
+
expect(
|
|
489
|
+
await spaceState.process(
|
|
490
|
+
await createCredential({
|
|
491
|
+
issuer: space,
|
|
492
|
+
subject: identity2,
|
|
493
|
+
assertion: {
|
|
494
|
+
'@type': 'dxos.halo.credentials.SpaceMember',
|
|
495
|
+
spaceKey: space,
|
|
496
|
+
role: SpaceMember.Role.EDITOR,
|
|
497
|
+
genesisFeedKey: feed,
|
|
498
|
+
},
|
|
499
|
+
signer: keyring,
|
|
500
|
+
}),
|
|
501
|
+
{ sourceFeed: feed },
|
|
502
|
+
),
|
|
503
|
+
).toEqual(false);
|
|
504
|
+
|
|
505
|
+
expect(spaceState.members.size).toEqual(1);
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
test('locked space allows admitted feeds for existing members', async ({ expect }) => {
|
|
509
|
+
const keyring = new Keyring();
|
|
510
|
+
const space = await keyring.createKey();
|
|
511
|
+
const identity = await keyring.createKey();
|
|
512
|
+
const device = await keyring.createKey();
|
|
513
|
+
const feed = await keyring.createKey();
|
|
514
|
+
const newFeed = await keyring.createKey();
|
|
515
|
+
|
|
516
|
+
const spaceState = new SpaceStateMachine(space);
|
|
517
|
+
|
|
518
|
+
await spaceState.process(
|
|
519
|
+
await createCredential({
|
|
520
|
+
issuer: space,
|
|
521
|
+
subject: space,
|
|
522
|
+
assertion: {
|
|
523
|
+
'@type': 'dxos.halo.credentials.SpaceGenesis',
|
|
524
|
+
spaceKey: space,
|
|
525
|
+
membershipPolicy: MembershipPolicy.LOCKED,
|
|
526
|
+
},
|
|
527
|
+
signer: keyring,
|
|
528
|
+
}),
|
|
529
|
+
{ sourceFeed: feed },
|
|
530
|
+
);
|
|
531
|
+
|
|
532
|
+
await spaceState.process(
|
|
533
|
+
await createCredential({
|
|
534
|
+
issuer: space,
|
|
535
|
+
subject: identity,
|
|
536
|
+
assertion: {
|
|
537
|
+
'@type': 'dxos.halo.credentials.SpaceMember',
|
|
538
|
+
spaceKey: space,
|
|
539
|
+
role: SpaceMember.Role.OWNER,
|
|
540
|
+
genesisFeedKey: feed,
|
|
541
|
+
},
|
|
542
|
+
signer: keyring,
|
|
543
|
+
}),
|
|
544
|
+
{ sourceFeed: feed },
|
|
545
|
+
);
|
|
546
|
+
|
|
547
|
+
const chain: Chain = {
|
|
548
|
+
credential: await createCredential({
|
|
549
|
+
assertion: {
|
|
550
|
+
'@type': 'dxos.halo.credentials.AuthorizedDevice',
|
|
551
|
+
deviceKey: device,
|
|
552
|
+
identityKey: identity,
|
|
553
|
+
},
|
|
554
|
+
subject: device,
|
|
555
|
+
issuer: identity,
|
|
556
|
+
signer: keyring,
|
|
557
|
+
}),
|
|
558
|
+
};
|
|
559
|
+
|
|
560
|
+
// AdmittedFeed should still work on locked space.
|
|
561
|
+
expect(
|
|
562
|
+
await spaceState.process(
|
|
563
|
+
await createCredential({
|
|
564
|
+
issuer: identity,
|
|
565
|
+
subject: newFeed,
|
|
566
|
+
assertion: {
|
|
567
|
+
'@type': 'dxos.halo.credentials.AdmittedFeed',
|
|
568
|
+
spaceKey: space,
|
|
569
|
+
identityKey: identity,
|
|
570
|
+
deviceKey: device,
|
|
571
|
+
designation: AdmittedFeed.Designation.CONTROL,
|
|
572
|
+
},
|
|
573
|
+
signer: keyring,
|
|
574
|
+
signingKey: device,
|
|
575
|
+
chain,
|
|
576
|
+
}),
|
|
577
|
+
{ sourceFeed: feed },
|
|
578
|
+
),
|
|
579
|
+
).toEqual(true);
|
|
580
|
+
|
|
581
|
+
expect(spaceState.feeds.size).toEqual(1);
|
|
582
|
+
});
|
|
342
583
|
});
|