@fedify/fedify 2.4.0-dev.2169 → 2.4.0-dev.2193
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-B2d_Pt0j.mjs → builder-0onlMY4u.mjs} +26 -6
- package/dist/{circuit-breaker-C5NqoLBJ.mjs → circuit-breaker-XJ53md4S.mjs} +16 -11
- 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-D28gMFqP.mjs → compound-proof-B-Fk6Slo.mjs} +3 -3
- package/dist/{context-BykNUf04.d.ts → context-9T--NSo4.d.ts} +55 -1
- package/dist/{context-L0Fnib9j.d.cts → context-CDcAgm4l.d.cts} +55 -1
- package/dist/{deno-DU8dvnWk.mjs → deno-DB_Sh0LK.mjs} +1 -1
- package/dist/{docloader-CghLH7Zi.mjs → docloader-DpWYYY6a.mjs} +25 -17
- package/dist/federation/builder.test.mjs +1 -1
- package/dist/federation/circuit-breaker.test.mjs +50 -1
- package/dist/federation/compound-fanout.test.mjs +3 -3
- package/dist/federation/handler-compound-proof.test.mjs +52 -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/keycache.test.mjs +1 -1
- package/dist/federation/metrics.test.mjs +1 -1
- package/dist/federation/middleware.test.mjs +182 -13
- package/dist/federation/mod.cjs +1 -1
- package/dist/federation/mod.d.cts +2 -2
- package/dist/federation/mod.d.ts +2 -2
- package/dist/federation/mod.js +1 -1
- package/dist/federation/outgoing-proof.test.mjs +5 -5
- package/dist/federation/portable-actor.test.mjs +3 -3
- package/dist/federation/portable-chain.test.mjs +3 -3
- package/dist/federation/portable-collection.test.mjs +4 -4
- package/dist/federation/portable-gateway-keys.test.mjs +6 -6
- package/dist/federation/portable-inbox.test.mjs +35 -5
- package/dist/federation/portable-parse-uri.test.mjs +1 -1
- package/dist/federation/portable-route-activity.test.d.mts +2 -0
- package/dist/federation/portable-route-activity.test.mjs +259 -0
- package/dist/federation/portable.test.mjs +49 -5
- 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-DrNgkLfJ.cjs → federation-BsGo0EH2.cjs} +76 -35
- package/dist/{http-5CJBdU8n.cjs → http-BEivI0TS.cjs} +279 -87
- package/dist/{http-BGpexPO7.js → http-BGmxtpo2.js} +263 -89
- package/dist/{http-Cok5-vIC.d.ts → http-BrwKINQn.d.cts} +29 -1
- package/dist/{http-DC1cqAQ2.d.cts → http-DGx0KOws.d.ts} +29 -1
- package/dist/{http-Bd6rZlJX.mjs → http-DQf9Mw4V.mjs} +180 -92
- package/dist/{key-gIhVZO3p.mjs → key-DA3Yx8p2.mjs} +4 -4
- package/dist/{key-owner-evidence-Bo3yaeL5.mjs → key-owner-evidence-APB6NpSk.mjs} +1 -1
- package/dist/{keycache-PiOM9pM0.mjs → keycache-Da8A8mZa.mjs} +1 -1
- package/dist/{kv-cache-YRi_g8GB.cjs → kv-cache-1hafSoqH.cjs} +23 -15
- package/dist/{kv-cache-iR55fqlk.js → kv-cache-DduYhpee.js} +24 -16
- package/dist/{kv-cache-D0qjSeWY.mjs → kv-cache-OFBvrKh3.mjs} +1 -1
- package/dist/{ld-BsTkRrwF.mjs → ld-C15yzuY6.mjs} +3 -3
- package/dist/{metrics-jUDFfVgi.mjs → metrics-B1H23mlq.mjs} +1 -1
- package/dist/{middleware-Dq-OCmb6.mjs → middleware-BZQnf0nI.mjs} +1 -1
- package/dist/{middleware-Coqgp-K0.mjs → middleware-BqqycC0P.mjs} +52 -36
- package/dist/{middleware-DlxMxVp5.js → middleware-ClaUFaLb.js} +77 -36
- package/dist/{mod-DK0y9fd-.d.ts → mod-B3OBLj5g.d.ts} +1 -1
- package/dist/{mod-B5z_q9TH.d.ts → mod-C5Mwx5ty.d.ts} +5 -1
- package/dist/{mod-CPPqkyV_.d.cts → mod-Cy9VNHVw.d.ts} +14 -3
- package/dist/{mod-BLOXGs6m.d.cts → mod-D58KCqUg.d.cts} +1 -1
- package/dist/{mod--jv6vyrP.d.ts → mod-DoTWIc7F.d.cts} +14 -3
- package/dist/{mod-BA_F2x53.d.cts → mod-Dp3Ij7sw.d.cts} +5 -1
- package/dist/mod.cjs +4 -4
- package/dist/mod.d.cts +5 -5
- package/dist/mod.d.ts +5 -5
- package/dist/mod.js +4 -4
- package/dist/nodeinfo/handler.test.mjs +1 -1
- package/dist/{owner-D3GoetXS.mjs → owner-BVpZBM-V.mjs} +5 -5
- package/dist/{portable-collection-CnlCamup.cjs → portable-collection-BFSShzwh.cjs} +1 -1
- package/dist/{portable-collection-Bkdh_WpL.js → portable-collection-D535cSz_.js} +1 -1
- package/dist/{portable-collection-C60_acmx.mjs → portable-collection-Dn2GdZdI.mjs} +2 -2
- package/dist/{portable-key-DyF5AnZo.mjs → portable-key-Bmwmq7Y6.mjs} +2 -2
- package/dist/{portable-key-id-Bg259c4Z.mjs → portable-key-id-Ca30-hOa.mjs} +45 -1
- package/dist/{proof-CQAYqWdj.mjs → proof-DrJWKeOl.mjs} +49 -7
- package/dist/{send-DjkQrxt5.mjs → send-CT3a-0Vd.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 +2 -2
- package/dist/sig/http.test.mjs +495 -23
- package/dist/sig/key-owner-evidence.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 +2 -2
- package/dist/sig/mod.d.cts +2 -2
- package/dist/sig/mod.d.ts +2 -2
- package/dist/sig/mod.js +2 -2
- package/dist/sig/owner.test.mjs +1 -1
- package/dist/sig/portable-collection.test.mjs +1 -1
- package/dist/sig/portable-key-id.test.d.mts +2 -0
- package/dist/sig/portable-key-id.test.mjs +67 -0
- package/dist/sig/portable-key.test.mjs +7 -7
- package/dist/sig/proof.test.mjs +141 -7
- package/dist/{temporal-oRKP803M.mjs → temporal-DNqluyEc.mjs} +1 -1
- package/dist/testing/kv.test.mjs +1 -1
- package/dist/testing/mod.d.mts +52 -0
- package/dist/utils/docloader.test.mjs +93 -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 +7 -7
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import "temporal-polyfill";
|
|
2
|
+
import "urlpattern-polyfill";
|
|
3
|
+
globalThis.addEventListener = () => {};
|
|
4
|
+
import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
|
|
5
|
+
import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
|
|
6
|
+
import { t as assertFalse } from "../assert_false-CPdRnc3E.mjs";
|
|
7
|
+
import "../std__assert-DvMDRPes.mjs";
|
|
8
|
+
import { t as assert } from "../assert-D1Eafmd_.mjs";
|
|
9
|
+
import { a as signObject } from "../proof-DrJWKeOl.mjs";
|
|
10
|
+
import { n as ed25519PrivateKey, r as ed25519PublicKey } from "../keys-DIJNHW-Y.mjs";
|
|
11
|
+
import { o as createFederation } from "../middleware-BqqycC0P.mjs";
|
|
12
|
+
import { Create, Note } from "@fedify/vocab";
|
|
13
|
+
import { mockDocumentLoader, test } from "@fedify/fixture";
|
|
14
|
+
import { exportDidKey, formatIri, parseIri, toCompatibleEf61Id } from "@fedify/vocab-runtime";
|
|
15
|
+
//#region src/federation/portable-route-activity.test.ts
|
|
16
|
+
const did = await exportDidKey(ed25519PublicKey.publicKey);
|
|
17
|
+
const keyId = new URL(`${did}#${did.slice(8)}`);
|
|
18
|
+
const otherKeyPair = await crypto.subtle.generateKey("Ed25519", true, ["sign", "verify"]);
|
|
19
|
+
const otherDid = await exportDidKey(otherKeyPair.publicKey);
|
|
20
|
+
const otherKeyId = new URL(`${otherDid}#${otherDid.slice(8)}`);
|
|
21
|
+
const GATEWAY = "https://gw.example";
|
|
22
|
+
function portableId(authority, path) {
|
|
23
|
+
return parseIri(`ap+ef61://${authority}${path}`);
|
|
24
|
+
}
|
|
25
|
+
function compatibleId(authority, path, gateway = GATEWAY) {
|
|
26
|
+
return new URL(`${gateway}/.well-known/apgateway/${authority}${path}`);
|
|
27
|
+
}
|
|
28
|
+
async function sign(activity, signer) {
|
|
29
|
+
return await signObject(activity, signer === "did" ? ed25519PrivateKey : otherKeyPair.privateKey, signer === "did" ? keyId : otherKeyId, { contextLoader: mockDocumentLoader });
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Routes a `Create` activity with the given ID through
|
|
33
|
+
* `Context.routeActivity()`. Unless `activity` is an `Activity`, the routed
|
|
34
|
+
* activity has no proof, so that it is trusted only if it can be dereferenced
|
|
35
|
+
* by its ID, which serves `served`.
|
|
36
|
+
* @returns Whether the activity was routed, and the IDs of the actors of
|
|
37
|
+
* the activities that reached the inbox listener.
|
|
38
|
+
*/
|
|
39
|
+
async function route(activity, { served, actors } = {}) {
|
|
40
|
+
const documents = /* @__PURE__ */ new Map();
|
|
41
|
+
if (served != null && activity instanceof URL) {
|
|
42
|
+
const document = await served.toJsonLd({ contextLoader: mockDocumentLoader });
|
|
43
|
+
documents.set(activity.href, document);
|
|
44
|
+
if (activity.protocol === "ap+ef61:") {
|
|
45
|
+
documents.set(formatIri(activity), document);
|
|
46
|
+
documents.set(toCompatibleEf61Id(activity, GATEWAY).href, document);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const documentLoader = (url, options) => {
|
|
50
|
+
if (documents.has(url)) return Promise.resolve({
|
|
51
|
+
contextUrl: null,
|
|
52
|
+
documentUrl: url,
|
|
53
|
+
document: documents.get(url)
|
|
54
|
+
});
|
|
55
|
+
return mockDocumentLoader(url, options);
|
|
56
|
+
};
|
|
57
|
+
const federation = createFederation({
|
|
58
|
+
kv: new MemoryKvStore(),
|
|
59
|
+
documentLoaderFactory: () => documentLoader,
|
|
60
|
+
contextLoaderFactory: () => mockDocumentLoader
|
|
61
|
+
});
|
|
62
|
+
const received = [];
|
|
63
|
+
federation.setInboxListeners("/users/{identifier}/inbox", "/inbox").on(Create, (_ctx, create) => {
|
|
64
|
+
received.push(...create.actorIds.map((id) => id.href));
|
|
65
|
+
});
|
|
66
|
+
return {
|
|
67
|
+
routed: await federation.createContext(new URL("https://example.com/")).routeActivity(null, activity instanceof URL ? new Create({
|
|
68
|
+
id: activity,
|
|
69
|
+
actors: [...actors ?? served?.actorIds ?? []]
|
|
70
|
+
}) : activity),
|
|
71
|
+
received
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
async function portableCreate(id, actors, signer = "did") {
|
|
75
|
+
return await sign(new Create({
|
|
76
|
+
id,
|
|
77
|
+
actors: [...actors],
|
|
78
|
+
object: new Note({ content: "Hello" })
|
|
79
|
+
}), signer);
|
|
80
|
+
}
|
|
81
|
+
test("Context.routeActivity() rejects portable activities attributed to other DIDs", async (t) => {
|
|
82
|
+
const cases = [
|
|
83
|
+
[
|
|
84
|
+
"ap+ef61: IDs",
|
|
85
|
+
portableId(did, "/activities/1"),
|
|
86
|
+
[portableId(otherDid, "/actor")]
|
|
87
|
+
],
|
|
88
|
+
[
|
|
89
|
+
"compatible identifiers on one gateway",
|
|
90
|
+
compatibleId(did, "/activities/1"),
|
|
91
|
+
[compatibleId(otherDid, "/actor")]
|
|
92
|
+
],
|
|
93
|
+
[
|
|
94
|
+
"compatible activity ID and ap+ef61: actor ID",
|
|
95
|
+
compatibleId(did, "/activities/1"),
|
|
96
|
+
[portableId(otherDid, "/actor")]
|
|
97
|
+
],
|
|
98
|
+
[
|
|
99
|
+
"ap+ef61: activity ID and compatible actor ID",
|
|
100
|
+
portableId(did, "/activities/1"),
|
|
101
|
+
[compatibleId(otherDid, "/actor")]
|
|
102
|
+
],
|
|
103
|
+
[
|
|
104
|
+
"one of multiple actors with another DID",
|
|
105
|
+
portableId(did, "/activities/1"),
|
|
106
|
+
[portableId(did, "/actor"), portableId(otherDid, "/actor")]
|
|
107
|
+
],
|
|
108
|
+
[
|
|
109
|
+
"ap+ef61: activity ID and HTTP(S) actor ID",
|
|
110
|
+
portableId(did, "/activities/1"),
|
|
111
|
+
[new URL("https://example.com/person")]
|
|
112
|
+
],
|
|
113
|
+
[
|
|
114
|
+
"compatible activity ID and HTTP(S) actor ID on the gateway",
|
|
115
|
+
compatibleId(did, "/activities/1"),
|
|
116
|
+
[new URL(`${GATEWAY}/users/alice`)]
|
|
117
|
+
]
|
|
118
|
+
];
|
|
119
|
+
for (const [name, id, actors] of cases) await t.step(name, async () => {
|
|
120
|
+
const served = await portableCreate(id, actors);
|
|
121
|
+
assertEquals(await route(id, { served }), {
|
|
122
|
+
routed: false,
|
|
123
|
+
received: []
|
|
124
|
+
});
|
|
125
|
+
assertEquals(await route(served), {
|
|
126
|
+
routed: false,
|
|
127
|
+
received: []
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
test("Context.routeActivity() accepts portable activities of the same DID", async (t) => {
|
|
132
|
+
const cases = [
|
|
133
|
+
[
|
|
134
|
+
"ap+ef61: IDs",
|
|
135
|
+
portableId(did, "/activities/1"),
|
|
136
|
+
[portableId(did, "/actor")]
|
|
137
|
+
],
|
|
138
|
+
[
|
|
139
|
+
"compatible identifiers on one gateway",
|
|
140
|
+
compatibleId(did, "/activities/1"),
|
|
141
|
+
[compatibleId(did, "/actor")]
|
|
142
|
+
],
|
|
143
|
+
[
|
|
144
|
+
"compatible identifiers on different gateways",
|
|
145
|
+
compatibleId(did, "/activities/1"),
|
|
146
|
+
[compatibleId(did, "/actor", "https://gw2.example")]
|
|
147
|
+
],
|
|
148
|
+
[
|
|
149
|
+
"compatible activity ID and ap+ef61: actor ID",
|
|
150
|
+
compatibleId(did, "/activities/1"),
|
|
151
|
+
[portableId(did, "/actor")]
|
|
152
|
+
],
|
|
153
|
+
[
|
|
154
|
+
"ap+ef61: activity ID and compatible actor ID",
|
|
155
|
+
portableId(did, "/activities/1"),
|
|
156
|
+
[compatibleId(did, "/actor")]
|
|
157
|
+
],
|
|
158
|
+
[
|
|
159
|
+
"actor ID with a location hint",
|
|
160
|
+
portableId(did, "/activities/1"),
|
|
161
|
+
[portableId(did, `/actor?@gateway=${encodeURIComponent(GATEWAY)}`)]
|
|
162
|
+
]
|
|
163
|
+
];
|
|
164
|
+
for (const [name, id, actors] of cases) await t.step(name, async () => {
|
|
165
|
+
const served = await portableCreate(id, actors);
|
|
166
|
+
const expected = {
|
|
167
|
+
routed: true,
|
|
168
|
+
received: actors.map((actor) => actor.href)
|
|
169
|
+
};
|
|
170
|
+
assertEquals(await route(id, { served }), expected);
|
|
171
|
+
assertEquals(await route(served), expected);
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
test("Context.routeActivity() compares portable activity IDs canonically", async (t) => {
|
|
175
|
+
const id = portableId(did, "/activities/1");
|
|
176
|
+
const actors = [portableId(did, "/actor")];
|
|
177
|
+
await t.step("compatible ID serving an ap+ef61: ID", async () => {
|
|
178
|
+
const served = await portableCreate(id, actors);
|
|
179
|
+
const { routed } = await route(compatibleId(did, "/activities/1"), { served });
|
|
180
|
+
assert(routed);
|
|
181
|
+
});
|
|
182
|
+
await t.step("ap+ef61: ID serving a compatible ID", async () => {
|
|
183
|
+
const served = await portableCreate(compatibleId(did, "/activities/1"), actors);
|
|
184
|
+
const { routed } = await route(id, { served });
|
|
185
|
+
assert(routed);
|
|
186
|
+
});
|
|
187
|
+
await t.step("ap+ef61: ID with a location hint", async () => {
|
|
188
|
+
const served = await portableCreate(id, actors);
|
|
189
|
+
const { routed } = await route(portableId(did, `/activities/1?@gateway=${encodeURIComponent(GATEWAY)}`), { served });
|
|
190
|
+
assert(routed);
|
|
191
|
+
});
|
|
192
|
+
await t.step("another path of the same DID", async () => {
|
|
193
|
+
const served = await portableCreate(portableId(did, "/activities/2"), actors);
|
|
194
|
+
const { routed } = await route(id, { served });
|
|
195
|
+
assertFalse(routed);
|
|
196
|
+
});
|
|
197
|
+
await t.step("another fragment of the same DID", async () => {
|
|
198
|
+
const served = await portableCreate(portableId(did, "/activities/1#other"), actors);
|
|
199
|
+
const { routed } = await route(id, { served });
|
|
200
|
+
assertFalse(routed);
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
test("Context.routeActivity() rejects proofs of other DIDs for portable actors", async () => {
|
|
204
|
+
const id = portableId(did, "/activities/1");
|
|
205
|
+
const actors = [portableId(did, "/actor")];
|
|
206
|
+
assertEquals(await route(await portableCreate(id, actors, "otherDid")), {
|
|
207
|
+
routed: false,
|
|
208
|
+
received: []
|
|
209
|
+
});
|
|
210
|
+
assertEquals(await route(await portableCreate(id, actors)), {
|
|
211
|
+
routed: true,
|
|
212
|
+
received: actors.map((actor) => actor.href)
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
test("Context.routeActivity() keeps comparing web origins of HTTP(S) IDs", async (t) => {
|
|
216
|
+
const cases = [
|
|
217
|
+
[
|
|
218
|
+
"same origin",
|
|
219
|
+
new URL("https://remote.example/activities/1"),
|
|
220
|
+
new URL("https://remote.example/users/alice"),
|
|
221
|
+
true
|
|
222
|
+
],
|
|
223
|
+
[
|
|
224
|
+
"same origin with an explicit default port",
|
|
225
|
+
new URL("https://remote.example/activities/1"),
|
|
226
|
+
new URL("https://remote.example:443/users/alice"),
|
|
227
|
+
true
|
|
228
|
+
],
|
|
229
|
+
[
|
|
230
|
+
"another origin",
|
|
231
|
+
new URL("https://remote.example/activities/1"),
|
|
232
|
+
new URL("https://other.example/users/alice"),
|
|
233
|
+
false
|
|
234
|
+
],
|
|
235
|
+
[
|
|
236
|
+
"another port",
|
|
237
|
+
new URL("https://remote.example/activities/1"),
|
|
238
|
+
new URL("https://remote.example:8443/users/alice"),
|
|
239
|
+
false
|
|
240
|
+
],
|
|
241
|
+
[
|
|
242
|
+
"compatible actor ID on the same host",
|
|
243
|
+
new URL("https://remote.example/activities/1"),
|
|
244
|
+
compatibleId(did, "/actor", "https://remote.example"),
|
|
245
|
+
false
|
|
246
|
+
]
|
|
247
|
+
];
|
|
248
|
+
for (const [name, id, actor, expected] of cases) await t.step(name, async () => {
|
|
249
|
+
const served = new Create({
|
|
250
|
+
id,
|
|
251
|
+
actor
|
|
252
|
+
});
|
|
253
|
+
const { routed, received } = await route(id, { served });
|
|
254
|
+
assertEquals(routed, expected);
|
|
255
|
+
assertEquals(received, expected ? [actor.href] : []);
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
//#endregion
|
|
259
|
+
export {};
|
|
@@ -8,10 +8,10 @@ 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
10
|
import { t as assert } from "../assert-D1Eafmd_.mjs";
|
|
11
|
-
import { a as signObject, c as verifyPortableObjectProof } from "../proof-
|
|
12
|
-
import { l as signRequest } from "../http-
|
|
11
|
+
import { a as signObject, c as verifyPortableObjectProof } from "../proof-DrJWKeOl.mjs";
|
|
12
|
+
import { l as signRequest } from "../http-DQf9Mw4V.mjs";
|
|
13
13
|
import { a as rsaPrivateKey3, c as rsaPublicKey3, i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2 } from "../keys-DIJNHW-Y.mjs";
|
|
14
|
-
import { b as PORTABLE_OBJECT_CONTENT_TYPE, o as createFederation } from "../middleware-
|
|
14
|
+
import { b as PORTABLE_OBJECT_CONTENT_TYPE, o as createFederation } from "../middleware-BqqycC0P.mjs";
|
|
15
15
|
import { configure, reset } from "@logtape/logtape";
|
|
16
16
|
import { Collection, Create, Note, Object as Object$1, Person, Tombstone, lookupObject } from "@fedify/vocab";
|
|
17
17
|
import { mockDocumentLoader, test } from "@fedify/fixture";
|
|
@@ -215,6 +215,11 @@ test("Federation.fetch() applies the FEP-ef61 proof policy to portable objects",
|
|
|
215
215
|
object.id.pathname = "/objects/mutated-id";
|
|
216
216
|
return object;
|
|
217
217
|
},
|
|
218
|
+
"actor-without-gateways": () => sign(new Person({
|
|
219
|
+
id: parseIri(`ap+ef61://${did}/objects/actor-without-gateways`),
|
|
220
|
+
inbox: parseIri(`ap+ef61://${did}/objects/actor-without-gateways/inbox`),
|
|
221
|
+
outbox: parseIri(`ap+ef61://${did}/objects/actor-without-gateways/outbox`)
|
|
222
|
+
})),
|
|
218
223
|
"unsigned-actor": () => Promise.resolve(new Person({
|
|
219
224
|
id: parseIri(`ap+ef61://${did}/objects/unsigned-actor`),
|
|
220
225
|
inbox: parseIri(`ap+ef61://${did}/objects/unsigned-actor/inbox`),
|
|
@@ -234,6 +239,7 @@ test("Federation.fetch() applies the FEP-ef61 proof policy to portable objects",
|
|
|
234
239
|
"activity": 200,
|
|
235
240
|
"actor": 200,
|
|
236
241
|
"mutated-id": 404,
|
|
242
|
+
"actor-without-gateways": 500,
|
|
237
243
|
"unsigned-actor": 500
|
|
238
244
|
})) await t.step(`${id} → ${status}`, async () => {
|
|
239
245
|
const response = await federation.fetch(gatewayRequest(`/objects/${id}`), { contextData: void 0 });
|
|
@@ -716,9 +722,11 @@ test("Federation.fetch() serves compatible-ID actors", async (t) => {
|
|
|
716
722
|
})).map((r) => r.properties.gateway), ["https://primary.example"]);
|
|
717
723
|
});
|
|
718
724
|
await t.step("warns if the actor has no gateways", async () => {
|
|
719
|
-
|
|
725
|
+
const records = await captureWarnings(async () => {
|
|
720
726
|
assertEquals((await federation.fetch(new Request("https://example.com/users/nogateway", { headers: { Accept: ACCEPT } }), { contextData: void 0 })).status, 200);
|
|
721
|
-
})
|
|
727
|
+
});
|
|
728
|
+
assertEquals(records.map((r) => r.properties.actorId), [gatewayUrl("/actors/nogateway")]);
|
|
729
|
+
assert(String(records[0].rawMessage).includes("gateways property"));
|
|
722
730
|
});
|
|
723
731
|
await t.step("warns if the ID is a malformed compatible identifier", async () => {
|
|
724
732
|
const records = await captureWarnings(async () => {
|
|
@@ -728,5 +736,41 @@ test("Federation.fetch() serves compatible-ID actors", async (t) => {
|
|
|
728
736
|
assert(String(records[0].rawMessage).includes("malformed"));
|
|
729
737
|
});
|
|
730
738
|
});
|
|
739
|
+
test("Federation.fetch() warns about portable actors without valid gateways", async (t) => {
|
|
740
|
+
const federation = createTestFederation();
|
|
741
|
+
const actors = {
|
|
742
|
+
valid: (ctx) => new Person({
|
|
743
|
+
id: ctx.getPortableObjectUri(Person, { name: "valid" }, did),
|
|
744
|
+
gateways: [new URL("https://example.com"), new URL("https://other.example/")]
|
|
745
|
+
}),
|
|
746
|
+
empty: (ctx) => new Person({ id: ctx.getPortableObjectUri(Person, { name: "empty" }, did) }),
|
|
747
|
+
mutated: (ctx) => {
|
|
748
|
+
const actor = new Person({
|
|
749
|
+
id: ctx.getPortableObjectUri(Person, { name: "mutated" }, did),
|
|
750
|
+
gateways: [new URL("https://example.com"), new URL("https://other.example/")]
|
|
751
|
+
});
|
|
752
|
+
actor.gateways[1].pathname = "/gateway";
|
|
753
|
+
return actor;
|
|
754
|
+
},
|
|
755
|
+
https: (ctx) => new Person({ id: ctx.getActorUri("https") }),
|
|
756
|
+
tombstone: (ctx) => new Tombstone({ id: ctx.getPortableObjectUri(Person, { name: "tombstone" }, did) })
|
|
757
|
+
};
|
|
758
|
+
federation.setActorDispatcher("/users/{identifier}", (ctx, identifier) => actors[identifier]?.(ctx) ?? null);
|
|
759
|
+
federation.setObjectDispatcher(Person, "/actors/{name}", () => null);
|
|
760
|
+
const expected = {
|
|
761
|
+
valid: [],
|
|
762
|
+
empty: [`ap+ef61://${did}/actors/empty`],
|
|
763
|
+
mutated: [`ap+ef61://${did}/actors/mutated`],
|
|
764
|
+
https: [],
|
|
765
|
+
tombstone: []
|
|
766
|
+
};
|
|
767
|
+
for (const [identifier, actorIds] of globalThis.Object.entries(expected)) await t.step(identifier, async () => {
|
|
768
|
+
const actorRecords = (await captureWarnings(async () => {
|
|
769
|
+
await federation.fetch(new Request(`https://example.com/users/${identifier}`, { headers: { Accept: ACCEPT } }), { contextData: void 0 });
|
|
770
|
+
})).filter((r) => r.category.join(".") === "fedify.federation.actor");
|
|
771
|
+
assertEquals(actorRecords.map((r) => r.properties.actorId), actorIds.map((id) => parseIri(id).href));
|
|
772
|
+
for (const record of actorRecords) assert(String(record.rawMessage).includes("gateways property"));
|
|
773
|
+
});
|
|
774
|
+
});
|
|
731
775
|
//#endregion
|
|
732
776
|
export {};
|
|
@@ -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 {
|
|
13
|
+
import { f as verifyRequest } from "../http-DQf9Mw4V.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-BVpZBM-V.mjs";
|
|
16
|
+
import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "../send-CT3a-0Vd.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-IG3pHfvZ.mjs";
|
|
6
6
|
import "../../testing-C1itIjqW.mjs";
|
|
7
|
-
import { o as createFederation } from "../../middleware-
|
|
7
|
+
import { o as createFederation } from "../../middleware-BqqycC0P.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-IG3pHfvZ.mjs";
|
|
6
6
|
import "../../testing-C1itIjqW.mjs";
|
|
7
|
-
import { o as createFederation } from "../../middleware-
|
|
8
|
-
import { r as createFederationBuilder } from "../../builder-
|
|
7
|
+
import { o as createFederation } from "../../middleware-BqqycC0P.mjs";
|
|
8
|
+
import { r as createFederationBuilder } from "../../builder-0onlMY4u.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-DNqluyEc.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-BqqycC0P.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";
|