@fedify/fedify 2.4.0-pr.936.41 → 2.5.0-dev.2271
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/README.md +50 -39
- package/dist/{assert-CJ1KXcvv.mjs → assert-D1Eafmd_.mjs} +1 -1
- package/dist/{assert_equals-BV7oLMxm.mjs → assert_equals-Z6IcEbWU.mjs} +2 -28
- package/dist/assert_exists-cffSpJ7X.mjs +28 -0
- package/dist/assert_false-CPdRnc3E.mjs +23 -0
- package/dist/assert_greater_or_equal-BlafOF4-.mjs +31 -0
- package/dist/{assert_instance_of-Q9KTBnrf.mjs → assert_instance_of-mrXWWNY-.mjs} +1 -1
- package/dist/{assert_not_equals-EPJPoxuf.mjs → assert_not_equals-CpPK2t89.mjs} +2 -1
- package/dist/assert_rejects-CTptdYck.mjs +39 -0
- package/dist/{assert_strict_equals-Ec20AUwa.mjs → assert_strict_equals-CYvBmkpC.mjs} +2 -1
- package/dist/{assert_throws-NiFfqbnH.mjs → assert_throws-BYITOvPO.mjs} +2 -1
- package/dist/assertion_error-B-cx0fFA.mjs +31 -0
- package/dist/audience-CHQ-FlLD.mjs +95 -0
- package/dist/{builder-DonuxRl_.mjs → builder-Bda6LK7A.mjs} +169 -14
- package/dist/{circuit-breaker-CBGyz5F7.mjs → circuit-breaker-XJ53md4S.mjs} +224 -17
- package/dist/circuit-breaker-test-utils-DW6YC_Pe.mjs +14 -0
- package/dist/{client-CjF5IYQS.mjs → client-CEnNz8O7.mjs} +144 -8
- package/dist/{codec-BBga-FWI.mjs → codec-C6rTrpIp.mjs} +26 -3
- package/dist/{collection-BIhlldqK.mjs → collection-CnnP1UAv.mjs} +1 -1
- package/dist/compat/mod.cjs +1 -1
- package/dist/compat/mod.d.cts +1 -1
- package/dist/compat/mod.d.ts +1 -1
- package/dist/compat/mod.js +1 -1
- package/dist/compat/outgoing-jsonld.test.mjs +71 -3
- package/dist/compat/public-audience.test.mjs +14 -5
- 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 +5 -5
- package/dist/compound-proof-B-9mXxFW.mjs +989 -0
- package/dist/{context-Zii1f5xS.d.ts → context-7NyiqIcg.d.ts} +1600 -25
- package/dist/{context-BJXxPElm.mjs → context-8Tu9kea0.mjs} +21 -63
- package/dist/{context-lxX6I7as.d.cts → context-tsYPJ2Ew.d.cts} +1600 -25
- package/dist/{deno-BHzKn9Ig.mjs → deno-oR5aJKpU.mjs} +1 -1
- package/dist/dist-DJQM4u7i.mjs +189 -0
- package/dist/docloader-DBoYbvYW.mjs +87 -0
- package/dist/{esm-yYQfPhIY.mjs → esm-BHXXx4Wx.mjs} +13 -6
- package/dist/federation/audience.test.d.mts +2 -0
- package/dist/federation/audience.test.mjs +130 -0
- package/dist/federation/builder.test.mjs +54 -5
- package/dist/federation/circuit-breaker.test.mjs +838 -7
- package/dist/federation/collection.test.mjs +3 -3
- 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.d.mts +2 -0
- package/dist/federation/handler-compound-proof.test.mjs +384 -0
- package/dist/federation/handler.test.mjs +385 -15
- package/dist/federation/hashlink-media.test.d.mts +2 -0
- package/dist/federation/hashlink-media.test.mjs +422 -0
- package/dist/federation/idempotency.test.mjs +6 -6
- package/dist/federation/inbox-report.test.d.mts +2 -0
- package/dist/federation/inbox-report.test.mjs +475 -0
- package/dist/federation/inbox.test.mjs +3 -3
- package/dist/federation/keycache.test.mjs +215 -14
- package/dist/federation/kv.test.mjs +3 -3
- package/dist/federation/metrics.test.mjs +5 -5
- package/dist/federation/middleware.test.mjs +967 -35
- package/dist/federation/mod.cjs +18 -261
- package/dist/federation/mod.d.cts +5 -5
- package/dist/federation/mod.d.ts +5 -5
- package/dist/federation/mod.js +279 -1
- package/dist/federation/mq.test.mjs +123 -47
- package/dist/federation/negotiation.test.mjs +4 -4
- package/dist/federation/outgoing-proof.test.d.mts +2 -0
- package/dist/federation/outgoing-proof.test.mjs +931 -0
- package/dist/federation/portable-actor.test.d.mts +2 -0
- package/dist/federation/portable-actor.test.mjs +496 -0
- package/dist/federation/portable-chain.test.d.mts +2 -0
- package/dist/federation/portable-chain.test.mjs +370 -0
- package/dist/federation/portable-collection.test.d.mts +2 -0
- package/dist/federation/portable-collection.test.mjs +832 -0
- package/dist/federation/portable-delivery.test.d.mts +2 -0
- package/dist/federation/portable-delivery.test.mjs +746 -0
- package/dist/federation/portable-gateway-keys.test.d.mts +2 -0
- package/dist/federation/portable-gateway-keys.test.mjs +686 -0
- package/dist/federation/portable-inbox.test.d.mts +2 -0
- package/dist/federation/portable-inbox.test.mjs +1535 -0
- package/dist/federation/portable-parse-uri.test.d.mts +2 -0
- package/dist/federation/portable-parse-uri.test.mjs +348 -0
- 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.d.mts +2 -0
- package/dist/federation/portable.test.mjs +1072 -0
- package/dist/federation/retry.test.mjs +4 -3
- package/dist/federation/router.test.mjs +9 -159
- package/dist/federation/send.test.mjs +245 -27
- package/dist/federation/tasks/codec.test.mjs +4 -4
- package/dist/federation/tasks/enqueue.test.mjs +42 -4
- package/dist/federation/tasks/tasks.test.mjs +7 -6
- package/dist/federation/temporal.test.mjs +4 -4
- package/dist/federation/webfinger.test.mjs +275 -12
- package/dist/{middleware-FrSMAYEx.cjs → federation-CkZiAcWY.cjs} +8640 -3722
- package/dist/http-B5JT115n.cjs +10630 -0
- package/dist/http-DydWCB7R.js +6527 -0
- package/dist/{http-CPVMUgsW.mjs → http-siciWkmx.mjs} +397 -168
- package/dist/key-BspPiXqn.mjs +951 -0
- package/dist/key-C1nv7_jD.d.ts +1125 -0
- package/dist/key-DYxuyd-s.d.cts +1125 -0
- package/dist/key-owner-evidence-APB6NpSk.mjs +108 -0
- package/dist/keycache-Da8A8mZa.mjs +200 -0
- package/dist/{kv-D6hNiMTK.d.ts → kv-B2mzK5tl.d.cts} +3 -2
- package/dist/{kv-gJ8LYbxX.d.cts → kv-B2mzK5tl.d.ts} +3 -2
- package/dist/kv-DBRWVx3Q.mjs +104 -0
- package/dist/{kv-cache-KyGqJqSp.mjs → kv-cache-BF1hnW65.mjs} +1 -1
- package/dist/{kv-cache-5bnX3-Jq.js → kv-cache-CTQr1tbn.js} +54 -16
- package/dist/{kv-cache-DQ29C4XL.cjs → kv-cache-DCac1nEU.cjs} +55 -19
- package/dist/{ld-Cj9F_5AP.mjs → ld-CzaP-hJl.mjs} +44 -12
- package/dist/map-local-context-conflict-DOYEEJ3u.mjs +204 -0
- package/dist/map-local-create-note-B0SeXJms.mjs +214 -0
- package/dist/{metrics-DMk-nTxI.mjs → metrics-CYg9yxBL.mjs} +3 -2
- package/dist/metrics-DVKYY6K1.d.cts +36 -0
- package/dist/metrics-DoRtI0qU.d.ts +36 -0
- package/dist/{middleware-Y-kpsp6S.js → middleware-CsDDXKuL.js} +5493 -1103
- package/dist/{middleware-CvwTUgnP.mjs → middleware-D7_LO-Vs.mjs} +3157 -272
- package/dist/{middleware-CEctxBN6.mjs → middleware-DWjrfpcz.mjs} +1 -1
- package/dist/{mod-gOlPDuT8.d.cts → mod-BojP2Bw3.d.cts} +1 -1
- package/dist/mod-CavhAgLw.d.cts +320 -0
- package/dist/mod-CtpF2BU5.d.ts +320 -0
- package/dist/{mod-BtEaIEcD.d.cts → mod-DEZXwV6J.d.ts} +10 -4
- package/dist/{mod-UMmveiO0.d.ts → mod-Dw-nPxQh.d.cts} +10 -4
- package/dist/{mod-DLc2Ot6f.d.ts → mod-Xj5P4s4d.d.ts} +1 -1
- package/dist/mod.cjs +40 -39
- package/dist/mod.d.cts +10 -11
- package/dist/mod.d.ts +10 -11
- package/dist/mod.js +9 -9
- package/dist/{mq-llHKivST.mjs → mq-Dm1Wmtm-.mjs} +6 -0
- package/dist/nodeinfo/client.test.mjs +139 -4
- package/dist/nodeinfo/handler.test.mjs +5 -5
- package/dist/nodeinfo/mod.cjs +1 -1
- package/dist/nodeinfo/mod.d.cts +1 -1
- package/dist/nodeinfo/mod.d.ts +1 -1
- package/dist/nodeinfo/mod.js +1 -1
- package/dist/nodeinfo/types.test.mjs +4 -4
- package/dist/otel/exporter.test.mjs +3 -3
- package/dist/otel/mod.cjs +0 -1
- package/dist/otel/mod.d.cts +1 -1
- package/dist/otel/mod.d.ts +1 -1
- package/dist/{outgoing-jsonld-URuqsICz.mjs → outgoing-jsonld-4IJqPYos.mjs} +12 -10
- package/dist/{owner-DEvZuyOE.d.cts → owner-BF0L9cMt.d.cts} +11 -0
- package/dist/{owner-CnngXDNJ.d.ts → owner-BSzr5-I8.d.ts} +11 -0
- package/dist/owner-DYgfTFHy.mjs +267 -0
- package/dist/portable-collection-BpsVsRov.js +670 -0
- package/dist/portable-collection-C74P1vtC.mjs +418 -0
- package/dist/portable-collection-DZd2bm7M.cjs +686 -0
- package/dist/portable-key-BIMLrAP4.mjs +549 -0
- package/dist/portable-key-id-Ca30-hOa.mjs +248 -0
- package/dist/proof-Dxy6ByqI.mjs +1299 -0
- package/dist/{public-audience-Cr5MsnMq.mjs → public-audience--klBIOk1.mjs} +15 -10
- package/dist/router-u5O5KCQr.mjs +156 -0
- package/dist/runtime/mod.cjs +1 -1
- package/dist/runtime/mod.js +1 -1
- package/dist/secured-document-CxzZZ9nL.mjs +35 -0
- package/dist/send-C4dDpAxm.mjs +495 -0
- package/dist/sig/accept.test.mjs +1 -1
- package/dist/sig/compound-proof-discovery.test.d.mts +2 -0
- package/dist/sig/compound-proof-discovery.test.mjs +331 -0
- 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.d.mts +2 -0
- package/dist/sig/compound-proof-verification.test.mjs +833 -0
- package/dist/sig/compound-proof.test.d.mts +2 -0
- package/dist/sig/compound-proof.test.mjs +411 -0
- package/dist/sig/http.test.mjs +702 -31
- package/dist/sig/key-owner-evidence.test.d.mts +2 -0
- package/dist/sig/key-owner-evidence.test.mjs +100 -0
- package/dist/sig/key.test.mjs +220 -8
- package/dist/sig/ld.test.mjs +65 -7
- package/dist/sig/mitra-receive-interop.test.d.mts +2 -0
- package/dist/sig/mitra-receive-interop.test.mjs +260 -0
- package/dist/sig/mitra-send-interop.test.d.mts +2 -0
- package/dist/sig/mitra-send-interop.test.mjs +461 -0
- package/dist/sig/mod.cjs +18 -16
- package/dist/sig/mod.d.cts +4 -4
- package/dist/sig/mod.d.ts +4 -4
- package/dist/sig/mod.js +3 -3
- package/dist/sig/owner.test.mjs +108 -9
- package/dist/sig/portable-collection.test.d.mts +2 -0
- package/dist/sig/portable-collection.test.mjs +697 -0
- package/dist/sig/portable-interop.test.d.mts +2 -0
- package/dist/sig/portable-interop.test.mjs +156 -0
- 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.d.mts +2 -0
- package/dist/sig/portable-key.test.mjs +1439 -0
- package/dist/sig/proof.test.mjs +1753 -16
- package/dist/sig/tootik-request-interop.test.d.mts +2 -0
- package/dist/sig/tootik-request-interop.test.mjs +190 -0
- package/dist/sig/tootik-send-interop.test.d.mts +2 -0
- package/dist/sig/tootik-send-interop.test.mjs +331 -0
- package/dist/sig/verification.test.d.mts +2 -0
- package/dist/sig/verification.test.mjs +310 -0
- package/dist/std__assert-DvMDRPes.mjs +8 -0
- package/dist/{testing-CO7ftO8A.mjs → tasks-IG3pHfvZ.mjs} +2 -2
- package/dist/{temporal-fsXO5IvD.mjs → temporal-D0KJI4NF.mjs} +3 -3
- package/dist/testing/context.test.d.mts +2 -0
- package/dist/testing/context.test.mjs +19 -0
- package/dist/testing/kv.test.d.mts +2 -0
- package/dist/testing/kv.test.mjs +101 -0
- package/dist/testing/mod.d.mts +1617 -48
- package/dist/testing/mod.mjs +6 -3
- package/dist/testing-t8xDeJZ5.mjs +8 -0
- package/dist/{transformers-DGwl-hD1.cjs → transformers-BalCrSsr.cjs} +0 -1
- package/dist/{types-DC1pNgJA.js → types-_S2_MeLF.js} +144 -8
- package/dist/{types-CPym83zc.cjs → types-poIao40f.cjs} +160 -7
- package/dist/utils/docloader.test.mjs +530 -8
- package/dist/utils/kv-cache.test.mjs +2 -2
- 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/dist/verification-C3954hQh.mjs +5 -0
- package/dist/verification-Dpfcw8ml.mjs +105 -0
- package/dist/vocab/cjs.test.mjs +1 -1
- package/package.json +8 -8
- package/dist/assert_rejects-EgstcQy5.mjs +0 -82
- package/dist/docloader-e1khNUKy.mjs +0 -49
- package/dist/http-CsmQ-BQ4.js +0 -2483
- package/dist/http-VyDTd4G3.d.cts +0 -553
- package/dist/http-hlIjmewS.cjs +0 -2694
- package/dist/http-lf8Hsd91.d.ts +0 -553
- package/dist/key-DfPsX8hv.mjs +0 -453
- package/dist/keycache-_DHqz5z_.mjs +0 -97
- package/dist/metrics-CoUJLQhZ.d.cts +0 -276
- package/dist/metrics-Dc4jR-IC.d.ts +0 -276
- package/dist/mod-B0hW12_O.d.cts +0 -307
- package/dist/mod-COIAjwRS.d.ts +0 -307
- package/dist/owner-Br9YYzn_.mjs +0 -120
- package/dist/proof-B4ngoaNQ.mjs +0 -316
- package/dist/proof-B6E0XUXJ.js +0 -1415
- package/dist/proof-NN4xCQGL.cjs +0 -1562
- package/dist/send-CxHKUNzv.mjs +0 -239
- package/dist/std__assert-B2NnY9bq.mjs +0 -79
- package/dist/temporal-polyfill-BCdTHexd.cjs +0 -3565
- /package/dist/{accept-8PiolDP7.mjs → accept-CPVqki78.mjs} +0 -0
- /package/dist/{activity-listener-BtTSxZWA.mjs → activity-listener-C-BSbKI7.mjs} +0 -0
- /package/dist/{client-CAM_bQXx.d.cts → client-D-XP_Ccb.d.cts} +0 -0
- /package/dist/{client-CSddvgWN.d.ts → client-D-XP_Ccb.d.ts} +0 -0
- /package/dist/{keys-Dbyd7bhl.mjs → keys-DIJNHW-Y.mjs} +0 -0
- /package/dist/{kv-CXBAok3k.mjs → kv-DCHkaJ9-.mjs} +0 -0
- /package/dist/{negotiation-RuwrPFqi.mjs → negotiation-CCocWT_N.mjs} +0 -0
- /package/dist/{retry-DgFqeS5k.mjs → retry-zX5xw5e0.mjs} +0 -0
- /package/dist/{chunk-D5IdezsZ.js → rolldown-runtime-BC-Eo5Mj.js} +0 -0
- /package/dist/{chunk-Czpeue4M.cjs → rolldown-runtime-Dmm3t92W.cjs} +0 -0
- /package/dist/{transformers-DXze6FUB.js → transformers-DAtXl_Kc.js} +0 -0
- /package/dist/{types-CrtdVuDP.mjs → types-DE_LV1JB.mjs} +0 -0
|
@@ -1,22 +1,25 @@
|
|
|
1
1
|
import { Temporal } from "temporal-polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { n as createOutboxContext, r as createRequestContext, t as createInboxContext } from "../context-
|
|
5
|
-
import { t as MemoryKvStore } from "../kv-
|
|
6
|
-
import { t as assertEquals } from "../assert_equals-
|
|
7
|
-
import "../
|
|
8
|
-
import {
|
|
9
|
-
import
|
|
10
|
-
import { t as
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import { a as compactJsonLd, p as signJsonLd } from "../ld-
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
4
|
+
import { n as createOutboxContext, r as createRequestContext, t as createInboxContext } from "../context-8Tu9kea0.mjs";
|
|
5
|
+
import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
|
|
6
|
+
import { t as assertEquals } from "../assert_equals-Z6IcEbWU.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 "../std__assert-DvMDRPes.mjs";
|
|
10
|
+
import { t as assertRejects } from "../assert_rejects-CTptdYck.mjs";
|
|
11
|
+
import { t as assert } from "../assert-D1Eafmd_.mjs";
|
|
12
|
+
import { r as parseAcceptSignature } from "../accept-CPVqki78.mjs";
|
|
13
|
+
import { o as generateCryptoKeyPair } from "../key-BspPiXqn.mjs";
|
|
14
|
+
import { a as compactJsonLd, p as signJsonLd } from "../ld-CzaP-hJl.mjs";
|
|
15
|
+
import { a as signObject } from "../proof-Dxy6ByqI.mjs";
|
|
16
|
+
import { l as signRequest } from "../http-siciWkmx.mjs";
|
|
17
|
+
import { a as rsaPrivateKey3, c as rsaPublicKey3, s as rsaPublicKey2 } from "../keys-DIJNHW-Y.mjs";
|
|
18
|
+
import { d as handleCustomCollection, f as handleInbox, g as respondWithObjectIfAcceptable, h as respondWithObject, l as handleActor, m as handleOutbox, o as createFederation, p as handleObject, u as handleCollection } from "../middleware-D7_LO-Vs.mjs";
|
|
19
|
+
import { t as ActivityListenerSet } from "../activity-listener-C-BSbKI7.mjs";
|
|
20
|
+
import { Activity, Create, CryptographicKey, Multikey, Note, Person, Tombstone } from "@fedify/vocab";
|
|
21
|
+
import { FetchError, parseIri } from "@fedify/vocab-runtime";
|
|
18
22
|
import { createTestMeterProvider, createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
|
|
19
|
-
import { FetchError } from "@fedify/vocab-runtime";
|
|
20
23
|
//#region src/federation/handler.test.ts
|
|
21
24
|
const QUOTE_CONTEXT_TERMS = {
|
|
22
25
|
QuoteAuthorization: "https://w3id.org/fep/044f#QuoteAuthorization",
|
|
@@ -497,6 +500,119 @@ test("handleObject()", async () => {
|
|
|
497
500
|
assertEquals(onNotFoundCalled, null);
|
|
498
501
|
assertEquals(onUnauthorizedCalled, null);
|
|
499
502
|
});
|
|
503
|
+
test("handleObject() with a tombstone", async () => {
|
|
504
|
+
const federation = createFederation({ kv: new MemoryKvStore() });
|
|
505
|
+
const url = new URL("https://example.com/users/someone/notes/123");
|
|
506
|
+
let context = createRequestContext({
|
|
507
|
+
federation,
|
|
508
|
+
data: void 0,
|
|
509
|
+
url,
|
|
510
|
+
request: new Request(url, { headers: { Accept: "application/activity+json" } })
|
|
511
|
+
});
|
|
512
|
+
const objectDispatcher = (_ctx, values) => {
|
|
513
|
+
if (values.id !== "123") return null;
|
|
514
|
+
return new Tombstone({
|
|
515
|
+
id: new URL(`https://example.com/users/${values.identifier}/notes/${values.id}`),
|
|
516
|
+
formerType: Note,
|
|
517
|
+
deleted: Temporal.Instant.from("2024-01-15T00:00:00Z")
|
|
518
|
+
});
|
|
519
|
+
};
|
|
520
|
+
let onNotFoundCalled = null;
|
|
521
|
+
const onNotFound = (request) => {
|
|
522
|
+
onNotFoundCalled = request;
|
|
523
|
+
return new Response("Not found", { status: 404 });
|
|
524
|
+
};
|
|
525
|
+
let onUnauthorizedCalled = null;
|
|
526
|
+
const onUnauthorized = (request) => {
|
|
527
|
+
onUnauthorizedCalled = request;
|
|
528
|
+
return new Response("Unauthorized", { status: 401 });
|
|
529
|
+
};
|
|
530
|
+
let response = await handleObject(context.request, {
|
|
531
|
+
context,
|
|
532
|
+
values: {
|
|
533
|
+
identifier: "someone",
|
|
534
|
+
id: "123"
|
|
535
|
+
},
|
|
536
|
+
objectDispatcher,
|
|
537
|
+
onNotFound,
|
|
538
|
+
onUnauthorized
|
|
539
|
+
});
|
|
540
|
+
assertEquals(response.status, 410);
|
|
541
|
+
assertEquals(response.headers.get("Content-Type"), "application/activity+json");
|
|
542
|
+
assertEquals(response.headers.get("Vary"), "Accept");
|
|
543
|
+
const body = await response.json();
|
|
544
|
+
assertEquals(body.id, "https://example.com/users/someone/notes/123");
|
|
545
|
+
assertEquals(body.type, "Tombstone");
|
|
546
|
+
assertEquals(body.formerType, "as:Note");
|
|
547
|
+
assertEquals(body.deleted, "2024-01-15T00:00:00Z");
|
|
548
|
+
assertEquals(onNotFoundCalled, null);
|
|
549
|
+
assertEquals(onUnauthorizedCalled, null);
|
|
550
|
+
response = await handleObject(context.request, {
|
|
551
|
+
context,
|
|
552
|
+
values: {
|
|
553
|
+
identifier: "someone",
|
|
554
|
+
id: "456"
|
|
555
|
+
},
|
|
556
|
+
objectDispatcher,
|
|
557
|
+
onNotFound,
|
|
558
|
+
onUnauthorized
|
|
559
|
+
});
|
|
560
|
+
assertEquals(response.status, 404);
|
|
561
|
+
assertEquals(onNotFoundCalled, context.request);
|
|
562
|
+
assertEquals(onUnauthorizedCalled, null);
|
|
563
|
+
onNotFoundCalled = null;
|
|
564
|
+
let authorized = false;
|
|
565
|
+
const authorizePredicate = () => authorized;
|
|
566
|
+
response = await handleObject(context.request, {
|
|
567
|
+
context,
|
|
568
|
+
values: {
|
|
569
|
+
identifier: "someone",
|
|
570
|
+
id: "123"
|
|
571
|
+
},
|
|
572
|
+
objectDispatcher,
|
|
573
|
+
authorizePredicate,
|
|
574
|
+
onNotFound,
|
|
575
|
+
onUnauthorized
|
|
576
|
+
});
|
|
577
|
+
assertEquals(response.status, 401);
|
|
578
|
+
assertEquals(onNotFoundCalled, null);
|
|
579
|
+
assertEquals(onUnauthorizedCalled, context.request);
|
|
580
|
+
onUnauthorizedCalled = null;
|
|
581
|
+
authorized = true;
|
|
582
|
+
response = await handleObject(context.request, {
|
|
583
|
+
context,
|
|
584
|
+
values: {
|
|
585
|
+
identifier: "someone",
|
|
586
|
+
id: "123"
|
|
587
|
+
},
|
|
588
|
+
objectDispatcher,
|
|
589
|
+
authorizePredicate,
|
|
590
|
+
onNotFound,
|
|
591
|
+
onUnauthorized
|
|
592
|
+
});
|
|
593
|
+
assertEquals(response.status, 410);
|
|
594
|
+
assertEquals(onUnauthorizedCalled, null);
|
|
595
|
+
context = createRequestContext({
|
|
596
|
+
...context,
|
|
597
|
+
request: new Request(url, {
|
|
598
|
+
method: "HEAD",
|
|
599
|
+
headers: { Accept: "application/activity+json" }
|
|
600
|
+
})
|
|
601
|
+
});
|
|
602
|
+
response = await handleObject(context.request, {
|
|
603
|
+
context,
|
|
604
|
+
values: {
|
|
605
|
+
identifier: "someone",
|
|
606
|
+
id: "123"
|
|
607
|
+
},
|
|
608
|
+
objectDispatcher,
|
|
609
|
+
onNotFound,
|
|
610
|
+
onUnauthorized
|
|
611
|
+
});
|
|
612
|
+
assertEquals(response.status, 410);
|
|
613
|
+
assertEquals(response.headers.get("Content-Type"), "application/activity+json");
|
|
614
|
+
assertEquals(response.body, null);
|
|
615
|
+
});
|
|
500
616
|
test("handleCollection()", async () => {
|
|
501
617
|
let context = createRequestContext({
|
|
502
618
|
federation: createFederation({ kv: new MemoryKvStore() }),
|
|
@@ -1126,6 +1242,67 @@ test("handleCollection() records not_found collection metrics", async () => {
|
|
|
1126
1242
|
assertEquals(durations[0].attributes["activitypub.collection.result"], "not_found");
|
|
1127
1243
|
assertEquals(recorder.getMeasurements("activitypub.collection.page.items").length, 0);
|
|
1128
1244
|
});
|
|
1245
|
+
test("handleInbox() reports dispatch context errors before a failing error hook", async () => {
|
|
1246
|
+
const kv = new MemoryKvStore();
|
|
1247
|
+
const federation = createFederation({ kv });
|
|
1248
|
+
const request = await signRequest(new Request("https://example.com/inbox", {
|
|
1249
|
+
method: "POST",
|
|
1250
|
+
body: JSON.stringify(await new Create({ actor: rsaPublicKey3.ownerId }).toJsonLd({ contextLoader: mockDocumentLoader }))
|
|
1251
|
+
}), rsaPrivateKey3, rsaPublicKey3.id);
|
|
1252
|
+
const context = createRequestContext({
|
|
1253
|
+
federation,
|
|
1254
|
+
request,
|
|
1255
|
+
url: new URL(request.url),
|
|
1256
|
+
data: void 0,
|
|
1257
|
+
documentLoader: mockDocumentLoader,
|
|
1258
|
+
contextLoader: mockDocumentLoader
|
|
1259
|
+
});
|
|
1260
|
+
const error = /* @__PURE__ */ new Error("Cannot create dispatch context");
|
|
1261
|
+
const reports = [];
|
|
1262
|
+
const listeners = new ActivityListenerSet();
|
|
1263
|
+
let listenerCalled = false;
|
|
1264
|
+
listeners.add(Create, () => {
|
|
1265
|
+
listenerCalled = true;
|
|
1266
|
+
});
|
|
1267
|
+
let errorHookCalled = false;
|
|
1268
|
+
assertEquals((await handleInbox(request, {
|
|
1269
|
+
context,
|
|
1270
|
+
recipient: null,
|
|
1271
|
+
kv,
|
|
1272
|
+
kvPrefixes: {
|
|
1273
|
+
activityIdempotence: ["activity"],
|
|
1274
|
+
publicKey: ["key"],
|
|
1275
|
+
acceptSignatureNonce: ["nonce"]
|
|
1276
|
+
},
|
|
1277
|
+
actorDispatcher: () => new Person({}),
|
|
1278
|
+
inboxListeners: listeners,
|
|
1279
|
+
inboxContextFactory: () => {
|
|
1280
|
+
throw error;
|
|
1281
|
+
},
|
|
1282
|
+
inboxErrorHandler: (_ctx, value) => {
|
|
1283
|
+
assertEquals(value, error);
|
|
1284
|
+
errorHookCalled = true;
|
|
1285
|
+
throw new Error("Error hook failed");
|
|
1286
|
+
},
|
|
1287
|
+
inboxRequestFinishedHandler: (_ctx, report) => {
|
|
1288
|
+
reports.push(report);
|
|
1289
|
+
},
|
|
1290
|
+
onNotFound: () => new Response(null, { status: 404 }),
|
|
1291
|
+
signatureTimeWindow: false,
|
|
1292
|
+
skipSignatureVerification: false
|
|
1293
|
+
})).status, 500);
|
|
1294
|
+
assertEquals(listenerCalled, false);
|
|
1295
|
+
assertEquals(errorHookCalled, true);
|
|
1296
|
+
assertEquals(reports.length, 1);
|
|
1297
|
+
assertEquals(reports[0].authentication.status, "verified");
|
|
1298
|
+
assertEquals(reports[0].outcome, {
|
|
1299
|
+
type: "response",
|
|
1300
|
+
status: 500,
|
|
1301
|
+
disposition: "failed",
|
|
1302
|
+
reason: "listenerError",
|
|
1303
|
+
error
|
|
1304
|
+
});
|
|
1305
|
+
});
|
|
1129
1306
|
test("handleInbox()", async () => {
|
|
1130
1307
|
const activity = new Create({
|
|
1131
1308
|
id: new URL("https://example.com/activities/1"),
|
|
@@ -2748,6 +2925,73 @@ test("handleOutbox()", async () => {
|
|
|
2748
2925
|
assertEquals(response.status, 500);
|
|
2749
2926
|
assertEquals(onErrorCalled, true);
|
|
2750
2927
|
});
|
|
2928
|
+
test("handleOutbox() matches portable actors by their canonical IDs", async () => {
|
|
2929
|
+
const federation = createFederation({ kv: new MemoryKvStore() });
|
|
2930
|
+
const actorId = "ap+ef61://did:key:z6MkAlice/users/someone";
|
|
2931
|
+
const actorDispatcher = (_ctx, identifier) => {
|
|
2932
|
+
if (identifier !== "someone") return null;
|
|
2933
|
+
return new Person({
|
|
2934
|
+
id: parseIri(actorId),
|
|
2935
|
+
name: "Someone"
|
|
2936
|
+
});
|
|
2937
|
+
};
|
|
2938
|
+
const post = async (actor) => {
|
|
2939
|
+
const request = new Request("https://example.com/users/someone/outbox", {
|
|
2940
|
+
method: "POST",
|
|
2941
|
+
body: JSON.stringify({
|
|
2942
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
|
2943
|
+
type: "Create",
|
|
2944
|
+
id: "https://example.com/activities/1",
|
|
2945
|
+
actor,
|
|
2946
|
+
object: {
|
|
2947
|
+
type: "Note",
|
|
2948
|
+
content: "Hello, world!"
|
|
2949
|
+
}
|
|
2950
|
+
})
|
|
2951
|
+
});
|
|
2952
|
+
const context = createRequestContext({
|
|
2953
|
+
federation,
|
|
2954
|
+
request,
|
|
2955
|
+
url: new URL(request.url),
|
|
2956
|
+
data: void 0
|
|
2957
|
+
});
|
|
2958
|
+
const seen = [];
|
|
2959
|
+
const listeners = new ActivityListenerSet();
|
|
2960
|
+
listeners.add(Activity, (_ctx, activity) => {
|
|
2961
|
+
seen.push(activity.id.href);
|
|
2962
|
+
});
|
|
2963
|
+
return [(await handleOutbox(request, {
|
|
2964
|
+
identifier: "someone",
|
|
2965
|
+
context,
|
|
2966
|
+
outboxContextFactory(identifier) {
|
|
2967
|
+
return createOutboxContext({
|
|
2968
|
+
...context,
|
|
2969
|
+
clone: void 0,
|
|
2970
|
+
identifier
|
|
2971
|
+
});
|
|
2972
|
+
},
|
|
2973
|
+
actorDispatcher,
|
|
2974
|
+
outboxListeners: listeners,
|
|
2975
|
+
onNotFound: () => new Response("Not found", { status: 404 }),
|
|
2976
|
+
onUnauthorized: () => new Response("Unauthorized", { status: 401 })
|
|
2977
|
+
})).status, seen.length];
|
|
2978
|
+
};
|
|
2979
|
+
for (const actor of [
|
|
2980
|
+
actorId,
|
|
2981
|
+
"ap://did:key:z6MkAlice/users/someone",
|
|
2982
|
+
"ap+ef61://did%3Akey%3Az6MkAlice/users/someone",
|
|
2983
|
+
"ap://did:key:z6MkAlice/users/someone?@gateway=https%3A%2F%2Fserver1.example&@gateway=https%3A%2F%2Fserver2.example",
|
|
2984
|
+
"https://server2.example/.well-known/apgateway/did:key:z6MkAlice/users/someone"
|
|
2985
|
+
]) assertEquals(await post(actor), [202, 1], actor);
|
|
2986
|
+
for (const actor of [
|
|
2987
|
+
"ap://did:key:z6MkBob/users/someone",
|
|
2988
|
+
"ap://did:key:z6MkAlice/users/other?@gateway=https%3A%2F%2Fserver1.example",
|
|
2989
|
+
"ap://did:key:z6MkAlice/users/someone#main-key",
|
|
2990
|
+
"https://server1.example/.well-known/apgateway/did:key:z6MkAlice/users/someone?@gateway=https%3A%2F%2Fserver1.example",
|
|
2991
|
+
"https://example.com/users/someone",
|
|
2992
|
+
[actorId, "ap://did:key:z6MkAlice/users/other"]
|
|
2993
|
+
]) assertEquals(await post(actor), [400, 0], String(actor));
|
|
2994
|
+
});
|
|
2751
2995
|
test("handleInbox() preserves the raw signed payload for inboxContextFactory", async () => {
|
|
2752
2996
|
const federation = createFederation({ kv: new MemoryKvStore() });
|
|
2753
2997
|
const remoteContextUrl = "https://remote.example/contexts/ext";
|
|
@@ -4243,7 +4487,11 @@ test("handleInbox() nonce replay prevention", async () => {
|
|
|
4243
4487
|
if (identifier !== "someone") return null;
|
|
4244
4488
|
return new Person({ name: "Someone" });
|
|
4245
4489
|
};
|
|
4490
|
+
let report;
|
|
4246
4491
|
const response = await handleInbox(signedRequest, {
|
|
4492
|
+
inboxRequestFinishedHandler: (_ctx, value) => {
|
|
4493
|
+
report = value;
|
|
4494
|
+
},
|
|
4247
4495
|
recipient: "someone",
|
|
4248
4496
|
context,
|
|
4249
4497
|
inboxContextFactory(_activity) {
|
|
@@ -4271,6 +4519,12 @@ test("handleInbox() nonce replay prevention", async () => {
|
|
|
4271
4519
|
}
|
|
4272
4520
|
});
|
|
4273
4521
|
assertEquals(response.status, 401);
|
|
4522
|
+
assert(report != null);
|
|
4523
|
+
assertEquals(report.authentication, {
|
|
4524
|
+
status: "rejected",
|
|
4525
|
+
reason: { type: "invalidNonce" }
|
|
4526
|
+
});
|
|
4527
|
+
assertEquals(report.attempts.at(-1)?.status, "verified");
|
|
4274
4528
|
const acceptSig = response.headers.get("Accept-Signature");
|
|
4275
4529
|
assert(acceptSig != null, "Must emit fresh Accept-Signature challenge");
|
|
4276
4530
|
const parsed = parseAcceptSignature(acceptSig);
|
|
@@ -4573,5 +4827,121 @@ test("handleInbox() challenge policy enabled + unverifiedActivityHandler throws
|
|
|
4573
4827
|
assertEquals(response.headers.get("Cache-Control"), "no-store", "Cache-Control: no-store must be set for challenge-response");
|
|
4574
4828
|
assertEquals(response.headers.get("Vary"), "Accept, Signature", "Vary header must include Accept and Signature");
|
|
4575
4829
|
});
|
|
4830
|
+
test("handleInbox() rejects forged key ownership", async () => {
|
|
4831
|
+
const impersonated = "https://example.com/person2";
|
|
4832
|
+
const attackerKeyId = new URL("https://attacker.example/key");
|
|
4833
|
+
const attackerMultikeyId = new URL("https://attacker.example/multikey");
|
|
4834
|
+
const { privateKey, publicKey } = await generateCryptoKeyPair("RSASSA-PKCS1-v1_5");
|
|
4835
|
+
const forgedKeyDocument = await new CryptographicKey({
|
|
4836
|
+
id: attackerKeyId,
|
|
4837
|
+
owner: new URL(impersonated),
|
|
4838
|
+
publicKey
|
|
4839
|
+
}).toJsonLd({ contextLoader: mockDocumentLoader });
|
|
4840
|
+
const ed25519 = await generateCryptoKeyPair("Ed25519");
|
|
4841
|
+
const forgedMultikeyDocument = await new Multikey({
|
|
4842
|
+
id: attackerMultikeyId,
|
|
4843
|
+
controller: new URL(impersonated),
|
|
4844
|
+
publicKey: ed25519.publicKey
|
|
4845
|
+
}).toJsonLd({ contextLoader: mockDocumentLoader });
|
|
4846
|
+
const documentLoader = (resource) => {
|
|
4847
|
+
if (resource === attackerKeyId.href) return Promise.resolve({
|
|
4848
|
+
contextUrl: null,
|
|
4849
|
+
documentUrl: resource,
|
|
4850
|
+
document: forgedKeyDocument
|
|
4851
|
+
});
|
|
4852
|
+
if (resource === attackerMultikeyId.href) return Promise.resolve({
|
|
4853
|
+
contextUrl: null,
|
|
4854
|
+
documentUrl: resource,
|
|
4855
|
+
document: forgedMultikeyDocument
|
|
4856
|
+
});
|
|
4857
|
+
return mockDocumentLoader(resource);
|
|
4858
|
+
};
|
|
4859
|
+
const federation = createFederation({ kv: new MemoryKvStore() });
|
|
4860
|
+
const inboxOptions = {
|
|
4861
|
+
kv: new MemoryKvStore(),
|
|
4862
|
+
kvPrefixes: {
|
|
4863
|
+
activityIdempotence: ["_fedify", "activityIdempotence"],
|
|
4864
|
+
publicKey: ["_fedify", "publicKey"],
|
|
4865
|
+
acceptSignatureNonce: ["_fedify", "acceptSignatureNonce"]
|
|
4866
|
+
},
|
|
4867
|
+
actorDispatcher: ((_ctx, identifier) => identifier === "someone" ? new Person({ name: "Someone" }) : null),
|
|
4868
|
+
inboxListeners: new ActivityListenerSet(),
|
|
4869
|
+
onNotFound: () => new Response("Not found", { status: 404 }),
|
|
4870
|
+
signatureTimeWindow: { minutes: 5 },
|
|
4871
|
+
skipSignatureVerification: false
|
|
4872
|
+
};
|
|
4873
|
+
const handle = async (request) => {
|
|
4874
|
+
const context = createRequestContext({
|
|
4875
|
+
federation,
|
|
4876
|
+
request,
|
|
4877
|
+
url: new URL(request.url),
|
|
4878
|
+
data: void 0,
|
|
4879
|
+
documentLoader,
|
|
4880
|
+
contextLoader: mockDocumentLoader
|
|
4881
|
+
});
|
|
4882
|
+
return await handleInbox(request, {
|
|
4883
|
+
recipient: null,
|
|
4884
|
+
context,
|
|
4885
|
+
inboxContextFactory(_activity) {
|
|
4886
|
+
return createInboxContext({
|
|
4887
|
+
...context,
|
|
4888
|
+
clone: void 0
|
|
4889
|
+
});
|
|
4890
|
+
},
|
|
4891
|
+
...inboxOptions
|
|
4892
|
+
});
|
|
4893
|
+
};
|
|
4894
|
+
const activity = {
|
|
4895
|
+
"@context": [
|
|
4896
|
+
"https://www.w3.org/ns/activitystreams",
|
|
4897
|
+
"https://w3id.org/identity/v1",
|
|
4898
|
+
"https://w3id.org/security/v1",
|
|
4899
|
+
"https://w3id.org/security/data-integrity/v1"
|
|
4900
|
+
],
|
|
4901
|
+
id: "https://attacker.example/activities/1",
|
|
4902
|
+
type: "Create",
|
|
4903
|
+
actor: impersonated,
|
|
4904
|
+
object: {
|
|
4905
|
+
id: "https://attacker.example/notes/1",
|
|
4906
|
+
type: "Note",
|
|
4907
|
+
attributedTo: impersonated,
|
|
4908
|
+
content: "Hello World!"
|
|
4909
|
+
}
|
|
4910
|
+
};
|
|
4911
|
+
const httpSignedResponse = await handle(await signRequest(new Request("https://example.com/", {
|
|
4912
|
+
method: "POST",
|
|
4913
|
+
body: JSON.stringify(activity)
|
|
4914
|
+
}), privateKey, attackerKeyId));
|
|
4915
|
+
const ldSignedResponse = await handle(new Request("https://example.com/", {
|
|
4916
|
+
method: "POST",
|
|
4917
|
+
body: JSON.stringify(await signJsonLd(activity, privateKey, attackerKeyId, { contextLoader: mockDocumentLoader }))
|
|
4918
|
+
}));
|
|
4919
|
+
const signedObject = await signObject(await Create.fromJsonLd(activity, {
|
|
4920
|
+
documentLoader,
|
|
4921
|
+
contextLoader: mockDocumentLoader
|
|
4922
|
+
}), ed25519.privateKey, attackerMultikeyId, {
|
|
4923
|
+
documentLoader,
|
|
4924
|
+
contextLoader: mockDocumentLoader,
|
|
4925
|
+
context: ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/data-integrity/v1"]
|
|
4926
|
+
});
|
|
4927
|
+
const proofSignedResponse = await handle(new Request("https://example.com/", {
|
|
4928
|
+
method: "POST",
|
|
4929
|
+
body: JSON.stringify(await signedObject.toJsonLd({
|
|
4930
|
+
format: "compact",
|
|
4931
|
+
contextLoader: mockDocumentLoader,
|
|
4932
|
+
context: ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/data-integrity/v1"]
|
|
4933
|
+
}))
|
|
4934
|
+
}));
|
|
4935
|
+
assertEquals([
|
|
4936
|
+
httpSignedResponse.status,
|
|
4937
|
+
ldSignedResponse.status,
|
|
4938
|
+
proofSignedResponse.status
|
|
4939
|
+
], [
|
|
4940
|
+
401,
|
|
4941
|
+
401,
|
|
4942
|
+
401
|
|
4943
|
+
]);
|
|
4944
|
+
assertEquals(await httpSignedResponse.text(), "Failed to verify the request signature.");
|
|
4945
|
+
});
|
|
4576
4946
|
//#endregion
|
|
4577
4947
|
export {};
|