@fedify/fedify 2.4.0-dev.2010 → 2.4.0-dev.2030
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/{assert-CebeUf0C.mjs → assert-D1Eafmd_.mjs} +1 -1
- package/dist/{assert_equals-CBlRfA-_.mjs → assert_equals-Z6IcEbWU.mjs} +2 -28
- package/dist/{assert_exists-DtzkXC47.mjs → assert_exists-cffSpJ7X.mjs} +1 -1
- package/dist/{assert_false-3pLLLsO4.mjs → assert_false-CPdRnc3E.mjs} +1 -1
- package/dist/{assert_greater_or_equal-BApOTh1h.mjs → assert_greater_or_equal-BlafOF4-.mjs} +2 -1
- package/dist/{assert_instance_of-g3WcyYNR.mjs → assert_instance_of-mrXWWNY-.mjs} +1 -1
- package/dist/{assert_not_equals-BN7yDMWe.mjs → assert_not_equals-CpPK2t89.mjs} +2 -1
- package/dist/{assert_rejects-wFbSPn1O.mjs → assert_rejects-CTptdYck.mjs} +2 -2
- package/dist/{assert_strict_equals-Bpjl4Psw.mjs → assert_strict_equals-CYvBmkpC.mjs} +2 -1
- package/dist/{assert_throws-BJ5wuvR2.mjs → assert_throws-BYITOvPO.mjs} +2 -1
- package/dist/assertion_error-B-cx0fFA.mjs +31 -0
- package/dist/{builder-asm96yV1.mjs → builder-BYJ5otJa.mjs} +2 -2
- package/dist/compat/outgoing-jsonld.test.mjs +71 -3
- package/dist/compat/public-audience.test.mjs +3 -3
- package/dist/compat/secured-document.test.d.mts +2 -0
- package/dist/compat/secured-document.test.mjs +58 -0
- package/dist/compat/transformers.test.mjs +4 -4
- package/dist/{compound-proof-7UIAueLV.mjs → compound-proof-BIvUoJxm.mjs} +74 -2
- package/dist/{deno-Ckq9TwCv.mjs → deno-Vj_9wePq.mjs} +1 -1
- package/dist/{docloader-DUKEPG-O.mjs → docloader-qUudHZVH.mjs} +2 -2
- package/dist/federation/builder.test.mjs +5 -5
- package/dist/federation/circuit-breaker.test.mjs +3 -3
- package/dist/federation/collection.test.mjs +2 -2
- package/dist/federation/compound-fanout.test.d.mts +2 -0
- package/dist/federation/compound-fanout.test.mjs +228 -0
- package/dist/federation/handler-compound-proof.test.mjs +5 -5
- package/dist/federation/handler.test.mjs +11 -11
- package/dist/federation/idempotency.test.mjs +4 -4
- package/dist/federation/inbox.test.mjs +2 -2
- package/dist/federation/keycache.test.mjs +3 -3
- package/dist/federation/kv.test.mjs +2 -2
- package/dist/federation/metrics.test.mjs +4 -4
- package/dist/federation/middleware.test.mjs +17 -17
- package/dist/federation/mod.cjs +1 -1
- package/dist/federation/mod.js +1 -1
- package/dist/federation/mq.test.mjs +7 -6
- package/dist/federation/negotiation.test.mjs +3 -3
- package/dist/federation/outgoing-proof.test.d.mts +2 -0
- package/dist/federation/outgoing-proof.test.mjs +396 -0
- package/dist/federation/retry.test.mjs +3 -2
- package/dist/federation/router.test.mjs +5 -5
- package/dist/federation/send.test.mjs +11 -11
- package/dist/federation/tasks/enqueue.test.mjs +1 -1
- package/dist/federation/tasks/tasks.test.mjs +2 -2
- package/dist/federation/temporal.test.mjs +4 -4
- package/dist/federation/webfinger.test.mjs +4 -4
- package/dist/{federation-CgbvMauV.cjs → federation-CTnaljmy.cjs} +203 -37
- package/dist/{http-DQBfmkrS.cjs → http-B5F1JMh0.cjs} +1 -1
- package/dist/{http-DQA2JOEJ.js → http-By8tnmEr.js} +1 -1
- package/dist/{http-DZaAutqg.mjs → http-D4WPcJUe.mjs} +3 -3
- package/dist/{key-zxzx01Ec.mjs → key-B_Zc5Jbd.mjs} +2 -2
- package/dist/{kv-cache-sy8OMNT8.cjs → kv-cache-8K70pjtX.cjs} +1 -1
- package/dist/{kv-cache-CbrUX90H.mjs → kv-cache-BXdT7pJQ.mjs} +1 -1
- package/dist/{kv-cache-BBoePTOJ.js → kv-cache-BswMKQjC.js} +1 -1
- package/dist/{ld-CD_TN0e8.mjs → ld-B16KUSIQ.mjs} +3 -3
- package/dist/{metrics-B47Cm5gK.mjs → metrics-DpQqUAmh.mjs} +1 -1
- package/dist/{middleware-U_Gannqi.mjs → middleware-BL5HFFVU.mjs} +1 -1
- package/dist/{middleware-pHwDTAnq.mjs → middleware-C7_hqDnl.mjs} +143 -49
- package/dist/{middleware-U0OIuqwD.js → middleware-Cxto6Bq2.js} +204 -38
- package/dist/{mod-UDwMjuHM.d.ts → mod-CNRAxXEb.d.ts} +1 -5
- package/dist/{mod-CbKDOnCt.d.cts → mod-CVQFWefn.d.cts} +1 -5
- package/dist/mod.cjs +4 -4
- package/dist/mod.d.cts +1 -1
- package/dist/mod.d.ts +1 -1
- package/dist/mod.js +4 -4
- package/dist/nodeinfo/client.test.mjs +2 -2
- package/dist/nodeinfo/handler.test.mjs +3 -3
- package/dist/nodeinfo/types.test.mjs +3 -3
- package/dist/otel/exporter.test.mjs +2 -2
- package/dist/{outgoing-jsonld-B5Ne7JoL.mjs → outgoing-jsonld-4IJqPYos.mjs} +11 -9
- package/dist/{owner-u3IJRhVx.mjs → owner-BIOIhZoM.mjs} +2 -2
- package/dist/{owner-BNKC3s6G.js → owner-D-E0bT2O.js} +127 -35
- package/dist/{owner--mNatWct.cjs → owner-WUYke16i.cjs} +126 -34
- package/dist/{proof-D3te0SP-.mjs → proof-BVxJu5vv.mjs} +80 -23
- package/dist/{public-audience-Cc2YJ3ip.mjs → public-audience--klBIOk1.mjs} +13 -9
- package/dist/secured-document-CxzZZ9nL.mjs +35 -0
- package/dist/{send-CDr0Zivk.mjs → send-Cr5FJ7iu.mjs} +3 -3
- package/dist/sig/compound-proof-discovery.test.mjs +3 -3
- package/dist/sig/compound-proof-producer.test.d.mts +2 -0
- package/dist/sig/compound-proof-producer.test.mjs +107 -0
- package/dist/sig/compound-proof-verification.test.mjs +5 -5
- package/dist/sig/compound-proof.test.mjs +145 -58
- package/dist/sig/http.test.mjs +11 -10
- package/dist/sig/key.test.mjs +6 -6
- package/dist/sig/ld.test.mjs +9 -9
- package/dist/sig/mod.cjs +2 -2
- package/dist/sig/mod.d.cts +1 -1
- package/dist/sig/mod.d.ts +1 -1
- package/dist/sig/mod.js +2 -2
- package/dist/sig/owner.test.mjs +5 -5
- package/dist/sig/proof.test.mjs +9 -9
- package/dist/std__assert-DvMDRPes.mjs +8 -0
- package/dist/{temporal-DKhVF_Uk.mjs → temporal-CCf0lZV3.mjs} +1 -1
- package/dist/testing/kv.test.mjs +2 -2
- package/dist/utils/docloader.test.mjs +5 -5
- package/dist/utils/kv-cache.test.mjs +1 -1
- package/dist/utils/mod.cjs +1 -1
- package/dist/utils/mod.js +1 -1
- package/package.json +6 -6
- package/dist/std__assert-CxFT0MNt.mjs +0 -8
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
import { Temporal } from "temporal-polyfill";
|
|
2
|
+
import "urlpattern-polyfill";
|
|
3
|
+
globalThis.addEventListener = () => {};
|
|
4
|
+
import { t as esm_default } from "../esm-BHXXx4Wx.mjs";
|
|
5
|
+
import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
|
|
6
|
+
import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
|
|
7
|
+
import "../std__assert-DvMDRPes.mjs";
|
|
8
|
+
import { t as assertRejects } from "../assert_rejects-CTptdYck.mjs";
|
|
9
|
+
import { t as assert } from "../assert-D1Eafmd_.mjs";
|
|
10
|
+
import { t as exportJwk } from "../key-B_Zc5Jbd.mjs";
|
|
11
|
+
import { i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2 } from "../keys-DIJNHW-Y.mjs";
|
|
12
|
+
import { n as FederationImpl, o as createFederation } from "../middleware-C7_hqDnl.mjs";
|
|
13
|
+
import { s as detachSignature } from "../ld-B16KUSIQ.mjs";
|
|
14
|
+
import { r as signObject } from "../proof-BVxJu5vv.mjs";
|
|
15
|
+
import { a as verifyCompoundPortableObjectProofs } from "../compound-proof-BIvUoJxm.mjs";
|
|
16
|
+
import { Create, Note, Person } from "@fedify/vocab";
|
|
17
|
+
import { mockDocumentLoader, test } from "@fedify/fixture";
|
|
18
|
+
import { exportDidKey, parseIri } from "@fedify/vocab-runtime";
|
|
19
|
+
//#region src/federation/outgoing-proof.test.ts
|
|
20
|
+
const options = {
|
|
21
|
+
contextLoader: mockDocumentLoader,
|
|
22
|
+
documentLoader: mockDocumentLoader
|
|
23
|
+
};
|
|
24
|
+
const created = Temporal.Instant.from("2023-02-24T23:36:38Z");
|
|
25
|
+
const childContext = [
|
|
26
|
+
"https://www.w3.org/ns/activitystreams",
|
|
27
|
+
"https://w3id.org/security/data-integrity/v1",
|
|
28
|
+
{ ex: "https://example.com/ns#" }
|
|
29
|
+
];
|
|
30
|
+
const limits = {
|
|
31
|
+
maxDepth: 64,
|
|
32
|
+
maxMaps: 1e4,
|
|
33
|
+
maxProofs: 32,
|
|
34
|
+
maxBytes: 10 * 1024 * 1024
|
|
35
|
+
};
|
|
36
|
+
const recipient = {
|
|
37
|
+
id: new URL("https://example.com/users/bob"),
|
|
38
|
+
inboxId: new URL("https://example.com/inbox")
|
|
39
|
+
};
|
|
40
|
+
const rsaKey = {
|
|
41
|
+
keyId: rsaPublicKey2.id,
|
|
42
|
+
privateKey: rsaPrivateKey2
|
|
43
|
+
};
|
|
44
|
+
async function didKey(pair) {
|
|
45
|
+
const { privateKey, publicKey } = pair ?? await crypto.subtle.generateKey("Ed25519", true, ["sign", "verify"]);
|
|
46
|
+
const did = await exportDidKey(publicKey);
|
|
47
|
+
return {
|
|
48
|
+
did,
|
|
49
|
+
keyId: new URL(`${did}#${did.substring(8)}`),
|
|
50
|
+
privateKey,
|
|
51
|
+
publicKey
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function sender(key) {
|
|
55
|
+
return {
|
|
56
|
+
keyId: key.keyId,
|
|
57
|
+
privateKey: key.privateKey
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
async function signedChild(owner) {
|
|
61
|
+
return await signObject(new Note({
|
|
62
|
+
id: parseIri(`ap+ef61://${owner.did}/objects/${crypto.randomUUID()}`),
|
|
63
|
+
attribution: parseIri(`ap+ef61://${owner.did}/actor`),
|
|
64
|
+
content: "A portable note"
|
|
65
|
+
}), owner.privateKey, owner.keyId, {
|
|
66
|
+
...options,
|
|
67
|
+
context: childContext,
|
|
68
|
+
created
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
function portableCreate(owner, object) {
|
|
72
|
+
return new Create({
|
|
73
|
+
id: parseIri(`ap+ef61://${owner.did}/activities/${crypto.randomUUID()}`),
|
|
74
|
+
actor: parseIri(`ap+ef61://${owner.did}/actor`),
|
|
75
|
+
object
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
function httpCreate(object) {
|
|
79
|
+
return new Create({
|
|
80
|
+
id: new URL(`https://example.com/activities/${crypto.randomUUID()}`),
|
|
81
|
+
actor: new URL("https://example.com/users/alice"),
|
|
82
|
+
object
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
function createQueue() {
|
|
86
|
+
const queued = [];
|
|
87
|
+
return {
|
|
88
|
+
queued,
|
|
89
|
+
queue: {
|
|
90
|
+
enqueue(message) {
|
|
91
|
+
queued.push(message);
|
|
92
|
+
return Promise.resolve();
|
|
93
|
+
},
|
|
94
|
+
listen() {
|
|
95
|
+
return Promise.resolve();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function createTestFederation(queue) {
|
|
101
|
+
const federation = new FederationImpl({
|
|
102
|
+
kv: new MemoryKvStore(),
|
|
103
|
+
queue,
|
|
104
|
+
manuallyStartQueue: true,
|
|
105
|
+
contextLoaderFactory: () => mockDocumentLoader,
|
|
106
|
+
documentLoaderFactory: () => mockDocumentLoader
|
|
107
|
+
});
|
|
108
|
+
federation.setInboxListeners("/users/{identifier}/inbox", "/inbox");
|
|
109
|
+
return federation;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Captures what is POSTed to the recipient's inbox while `run` executes.
|
|
113
|
+
*/
|
|
114
|
+
async function capture(run) {
|
|
115
|
+
const bodies = [];
|
|
116
|
+
const requests = [];
|
|
117
|
+
esm_default.spyGlobal();
|
|
118
|
+
try {
|
|
119
|
+
esm_default.post(recipient.inboxId.href, async (cl) => {
|
|
120
|
+
const body = await cl.request.text();
|
|
121
|
+
requests.push(new Request(cl.request.url, {
|
|
122
|
+
method: cl.request.method,
|
|
123
|
+
headers: cl.request.headers,
|
|
124
|
+
body
|
|
125
|
+
}));
|
|
126
|
+
bodies.push(JSON.parse(body));
|
|
127
|
+
return new Response(null, { status: 202 });
|
|
128
|
+
});
|
|
129
|
+
await run();
|
|
130
|
+
} finally {
|
|
131
|
+
esm_default.hardReset();
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
bodies,
|
|
135
|
+
requests
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
function proofOf(document) {
|
|
139
|
+
const proof = document.proof;
|
|
140
|
+
assert(proof != null && typeof proof === "object" && !Array.isArray(proof), `expected exactly one direct proof, got ${JSON.stringify(proof)}`);
|
|
141
|
+
return proof;
|
|
142
|
+
}
|
|
143
|
+
async function assertCompoundVerifies(body) {
|
|
144
|
+
const result = await verifyCompoundPortableObjectProofs(detachSignature(body), limits, options);
|
|
145
|
+
assertEquals(result.status, "ok");
|
|
146
|
+
assert(result.status === "ok" && result.verified);
|
|
147
|
+
}
|
|
148
|
+
test("a portable activity with one Ed25519 key gets one direct proof", async () => {
|
|
149
|
+
const owner = await didKey();
|
|
150
|
+
const child = await signedChild(await didKey());
|
|
151
|
+
const ctx = createTestFederation().createContext(new URL("https://example.com/"));
|
|
152
|
+
const { bodies, requests } = await capture(() => ctx.sendActivity([rsaKey, sender(owner)], recipient, portableCreate(owner, child)));
|
|
153
|
+
assertEquals(bodies.length, 1);
|
|
154
|
+
assertEquals(proofOf(bodies[0]).verificationMethod, owner.keyId.href);
|
|
155
|
+
assert(requests[0].headers.has("Signature"));
|
|
156
|
+
assert(bodies[0].signature != null);
|
|
157
|
+
await assertCompoundVerifies(bodies[0]);
|
|
158
|
+
});
|
|
159
|
+
test("a portable activity is signed only by the key matching its DID", async () => {
|
|
160
|
+
const owner = await didKey();
|
|
161
|
+
const other = await didKey();
|
|
162
|
+
const childOwner = await didKey();
|
|
163
|
+
const child = await signedChild(childOwner);
|
|
164
|
+
for (const keys of [[other, owner], [owner, other]]) {
|
|
165
|
+
const ctx = createTestFederation().createContext(new URL("https://example.com/"));
|
|
166
|
+
const { bodies, requests } = await capture(() => ctx.sendActivity([
|
|
167
|
+
rsaKey,
|
|
168
|
+
...keys.map(sender),
|
|
169
|
+
sender(childOwner)
|
|
170
|
+
], recipient, portableCreate(owner, child)));
|
|
171
|
+
assertEquals(bodies.length, 1);
|
|
172
|
+
assertEquals(proofOf(bodies[0]).verificationMethod, owner.keyId.href);
|
|
173
|
+
assert(requests[0].headers.has("Signature"));
|
|
174
|
+
const embedded = bodies[0].object;
|
|
175
|
+
assertEquals(embedded["@context"], childContext);
|
|
176
|
+
assertEquals(proofOf(embedded).verificationMethod, childOwner.keyId.href);
|
|
177
|
+
await assertCompoundVerifies(bodies[0]);
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
test("a portable activity without exactly one DID-matching key is rejected", async () => {
|
|
181
|
+
const owner = await didKey();
|
|
182
|
+
const child = await signedChild(await didKey());
|
|
183
|
+
const cases = [
|
|
184
|
+
[
|
|
185
|
+
"no key matches",
|
|
186
|
+
[sender(await didKey()), sender(await didKey())],
|
|
187
|
+
"none of its 2 Ed25519 keys"
|
|
188
|
+
],
|
|
189
|
+
[
|
|
190
|
+
"a same-origin key ID is not a DID URL",
|
|
191
|
+
[{
|
|
192
|
+
keyId: parseIri(`ap+ef61://${owner.did}/actor#main-key`),
|
|
193
|
+
privateKey: owner.privateKey
|
|
194
|
+
}, sender(await didKey())],
|
|
195
|
+
"none of its 2 Ed25519 keys"
|
|
196
|
+
],
|
|
197
|
+
[
|
|
198
|
+
"several keys match",
|
|
199
|
+
[sender(owner), {
|
|
200
|
+
keyId: new URL(`${owner.did}#second`),
|
|
201
|
+
privateKey: owner.privateKey
|
|
202
|
+
}],
|
|
203
|
+
"2 of its Ed25519 keys"
|
|
204
|
+
]
|
|
205
|
+
];
|
|
206
|
+
for (const [name, keys, message] of cases) {
|
|
207
|
+
const ctx = createTestFederation().createContext(new URL("https://example.com/"));
|
|
208
|
+
const { bodies } = await capture(() => assertRejects(() => ctx.sendActivity([rsaKey, ...keys], recipient, portableCreate(owner, child)), TypeError, message));
|
|
209
|
+
assertEquals(bodies.length, 0, name);
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
test("a non-portable activity embedding portable objects needs one key", async () => {
|
|
213
|
+
const child = await signedChild(await didKey());
|
|
214
|
+
const ctx = createTestFederation().createContext(new URL("https://example.com/"));
|
|
215
|
+
const keys = [sender(await didKey()), sender(await didKey())];
|
|
216
|
+
assertEquals((await capture(() => assertRejects(() => ctx.sendActivity([rsaKey, ...keys], recipient, httpCreate(child)), TypeError, "exactly one Ed25519 key"))).bodies.length, 0);
|
|
217
|
+
const key = {
|
|
218
|
+
keyId: ed25519PublicKey.id,
|
|
219
|
+
privateKey: ed25519PrivateKey
|
|
220
|
+
};
|
|
221
|
+
const accepted = await capture(() => ctx.sendActivity([rsaKey, key], recipient, httpCreate(child)));
|
|
222
|
+
assertEquals(accepted.bodies.length, 1);
|
|
223
|
+
assertEquals(proofOf(accepted.bodies[0]).verificationMethod, ed25519PublicKey.id.href);
|
|
224
|
+
});
|
|
225
|
+
test("an activity without portable objects is still signed by every key", async () => {
|
|
226
|
+
const ctx = createTestFederation().createContext(new URL("https://example.com/"));
|
|
227
|
+
const keys = [await didKey(), await didKey()];
|
|
228
|
+
const { bodies } = await capture(() => ctx.sendActivity([rsaKey, ...keys.map(sender)], recipient, httpCreate(new Note({ content: "Hello" }))));
|
|
229
|
+
assertEquals(bodies.length, 1);
|
|
230
|
+
const proofs = bodies[0].proof;
|
|
231
|
+
assert(Array.isArray(proofs));
|
|
232
|
+
assertEquals(proofs.map((proof) => proof.verificationMethod), keys.map((key) => key.keyId.href));
|
|
233
|
+
});
|
|
234
|
+
test("a pre-signed portable activity is not re-signed", async () => {
|
|
235
|
+
const owner = await didKey();
|
|
236
|
+
const activity = await signObject(portableCreate(owner, await signedChild(await didKey())), owner.privateKey, owner.keyId, {
|
|
237
|
+
...options,
|
|
238
|
+
created
|
|
239
|
+
});
|
|
240
|
+
const ctx = createTestFederation().createContext(new URL("https://example.com/"));
|
|
241
|
+
const keys = [sender(await didKey()), sender(await didKey())];
|
|
242
|
+
const { bodies } = await capture(() => ctx.sendActivity([rsaKey, ...keys], recipient, activity));
|
|
243
|
+
assertEquals(bodies.length, 1);
|
|
244
|
+
assertEquals(proofOf(bodies[0]).verificationMethod, owner.keyId.href);
|
|
245
|
+
await assertCompoundVerifies(bodies[0]);
|
|
246
|
+
});
|
|
247
|
+
test("a portable activity carrying a proof set is not delivered", async () => {
|
|
248
|
+
const owner = await didKey();
|
|
249
|
+
const second = {
|
|
250
|
+
...owner,
|
|
251
|
+
keyId: new URL(`${owner.did}#second`)
|
|
252
|
+
};
|
|
253
|
+
let activity = portableCreate(owner, await signedChild(owner));
|
|
254
|
+
for (const key of [owner, second]) activity = await signObject(activity, key.privateKey, key.keyId, {
|
|
255
|
+
...options,
|
|
256
|
+
created
|
|
257
|
+
});
|
|
258
|
+
assertEquals((await capture(() => assertRejects(() => createTestFederation().createContext(new URL("https://example.com/")).sendActivity(rsaKey, recipient, activity), TypeError, "JSON Pointer \"/proof\""))).bodies.length, 0);
|
|
259
|
+
const outbox = createQueue();
|
|
260
|
+
await assertRejects(() => createTestFederation(outbox.queue).createContext(new URL("https://example.com/")).sendActivity(rsaKey, recipient, activity, { fanout: "skip" }), TypeError, "JSON Pointer \"/proof\"");
|
|
261
|
+
assertEquals(outbox.queued.length, 0);
|
|
262
|
+
const fanout = createQueue();
|
|
263
|
+
await assertRejects(() => createTestFederation(fanout.queue).createContext(new URL("https://example.com/")).sendActivity(rsaKey, recipient, activity, { fanout: "force" }), TypeError, "JSON Pointer \"/proof\"");
|
|
264
|
+
assertEquals(fanout.queued.length, 0);
|
|
265
|
+
});
|
|
266
|
+
test("a proof set on an embedded map is not delivered either", async () => {
|
|
267
|
+
const owner = await didKey();
|
|
268
|
+
let child = await signedChild(owner);
|
|
269
|
+
child = await signObject(child, owner.privateKey, new URL(`${owner.did}#b`), {
|
|
270
|
+
...options,
|
|
271
|
+
created
|
|
272
|
+
});
|
|
273
|
+
const ctx = createTestFederation().createContext(new URL("https://example.com/"));
|
|
274
|
+
const { bodies } = await capture(() => assertRejects(() => ctx.sendActivity(rsaKey, recipient, httpCreate(child)), TypeError, "JSON Pointer \"/object/proof\""));
|
|
275
|
+
assertEquals(bodies.length, 0);
|
|
276
|
+
});
|
|
277
|
+
test("forced fanout selects the DID-matching key before enqueueing", async () => {
|
|
278
|
+
const owner = await didKey();
|
|
279
|
+
const childOwner = await didKey();
|
|
280
|
+
const child = await signedChild(childOwner);
|
|
281
|
+
const { queue, queued } = createQueue();
|
|
282
|
+
const federation = createTestFederation(queue);
|
|
283
|
+
const ctx = federation.createContext(new URL("https://example.com/"));
|
|
284
|
+
const { bodies } = await capture(async () => {
|
|
285
|
+
await ctx.sendActivity([
|
|
286
|
+
rsaKey,
|
|
287
|
+
sender(await didKey()),
|
|
288
|
+
sender(owner)
|
|
289
|
+
], recipient, portableCreate(owner, child), { fanout: "force" });
|
|
290
|
+
assertEquals(queued.length, 1);
|
|
291
|
+
const message = queued[0];
|
|
292
|
+
assertEquals(message.type, "fanout");
|
|
293
|
+
assertEquals(message.keys.length, 3);
|
|
294
|
+
assertEquals(message.keys[0].keyId, rsaKey.keyId.href);
|
|
295
|
+
for (let i = 0; i < queued.length; i++) await federation.processQueuedTask(void 0, queued[i]);
|
|
296
|
+
});
|
|
297
|
+
assertEquals(bodies.length, 1);
|
|
298
|
+
assertEquals(proofOf(bodies[0]).verificationMethod, owner.keyId.href);
|
|
299
|
+
const embedded = bodies[0].object;
|
|
300
|
+
assertEquals(embedded["@context"], childContext);
|
|
301
|
+
assertEquals(proofOf(embedded).verificationMethod, childOwner.keyId.href);
|
|
302
|
+
await assertCompoundVerifies(bodies[0]);
|
|
303
|
+
});
|
|
304
|
+
test("the fanout worker selects one key for an unsigned portable activity", async () => {
|
|
305
|
+
const owner = await didKey();
|
|
306
|
+
const activity = portableCreate(owner, await signedChild(await didKey()));
|
|
307
|
+
const federation = createTestFederation();
|
|
308
|
+
const message = {
|
|
309
|
+
type: "fanout",
|
|
310
|
+
id: crypto.randomUUID(),
|
|
311
|
+
baseUrl: "https://example.com",
|
|
312
|
+
keys: await Promise.all([
|
|
313
|
+
rsaKey,
|
|
314
|
+
sender(await didKey()),
|
|
315
|
+
sender(owner)
|
|
316
|
+
].map(async (key) => ({
|
|
317
|
+
keyId: key.keyId.href,
|
|
318
|
+
privateKey: await exportJwk(key.privateKey)
|
|
319
|
+
}))),
|
|
320
|
+
inboxes: { [recipient.inboxId.href]: {
|
|
321
|
+
actorIds: [recipient.id.href],
|
|
322
|
+
sharedInbox: false
|
|
323
|
+
} },
|
|
324
|
+
activity: await activity.toJsonLd({
|
|
325
|
+
format: "compact",
|
|
326
|
+
...options
|
|
327
|
+
}),
|
|
328
|
+
activityId: activity.id.href,
|
|
329
|
+
activityType: "https://www.w3.org/ns/activitystreams#Create",
|
|
330
|
+
traceContext: {}
|
|
331
|
+
};
|
|
332
|
+
const { bodies } = await capture(() => federation.processQueuedTask(void 0, message));
|
|
333
|
+
assertEquals(bodies.length, 1);
|
|
334
|
+
assertEquals(proofOf(bodies[0]).verificationMethod, owner.keyId.href);
|
|
335
|
+
});
|
|
336
|
+
test("actor key pairs follow the compound-proof key selection too", async () => {
|
|
337
|
+
const dispatched = [];
|
|
338
|
+
const federation = createTestFederation();
|
|
339
|
+
federation.setActorDispatcher("/users/{identifier}", (ctx, identifier) => new Person({
|
|
340
|
+
id: ctx.getActorUri(identifier),
|
|
341
|
+
inbox: ctx.getInboxUri(identifier)
|
|
342
|
+
})).setKeyPairsDispatcher(() => dispatched);
|
|
343
|
+
const ctx = federation.createContext(new URL("https://example.com/"));
|
|
344
|
+
const owner = await didKey();
|
|
345
|
+
const child = await signedChild(await didKey());
|
|
346
|
+
const other = await didKey();
|
|
347
|
+
dispatched.push({
|
|
348
|
+
privateKey: rsaPrivateKey2,
|
|
349
|
+
publicKey: rsaPublicKey2.publicKey
|
|
350
|
+
}, {
|
|
351
|
+
privateKey: owner.privateKey,
|
|
352
|
+
publicKey: owner.publicKey
|
|
353
|
+
}, {
|
|
354
|
+
privateKey: other.privateKey,
|
|
355
|
+
publicKey: other.publicKey
|
|
356
|
+
});
|
|
357
|
+
assertEquals((await capture(() => assertRejects(() => ctx.sendActivity({ identifier: "alice" }, recipient, portableCreate(owner, child)), TypeError, "none of its 2 Ed25519 keys"))).bodies.length, 0);
|
|
358
|
+
dispatched.splice(2);
|
|
359
|
+
const presigned = await signObject(portableCreate(owner, child), owner.privateKey, owner.keyId, {
|
|
360
|
+
...options,
|
|
361
|
+
created
|
|
362
|
+
});
|
|
363
|
+
const kept = await capture(() => ctx.sendActivity({ identifier: "alice" }, recipient, presigned));
|
|
364
|
+
assertEquals(kept.bodies.length, 1);
|
|
365
|
+
assertEquals(proofOf(kept.bodies[0]).verificationMethod, owner.keyId.href);
|
|
366
|
+
const ordinary = await signObject(httpCreate(new Note({ content: "Hello" })), owner.privateKey, owner.keyId, {
|
|
367
|
+
...options,
|
|
368
|
+
created
|
|
369
|
+
});
|
|
370
|
+
const appended = await capture(() => ctx.sendActivity({ identifier: "alice" }, recipient, ordinary));
|
|
371
|
+
assertEquals(appended.bodies.length, 1);
|
|
372
|
+
const proofs = appended.bodies[0].proof;
|
|
373
|
+
assert(Array.isArray(proofs));
|
|
374
|
+
assertEquals(proofs.map((proof) => proof.verificationMethod), [owner.keyId.href, "https://example.com/users/alice#multikey-2"]);
|
|
375
|
+
});
|
|
376
|
+
test("a Fedify inbox accepts a portable activity Fedify produced", async () => {
|
|
377
|
+
const owner = await didKey();
|
|
378
|
+
const child = await signedChild(await didKey());
|
|
379
|
+
const ctx = createTestFederation().createContext(new URL("https://example.com/"));
|
|
380
|
+
const other = await didKey();
|
|
381
|
+
const { requests } = await capture(() => ctx.sendActivity([sender(other), sender(owner)], recipient, portableCreate(owner, child)));
|
|
382
|
+
assertEquals(requests.length, 1);
|
|
383
|
+
let received = 0;
|
|
384
|
+
const receiving = createFederation({
|
|
385
|
+
kv: new MemoryKvStore(),
|
|
386
|
+
contextLoaderFactory: () => mockDocumentLoader,
|
|
387
|
+
documentLoaderFactory: () => mockDocumentLoader
|
|
388
|
+
});
|
|
389
|
+
receiving.setActorDispatcher("/users/{identifier}", () => null);
|
|
390
|
+
receiving.setInboxListeners("/users/{identifier}/inbox", "/inbox").on(Create, () => {
|
|
391
|
+
received++;
|
|
392
|
+
});
|
|
393
|
+
assertEquals([(await receiving.fetch(requests[0], { contextData: void 0 })).status, received], [202, 1]);
|
|
394
|
+
});
|
|
395
|
+
//#endregion
|
|
396
|
+
export {};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { Temporal } from "temporal-polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import {
|
|
5
|
-
import { t as
|
|
4
|
+
import { t as AssertionError } from "../assertion_error-B-cx0fFA.mjs";
|
|
5
|
+
import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
|
|
6
|
+
import { t as assertNotEquals } from "../assert_not_equals-CpPK2t89.mjs";
|
|
6
7
|
import { t as createExponentialBackoffPolicy } from "../retry-zX5xw5e0.mjs";
|
|
7
8
|
import { test } from "@fedify/fixture";
|
|
8
9
|
//#region src/federation/retry.test.ts
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import "temporal-polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { t as assertEquals } from "../assert_equals-
|
|
5
|
-
import { t as assertFalse } from "../assert_false-
|
|
6
|
-
import { t as assertThrows } from "../assert_throws-
|
|
7
|
-
import "../std__assert-
|
|
8
|
-
import { t as assert } from "../assert-
|
|
4
|
+
import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
|
|
5
|
+
import { t as assertFalse } from "../assert_false-CPdRnc3E.mjs";
|
|
6
|
+
import { t as assertThrows } from "../assert_throws-BYITOvPO.mjs";
|
|
7
|
+
import "../std__assert-DvMDRPes.mjs";
|
|
8
|
+
import { t as assert } from "../assert-D1Eafmd_.mjs";
|
|
9
9
|
import { getLogger } from "@logtape/logtape";
|
|
10
10
|
import { Router, RouterError, assertPath, isPath } from "@fedify/uri-template";
|
|
11
11
|
import { test } from "@fedify/fixture";
|
|
@@ -2,18 +2,18 @@ import "temporal-polyfill";
|
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
4
|
import { t as esm_default } from "../esm-BHXXx4Wx.mjs";
|
|
5
|
-
import { t as assertEquals } from "../assert_equals-
|
|
6
|
-
import { t as assertFalse } from "../assert_false-
|
|
7
|
-
import { t as assertGreaterOrEqual } from "../assert_greater_or_equal-
|
|
8
|
-
import { t as assertInstanceOf } from "../assert_instance_of-
|
|
9
|
-
import { t as assertNotEquals } from "../assert_not_equals-
|
|
10
|
-
import "../std__assert-
|
|
11
|
-
import { t as assertRejects } from "../assert_rejects-
|
|
12
|
-
import { t as assert } from "../assert-
|
|
13
|
-
import { l as verifyRequest } from "../http-
|
|
5
|
+
import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
|
|
6
|
+
import { t as assertFalse } from "../assert_false-CPdRnc3E.mjs";
|
|
7
|
+
import { t as assertGreaterOrEqual } from "../assert_greater_or_equal-BlafOF4-.mjs";
|
|
8
|
+
import { t as assertInstanceOf } from "../assert_instance_of-mrXWWNY-.mjs";
|
|
9
|
+
import { t as assertNotEquals } from "../assert_not_equals-CpPK2t89.mjs";
|
|
10
|
+
import "../std__assert-DvMDRPes.mjs";
|
|
11
|
+
import { t as assertRejects } from "../assert_rejects-CTptdYck.mjs";
|
|
12
|
+
import { t as assert } from "../assert-D1Eafmd_.mjs";
|
|
13
|
+
import { l as verifyRequest } from "../http-D4WPcJUe.mjs";
|
|
14
14
|
import { i as rsaPrivateKey2, n as ed25519PrivateKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-DIJNHW-Y.mjs";
|
|
15
|
-
import { t as doesActorOwnKey } from "../owner-
|
|
16
|
-
import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "../send-
|
|
15
|
+
import { t as doesActorOwnKey } from "../owner-BIOIhZoM.mjs";
|
|
16
|
+
import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "../send-Cr5FJ7iu.mjs";
|
|
17
17
|
import { Activity, Application, Endpoints, Group, Person, Service } from "@fedify/vocab";
|
|
18
18
|
import { createTestMeterProvider, createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
|
|
19
19
|
import { FetchError, UrlError } from "@fedify/vocab-runtime";
|
|
@@ -4,7 +4,7 @@ globalThis.addEventListener = () => {};
|
|
|
4
4
|
import { t as MemoryKvStore } from "../../kv-DCHkaJ9-.mjs";
|
|
5
5
|
import { a as numberSchema, i as makeSchema, n as baseOptions, o as stringSchema, t as MockQueue } from "../../tasks-dbCShlCb.mjs";
|
|
6
6
|
import "../../testing-hbky2gMN.mjs";
|
|
7
|
-
import { o as createFederation } from "../../middleware-
|
|
7
|
+
import { o as createFederation } from "../../middleware-C7_hqDnl.mjs";
|
|
8
8
|
import { n as ParallelMessageQueue } from "../../mq-Dm1Wmtm-.mjs";
|
|
9
9
|
import { configure, reset } from "@logtape/logtape";
|
|
10
10
|
import { delay } from "es-toolkit";
|
|
@@ -4,8 +4,8 @@ globalThis.addEventListener = () => {};
|
|
|
4
4
|
import { t as MemoryKvStore } from "../../kv-DCHkaJ9-.mjs";
|
|
5
5
|
import { a as numberSchema, i as makeSchema, n as baseOptions, o as stringSchema, t as MockQueue } from "../../tasks-dbCShlCb.mjs";
|
|
6
6
|
import "../../testing-hbky2gMN.mjs";
|
|
7
|
-
import { o as createFederation } from "../../middleware-
|
|
8
|
-
import { r as createFederationBuilder } from "../../builder-
|
|
7
|
+
import { o as createFederation } from "../../middleware-C7_hqDnl.mjs";
|
|
8
|
+
import { r as createFederationBuilder } from "../../builder-BYJ5otJa.mjs";
|
|
9
9
|
import { t as TaskCodec } from "../../codec-C6rTrpIp.mjs";
|
|
10
10
|
import { t as InProcessMessageQueue } from "../../mq-Dm1Wmtm-.mjs";
|
|
11
11
|
import { W3CTraceContextPropagator } from "@opentelemetry/core";
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import "temporal-polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { t as assertFalse } from "../assert_false-
|
|
5
|
-
import "../std__assert-
|
|
6
|
-
import { t as assert } from "../assert-
|
|
7
|
-
import { t as hasMalformedKnownTemporalLiteral } from "../temporal-
|
|
4
|
+
import { t as assertFalse } from "../assert_false-CPdRnc3E.mjs";
|
|
5
|
+
import "../std__assert-DvMDRPes.mjs";
|
|
6
|
+
import { t as assert } from "../assert-D1Eafmd_.mjs";
|
|
7
|
+
import { t as hasMalformedKnownTemporalLiteral } from "../temporal-CCf0lZV3.mjs";
|
|
8
8
|
import { test } from "@fedify/fixture";
|
|
9
9
|
//#region src/federation/temporal.test.ts
|
|
10
10
|
test("hasMalformedKnownTemporalLiteral() detects expanded proof timestamps", async () => {
|
|
@@ -3,10 +3,10 @@ import "urlpattern-polyfill";
|
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
4
|
import { r as createRequestContext } from "../context-CbS35fLr.mjs";
|
|
5
5
|
import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
|
|
6
|
-
import { t as assertEquals } from "../assert_equals-
|
|
7
|
-
import { t as assertNotEquals } from "../assert_not_equals-
|
|
8
|
-
import "../std__assert-
|
|
9
|
-
import { o as createFederation, s as handleWebFinger } from "../middleware-
|
|
6
|
+
import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
|
|
7
|
+
import { t as assertNotEquals } from "../assert_not_equals-CpPK2t89.mjs";
|
|
8
|
+
import "../std__assert-DvMDRPes.mjs";
|
|
9
|
+
import { o as createFederation, s as handleWebFinger } from "../middleware-C7_hqDnl.mjs";
|
|
10
10
|
import { Image, Link, Person, Tombstone } from "@fedify/vocab";
|
|
11
11
|
import { createTestMeterProvider, test } from "@fedify/fixture";
|
|
12
12
|
//#region src/federation/webfinger.test.ts
|