@fedify/fedify 2.4.0-dev.2065 → 2.4.0-dev.2071
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/{builder-CMkrnYbY.mjs → builder-DVQ0yhKJ.mjs} +6 -2
- package/dist/compat/mod.d.cts +1 -1
- package/dist/compat/mod.d.ts +1 -1
- package/dist/compat/transformers.test.mjs +1 -1
- package/dist/{compound-proof-Dj3DCsoj.mjs → compound-proof-KmXDfWFp.mjs} +1 -1
- package/dist/{context-b36TpPoG.d.cts → context-DijrgHDe.d.cts} +45 -2
- package/dist/{context-D_gg4M0n.d.ts → context-DsB9kF6q.d.ts} +45 -2
- package/dist/{deno-BuRFI7wk.mjs → deno-B-NtYZVW.mjs} +1 -1
- package/dist/{docloader-_aSNLA90.mjs → docloader-C6yOVoaF.mjs} +2 -2
- package/dist/federation/builder.test.mjs +1 -1
- package/dist/federation/compound-fanout.test.mjs +4 -4
- package/dist/federation/handler-compound-proof.test.mjs +3 -3
- package/dist/federation/handler.test.mjs +5 -5
- package/dist/federation/hashlink-media.test.mjs +2 -2
- package/dist/federation/idempotency.test.mjs +2 -2
- package/dist/federation/metrics.test.mjs +1 -1
- package/dist/federation/middleware.test.mjs +8 -8
- package/dist/federation/mod.cjs +1 -1
- package/dist/federation/mod.d.cts +3 -3
- package/dist/federation/mod.d.ts +3 -3
- package/dist/federation/mod.js +1 -1
- package/dist/federation/outgoing-proof.test.mjs +6 -6
- package/dist/federation/portable-gateway-keys.test.d.mts +2 -0
- package/dist/federation/portable-gateway-keys.test.mjs +363 -0
- package/dist/federation/portable.test.mjs +3 -3
- package/dist/federation/send.test.mjs +3 -3
- package/dist/federation/tasks/enqueue.test.mjs +1 -1
- package/dist/federation/tasks/tasks.test.mjs +2 -2
- package/dist/federation/temporal.test.mjs +1 -1
- package/dist/federation/webfinger.test.mjs +1 -1
- package/dist/{federation-Coi_bg12.cjs → federation-CUiHc7dP.cjs} +182 -76
- package/dist/http-BoWBwC0g.js +5087 -0
- package/dist/{http-BGShIsd6.cjs → http-CuLWIJdf.cjs} +2684 -48
- package/dist/{http-f1qaBtOh.mjs → http-DKWi8RjI.mjs} +20 -5
- package/dist/{http-65PYi9jY.d.ts → http-n2v6DPxu.d.ts} +52 -2
- package/dist/{http-CP-7OdSq.d.cts → http-xlG-jRdB.d.cts} +52 -2
- package/dist/{key-BxHsA1s9.mjs → key-lfcx_NaE.mjs} +147 -7
- package/dist/{kv-cache-Cgj-kpQS.js → kv-cache-BdnmnAVc.js} +1 -1
- package/dist/{kv-cache-CY6pDNoS.cjs → kv-cache-DE8kSNQU.cjs} +1 -1
- package/dist/{kv-cache-C9IY-FMN.mjs → kv-cache-d8EjO29d.mjs} +1 -1
- package/dist/{ld-CqJq5erX.mjs → ld-jyjdrWjG.mjs} +3 -3
- package/dist/{metrics-DOqknlZd.mjs → metrics-B5Rei-HC.mjs} +1 -1
- package/dist/{middleware-1h_LgwXC.js → middleware-CAObohey.js} +149 -43
- package/dist/{middleware-s-AZzx4r.mjs → middleware-CPx4ZWwt.mjs} +155 -53
- package/dist/{middleware-VOCZN3SO.mjs → middleware-CdVnCDXD.mjs} +1 -1
- package/dist/{mod-Dhm7SqyE.d.ts → mod-C9WSpZWm.d.ts} +1 -1
- package/dist/{mod-C0BYc0OU.d.ts → mod-CVZrXSdm.d.ts} +1 -1
- package/dist/{mod-BhMvsgdF.d.ts → mod-DMkflQdF.d.ts} +1 -1
- package/dist/{mod-Bfenkg_c.d.cts → mod-DTAaBwiY.d.cts} +1 -1
- package/dist/{mod-pLbeNwcC.d.cts → mod-HDSvS-ww.d.cts} +1 -1
- package/dist/{mod-CyiSjRkk.d.cts → mod-xGFCHqq_.d.cts} +1 -1
- package/dist/mod.cjs +18 -18
- package/dist/mod.d.cts +7 -7
- package/dist/mod.d.ts +7 -7
- package/dist/mod.js +5 -5
- package/dist/nodeinfo/handler.test.mjs +1 -1
- package/dist/owner-1TFakcTF.js +215 -0
- package/dist/owner-BXufwAzm.cjs +225 -0
- package/dist/{owner-D_F22lfM.mjs → owner-ByqNQVaa.mjs} +45 -2
- package/dist/portable-key-Cw_ow5vn.mjs +194 -0
- package/dist/{proof-B03JXhrk.mjs → proof-B6gwdhN5.mjs} +5 -5
- package/dist/{send-NNkfKtVN.mjs → send-DsK7xevO.mjs} +3 -3
- package/dist/sig/compound-proof-discovery.test.mjs +1 -1
- package/dist/sig/compound-proof-producer.test.mjs +1 -1
- package/dist/sig/compound-proof-verification.test.mjs +2 -2
- package/dist/sig/compound-proof.test.mjs +3 -3
- package/dist/sig/http.test.mjs +2 -2
- package/dist/sig/key.test.mjs +1 -1
- package/dist/sig/ld.test.mjs +2 -2
- package/dist/sig/mod.cjs +16 -16
- package/dist/sig/mod.d.cts +2 -2
- package/dist/sig/mod.d.ts +2 -2
- package/dist/sig/mod.js +3 -3
- package/dist/sig/owner.test.mjs +1 -1
- package/dist/sig/portable-collection.test.mjs +3 -3
- package/dist/sig/portable-key.test.d.mts +2 -0
- package/dist/sig/portable-key.test.mjs +275 -0
- package/dist/sig/proof.test.mjs +4 -4
- package/dist/{sig-Bb20Pyrd.js → sig-BR_Z6mpY.js} +2 -2
- package/dist/{sig-B1E2uyhQ.cjs → sig-TTn9wYrE.cjs} +4 -4
- package/dist/{temporal-VVVSuruq.mjs → temporal-Bw5jtrBY.mjs} +1 -1
- package/dist/testing/kv.test.mjs +1 -1
- package/dist/testing/mod.d.mts +43 -0
- package/dist/utils/docloader.test.mjs +2 -2
- package/dist/utils/kv-cache.test.mjs +1 -1
- package/dist/utils/mod.cjs +1 -1
- package/dist/utils/mod.d.cts +1 -1
- package/dist/utils/mod.d.ts +1 -1
- package/dist/utils/mod.js +1 -1
- package/package.json +6 -6
- package/dist/http-z5n7chWw.js +0 -2647
- package/dist/owner-BLBWXCNt.js +0 -2276
- package/dist/owner-D8ZpRusc.cjs +0 -2446
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
import "temporal-polyfill";
|
|
2
|
+
import "urlpattern-polyfill";
|
|
3
|
+
globalThis.addEventListener = () => {};
|
|
4
|
+
import { t as esm_default } from "../esm-BHXXx4Wx.mjs";
|
|
5
|
+
import { r as createRequestContext, t as createInboxContext } from "../context-DG5QeQ8W.mjs";
|
|
6
|
+
import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
|
|
7
|
+
import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
|
|
8
|
+
import "../std__assert-DvMDRPes.mjs";
|
|
9
|
+
import { t as assertRejects } from "../assert_rejects-CTptdYck.mjs";
|
|
10
|
+
import { p as signJsonLd } from "../ld-jyjdrWjG.mjs";
|
|
11
|
+
import { s as signRequest } from "../http-DKWi8RjI.mjs";
|
|
12
|
+
import { i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2 } from "../keys-DIJNHW-Y.mjs";
|
|
13
|
+
import { d as handleInbox, n as FederationImpl, o as createFederation } from "../middleware-CPx4ZWwt.mjs";
|
|
14
|
+
import { t as ActivityListenerSet } from "../activity-listener-C-BSbKI7.mjs";
|
|
15
|
+
import { t as KvKeyCache } from "../keycache-BUDHB5rY.mjs";
|
|
16
|
+
import { Create, CryptographicKey, Person } from "@fedify/vocab";
|
|
17
|
+
import { mockDocumentLoader, test } from "@fedify/fixture";
|
|
18
|
+
import { encodeMultibase, exportDidKey, parseIri } from "@fedify/vocab-runtime";
|
|
19
|
+
import serialize from "json-canon";
|
|
20
|
+
//#region src/federation/portable-gateway-keys.test.ts
|
|
21
|
+
const did = await exportDidKey(ed25519PublicKey.publicKey);
|
|
22
|
+
const didKeyId = new URL(`${did}#${did.slice(8)}`);
|
|
23
|
+
const actorId = parseIri(`ap+ef61://${did}/actors/alice`);
|
|
24
|
+
const gateway = "https://example.com";
|
|
25
|
+
const otherGateway = "https://other.example";
|
|
26
|
+
const compatibleActorId = (origin) => `${origin}/.well-known/apgateway/${did}/actors/alice`;
|
|
27
|
+
const gatewayKeyId = new URL(`${compatibleActorId(gateway)}#main-key`);
|
|
28
|
+
const ed25519GatewayKeyPair = await crypto.subtle.generateKey("Ed25519", true, ["sign", "verify"]);
|
|
29
|
+
const rsaGatewayKeyPair = {
|
|
30
|
+
privateKey: rsaPrivateKey2,
|
|
31
|
+
publicKey: rsaPublicKey2.publicKey
|
|
32
|
+
};
|
|
33
|
+
const portableContext = [
|
|
34
|
+
"https://www.w3.org/ns/activitystreams",
|
|
35
|
+
"https://w3id.org/security/data-integrity/v1",
|
|
36
|
+
"https://w3id.org/fep/ef61"
|
|
37
|
+
];
|
|
38
|
+
const recipient = {
|
|
39
|
+
id: new URL("https://example.com/users/bob"),
|
|
40
|
+
inboxId: new URL("https://example.com/inbox")
|
|
41
|
+
};
|
|
42
|
+
async function sign(document) {
|
|
43
|
+
const proofConfig = {
|
|
44
|
+
"@context": document["@context"],
|
|
45
|
+
type: "DataIntegrityProof",
|
|
46
|
+
cryptosuite: "eddsa-jcs-2022",
|
|
47
|
+
verificationMethod: didKeyId.href,
|
|
48
|
+
proofPurpose: "assertionMethod",
|
|
49
|
+
created: "2023-02-24T23:36:38Z"
|
|
50
|
+
};
|
|
51
|
+
const encoder = new TextEncoder();
|
|
52
|
+
const digest = /* @__PURE__ */ new Uint8Array(64);
|
|
53
|
+
digest.set(new Uint8Array(await crypto.subtle.digest("SHA-256", encoder.encode(serialize(proofConfig)))), 0);
|
|
54
|
+
digest.set(new Uint8Array(await crypto.subtle.digest("SHA-256", encoder.encode(serialize(document)))), 32);
|
|
55
|
+
const signature = await crypto.subtle.sign("Ed25519", ed25519PrivateKey, digest);
|
|
56
|
+
return {
|
|
57
|
+
...document,
|
|
58
|
+
proof: {
|
|
59
|
+
...proofConfig,
|
|
60
|
+
proofValue: new TextDecoder().decode(encodeMultibase("base58btc", new Uint8Array(signature)))
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function createTestFederation(mapper) {
|
|
65
|
+
const federation = new FederationImpl({
|
|
66
|
+
kv: new MemoryKvStore(),
|
|
67
|
+
manuallyStartQueue: true,
|
|
68
|
+
contextLoaderFactory: () => mockDocumentLoader,
|
|
69
|
+
documentLoaderFactory: () => mockDocumentLoader
|
|
70
|
+
});
|
|
71
|
+
const setters = federation.setActorDispatcher("/users/{identifier}", () => null).setKeyPairsDispatcher(() => [rsaGatewayKeyPair, ed25519GatewayKeyPair]);
|
|
72
|
+
if (mapper != null) setters.mapPortableActorId((_ctx, id) => mapper(id));
|
|
73
|
+
return federation;
|
|
74
|
+
}
|
|
75
|
+
function portableCreate(id = "1") {
|
|
76
|
+
return new Create({
|
|
77
|
+
id: parseIri(`ap+ef61://${did}/activities/${id}`),
|
|
78
|
+
actor: actorId,
|
|
79
|
+
object: new URL("https://example.com/notes/1")
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
async function signedPortableCreate(id = "1") {
|
|
83
|
+
const json = await portableCreate(id).toJsonLd({
|
|
84
|
+
format: "compact",
|
|
85
|
+
context: portableContext,
|
|
86
|
+
contextLoader: mockDocumentLoader
|
|
87
|
+
});
|
|
88
|
+
return await Create.fromJsonLd(await sign(json), {
|
|
89
|
+
contextLoader: mockDocumentLoader,
|
|
90
|
+
documentLoader: mockDocumentLoader
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
async function capture(run) {
|
|
94
|
+
const bodies = [];
|
|
95
|
+
const requests = [];
|
|
96
|
+
esm_default.spyGlobal();
|
|
97
|
+
try {
|
|
98
|
+
esm_default.post(recipient.inboxId.href, async (cl) => {
|
|
99
|
+
const body = await cl.request.text();
|
|
100
|
+
requests.push(new Request(cl.request.url, {
|
|
101
|
+
method: cl.request.method,
|
|
102
|
+
headers: cl.request.headers,
|
|
103
|
+
body
|
|
104
|
+
}));
|
|
105
|
+
bodies.push(JSON.parse(body));
|
|
106
|
+
return new Response(null, { status: 202 });
|
|
107
|
+
});
|
|
108
|
+
await run();
|
|
109
|
+
} finally {
|
|
110
|
+
esm_default.hardReset();
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
bodies,
|
|
114
|
+
requests
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
function signatureKeyId(request) {
|
|
118
|
+
return request.headers.get("Signature-Input")?.match(/keyid="([^"]+)"/)?.[1] ?? request.headers.get("Signature")?.match(/keyId="([^"]+)"/)?.[1];
|
|
119
|
+
}
|
|
120
|
+
test("Context.getActorKeyPairs() derives gateway keys for portable actors", async () => {
|
|
121
|
+
const ctx = createTestFederation((id) => id === "alice" ? actorId : null).createContext(new URL(gateway));
|
|
122
|
+
const keys = await ctx.getActorKeyPairs("alice");
|
|
123
|
+
assertEquals(keys.map((k) => k.keyId.href), [gatewayKeyId.href, `${compatibleActorId(gateway)}#key-2`]);
|
|
124
|
+
for (const key of keys) {
|
|
125
|
+
assertEquals(key.cryptographicKey.id?.href, key.keyId.href);
|
|
126
|
+
assertEquals(key.cryptographicKey.ownerId?.href, actorId.href);
|
|
127
|
+
assertEquals(key.multikey.id?.href, key.keyId.href);
|
|
128
|
+
assertEquals(key.multikey.controllerId?.href, actorId.href);
|
|
129
|
+
}
|
|
130
|
+
assertEquals((await ctx.getActorKeyPairs("bob")).map((k) => [k.keyId.href, k.multikey.id?.href]), [["https://example.com/users/bob#main-key", "https://example.com/users/bob#multikey-1"], ["https://example.com/users/bob#key-2", "https://example.com/users/bob#multikey-2"]]);
|
|
131
|
+
const json = await new Person({
|
|
132
|
+
id: actorId,
|
|
133
|
+
gateways: [new URL(gateway)],
|
|
134
|
+
publicKey: keys[0].cryptographicKey,
|
|
135
|
+
assertionMethods: keys.map((k) => k.multikey)
|
|
136
|
+
}).toJsonLd({
|
|
137
|
+
format: "compact",
|
|
138
|
+
contextLoader: mockDocumentLoader
|
|
139
|
+
});
|
|
140
|
+
const assertionMethod = json.assertionMethod;
|
|
141
|
+
assertEquals(assertionMethod.map((k) => [k.id, k.controller]), [[gatewayKeyId.href, `ap+ef61://${did}/actors/alice`], [`${compatibleActorId(gateway)}#key-2`, `ap+ef61://${did}/actors/alice`]]);
|
|
142
|
+
assertEquals(json.publicKey.id, gatewayKeyId.href);
|
|
143
|
+
});
|
|
144
|
+
test("Context.getActorKeyPairs() accepts compatible IDs as portable actor IDs", async () => {
|
|
145
|
+
const published = new URL(compatibleActorId(otherGateway));
|
|
146
|
+
const [key] = await createTestFederation(() => published).createContext(new URL(gateway)).getActorKeyPairs("alice");
|
|
147
|
+
assertEquals(key.keyId.href, gatewayKeyId.href);
|
|
148
|
+
assertEquals(key.cryptographicKey.ownerId?.href, published.href);
|
|
149
|
+
});
|
|
150
|
+
test("Context.getActorKeyPairs() propagates portable actor ID mapper errors", async () => {
|
|
151
|
+
for (const id of [new URL(`${actorId.href}#me`), new URL("https://example.com/users/alice")]) {
|
|
152
|
+
const ctx = createTestFederation(() => id).createContext(new URL(gateway));
|
|
153
|
+
await assertRejects(() => ctx.getActorKeyPairs("alice"), TypeError);
|
|
154
|
+
}
|
|
155
|
+
const ctx = createTestFederation(() => {
|
|
156
|
+
throw new RangeError("mapper failed");
|
|
157
|
+
}).createContext(new URL(gateway));
|
|
158
|
+
await assertRejects(() => ctx.getActorKeyPairs("alice"), RangeError);
|
|
159
|
+
const federation = new FederationImpl({ kv: new MemoryKvStore() });
|
|
160
|
+
federation.setActorDispatcher("/users/{identifier}", () => null).setKeyPairsDispatcher(() => {
|
|
161
|
+
throw new Error("dispatcher failed");
|
|
162
|
+
}).mapPortableActorId(() => actorId);
|
|
163
|
+
assertEquals(await federation.createContext(new URL(gateway)).getActorKeyPairs("alice"), []);
|
|
164
|
+
});
|
|
165
|
+
test("Context.sendActivity() signs requests with gateway keys for portable actors", async () => {
|
|
166
|
+
const ctx = createTestFederation(() => actorId).createContext(new URL(gateway));
|
|
167
|
+
const activity = await signedPortableCreate();
|
|
168
|
+
const { bodies, requests } = await capture(() => ctx.sendActivity({ identifier: "alice" }, recipient, activity));
|
|
169
|
+
assertEquals(bodies.length, 1);
|
|
170
|
+
assertEquals(signatureKeyId(requests[0]), gatewayKeyId.href);
|
|
171
|
+
const proof = bodies[0].proof;
|
|
172
|
+
assertEquals(Array.isArray(proof), false);
|
|
173
|
+
assertEquals(proof.verificationMethod, didKeyId.href);
|
|
174
|
+
assertEquals(bodies[0].signature, void 0);
|
|
175
|
+
});
|
|
176
|
+
test("Context.sendActivity() refuses portable activities gateway keys would sign", async () => {
|
|
177
|
+
const ctx = createTestFederation(() => actorId).createContext(new URL(gateway));
|
|
178
|
+
await assertRejects(() => ctx.sendActivity({ identifier: "alice" }, recipient, portableCreate()), TypeError);
|
|
179
|
+
const httpActivity = new Create({
|
|
180
|
+
id: new URL("https://example.com/activities/1"),
|
|
181
|
+
actor: actorId,
|
|
182
|
+
object: new URL("https://example.com/notes/1")
|
|
183
|
+
});
|
|
184
|
+
await assertRejects(() => ctx.sendActivity({ identifier: "alice" }, recipient, httpActivity), TypeError);
|
|
185
|
+
await assertRejects(() => ctx.sendActivity({
|
|
186
|
+
keyId: new URL("https://example.com/key"),
|
|
187
|
+
privateKey: ed25519PrivateKey
|
|
188
|
+
}, recipient, portableCreate()), TypeError);
|
|
189
|
+
const { bodies } = await capture(() => ctx.sendActivity([{
|
|
190
|
+
keyId: gatewayKeyId,
|
|
191
|
+
privateKey: rsaPrivateKey2
|
|
192
|
+
}, {
|
|
193
|
+
keyId: didKeyId,
|
|
194
|
+
privateKey: ed25519PrivateKey
|
|
195
|
+
}], recipient, portableCreate()));
|
|
196
|
+
assertEquals(bodies[0].proof.verificationMethod, didKeyId.href);
|
|
197
|
+
});
|
|
198
|
+
test("Context.sendActivity() never signs documents with gateway keys", async () => {
|
|
199
|
+
const ctx = createTestFederation().createContext(new URL(gateway));
|
|
200
|
+
const keys = [{
|
|
201
|
+
keyId: gatewayKeyId,
|
|
202
|
+
privateKey: rsaPrivateKey2
|
|
203
|
+
}, {
|
|
204
|
+
keyId: new URL(`${compatibleActorId(gateway)}#key-2`),
|
|
205
|
+
privateKey: ed25519GatewayKeyPair.privateKey
|
|
206
|
+
}];
|
|
207
|
+
const activity = new Create({
|
|
208
|
+
id: new URL("https://example.com/activities/1"),
|
|
209
|
+
actor: new URL("https://example.com/users/bob"),
|
|
210
|
+
object: new URL("https://example.com/notes/1")
|
|
211
|
+
});
|
|
212
|
+
const { bodies, requests } = await capture(() => ctx.sendActivity(keys, recipient, activity));
|
|
213
|
+
assertEquals(signatureKeyId(requests[0]), gatewayKeyId.href);
|
|
214
|
+
assertEquals(bodies[0].proof, void 0);
|
|
215
|
+
assertEquals(bodies[0].signature, void 0);
|
|
216
|
+
});
|
|
217
|
+
test("Context.getDocumentLoader() signs requests with gateway keys for portable actors", async () => {
|
|
218
|
+
const documentLoader = await createTestFederation(() => actorId).createContext(new URL(gateway)).getDocumentLoader({ identifier: "alice" });
|
|
219
|
+
let keyId;
|
|
220
|
+
esm_default.spyGlobal();
|
|
221
|
+
try {
|
|
222
|
+
esm_default.get("https://example.com/notes/1", (cl) => {
|
|
223
|
+
keyId = signatureKeyId(cl.request);
|
|
224
|
+
return new Response(JSON.stringify({ id: cl.request.url }), { headers: { "Content-Type": "application/activity+json" } });
|
|
225
|
+
});
|
|
226
|
+
await documentLoader("https://example.com/notes/1");
|
|
227
|
+
} finally {
|
|
228
|
+
esm_default.hardReset();
|
|
229
|
+
}
|
|
230
|
+
assertEquals(keyId, gatewayKeyId.href);
|
|
231
|
+
});
|
|
232
|
+
async function actorDocument(gateways) {
|
|
233
|
+
const keys = await createTestFederation(() => actorId).createContext(new URL(gateway)).getActorKeyPairs("alice");
|
|
234
|
+
const json = await new Person({
|
|
235
|
+
id: actorId,
|
|
236
|
+
inbox: parseIri(`${actorId.href}/inbox`),
|
|
237
|
+
gateways: gateways.map((g) => new URL(g)),
|
|
238
|
+
publicKey: keys[0].cryptographicKey,
|
|
239
|
+
assertionMethods: keys.map((k) => k.multikey)
|
|
240
|
+
}).toJsonLd({
|
|
241
|
+
format: "compact",
|
|
242
|
+
contextLoader: mockDocumentLoader
|
|
243
|
+
});
|
|
244
|
+
json["@context"] = [...json["@context"], "https://w3id.org/security/data-integrity/v1"];
|
|
245
|
+
return await sign(json);
|
|
246
|
+
}
|
|
247
|
+
function createLoader(responses) {
|
|
248
|
+
return (url) => {
|
|
249
|
+
const document = responses[url.replace(/#.*$/, "")];
|
|
250
|
+
if (document == null) return mockDocumentLoader(url);
|
|
251
|
+
return Promise.resolve({
|
|
252
|
+
contextUrl: null,
|
|
253
|
+
documentUrl: url,
|
|
254
|
+
document: structuredClone(document)
|
|
255
|
+
});
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
async function deliver(body, gateways, { kv = new MemoryKvStore(), httpSignature = true } = {}) {
|
|
259
|
+
const documentLoader = createLoader({ [compatibleActorId(gateway)]: await actorDocument(gateways) });
|
|
260
|
+
const unsigned = new Request("https://local.example/inbox", {
|
|
261
|
+
method: "POST",
|
|
262
|
+
body: JSON.stringify(body),
|
|
263
|
+
headers: { "Content-Type": "application/activity+json" }
|
|
264
|
+
});
|
|
265
|
+
const request = httpSignature ? await signRequest(unsigned, rsaPrivateKey2, gatewayKeyId) : unsigned;
|
|
266
|
+
const context = createRequestContext({
|
|
267
|
+
federation: createFederation({ kv: new MemoryKvStore() }),
|
|
268
|
+
request,
|
|
269
|
+
url: new URL(request.url),
|
|
270
|
+
data: void 0,
|
|
271
|
+
documentLoader,
|
|
272
|
+
contextLoader: mockDocumentLoader
|
|
273
|
+
});
|
|
274
|
+
let dispatched = 0;
|
|
275
|
+
const inboxListeners = new ActivityListenerSet();
|
|
276
|
+
inboxListeners.add(Create, () => {
|
|
277
|
+
dispatched++;
|
|
278
|
+
});
|
|
279
|
+
const response = await handleInbox(request, {
|
|
280
|
+
recipient: null,
|
|
281
|
+
context,
|
|
282
|
+
inboxContextFactory() {
|
|
283
|
+
return createInboxContext({
|
|
284
|
+
...context,
|
|
285
|
+
clone: void 0
|
|
286
|
+
});
|
|
287
|
+
},
|
|
288
|
+
kv,
|
|
289
|
+
kvPrefixes: {
|
|
290
|
+
activityIdempotence: ["_fedify", "activityIdempotence"],
|
|
291
|
+
publicKey: ["_fedify", "publicKey"],
|
|
292
|
+
acceptSignatureNonce: ["_fedify", "acceptSignatureNonce"]
|
|
293
|
+
},
|
|
294
|
+
actorDispatcher() {
|
|
295
|
+
return null;
|
|
296
|
+
},
|
|
297
|
+
inboxListeners,
|
|
298
|
+
onNotFound() {
|
|
299
|
+
return new Response("Not found", { status: 404 });
|
|
300
|
+
},
|
|
301
|
+
signatureTimeWindow: { minutes: 5 },
|
|
302
|
+
skipSignatureVerification: false
|
|
303
|
+
});
|
|
304
|
+
lastResponseText = await response.text();
|
|
305
|
+
return {
|
|
306
|
+
status: response.status,
|
|
307
|
+
dispatched
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
let lastResponseText = "";
|
|
311
|
+
async function createJson(id) {
|
|
312
|
+
return await portableCreate(id).toJsonLd({
|
|
313
|
+
format: "compact",
|
|
314
|
+
context: portableContext,
|
|
315
|
+
contextLoader: mockDocumentLoader
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
test("handleInbox() requires proofs of portable actors despite gateway signatures", async () => {
|
|
319
|
+
assertEquals(await deliver(await createJson("unsigned"), [gateway]), {
|
|
320
|
+
status: 401,
|
|
321
|
+
dispatched: 0
|
|
322
|
+
});
|
|
323
|
+
assertEquals(await deliver({
|
|
324
|
+
...await sign(await createJson("tampered")),
|
|
325
|
+
object: "https://remote.example/notes/2"
|
|
326
|
+
}, [gateway]), {
|
|
327
|
+
status: 401,
|
|
328
|
+
dispatched: 0
|
|
329
|
+
});
|
|
330
|
+
assertEquals(await deliver(await sign(await createJson("signed")), [gateway]), {
|
|
331
|
+
status: 202,
|
|
332
|
+
dispatched: 1
|
|
333
|
+
});
|
|
334
|
+
});
|
|
335
|
+
test("handleInbox() does not need gateway signatures for portable actors", async () => {
|
|
336
|
+
assertEquals(await deliver(await sign(await createJson("signed")), [otherGateway]), {
|
|
337
|
+
status: 202,
|
|
338
|
+
dispatched: 1
|
|
339
|
+
});
|
|
340
|
+
assertEquals(await deliver(await createJson("unsigned"), [otherGateway]), {
|
|
341
|
+
status: 401,
|
|
342
|
+
dispatched: 0
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
test("handleInbox() does not take Linked Data Signatures for portable actors", async () => {
|
|
346
|
+
const kv = new MemoryKvStore();
|
|
347
|
+
await new KvKeyCache(kv, ["_fedify", "publicKey"]).set(rsaPublicKey2.id, new CryptographicKey({
|
|
348
|
+
id: rsaPublicKey2.id,
|
|
349
|
+
owner: actorId,
|
|
350
|
+
publicKey: rsaPublicKey2.publicKey
|
|
351
|
+
}));
|
|
352
|
+
const ldSign = (document) => signJsonLd(document, rsaPrivateKey2, rsaPublicKey2.id, { contextLoader: mockDocumentLoader });
|
|
353
|
+
assertEquals(await deliver(await ldSign(await createJson("ld-only")), [gateway], {
|
|
354
|
+
kv,
|
|
355
|
+
httpSignature: false
|
|
356
|
+
}), {
|
|
357
|
+
status: 401,
|
|
358
|
+
dispatched: 0
|
|
359
|
+
});
|
|
360
|
+
assertEquals(lastResponseText, "Activities of portable actors must have valid Object Integrity Proofs.");
|
|
361
|
+
});
|
|
362
|
+
//#endregion
|
|
363
|
+
export {};
|
|
@@ -7,10 +7,10 @@ import { t as assertInstanceOf } from "../assert_instance_of-mrXWWNY-.mjs";
|
|
|
7
7
|
import { t as assertThrows } from "../assert_throws-BYITOvPO.mjs";
|
|
8
8
|
import "../std__assert-DvMDRPes.mjs";
|
|
9
9
|
import { t as assertStrictEquals } from "../assert_strict_equals-CYvBmkpC.mjs";
|
|
10
|
-
import {
|
|
10
|
+
import { o as verifyPortableObjectProof, r as signObject } from "../proof-B6gwdhN5.mjs";
|
|
11
|
+
import { s as signRequest } from "../http-DKWi8RjI.mjs";
|
|
11
12
|
import { a as rsaPrivateKey3, c as rsaPublicKey3, i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2 } from "../keys-DIJNHW-Y.mjs";
|
|
12
|
-
import { g as PORTABLE_OBJECT_CONTENT_TYPE, o as createFederation } from "../middleware-
|
|
13
|
-
import { o as verifyPortableObjectProof, r as signObject } from "../proof-B03JXhrk.mjs";
|
|
13
|
+
import { g as PORTABLE_OBJECT_CONTENT_TYPE, o as createFederation } from "../middleware-CPx4ZWwt.mjs";
|
|
14
14
|
import { Collection, Create, Note, Object as Object$1, Person, Tombstone, lookupObject } from "@fedify/vocab";
|
|
15
15
|
import { mockDocumentLoader, test } from "@fedify/fixture";
|
|
16
16
|
import { exportDidKey, formatIri, parseIri } from "@fedify/vocab-runtime";
|
|
@@ -10,10 +10,10 @@ import { t as assertNotEquals } from "../assert_not_equals-CpPK2t89.mjs";
|
|
|
10
10
|
import "../std__assert-DvMDRPes.mjs";
|
|
11
11
|
import { t as assertRejects } from "../assert_rejects-CTptdYck.mjs";
|
|
12
12
|
import { t as assert } from "../assert-D1Eafmd_.mjs";
|
|
13
|
-
import { l as verifyRequest } from "../http-
|
|
13
|
+
import { l as verifyRequest } from "../http-DKWi8RjI.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-ByqNQVaa.mjs";
|
|
16
|
+
import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "../send-DsK7xevO.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-By1YYsIK.mjs";
|
|
7
|
-
import { o as createFederation } from "../../middleware-
|
|
7
|
+
import { o as createFederation } from "../../middleware-CPx4ZWwt.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-By1YYsIK.mjs";
|
|
7
|
-
import { o as createFederation } from "../../middleware-
|
|
8
|
-
import { r as createFederationBuilder } from "../../builder-
|
|
7
|
+
import { o as createFederation } from "../../middleware-CPx4ZWwt.mjs";
|
|
8
|
+
import { r as createFederationBuilder } from "../../builder-DVQ0yhKJ.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";
|
|
@@ -4,7 +4,7 @@ globalThis.addEventListener = () => {};
|
|
|
4
4
|
import { t as assertFalse } from "../assert_false-CPdRnc3E.mjs";
|
|
5
5
|
import "../std__assert-DvMDRPes.mjs";
|
|
6
6
|
import { t as assert } from "../assert-D1Eafmd_.mjs";
|
|
7
|
-
import { t as hasMalformedKnownTemporalLiteral } from "../temporal-
|
|
7
|
+
import { t as hasMalformedKnownTemporalLiteral } from "../temporal-Bw5jtrBY.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 () => {
|
|
@@ -6,7 +6,7 @@ import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
|
|
|
6
6
|
import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
|
|
7
7
|
import { t as assertNotEquals } from "../assert_not_equals-CpPK2t89.mjs";
|
|
8
8
|
import "../std__assert-DvMDRPes.mjs";
|
|
9
|
-
import { o as createFederation, s as handleWebFinger } from "../middleware-
|
|
9
|
+
import { o as createFederation, s as handleWebFinger } from "../middleware-CPx4ZWwt.mjs";
|
|
10
10
|
import { Image, Link, Person, Tombstone } from "@fedify/vocab";
|
|
11
11
|
import { createTestMeterProvider, test } from "@fedify/fixture";
|
|
12
12
|
import { parseIri } from "@fedify/vocab-runtime";
|