agent-comms 3.14.0 → 3.16.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +52 -1
- package/dist/core/bridge-mesh.d.ts +2 -0
- package/dist/core/bridge-mesh.d.ts.map +1 -1
- package/dist/core/bridge-mesh.js +3 -1
- package/dist/core/bridge-mesh.js.map +1 -1
- package/dist/core/bridge.d.ts +10 -0
- package/dist/core/bridge.d.ts.map +1 -1
- package/dist/core/bridge.js +40 -1
- package/dist/core/bridge.js.map +1 -1
- package/dist/core/connection-code-tool.d.ts +18 -0
- package/dist/core/connection-code-tool.d.ts.map +1 -0
- package/dist/core/connection-code-tool.js +74 -0
- package/dist/core/connection-code-tool.js.map +1 -0
- package/dist/core/connection-code.d.ts +65 -0
- package/dist/core/connection-code.d.ts.map +1 -0
- package/dist/core/connection-code.js +180 -0
- package/dist/core/connection-code.js.map +1 -0
- package/dist/core/dm-send-delegation.d.ts +32 -0
- package/dist/core/dm-send-delegation.d.ts.map +1 -0
- package/dist/core/dm-send-delegation.js +35 -0
- package/dist/core/dm-send-delegation.js.map +1 -0
- package/dist/core/gateway-trust.d.ts +27 -6
- package/dist/core/gateway-trust.d.ts.map +1 -1
- package/dist/core/gateway-trust.js +52 -4
- package/dist/core/gateway-trust.js.map +1 -1
- package/dist/core/identity-store.d.ts +33 -1
- package/dist/core/identity-store.d.ts.map +1 -1
- package/dist/core/identity-store.js +104 -1
- package/dist/core/identity-store.js.map +1 -1
- package/dist/core/mesh-store.d.ts +15 -5
- package/dist/core/mesh-store.d.ts.map +1 -1
- package/dist/core/mesh-store.js +26 -6
- package/dist/core/mesh-store.js.map +1 -1
- package/dist/core/pgp-keyserver.d.ts +12 -0
- package/dist/core/pgp-keyserver.d.ts.map +1 -0
- package/dist/core/pgp-keyserver.js +22 -0
- package/dist/core/pgp-keyserver.js.map +1 -0
- package/dist/core/room-lifecycle.d.ts +3 -1
- package/dist/core/room-lifecycle.d.ts.map +1 -1
- package/dist/core/room-lifecycle.js +4 -3
- package/dist/core/room-lifecycle.js.map +1 -1
- package/dist/core/tool.d.ts +11 -2
- package/dist/core/tool.d.ts.map +1 -1
- package/dist/core/tool.js +12 -2
- package/dist/core/tool.js.map +1 -1
- package/dist/core/types.d.ts +41 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +25 -0
- package/dist/core/types.js.map +1 -1
- package/dist/test/bridge-mesh.test.js +14 -0
- package/dist/test/bridge-mesh.test.js.map +1 -1
- package/dist/test/connection-code-ledger-persistence.test.d.ts +5 -0
- package/dist/test/connection-code-ledger-persistence.test.d.ts.map +1 -0
- package/dist/test/connection-code-ledger-persistence.test.js +84 -0
- package/dist/test/connection-code-ledger-persistence.test.js.map +1 -0
- package/dist/test/connection-code-tool.test.d.ts +2 -0
- package/dist/test/connection-code-tool.test.d.ts.map +1 -0
- package/dist/test/connection-code-tool.test.js +296 -0
- package/dist/test/connection-code-tool.test.js.map +1 -0
- package/dist/test/connection-code.test.d.ts +5 -0
- package/dist/test/connection-code.test.d.ts.map +1 -0
- package/dist/test/connection-code.test.js +236 -0
- package/dist/test/connection-code.test.js.map +1 -0
- package/dist/test/dm-send-capability-admission.integration.test.js +82 -1
- package/dist/test/dm-send-capability-admission.integration.test.js.map +1 -1
- package/dist/test/dm-send-delegation.test.d.ts +2 -0
- package/dist/test/dm-send-delegation.test.d.ts.map +1 -0
- package/dist/test/dm-send-delegation.test.js +146 -0
- package/dist/test/dm-send-delegation.test.js.map +1 -0
- package/dist/test/dm-send-principal-delegation.integration.test.d.ts +2 -0
- package/dist/test/dm-send-principal-delegation.integration.test.d.ts.map +1 -0
- package/dist/test/dm-send-principal-delegation.integration.test.js +89 -0
- package/dist/test/dm-send-principal-delegation.integration.test.js.map +1 -0
- package/dist/test/gateway-trust.test.js +150 -1
- package/dist/test/gateway-trust.test.js.map +1 -1
- package/dist/test/identity-store.test.js +88 -1
- package/dist/test/identity-store.test.js.map +1 -1
- package/dist/test/pgp-keyserver.test.d.ts +5 -0
- package/dist/test/pgp-keyserver.test.d.ts.map +1 -0
- package/dist/test/pgp-keyserver.test.js +24 -0
- package/dist/test/pgp-keyserver.test.js.map +1 -0
- package/dist/test/test-transport.d.ts +2 -1
- package/dist/test/test-transport.d.ts.map +1 -1
- package/dist/test/test-transport.js +5 -5
- package/dist/test/test-transport.js.map +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for ConnectionCodeLedger (agent-comms#188): the generate/redeem pair backing gateway_generate_connection_code/gateway_redeem_connection_code. Covers the always-checked nonce/expiry path standalone from the optional PGP-signature path, mirroring gateway-trust.test.ts's own split between plain-trust and slot-persistence describe blocks for the sibling allowlist class.
|
|
3
|
+
*/
|
|
4
|
+
import * as fs from "node:fs";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import * as openpgp from "openpgp";
|
|
8
|
+
import { describe, expect, it, beforeAll } from "vitest";
|
|
9
|
+
import { ConnectionCodeLedger, } from "../core/connection-code.js";
|
|
10
|
+
function tempSlot(harness) {
|
|
11
|
+
const dir = fs.mkdtempSync(path.join(tmpdir(), "agent-comms-connection-code-test-"));
|
|
12
|
+
return { harness, cwd: "/tmp/project", dir };
|
|
13
|
+
}
|
|
14
|
+
const DEVICE_HEX = "aabbccdd";
|
|
15
|
+
/** A short TTL used throughout the expiry tests below -- long enough to distinguish "before" from "at/after" expiry, short enough to keep every test's own arithmetic obvious. */
|
|
16
|
+
const SHORT_TTL_MS = 1000;
|
|
17
|
+
/** Comfortably past SHORT_TTL_MS, used wherever a test needs "well after expiry" rather than "at the exact instant". */
|
|
18
|
+
const WELL_PAST_EXPIRY_MS = 2000;
|
|
19
|
+
/** A gap comfortably larger than SHORT_TTL_MS, used by the pruning test to land after the first code has expired. */
|
|
20
|
+
const PRUNE_GAP_MS = 10_000;
|
|
21
|
+
let signingKey;
|
|
22
|
+
let otherKey;
|
|
23
|
+
beforeAll(async () => {
|
|
24
|
+
const generated = await openpgp.generateKey({
|
|
25
|
+
type: "ecc",
|
|
26
|
+
curve: "curve25519Legacy",
|
|
27
|
+
userIDs: [{ name: "Alice", email: "alice@example.com" }],
|
|
28
|
+
format: "armored",
|
|
29
|
+
});
|
|
30
|
+
signingKey = generated;
|
|
31
|
+
const other = await openpgp.generateKey({
|
|
32
|
+
type: "ecc",
|
|
33
|
+
curve: "curve25519Legacy",
|
|
34
|
+
userIDs: [{ name: "Mallory", email: "mallory@example.com" }],
|
|
35
|
+
format: "armored",
|
|
36
|
+
});
|
|
37
|
+
otherKey = other;
|
|
38
|
+
});
|
|
39
|
+
describe("ConnectionCodeLedger -- generation", () => {
|
|
40
|
+
it("generates a bare code with no signature when no private key is supplied", async () => {
|
|
41
|
+
const ledger = new ConnectionCodeLedger();
|
|
42
|
+
const code = await ledger.generate(DEVICE_HEX);
|
|
43
|
+
expect(code.deviceId).toBe(DEVICE_HEX);
|
|
44
|
+
expect(code.signature).toBeUndefined();
|
|
45
|
+
expect(typeof code.code).toBe("string");
|
|
46
|
+
expect(code.code.length).toBeGreaterThan(0);
|
|
47
|
+
expect(Date.parse(code.expiresAt)).toBeGreaterThan(Date.now());
|
|
48
|
+
});
|
|
49
|
+
it("generates a fresh nonce on every call", async () => {
|
|
50
|
+
const ledger = new ConnectionCodeLedger();
|
|
51
|
+
const a = await ledger.generate(DEVICE_HEX);
|
|
52
|
+
const b = await ledger.generate(DEVICE_HEX);
|
|
53
|
+
expect(a.code).not.toBe(b.code);
|
|
54
|
+
});
|
|
55
|
+
it("honours an explicit ttlMs", async () => {
|
|
56
|
+
const ledger = new ConnectionCodeLedger();
|
|
57
|
+
const now = Date.parse("2026-01-01T00:00:00.000Z");
|
|
58
|
+
const code = await ledger.generate(DEVICE_HEX, { ttlMs: 60_000, now });
|
|
59
|
+
expect(code.expiresAt).toBe("2026-01-01T00:01:00.000Z");
|
|
60
|
+
});
|
|
61
|
+
it("signs the code when a private key is supplied, producing a signature verifiable against the matching public key", async () => {
|
|
62
|
+
const ledger = new ConnectionCodeLedger();
|
|
63
|
+
const code = await ledger.generate(DEVICE_HEX, {
|
|
64
|
+
privateKeyArmored: signingKey.privateKey,
|
|
65
|
+
});
|
|
66
|
+
expect(code.signature).toBeDefined();
|
|
67
|
+
const result = await ledger.redeem(code, {
|
|
68
|
+
publicKeyArmored: signingKey.publicKey,
|
|
69
|
+
});
|
|
70
|
+
expect(result.deviceId).toBe(DEVICE_HEX);
|
|
71
|
+
expect(result.fingerprint).toBeDefined();
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
describe("ConnectionCodeLedger -- redemption freshness/liveness (always checked)", () => {
|
|
75
|
+
it("redeems a fresh, unsigned code", async () => {
|
|
76
|
+
const ledger = new ConnectionCodeLedger();
|
|
77
|
+
const code = await ledger.generate(DEVICE_HEX);
|
|
78
|
+
const result = await ledger.redeem(code);
|
|
79
|
+
expect(result).toEqual({ deviceId: DEVICE_HEX });
|
|
80
|
+
});
|
|
81
|
+
it("rejects an expired code", async () => {
|
|
82
|
+
const ledger = new ConnectionCodeLedger();
|
|
83
|
+
const now = Date.parse("2026-01-01T00:00:00.000Z");
|
|
84
|
+
const code = await ledger.generate(DEVICE_HEX, {
|
|
85
|
+
ttlMs: SHORT_TTL_MS,
|
|
86
|
+
now,
|
|
87
|
+
});
|
|
88
|
+
await expect(ledger.redeem(code, { now: now + WELL_PAST_EXPIRY_MS })).rejects.toMatchObject({ reason: "expired" });
|
|
89
|
+
});
|
|
90
|
+
it("rejects a code at the exact instant it expires", async () => {
|
|
91
|
+
const ledger = new ConnectionCodeLedger();
|
|
92
|
+
const now = Date.parse("2026-01-01T00:00:00.000Z");
|
|
93
|
+
const code = await ledger.generate(DEVICE_HEX, {
|
|
94
|
+
ttlMs: SHORT_TTL_MS,
|
|
95
|
+
now,
|
|
96
|
+
});
|
|
97
|
+
await expect(ledger.redeem(code, { now: now + SHORT_TTL_MS })).rejects.toMatchObject({ reason: "expired" });
|
|
98
|
+
});
|
|
99
|
+
it("rejects a second redemption of the same code (single-use)", async () => {
|
|
100
|
+
const ledger = new ConnectionCodeLedger();
|
|
101
|
+
const code = await ledger.generate(DEVICE_HEX);
|
|
102
|
+
await ledger.redeem(code);
|
|
103
|
+
await expect(ledger.redeem(code)).rejects.toMatchObject({
|
|
104
|
+
reason: "already_redeemed",
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
it("rejects a code whose fields have been tampered with even though the nonce matches something once issued", async () => {
|
|
108
|
+
const ledger = new ConnectionCodeLedger();
|
|
109
|
+
const code = await ledger.generate(DEVICE_HEX);
|
|
110
|
+
const tampered = { ...code, deviceId: "112233" };
|
|
111
|
+
// Tampering the deviceId of an unsigned code can't be detected by this ledger (there is nothing to check it against) -- redemption still succeeds, but against the tampered deviceId, proving the caller's own copy of the code is what's trusted, not some hidden ledger-side truth.
|
|
112
|
+
const result = await ledger.redeem(tampered);
|
|
113
|
+
expect(result.deviceId).toBe("112233");
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
describe("ConnectionCodeLedger -- optional PGP signature (only checked when present)", () => {
|
|
117
|
+
it("rejects a signed code redeemed with no public key supplied", async () => {
|
|
118
|
+
const ledger = new ConnectionCodeLedger();
|
|
119
|
+
const code = await ledger.generate(DEVICE_HEX, {
|
|
120
|
+
privateKeyArmored: signingKey.privateKey,
|
|
121
|
+
});
|
|
122
|
+
await expect(ledger.redeem(code)).rejects.toMatchObject({
|
|
123
|
+
reason: "signature_required",
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
it("rejects a signed code verified against the wrong public key", async () => {
|
|
127
|
+
const ledger = new ConnectionCodeLedger();
|
|
128
|
+
const code = await ledger.generate(DEVICE_HEX, {
|
|
129
|
+
privateKeyArmored: signingKey.privateKey,
|
|
130
|
+
});
|
|
131
|
+
await expect(ledger.redeem(code, { publicKeyArmored: otherKey.publicKey })).rejects.toMatchObject({ reason: "signature_invalid" });
|
|
132
|
+
});
|
|
133
|
+
it("rejects a signature over a tampered field even against the correct public key", async () => {
|
|
134
|
+
const ledger = new ConnectionCodeLedger();
|
|
135
|
+
const code = await ledger.generate(DEVICE_HEX, {
|
|
136
|
+
privateKeyArmored: signingKey.privateKey,
|
|
137
|
+
});
|
|
138
|
+
const tampered = { ...code, deviceId: "112233" };
|
|
139
|
+
await expect(ledger.redeem(tampered, { publicKeyArmored: signingKey.publicKey })).rejects.toMatchObject({ reason: "signature_invalid" });
|
|
140
|
+
});
|
|
141
|
+
it("accepts a signature verified against the correct public key with no expected fingerprint pinned", async () => {
|
|
142
|
+
const ledger = new ConnectionCodeLedger();
|
|
143
|
+
const code = await ledger.generate(DEVICE_HEX, {
|
|
144
|
+
privateKeyArmored: signingKey.privateKey,
|
|
145
|
+
});
|
|
146
|
+
const result = await ledger.redeem(code, {
|
|
147
|
+
publicKeyArmored: signingKey.publicKey,
|
|
148
|
+
});
|
|
149
|
+
expect(result.deviceId).toBe(DEVICE_HEX);
|
|
150
|
+
});
|
|
151
|
+
it("accepts a signature whose verified fingerprint matches the expected (trusted) fingerprint", async () => {
|
|
152
|
+
const ledger = new ConnectionCodeLedger();
|
|
153
|
+
const code = await ledger.generate(DEVICE_HEX, {
|
|
154
|
+
privateKeyArmored: signingKey.privateKey,
|
|
155
|
+
});
|
|
156
|
+
const publicKey = await openpgp.readKey({
|
|
157
|
+
armoredKey: signingKey.publicKey,
|
|
158
|
+
});
|
|
159
|
+
const result = await ledger.redeem(code, {
|
|
160
|
+
publicKeyArmored: signingKey.publicKey,
|
|
161
|
+
expectedFingerprint: publicKey.getFingerprint(),
|
|
162
|
+
});
|
|
163
|
+
expect(result.fingerprint).toBe(publicKey.getFingerprint());
|
|
164
|
+
});
|
|
165
|
+
it("is tolerant of case and surrounding whitespace when comparing the expected fingerprint", async () => {
|
|
166
|
+
const ledger = new ConnectionCodeLedger();
|
|
167
|
+
const code = await ledger.generate(DEVICE_HEX, {
|
|
168
|
+
privateKeyArmored: signingKey.privateKey,
|
|
169
|
+
});
|
|
170
|
+
const publicKey = await openpgp.readKey({
|
|
171
|
+
armoredKey: signingKey.publicKey,
|
|
172
|
+
});
|
|
173
|
+
const result = await ledger.redeem(code, {
|
|
174
|
+
publicKeyArmored: signingKey.publicKey,
|
|
175
|
+
expectedFingerprint: ` ${publicKey.getFingerprint().toUpperCase()} `,
|
|
176
|
+
});
|
|
177
|
+
expect(result.deviceId).toBe(DEVICE_HEX);
|
|
178
|
+
});
|
|
179
|
+
it("rejects a signature that verifies but against an unexpected fingerprint", async () => {
|
|
180
|
+
const ledger = new ConnectionCodeLedger();
|
|
181
|
+
const code = await ledger.generate(DEVICE_HEX, {
|
|
182
|
+
privateKeyArmored: signingKey.privateKey,
|
|
183
|
+
});
|
|
184
|
+
await expect(ledger.redeem(code, {
|
|
185
|
+
publicKeyArmored: signingKey.publicKey,
|
|
186
|
+
expectedFingerprint: "0000000000000000000000000000000000000000",
|
|
187
|
+
})).rejects.toMatchObject({ reason: "fingerprint_mismatch" });
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
describe("ConnectionCodeLedger -- persistence (agent-comms#188)", () => {
|
|
191
|
+
it("constructed with no slot, never touches disk", async () => {
|
|
192
|
+
const ledger = new ConnectionCodeLedger();
|
|
193
|
+
const code = await ledger.generate(DEVICE_HEX);
|
|
194
|
+
await expect(ledger.redeem(code)).resolves.toEqual({
|
|
195
|
+
deviceId: DEVICE_HEX,
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
it("survives a restart: an issued code recorded before restart is still recorded after", async () => {
|
|
199
|
+
const slot = tempSlot("test");
|
|
200
|
+
const first = new ConnectionCodeLedger(slot);
|
|
201
|
+
const now = Date.parse("2026-01-01T00:00:00.000Z");
|
|
202
|
+
await first.generate(DEVICE_HEX, { ttlMs: 60_000, now });
|
|
203
|
+
const restarted = new ConnectionCodeLedger(slot);
|
|
204
|
+
// The restarted instance still knows about the redemption record it will create once this same code is redeemed through it -- proven indirectly below by confirming a redemption recorded before restart is not forgotten after it.
|
|
205
|
+
expect(restarted).toBeInstanceOf(ConnectionCodeLedger);
|
|
206
|
+
});
|
|
207
|
+
it("survives a restart: single-use enforcement holds across a restart within the code's validity window", async () => {
|
|
208
|
+
const slot = tempSlot("test");
|
|
209
|
+
const now = Date.parse("2026-01-01T00:00:00.000Z");
|
|
210
|
+
const issuer = new ConnectionCodeLedger();
|
|
211
|
+
const code = await issuer.generate(DEVICE_HEX, { ttlMs: 60_000, now });
|
|
212
|
+
const redeemer = new ConnectionCodeLedger(slot);
|
|
213
|
+
await redeemer.redeem(code, { now });
|
|
214
|
+
const restartedRedeemer = new ConnectionCodeLedger(slot);
|
|
215
|
+
await expect(restartedRedeemer.redeem(code, { now: now + SHORT_TTL_MS })).rejects.toMatchObject({ reason: "already_redeemed" });
|
|
216
|
+
});
|
|
217
|
+
it("prunes an expired redeemed nonce so it does not accumulate forever", async () => {
|
|
218
|
+
const slot = tempSlot("test");
|
|
219
|
+
const now = Date.parse("2026-01-01T00:00:00.000Z");
|
|
220
|
+
const issuer = new ConnectionCodeLedger();
|
|
221
|
+
const code = await issuer.generate(DEVICE_HEX, {
|
|
222
|
+
ttlMs: SHORT_TTL_MS,
|
|
223
|
+
now,
|
|
224
|
+
});
|
|
225
|
+
const redeemer = new ConnectionCodeLedger(slot);
|
|
226
|
+
await redeemer.redeem(code, { now });
|
|
227
|
+
// Generating a second, unrelated code well after the first has expired triggers pruning; the first nonce is no longer tracked as redeemed.
|
|
228
|
+
const laterCode = await issuer.generate(DEVICE_HEX, {
|
|
229
|
+
ttlMs: SHORT_TTL_MS,
|
|
230
|
+
now: now + PRUNE_GAP_MS,
|
|
231
|
+
});
|
|
232
|
+
const laterRedeemer = new ConnectionCodeLedger(slot);
|
|
233
|
+
await laterRedeemer.redeem(laterCode, { now: now + PRUNE_GAP_MS });
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
//# sourceMappingURL=connection-code.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection-code.test.js","sourceRoot":"","sources":["../../src/test/connection-code.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Integration test for receiver-side DM gating with a user-issued capability (agent-comms#162): a receiver's own user principal can admit an agent into its DM-communication scope by minting a dm:send grant, which the agent then presents alongside its DM join request to auto-admit without needing a fresh human decision each time -- the durable admission list this issue adds, distinct from (and additive to) dm-admission.integration.test.ts's own two-round human-consent flow, which remains the fallback when no dm:send grant is presented at all.
|
|
3
3
|
*/
|
|
4
|
+
import * as fs from "node:fs";
|
|
5
|
+
import * as path from "node:path";
|
|
6
|
+
import { tmpdir } from "node:os";
|
|
4
7
|
import { test, expect } from "vitest";
|
|
5
|
-
import { deviceIdFromHex } from "wire-mesh-core/domain/device-id";
|
|
8
|
+
import { deviceIdFromHex, deviceIdToHex, } from "wire-mesh-core/domain/device-id";
|
|
9
|
+
import { mintCapabilityToken } from "wire-mesh-core/domain/tokens";
|
|
10
|
+
import { createSystemClock } from "wire-mesh-core/adapters/system-clock";
|
|
6
11
|
import { MeshStore } from "../core/mesh-store.js";
|
|
12
|
+
import { generateIdentity } from "../core/identity.js";
|
|
13
|
+
import { toIdentityPort } from "../core/wire-mesh-identity.js";
|
|
14
|
+
import { randomId } from "../core/random-id.js";
|
|
15
|
+
import { loadOrCreateUserIdentity } from "../core/user-identity.js";
|
|
16
|
+
import { DM_SEND_CAPABILITY } from "../core/dm-token-verification.js";
|
|
7
17
|
import { waitFor, wireTestTransport } from "./test-transport.js";
|
|
8
18
|
/** A device-id, hex-encoded, is always exactly this many characters (32 raw bytes). */
|
|
9
19
|
const DEVICE_ID_HEX_LENGTH = 64;
|
|
20
|
+
/** Expiry window for a delegated dm:send token minted directly in these tests (agent-comms#187) -- comfortably longer than any single test run, matching device-membership.test.ts's own TOKEN_TTL_MS convention. */
|
|
21
|
+
const DELEGATED_TOKEN_TTL_MS = 60_000;
|
|
10
22
|
let nextPort = 20_990;
|
|
11
23
|
function freshPort() {
|
|
12
24
|
nextPort += 1;
|
|
@@ -87,4 +99,73 @@ test("revoking a bearer that was never admitted is a harmless no-op", async () =
|
|
|
87
99
|
await a.shutdown();
|
|
88
100
|
}
|
|
89
101
|
});
|
|
102
|
+
/** B's own user-principal identity, loaded from a directory this test controls directly (rather than the throwaway one wireTestTransport would otherwise generate internally and never disclose), so a test can compute the exact "user" scope path (B's own principal device-id, hex) a dm:send grant B mints is rooted at -- agent-comms#187's own principal-keyed admission tests need this to construct a further delegation whose scope actually narrows the grant it chains from. */
|
|
103
|
+
function tempUserIdentityDir() {
|
|
104
|
+
return {
|
|
105
|
+
dir: fs.mkdtempSync(path.join(tmpdir(), "agent-comms-b-user-identity-test-")),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
test("admitting a bearer with a positive delegationsRemaining lets that bearer itself mint a further dm:send delegation (agent-comms#187)", async () => {
|
|
109
|
+
const bUserIdentityOptions = tempUserIdentityDir();
|
|
110
|
+
const bPrincipalHex = deviceIdToHex((await toIdentityPort(loadOrCreateUserIdentity(bUserIdentityOptions)))
|
|
111
|
+
.deviceId);
|
|
112
|
+
const clock = createSystemClock();
|
|
113
|
+
const b = new MeshStore(freshPort());
|
|
114
|
+
await wireTestTransport(b, undefined, undefined, undefined, bUserIdentityOptions);
|
|
115
|
+
await b.init();
|
|
116
|
+
try {
|
|
117
|
+
// The admitted bearer is a real principal identity here, not just a bare hex string -- only the entity actually holding bearerId's own key can mint a delegation of the grant, since mintCapabilityToken's own parent-narrowing requires the child's issuer to equal the parent's bearer.
|
|
118
|
+
const principal = await toIdentityPort(generateIdentity());
|
|
119
|
+
const bearerHex = deviceIdToHex(principal.deviceId);
|
|
120
|
+
const grant = await b.admitAgentForDm(bearerHex, 1);
|
|
121
|
+
const device = await toIdentityPort(generateIdentity());
|
|
122
|
+
const delegated = await mintCapabilityToken({
|
|
123
|
+
identity: principal,
|
|
124
|
+
clock,
|
|
125
|
+
tokenId: randomId(),
|
|
126
|
+
bearer: device.deviceId,
|
|
127
|
+
capability: DM_SEND_CAPABILITY,
|
|
128
|
+
scope: { kind: "user", path: bPrincipalHex },
|
|
129
|
+
expires: clock.now() + DELEGATED_TOKEN_TTL_MS,
|
|
130
|
+
delegationsRemaining: 0,
|
|
131
|
+
parent: grant,
|
|
132
|
+
});
|
|
133
|
+
expect(delegated.ok, `expected the delegation to succeed, got ${JSON.stringify(delegated)}`).toBe(true);
|
|
134
|
+
}
|
|
135
|
+
finally {
|
|
136
|
+
await b.shutdown();
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
test("admitting a bearer with no delegationsRemaining given stays non-delegable, exactly like the existing bare-device path", async () => {
|
|
140
|
+
const bUserIdentityOptions = tempUserIdentityDir();
|
|
141
|
+
const bPrincipalHex = deviceIdToHex((await toIdentityPort(loadOrCreateUserIdentity(bUserIdentityOptions)))
|
|
142
|
+
.deviceId);
|
|
143
|
+
const clock = createSystemClock();
|
|
144
|
+
const b = new MeshStore(freshPort());
|
|
145
|
+
await wireTestTransport(b, undefined, undefined, undefined, bUserIdentityOptions);
|
|
146
|
+
await b.init();
|
|
147
|
+
try {
|
|
148
|
+
const principal = await toIdentityPort(generateIdentity());
|
|
149
|
+
const bearerHex = deviceIdToHex(principal.deviceId);
|
|
150
|
+
const grant = await b.admitAgentForDm(bearerHex);
|
|
151
|
+
const device = await toIdentityPort(generateIdentity());
|
|
152
|
+
const delegated = await mintCapabilityToken({
|
|
153
|
+
identity: principal,
|
|
154
|
+
clock,
|
|
155
|
+
tokenId: randomId(),
|
|
156
|
+
bearer: device.deviceId,
|
|
157
|
+
capability: DM_SEND_CAPABILITY,
|
|
158
|
+
scope: { kind: "user", path: bPrincipalHex },
|
|
159
|
+
expires: clock.now() + DELEGATED_TOKEN_TTL_MS,
|
|
160
|
+
delegationsRemaining: 0,
|
|
161
|
+
parent: grant,
|
|
162
|
+
});
|
|
163
|
+
expect(delegated.ok).toBe(false);
|
|
164
|
+
if (!delegated.ok)
|
|
165
|
+
expect(delegated.reason).toBe("delegation_exceeds_parent");
|
|
166
|
+
}
|
|
167
|
+
finally {
|
|
168
|
+
await b.shutdown();
|
|
169
|
+
}
|
|
170
|
+
});
|
|
90
171
|
//# sourceMappingURL=dm-send-capability-admission.integration.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dm-send-capability-admission.integration.test.js","sourceRoot":"","sources":["../../src/test/dm-send-capability-admission.integration.test.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,
|
|
1
|
+
{"version":3,"file":"dm-send-capability-admission.integration.test.js","sourceRoot":"","sources":["../../src/test/dm-send-capability-admission.integration.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dm-send-delegation.test.d.ts","sourceRoot":"","sources":["../../src/test/dm-send-delegation.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for core/dm-send-delegation: delegateDmSendToDevice, the primitive that lets a user principal already admitted into a remote user's DM-communication scope (room-lifecycle.ts's admitAgentForDm, minted with delegationsRemaining \> 0) sub-delegate that admission to one of its own devices (agent-comms#187) -- the dm:send counterpart to device-membership.test.ts's own admitDevice/removeDevice coverage.
|
|
3
|
+
*/
|
|
4
|
+
import * as fs from "node:fs";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { describe, it, expect } from "vitest";
|
|
8
|
+
import { deviceIdToHex } from "wire-mesh-core/domain/device-id";
|
|
9
|
+
import { mintCapabilityToken } from "wire-mesh-core/domain/tokens";
|
|
10
|
+
import { createSystemClock } from "wire-mesh-core/adapters/system-clock";
|
|
11
|
+
import { createRevocationView } from "wire-mesh-core/domain/revocation-view";
|
|
12
|
+
import { generateIdentity } from "../core/identity.js";
|
|
13
|
+
import { toIdentityPort } from "../core/wire-mesh-identity.js";
|
|
14
|
+
import { randomId } from "../core/random-id.js";
|
|
15
|
+
import { loadIssuedDmGrant, loadOrCreateUserIdentity, } from "../core/user-identity.js";
|
|
16
|
+
import { DM_SEND_CAPABILITY, DM_SEND_SCOPE_KIND, verifyDmSendToken, } from "../core/dm-token-verification.js";
|
|
17
|
+
import { delegateDmSendToDevice } from "../core/dm-send-delegation.js";
|
|
18
|
+
const TOKEN_TTL_MS = 60_000;
|
|
19
|
+
const NON_DELEGABLE = 0;
|
|
20
|
+
const ONE_HOP_DELEGABLE = 1;
|
|
21
|
+
function tempDir() {
|
|
22
|
+
return fs.mkdtempSync(path.join(tmpdir(), "agent-comms-dm-delegation-test-"));
|
|
23
|
+
}
|
|
24
|
+
/** A real, persisted user-principal identity -- delegateDmSendToDevice writes through to the same user-identity.json this identity's own key material lives in, so the fixture must actually create that file first, the same way device-membership.test.ts's own makeUser() does. */
|
|
25
|
+
async function makeUser() {
|
|
26
|
+
const dir = tempDir();
|
|
27
|
+
const identity = await toIdentityPort(loadOrCreateUserIdentity({ dir }));
|
|
28
|
+
return { identity, userIdentityOptions: { dir } };
|
|
29
|
+
}
|
|
30
|
+
/** Alice's own root-level dm:send grant naming bearer as the admitted party -- the same shape room-lifecycle.ts's admitAgentForDm mints, built directly here so this file's own tests don't need a full MeshStore. Takes an explicit expires (rather than deriving one from clock.now() itself) so a caller can mint a child token sharing the identical expiry -- a real wall clock advances between two calls, and a child's own expires must never exceed its parent's. */
|
|
31
|
+
async function mintRootGrant(admitter, bearer, clock, delegationsRemaining, expires) {
|
|
32
|
+
const verdict = await mintCapabilityToken({
|
|
33
|
+
identity: admitter,
|
|
34
|
+
clock,
|
|
35
|
+
tokenId: randomId(),
|
|
36
|
+
bearer,
|
|
37
|
+
capability: DM_SEND_CAPABILITY,
|
|
38
|
+
scope: { kind: DM_SEND_SCOPE_KIND, path: deviceIdToHex(admitter.deviceId) },
|
|
39
|
+
expires,
|
|
40
|
+
delegationsRemaining,
|
|
41
|
+
});
|
|
42
|
+
if (!verdict.ok)
|
|
43
|
+
throw new Error("expected root grant to mint");
|
|
44
|
+
return verdict.token;
|
|
45
|
+
}
|
|
46
|
+
describe("delegateDmSendToDevice", () => {
|
|
47
|
+
it("mints a dm:send token bearing the device, chaining back to the remote admitting principal", async () => {
|
|
48
|
+
const alice = await toIdentityPort(generateIdentity());
|
|
49
|
+
const { identity: bobPrincipal, userIdentityOptions } = await makeUser();
|
|
50
|
+
const bobDevice = await toIdentityPort(generateIdentity());
|
|
51
|
+
const clock = createSystemClock();
|
|
52
|
+
const expires = clock.now() + TOKEN_TTL_MS;
|
|
53
|
+
const grant = await mintRootGrant(alice, bobPrincipal.deviceId, clock, ONE_HOP_DELEGABLE, expires);
|
|
54
|
+
const verdict = await delegateDmSendToDevice({
|
|
55
|
+
userIdentity: bobPrincipal,
|
|
56
|
+
userIdentityOptions,
|
|
57
|
+
clock,
|
|
58
|
+
tokenId: randomId(),
|
|
59
|
+
parent: grant,
|
|
60
|
+
deviceId: bobDevice.deviceId,
|
|
61
|
+
remoteUserPrincipalDeviceId: alice.deviceId,
|
|
62
|
+
expires,
|
|
63
|
+
});
|
|
64
|
+
expect(verdict.ok, `expected delegation to succeed, got ${JSON.stringify(verdict)}`).toBe(true);
|
|
65
|
+
if (!verdict.ok)
|
|
66
|
+
return;
|
|
67
|
+
const checked = await verifyDmSendToken(verdict.token, {
|
|
68
|
+
identity: alice,
|
|
69
|
+
clock,
|
|
70
|
+
revocation: createRevocationView(),
|
|
71
|
+
expectedBearer: bobDevice.deviceId,
|
|
72
|
+
userPrincipalDeviceId: alice.deviceId,
|
|
73
|
+
});
|
|
74
|
+
expect(checked.ok, JSON.stringify(checked)).toBe(true);
|
|
75
|
+
});
|
|
76
|
+
it("records the delegated grant's token-id under the delegating principal's own store, keyed by device hex", async () => {
|
|
77
|
+
const alice = await toIdentityPort(generateIdentity());
|
|
78
|
+
const { identity: bobPrincipal, userIdentityOptions } = await makeUser();
|
|
79
|
+
const bobDevice = await toIdentityPort(generateIdentity());
|
|
80
|
+
const clock = createSystemClock();
|
|
81
|
+
const tokenId = randomId();
|
|
82
|
+
const expires = clock.now() + TOKEN_TTL_MS;
|
|
83
|
+
const grant = await mintRootGrant(alice, bobPrincipal.deviceId, clock, ONE_HOP_DELEGABLE, expires);
|
|
84
|
+
const verdict = await delegateDmSendToDevice({
|
|
85
|
+
userIdentity: bobPrincipal,
|
|
86
|
+
userIdentityOptions,
|
|
87
|
+
clock,
|
|
88
|
+
tokenId,
|
|
89
|
+
parent: grant,
|
|
90
|
+
deviceId: bobDevice.deviceId,
|
|
91
|
+
remoteUserPrincipalDeviceId: alice.deviceId,
|
|
92
|
+
expires,
|
|
93
|
+
});
|
|
94
|
+
expect(verdict.ok).toBe(true);
|
|
95
|
+
const deviceHex = deviceIdToHex(bobDevice.deviceId);
|
|
96
|
+
expect(loadIssuedDmGrant(userIdentityOptions, deviceHex)).toEqual(tokenId);
|
|
97
|
+
});
|
|
98
|
+
it("refuses when the parent grant carries no further delegation depth, and records nothing", async () => {
|
|
99
|
+
const alice = await toIdentityPort(generateIdentity());
|
|
100
|
+
const { identity: bobPrincipal, userIdentityOptions } = await makeUser();
|
|
101
|
+
const bobDevice = await toIdentityPort(generateIdentity());
|
|
102
|
+
const clock = createSystemClock();
|
|
103
|
+
const expires = clock.now() + TOKEN_TTL_MS;
|
|
104
|
+
// The original, non-delegable admission -- exactly what admitAgentForDm mints by default.
|
|
105
|
+
const grant = await mintRootGrant(alice, bobPrincipal.deviceId, clock, NON_DELEGABLE, expires);
|
|
106
|
+
const verdict = await delegateDmSendToDevice({
|
|
107
|
+
userIdentity: bobPrincipal,
|
|
108
|
+
userIdentityOptions,
|
|
109
|
+
clock,
|
|
110
|
+
tokenId: randomId(),
|
|
111
|
+
parent: grant,
|
|
112
|
+
deviceId: bobDevice.deviceId,
|
|
113
|
+
remoteUserPrincipalDeviceId: alice.deviceId,
|
|
114
|
+
expires,
|
|
115
|
+
});
|
|
116
|
+
expect(verdict.ok).toBe(false);
|
|
117
|
+
if (!verdict.ok)
|
|
118
|
+
expect(verdict.reason).toBe("delegation_exceeds_parent");
|
|
119
|
+
const deviceHex = deviceIdToHex(bobDevice.deviceId);
|
|
120
|
+
expect(loadIssuedDmGrant(userIdentityOptions, deviceHex)).toBeUndefined();
|
|
121
|
+
});
|
|
122
|
+
it("refuses when the caller's identity is not the parent grant's own bearer", async () => {
|
|
123
|
+
const alice = await toIdentityPort(generateIdentity());
|
|
124
|
+
const { identity: bobPrincipal, userIdentityOptions } = await makeUser();
|
|
125
|
+
const someoneElse = await toIdentityPort(generateIdentity());
|
|
126
|
+
const bobDevice = await toIdentityPort(generateIdentity());
|
|
127
|
+
const clock = createSystemClock();
|
|
128
|
+
const expires = clock.now() + TOKEN_TTL_MS;
|
|
129
|
+
const grant = await mintRootGrant(alice, bobPrincipal.deviceId, clock, ONE_HOP_DELEGABLE, expires);
|
|
130
|
+
const verdict = await delegateDmSendToDevice({
|
|
131
|
+
// someoneElse never received this grant -- only bobPrincipal (the parent's own bearer) may mint a delegation of it.
|
|
132
|
+
userIdentity: someoneElse,
|
|
133
|
+
userIdentityOptions,
|
|
134
|
+
clock,
|
|
135
|
+
tokenId: randomId(),
|
|
136
|
+
parent: grant,
|
|
137
|
+
deviceId: bobDevice.deviceId,
|
|
138
|
+
remoteUserPrincipalDeviceId: alice.deviceId,
|
|
139
|
+
expires,
|
|
140
|
+
});
|
|
141
|
+
expect(verdict.ok).toBe(false);
|
|
142
|
+
if (!verdict.ok)
|
|
143
|
+
expect(verdict.reason).toBe("parent_bearer_mismatch");
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
//# sourceMappingURL=dm-send-delegation.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dm-send-delegation.test.js","sourceRoot":"","sources":["../../src/test/dm-send-delegation.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dm-send-principal-delegation.integration.test.d.ts","sourceRoot":"","sources":["../../src/test/dm-send-principal-delegation.integration.test.ts"],"names":[],"mappings":""}
|