@fedify/fedify 2.4.2 → 2.4.3
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-9ovCSqLp.mjs → builder-CztvQYX0.mjs} +2 -2
- package/dist/compat/transformers.test.mjs +1 -1
- package/dist/{compound-proof-G2J_bU0u.mjs → compound-proof-rcvB6i5j.mjs} +2 -2
- package/dist/{deno-BZ4z-lJJ.mjs → deno-BEgaR4dC.mjs} +1 -1
- package/dist/{docloader-DUXXI-7G.mjs → docloader-Wf9RroxL.mjs} +4 -2
- package/dist/federation/builder.test.mjs +1 -1
- package/dist/federation/compound-fanout.test.mjs +3 -3
- package/dist/federation/handler-compound-proof.test.mjs +5 -5
- package/dist/federation/handler.test.mjs +258 -5
- package/dist/federation/hashlink-media.test.mjs +2 -2
- package/dist/federation/idempotency.test.mjs +2 -2
- package/dist/federation/inbox-report.test.mjs +5 -5
- package/dist/federation/inbox.test.mjs +43 -0
- package/dist/federation/metrics.test.mjs +1 -1
- package/dist/federation/middleware.test.mjs +172 -10
- package/dist/federation/mod.cjs +1 -1
- package/dist/federation/mod.js +1 -1
- package/dist/federation/outgoing-proof.test.mjs +4 -4
- 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-delivery.test.mjs +2 -2
- package/dist/federation/portable-gateway-keys.test.mjs +4 -4
- package/dist/federation/portable-inbox.test.mjs +4 -4
- package/dist/federation/portable-parse-uri.test.mjs +1 -1
- package/dist/federation/portable-route-activity.test.mjs +2 -2
- 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-DpT8bDM6.cjs → federation-O8HwbuUv.cjs} +121 -20
- package/dist/{http-Qq7jR2rC.js → http-B5pRXkmQ.js} +1 -1
- package/dist/{http-x1satM4y.cjs → http-BFC8b7mH.cjs} +1 -1
- package/dist/{http-B5w_qcIg.mjs → http-CQcqhcga.mjs} +4 -4
- package/dist/inbox-CsErRN6L.mjs +188 -0
- package/dist/{kv-cache-BgQMAKIg.mjs → kv-cache-4oZXC3pf.mjs} +1 -1
- package/dist/{kv-cache-B0SZfzFb.js → kv-cache-Bih-8AoZ.js} +3 -1
- package/dist/{kv-cache-D4throaQ.cjs → kv-cache-JOKAStz2.cjs} +3 -1
- package/dist/{ld-Dkkyk65e.mjs → ld-Qk4WqslS.mjs} +2 -2
- package/dist/{metrics-CRv6_IBg.mjs → metrics-DAhJVtgg.mjs} +1 -1
- package/dist/{middleware-C-swet0z.mjs → middleware-B582s7UO.mjs} +103 -179
- package/dist/{middleware-DFtNaBcH.mjs → middleware-C76AHpbO.mjs} +1 -1
- package/dist/{middleware-BqefXH6B.js → middleware-KeANQWIR.js} +121 -20
- package/dist/mod.cjs +4 -4
- package/dist/mod.js +4 -4
- package/dist/nodeinfo/handler.test.mjs +1 -1
- package/dist/{owner-BHUhedVc.mjs → owner-CcyHGdSK.mjs} +3 -3
- package/dist/{portable-collection-DONEPTXV.mjs → portable-collection-0RzCsflU.mjs} +2 -2
- package/dist/{portable-collection-DL-1Wy1s.cjs → portable-collection-BHZq0F_c.cjs} +1 -1
- package/dist/{portable-collection-CfIc190c.js → portable-collection-Chb6A43p.js} +1 -1
- package/dist/{portable-key-Cdw3vfl-.mjs → portable-key-BY6G_X28.mjs} +1 -1
- package/dist/{proof-DuSE1ABD.mjs → proof-nIOXkRme.mjs} +3 -3
- package/dist/{send-D9oTjA6A.mjs → send-C2r9whkz.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 +2 -2
- package/dist/sig/key.test.mjs +1 -1
- package/dist/sig/ld.test.mjs +1 -1
- package/dist/sig/mitra-receive-interop.test.mjs +4 -4
- package/dist/sig/mitra-send-interop.test.mjs +3 -3
- package/dist/sig/mod.cjs +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-interop.test.mjs +3 -3
- package/dist/sig/portable-key.test.mjs +5 -5
- package/dist/sig/proof.test.mjs +3 -3
- package/dist/sig/tootik-request-interop.test.mjs +4 -4
- package/dist/sig/tootik-send-interop.test.mjs +3 -3
- package/dist/sig/verification.test.mjs +3 -3
- package/dist/{temporal-iyAxBq0I.mjs → temporal-D5tHfTGO.mjs} +1 -1
- package/dist/testing/kv.test.mjs +1 -1
- package/dist/utils/docloader.test.mjs +112 -37
- 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 +7 -7
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
const { URLPattern } = require("urlpattern-polyfill");
|
|
2
2
|
const require_rolldown_runtime = require("./rolldown-runtime-Dmm3t92W.cjs");
|
|
3
3
|
const require_transformers = require("./transformers-BalCrSsr.cjs");
|
|
4
|
-
const require_http = require("./http-
|
|
4
|
+
const require_http = require("./http-BFC8b7mH.cjs");
|
|
5
5
|
const require_types = require("./types-poIao40f.cjs");
|
|
6
|
-
const require_portable_collection = require("./portable-collection-
|
|
7
|
-
const require_kv_cache = require("./kv-cache-
|
|
6
|
+
const require_portable_collection = require("./portable-collection-BHZq0F_c.cjs");
|
|
7
|
+
const require_kv_cache = require("./kv-cache-JOKAStz2.cjs");
|
|
8
8
|
let _logtape_logtape = require("@logtape/logtape");
|
|
9
9
|
let _fedify_uri_template = require("@fedify/uri-template");
|
|
10
10
|
let _fedify_vocab = require("@fedify/vocab");
|
|
@@ -2779,12 +2779,41 @@ async function isInAudience(object, actor, options = {}) {
|
|
|
2779
2779
|
//#endregion
|
|
2780
2780
|
//#region src/federation/inbox.ts
|
|
2781
2781
|
const Temporal$10 = require_http.Temporal;
|
|
2782
|
+
/**
|
|
2783
|
+
* Checks whether an activity's ID and all of its actor IDs have the same
|
|
2784
|
+
* [FEP-fe34] origin. Activities without an ID have no origin to authenticate.
|
|
2785
|
+
*
|
|
2786
|
+
* Origins are compared by `getAuthenticationOrigin()`, so a portable activity
|
|
2787
|
+
* and its portable actors, including compatible identifiers on any gateway,
|
|
2788
|
+
* are compared by their DIDs. An ID that has no supported origin, such as an
|
|
2789
|
+
* opaque URN, never matches.
|
|
2790
|
+
*
|
|
2791
|
+
* The caller is responsible for rejecting a missing primary actor ID before
|
|
2792
|
+
* relying on this check.
|
|
2793
|
+
*
|
|
2794
|
+
* [FEP-fe34]: https://w3id.org/fep/fe34
|
|
2795
|
+
* @internal
|
|
2796
|
+
*/
|
|
2797
|
+
function actorsHaveSameOrigin(activity) {
|
|
2798
|
+
if (activity.id == null) return true;
|
|
2799
|
+
const origin = require_http.getAuthenticationOrigin(activity.id);
|
|
2800
|
+
return origin != null && activity.actorIds.every((actor) => require_http.getAuthenticationOrigin(actor) === origin);
|
|
2801
|
+
}
|
|
2782
2802
|
async function routeActivity({ context: ctx, json, originalJson, normalizedActivity, ldSignatureVerified, activity, recipient, inboxListeners, inboxContextFactory, listenerInboxContextFactory, inboxErrorHandler, kv, kvPrefixes, queue, span, meterProvider, tracerProvider, idempotencyStrategy }) {
|
|
2783
2803
|
const logger = (0, _logtape_logtape.getLogger)([
|
|
2784
2804
|
"fedify",
|
|
2785
2805
|
"federation",
|
|
2786
2806
|
"inbox"
|
|
2787
2807
|
]);
|
|
2808
|
+
if (activity.actorId == null) {
|
|
2809
|
+
logger.error("Missing actor.", { activity: json });
|
|
2810
|
+
span.setStatus({
|
|
2811
|
+
code: _opentelemetry_api.SpanStatusCode.ERROR,
|
|
2812
|
+
message: "Missing actor."
|
|
2813
|
+
});
|
|
2814
|
+
require_http.recordInboxActivity(meterProvider, "rejected", (0, _fedify_vocab.getTypeId)(activity).href);
|
|
2815
|
+
return "missingActor";
|
|
2816
|
+
}
|
|
2788
2817
|
let cacheKey = null;
|
|
2789
2818
|
if (activity.id != null) {
|
|
2790
2819
|
const inboxContext = inboxContextFactory(recipient, json, activity.id?.href, (0, _fedify_vocab.getTypeId)(activity).href);
|
|
@@ -2820,15 +2849,6 @@ async function routeActivity({ context: ctx, json, originalJson, normalizedActiv
|
|
|
2820
2849
|
return "alreadyProcessed";
|
|
2821
2850
|
}
|
|
2822
2851
|
}
|
|
2823
|
-
if (activity.actorId == null) {
|
|
2824
|
-
logger.error("Missing actor.", { activity: json });
|
|
2825
|
-
span.setStatus({
|
|
2826
|
-
code: _opentelemetry_api.SpanStatusCode.ERROR,
|
|
2827
|
-
message: "Missing actor."
|
|
2828
|
-
});
|
|
2829
|
-
require_http.recordInboxActivity(meterProvider, "rejected", (0, _fedify_vocab.getTypeId)(activity).href);
|
|
2830
|
-
return "missingActor";
|
|
2831
|
-
}
|
|
2832
2852
|
span.setAttribute("activitypub.actor.id", activity.actorId.href);
|
|
2833
2853
|
if (queue != null) {
|
|
2834
2854
|
const carrier = {};
|
|
@@ -4746,13 +4766,13 @@ function recordCollectionMetrics(meterProvider, base, result, options = {}) {
|
|
|
4746
4766
|
* @param parameters The parameters for handling the collection.
|
|
4747
4767
|
* @returns A promise that resolves to an HTTP response.
|
|
4748
4768
|
*/
|
|
4749
|
-
async function handleCollection(request, { name: name$
|
|
4750
|
-
const spanName = name$
|
|
4769
|
+
async function handleCollection(request, { name: name$1, identifier, uriGetter, filter, filterPredicate, context, collectionCallbacks, portable, tracerProvider, meterProvider, onUnauthorized, onNotFound }) {
|
|
4770
|
+
const spanName = name$1.trim().replace(/\s+/g, "_");
|
|
4751
4771
|
tracerProvider = tracerProvider ?? _opentelemetry_api.trace.getTracerProvider();
|
|
4752
4772
|
const tracer = tracerProvider.getTracer(require_http.name, require_http.version);
|
|
4753
4773
|
const cursor = new URL(request.url).searchParams.get("cursor");
|
|
4754
4774
|
const metricBase = {
|
|
4755
|
-
kind: getCollectionMetricKind(name$
|
|
4775
|
+
kind: getCollectionMetricKind(name$1),
|
|
4756
4776
|
page: cursor != null,
|
|
4757
4777
|
dispatcher: "built_in"
|
|
4758
4778
|
};
|
|
@@ -4798,7 +4818,7 @@ async function handleCollection(request, { name: name$2, identifier, uriGetter,
|
|
|
4798
4818
|
span.setStatus({ code: _opentelemetry_api.SpanStatusCode.ERROR });
|
|
4799
4819
|
return await onNotFound(request);
|
|
4800
4820
|
}
|
|
4801
|
-
const items = filterCollectionItems(page.items, name$
|
|
4821
|
+
const items = filterCollectionItems(page.items, name$1, filterPredicate);
|
|
4802
4822
|
itemCount = items.length;
|
|
4803
4823
|
span.setAttribute("fedify.collection.items", itemCount);
|
|
4804
4824
|
return items;
|
|
@@ -4840,7 +4860,7 @@ async function handleCollection(request, { name: name$2, identifier, uriGetter,
|
|
|
4840
4860
|
} else {
|
|
4841
4861
|
const uri = new URL(portable == null ? baseUri : pageBase);
|
|
4842
4862
|
uri.searchParams.set("cursor", cursor);
|
|
4843
|
-
const pageOrResponse = await tracer.startActiveSpan(`activitypub.dispatch_collection_page ${name$
|
|
4863
|
+
const pageOrResponse = await tracer.startActiveSpan(`activitypub.dispatch_collection_page ${name$1}`, {
|
|
4844
4864
|
kind: _opentelemetry_api.SpanKind.SERVER,
|
|
4845
4865
|
attributes: {
|
|
4846
4866
|
"activitypub.collection.id": uri.href,
|
|
@@ -4856,7 +4876,7 @@ async function handleCollection(request, { name: name$2, identifier, uriGetter,
|
|
|
4856
4876
|
span.setStatus({ code: _opentelemetry_api.SpanStatusCode.ERROR });
|
|
4857
4877
|
return await onNotFound(request);
|
|
4858
4878
|
}
|
|
4859
|
-
const items = filterCollectionItems(page.items, name$
|
|
4879
|
+
const items = filterCollectionItems(page.items, name$1, filterPredicate);
|
|
4860
4880
|
itemCount = items.length;
|
|
4861
4881
|
span.setAttribute("fedify.collection.items", itemCount);
|
|
4862
4882
|
return {
|
|
@@ -6005,11 +6025,49 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
6005
6025
|
return await getFailedSignatureResponse(inboxChallengePolicy, kv, kvPrefixes);
|
|
6006
6026
|
}
|
|
6007
6027
|
}
|
|
6028
|
+
if (!skipSignatureVerification && activity.id != null && !actorsHaveSameOrigin(activity)) {
|
|
6029
|
+
observation.authentication = {
|
|
6030
|
+
status: "rejected",
|
|
6031
|
+
reason: { type: "verificationFailed" }
|
|
6032
|
+
};
|
|
6033
|
+
logger.error("The activity ({activityId}) and its actors do not have the same origin.", {
|
|
6034
|
+
activity: json,
|
|
6035
|
+
recipient,
|
|
6036
|
+
activityId: activity.id.href,
|
|
6037
|
+
actorIds: activity.actorIds.map((actor) => actor.href)
|
|
6038
|
+
});
|
|
6039
|
+
span.setStatus({
|
|
6040
|
+
code: _opentelemetry_api.SpanStatusCode.ERROR,
|
|
6041
|
+
message: `The activity (${activity.id.href}) and its actors do not have the same origin.`
|
|
6042
|
+
});
|
|
6043
|
+
return new Response("The activity and its actors do not have the same origin.", {
|
|
6044
|
+
status: 401,
|
|
6045
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
6046
|
+
});
|
|
6047
|
+
}
|
|
6008
6048
|
observation.authentication = skipSignatureVerification ? { status: "skipped" } : {
|
|
6009
6049
|
status: "verified",
|
|
6010
6050
|
attempts: observation.verification.attempts.filter((attempt) => attempt.status === "verified")
|
|
6011
6051
|
};
|
|
6012
6052
|
observation.stage = "dispatch";
|
|
6053
|
+
if (activity.actorId == null) {
|
|
6054
|
+
observation.result = {
|
|
6055
|
+
disposition: "rejected",
|
|
6056
|
+
reason: "missingActor"
|
|
6057
|
+
};
|
|
6058
|
+
logger.error("Missing actor.", {
|
|
6059
|
+
activity: json,
|
|
6060
|
+
recipient
|
|
6061
|
+
});
|
|
6062
|
+
span.setStatus({
|
|
6063
|
+
code: _opentelemetry_api.SpanStatusCode.ERROR,
|
|
6064
|
+
message: "Missing actor."
|
|
6065
|
+
});
|
|
6066
|
+
return new Response("Missing actor.", {
|
|
6067
|
+
status: 400,
|
|
6068
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
6069
|
+
});
|
|
6070
|
+
}
|
|
6013
6071
|
const routeResult = await routeActivity({
|
|
6014
6072
|
context: ctx,
|
|
6015
6073
|
json,
|
|
@@ -6230,8 +6288,8 @@ var CustomCollectionHandler = class {
|
|
|
6230
6288
|
* @param CollectionPage The CollectionPage constructor.
|
|
6231
6289
|
* @param filterPredicate Optional filter predicate for items.
|
|
6232
6290
|
*/
|
|
6233
|
-
constructor(name$
|
|
6234
|
-
this.name = name$
|
|
6291
|
+
constructor(name$2, values, context, callbacks, tracerProvider = _opentelemetry_api.trace.getTracerProvider(), meterProvider, Collection, CollectionPage, filterPredicate, portable) {
|
|
6292
|
+
this.name = name$2;
|
|
6235
6293
|
this.values = values;
|
|
6236
6294
|
this.context = context;
|
|
6237
6295
|
this.callbacks = callbacks;
|
|
@@ -9357,6 +9415,35 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
9357
9415
|
span.setAttribute("activitypub.activity.id", activity.id.href);
|
|
9358
9416
|
listenerSpan.setAttribute("activitypub.activity.id", activity.id.href);
|
|
9359
9417
|
}
|
|
9418
|
+
if (activity.actorId == null) {
|
|
9419
|
+
logger.error("Missing actor in queued incoming activity.", {
|
|
9420
|
+
activityId: activity.id?.href,
|
|
9421
|
+
activity: message.activity,
|
|
9422
|
+
recipient: message.identifier
|
|
9423
|
+
});
|
|
9424
|
+
listenerSpan.setStatus({
|
|
9425
|
+
code: _opentelemetry_api.SpanStatusCode.ERROR,
|
|
9426
|
+
message: "Missing actor."
|
|
9427
|
+
});
|
|
9428
|
+
require_http.recordInboxActivity(this.meterProvider, "rejected", activityType);
|
|
9429
|
+
listenerSpan.end();
|
|
9430
|
+
return;
|
|
9431
|
+
}
|
|
9432
|
+
if (!this.skipSignatureVerification && activity.id != null && !actorsHaveSameOrigin(activity)) {
|
|
9433
|
+
logger.error("The queued activity ({activityId}) and its actors do not have the same origin.", {
|
|
9434
|
+
activityId: activity.id.href,
|
|
9435
|
+
actorIds: activity.actorIds.map((actor) => actor.href),
|
|
9436
|
+
activity: message.activity,
|
|
9437
|
+
recipient: message.identifier
|
|
9438
|
+
});
|
|
9439
|
+
listenerSpan.setStatus({
|
|
9440
|
+
code: _opentelemetry_api.SpanStatusCode.ERROR,
|
|
9441
|
+
message: `The activity (${activity.id.href}) and its actors do not have the same origin.`
|
|
9442
|
+
});
|
|
9443
|
+
require_http.recordInboxActivity(this.meterProvider, "rejected", activityType);
|
|
9444
|
+
listenerSpan.end();
|
|
9445
|
+
return;
|
|
9446
|
+
}
|
|
9360
9447
|
cacheKey = activity.id == null ? null : [
|
|
9361
9448
|
...this.kvPrefixes.activityIdempotence,
|
|
9362
9449
|
context.origin,
|
|
@@ -11433,6 +11520,20 @@ var ContextImpl = class ContextImpl {
|
|
|
11433
11520
|
recipient,
|
|
11434
11521
|
activity: json
|
|
11435
11522
|
});
|
|
11523
|
+
if (activity.actorId == null) {
|
|
11524
|
+
logger.debug("Activity is missing an actor; unable to verify.", {
|
|
11525
|
+
recipient,
|
|
11526
|
+
activity: json
|
|
11527
|
+
});
|
|
11528
|
+
return false;
|
|
11529
|
+
}
|
|
11530
|
+
if (activity.id != null && !actorsHaveSameOrigin(activity)) {
|
|
11531
|
+
logger.debug("Activity has actors from different origins; unable to verify.", {
|
|
11532
|
+
recipient,
|
|
11533
|
+
activity: json
|
|
11534
|
+
});
|
|
11535
|
+
return false;
|
|
11536
|
+
}
|
|
11436
11537
|
const routeResult = await routeActivity({
|
|
11437
11538
|
context: this,
|
|
11438
11539
|
json,
|
|
@@ -15,7 +15,7 @@ import serialize from "json-canon";
|
|
|
15
15
|
import { ATTR_HTTP_REQUEST_HEADER, ATTR_HTTP_REQUEST_METHOD, ATTR_URL_FULL } from "@opentelemetry/semantic-conventions";
|
|
16
16
|
//#region deno.json
|
|
17
17
|
var name = "@fedify/fedify";
|
|
18
|
-
var version = "2.4.
|
|
18
|
+
var version = "2.4.3";
|
|
19
19
|
//#endregion
|
|
20
20
|
//#region src/sig/portable-key-id.ts
|
|
21
21
|
const COMPATIBLE_KEY_ID_PATH_PATTERN = /^\/\.well-known\/apgateway\/did(?::|%3A)/i;
|
|
@@ -17,7 +17,7 @@ json_canon = require_rolldown_runtime.__toESM(json_canon, 1);
|
|
|
17
17
|
let _opentelemetry_semantic_conventions = require("@opentelemetry/semantic-conventions");
|
|
18
18
|
//#region deno.json
|
|
19
19
|
var name = "@fedify/fedify";
|
|
20
|
-
var version = "2.4.
|
|
20
|
+
var version = "2.4.3";
|
|
21
21
|
//#endregion
|
|
22
22
|
//#region src/sig/portable-key-id.ts
|
|
23
23
|
const COMPATIBLE_KEY_ID_PATH_PATTERN = /^\/\.well-known\/apgateway\/did(?::|%3A)/i;
|
|
@@ -3,12 +3,12 @@ import "urlpattern-polyfill";
|
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
4
|
import { m as parseKeyIdString } from "./portable-key-id-Ca30-hOa.mjs";
|
|
5
5
|
import { a as verificationObservation, i as triedKey, n as observeCheck, t as observeAttempt } from "./verification-Dpfcw8ml.mjs";
|
|
6
|
-
import { n as version, t as name } from "./deno-
|
|
7
|
-
import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-
|
|
6
|
+
import { n as version, t as name } from "./deno-BEgaR4dC.mjs";
|
|
7
|
+
import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-DAhJVtgg.mjs";
|
|
8
8
|
import { i as validateAcceptSignature, n as fulfillAcceptSignature, r as parseAcceptSignature } from "./accept-CPVqki78.mjs";
|
|
9
9
|
import { a as promoteKeyOwnerEvidence } from "./key-owner-evidence-APB6NpSk.mjs";
|
|
10
|
-
import { S as fetchKeyDetailed, T as validateCryptoKey, v as bypassKeyCacheReads } from "./ld-
|
|
11
|
-
import { n as resolvePortableActorKey, r as verifyPortableGatewayKeyDocument } from "./portable-key-
|
|
10
|
+
import { S as fetchKeyDetailed, T as validateCryptoKey, v as bypassKeyCacheReads } from "./ld-Qk4WqslS.mjs";
|
|
11
|
+
import { n as resolvePortableActorKey, r as verifyPortableGatewayKeyDocument } from "./portable-key-BY6G_X28.mjs";
|
|
12
12
|
import { getLogger } from "@logtape/logtape";
|
|
13
13
|
import { CryptographicKey } from "@fedify/vocab";
|
|
14
14
|
import { SpanStatusCode, trace } from "@opentelemetry/api";
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { Temporal } from "temporal-polyfill";
|
|
2
|
+
import "urlpattern-polyfill";
|
|
3
|
+
globalThis.addEventListener = () => {};
|
|
4
|
+
import { t as getAuthenticationOrigin } from "./portable-key-id-Ca30-hOa.mjs";
|
|
5
|
+
import { n as version, t as name } from "./deno-BEgaR4dC.mjs";
|
|
6
|
+
import { g as recordInboxActivity, n as getDurationMs, r as getFederationMetrics } from "./metrics-DAhJVtgg.mjs";
|
|
7
|
+
import { getLogger } from "@logtape/logtape";
|
|
8
|
+
import { getTypeId } from "@fedify/vocab";
|
|
9
|
+
import { SpanKind, SpanStatusCode, context, propagation, trace } from "@opentelemetry/api";
|
|
10
|
+
//#region src/federation/inbox.ts
|
|
11
|
+
/**
|
|
12
|
+
* Checks whether an activity's ID and all of its actor IDs have the same
|
|
13
|
+
* [FEP-fe34] origin. Activities without an ID have no origin to authenticate.
|
|
14
|
+
*
|
|
15
|
+
* Origins are compared by `getAuthenticationOrigin()`, so a portable activity
|
|
16
|
+
* and its portable actors, including compatible identifiers on any gateway,
|
|
17
|
+
* are compared by their DIDs. An ID that has no supported origin, such as an
|
|
18
|
+
* opaque URN, never matches.
|
|
19
|
+
*
|
|
20
|
+
* The caller is responsible for rejecting a missing primary actor ID before
|
|
21
|
+
* relying on this check.
|
|
22
|
+
*
|
|
23
|
+
* [FEP-fe34]: https://w3id.org/fep/fe34
|
|
24
|
+
* @internal
|
|
25
|
+
*/
|
|
26
|
+
function actorsHaveSameOrigin(activity) {
|
|
27
|
+
if (activity.id == null) return true;
|
|
28
|
+
const origin = getAuthenticationOrigin(activity.id);
|
|
29
|
+
return origin != null && activity.actorIds.every((actor) => getAuthenticationOrigin(actor) === origin);
|
|
30
|
+
}
|
|
31
|
+
async function routeActivity({ context: ctx, json, originalJson, normalizedActivity, ldSignatureVerified, activity, recipient, inboxListeners, inboxContextFactory, listenerInboxContextFactory, inboxErrorHandler, kv, kvPrefixes, queue, span, meterProvider, tracerProvider, idempotencyStrategy }) {
|
|
32
|
+
const logger = getLogger([
|
|
33
|
+
"fedify",
|
|
34
|
+
"federation",
|
|
35
|
+
"inbox"
|
|
36
|
+
]);
|
|
37
|
+
if (activity.actorId == null) {
|
|
38
|
+
logger.error("Missing actor.", { activity: json });
|
|
39
|
+
span.setStatus({
|
|
40
|
+
code: SpanStatusCode.ERROR,
|
|
41
|
+
message: "Missing actor."
|
|
42
|
+
});
|
|
43
|
+
recordInboxActivity(meterProvider, "rejected", getTypeId(activity).href);
|
|
44
|
+
return "missingActor";
|
|
45
|
+
}
|
|
46
|
+
let cacheKey = null;
|
|
47
|
+
if (activity.id != null) {
|
|
48
|
+
const inboxContext = inboxContextFactory(recipient, json, activity.id?.href, getTypeId(activity).href);
|
|
49
|
+
const strategy = idempotencyStrategy ?? "per-inbox";
|
|
50
|
+
let keyString;
|
|
51
|
+
if (typeof strategy === "function") keyString = await strategy(inboxContext, activity);
|
|
52
|
+
else switch (strategy) {
|
|
53
|
+
case "global":
|
|
54
|
+
keyString = activity.id.href;
|
|
55
|
+
break;
|
|
56
|
+
case "per-origin":
|
|
57
|
+
keyString = `${ctx.origin}\n${activity.id.href}`;
|
|
58
|
+
break;
|
|
59
|
+
case "per-inbox":
|
|
60
|
+
keyString = `${ctx.origin}\n${activity.id.href}\n${recipient == null ? "sharedInbox" : `inbox\n${recipient}`}`;
|
|
61
|
+
break;
|
|
62
|
+
default: keyString = `${ctx.origin}\n${activity.id.href}`;
|
|
63
|
+
}
|
|
64
|
+
if (keyString != null) cacheKey = [...kvPrefixes.activityIdempotence, keyString];
|
|
65
|
+
}
|
|
66
|
+
if (cacheKey != null) {
|
|
67
|
+
if (await kv.get(cacheKey) === true) {
|
|
68
|
+
logger.debug("Activity {activityId} has already been processed.", {
|
|
69
|
+
activityId: activity.id?.href,
|
|
70
|
+
activity: json,
|
|
71
|
+
recipient
|
|
72
|
+
});
|
|
73
|
+
span.setStatus({
|
|
74
|
+
code: SpanStatusCode.UNSET,
|
|
75
|
+
message: `Activity ${activity.id?.href} has already been processed.`
|
|
76
|
+
});
|
|
77
|
+
recordInboxActivity(meterProvider, "rejected", getTypeId(activity).href);
|
|
78
|
+
return "alreadyProcessed";
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
span.setAttribute("activitypub.actor.id", activity.actorId.href);
|
|
82
|
+
if (queue != null) {
|
|
83
|
+
const carrier = {};
|
|
84
|
+
propagation.inject(context.active(), carrier);
|
|
85
|
+
try {
|
|
86
|
+
await queue.enqueue({
|
|
87
|
+
type: "inbox",
|
|
88
|
+
id: crypto.randomUUID(),
|
|
89
|
+
baseUrl: ctx.origin,
|
|
90
|
+
activity: originalJson ?? json,
|
|
91
|
+
...normalizedActivity == null ? {} : { normalizedActivity },
|
|
92
|
+
...ldSignatureVerified == null ? {} : { ldSignatureVerified },
|
|
93
|
+
identifier: recipient,
|
|
94
|
+
attempt: 0,
|
|
95
|
+
started: (/* @__PURE__ */ new Date()).toISOString(),
|
|
96
|
+
traceContext: carrier
|
|
97
|
+
});
|
|
98
|
+
} catch (error) {
|
|
99
|
+
logger.error("Failed to enqueue the incoming activity {activityId}:\n{error}", {
|
|
100
|
+
error,
|
|
101
|
+
activityId: activity.id?.href,
|
|
102
|
+
activity: json,
|
|
103
|
+
recipient
|
|
104
|
+
});
|
|
105
|
+
span.setStatus({
|
|
106
|
+
code: SpanStatusCode.ERROR,
|
|
107
|
+
message: `Failed to enqueue the incoming activity ${activity.id?.href}.`
|
|
108
|
+
});
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
getFederationMetrics(meterProvider).recordQueueTaskEnqueued({
|
|
112
|
+
role: "inbox",
|
|
113
|
+
queue,
|
|
114
|
+
activityType: getTypeId(activity).href
|
|
115
|
+
}, 0);
|
|
116
|
+
recordInboxActivity(meterProvider, "queued", getTypeId(activity).href);
|
|
117
|
+
logger.info("Activity {activityId} is enqueued.", {
|
|
118
|
+
activityId: activity.id?.href,
|
|
119
|
+
activity: json,
|
|
120
|
+
recipient
|
|
121
|
+
});
|
|
122
|
+
return "enqueued";
|
|
123
|
+
}
|
|
124
|
+
tracerProvider = tracerProvider ?? trace.getTracerProvider();
|
|
125
|
+
return await tracerProvider.getTracer(name, version).startActiveSpan("activitypub.dispatch_inbox_listener", { kind: SpanKind.INTERNAL }, async (span) => {
|
|
126
|
+
const dispatched = inboxListeners?.dispatchWithClass(activity);
|
|
127
|
+
if (dispatched == null) {
|
|
128
|
+
logger.error("Unsupported activity type:\n{activity}", {
|
|
129
|
+
activity: json,
|
|
130
|
+
recipient
|
|
131
|
+
});
|
|
132
|
+
span.setStatus({
|
|
133
|
+
code: SpanStatusCode.UNSET,
|
|
134
|
+
message: `Unsupported activity type: ${getTypeId(activity).href}`
|
|
135
|
+
});
|
|
136
|
+
recordInboxActivity(meterProvider, "rejected", getTypeId(activity).href);
|
|
137
|
+
span.end();
|
|
138
|
+
return "unsupportedActivity";
|
|
139
|
+
}
|
|
140
|
+
const { class: cls, listener } = dispatched;
|
|
141
|
+
span.updateName(`activitypub.dispatch_inbox_listener ${cls.name}`);
|
|
142
|
+
try {
|
|
143
|
+
const activityType = getTypeId(activity).href;
|
|
144
|
+
const started = performance.now();
|
|
145
|
+
try {
|
|
146
|
+
const contextFactory = listenerInboxContextFactory ?? inboxContextFactory;
|
|
147
|
+
await listener(contextFactory(recipient, contextFactory === inboxContextFactory ? json : originalJson ?? json, activity.id?.href, activityType), activity);
|
|
148
|
+
} finally {
|
|
149
|
+
getFederationMetrics(meterProvider).recordInboxProcessingDuration(activityType, getDurationMs(started));
|
|
150
|
+
}
|
|
151
|
+
} catch (error) {
|
|
152
|
+
try {
|
|
153
|
+
await inboxErrorHandler?.(ctx, error);
|
|
154
|
+
} catch (error) {
|
|
155
|
+
logger.error("An unexpected error occurred in inbox error handler:\n{error}", {
|
|
156
|
+
error,
|
|
157
|
+
activityId: activity.id?.href,
|
|
158
|
+
activity: json,
|
|
159
|
+
recipient
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
logger.error("Failed to process the incoming activity {activityId}:\n{error}", {
|
|
163
|
+
error,
|
|
164
|
+
activityId: activity.id?.href,
|
|
165
|
+
activity: json,
|
|
166
|
+
recipient
|
|
167
|
+
});
|
|
168
|
+
span.setStatus({
|
|
169
|
+
code: SpanStatusCode.ERROR,
|
|
170
|
+
message: String(error)
|
|
171
|
+
});
|
|
172
|
+
recordInboxActivity(meterProvider, "rejected", getTypeId(activity).href);
|
|
173
|
+
span.end();
|
|
174
|
+
return "error";
|
|
175
|
+
}
|
|
176
|
+
recordInboxActivity(meterProvider, "processed", getTypeId(activity).href);
|
|
177
|
+
if (cacheKey != null) await kv.set(cacheKey, true, { ttl: Temporal.Duration.from({ days: 1 }) });
|
|
178
|
+
logger.info("Activity {activityId} has been processed.", {
|
|
179
|
+
activityId: activity.id?.href,
|
|
180
|
+
activity: json,
|
|
181
|
+
recipient
|
|
182
|
+
});
|
|
183
|
+
span.end();
|
|
184
|
+
return "success";
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
//#endregion
|
|
188
|
+
export { routeActivity as n, actorsHaveSameOrigin as t };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Temporal } from "temporal-polyfill";
|
|
2
2
|
import { URLPattern } from "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { p as recordDocumentCache } from "./metrics-
|
|
4
|
+
import { p as recordDocumentCache } from "./metrics-DAhJVtgg.mjs";
|
|
5
5
|
import { getLogger } from "@logtape/logtape";
|
|
6
6
|
import { preloadedContexts } from "@fedify/vocab-runtime";
|
|
7
7
|
//#region src/utils/kv-cache.ts
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Temporal } from "temporal-polyfill";
|
|
2
2
|
import { URLPattern } from "urlpattern-polyfill";
|
|
3
|
-
import { G as validateCryptoKey, ot as recordDocumentCache, t as doubleKnock } from "./http-
|
|
3
|
+
import { G as validateCryptoKey, ot as recordDocumentCache, t as doubleKnock } from "./http-B5pRXkmQ.js";
|
|
4
4
|
import { getLogger } from "@logtape/logtape";
|
|
5
5
|
import { FetchError, UrlError, createActivityPubRequest, getRemoteDocument, logRequest, preloadedContexts, resolveDocumentLoaderTimeout, validatePublicUrl, withDocumentLoaderTimeout } from "@fedify/vocab-runtime";
|
|
6
6
|
import { curry } from "es-toolkit";
|
|
@@ -62,6 +62,8 @@ function getAuthenticatedDocumentLoader(identity, { allowPrivateAddress, maxRedi
|
|
|
62
62
|
}, options);
|
|
63
63
|
}
|
|
64
64
|
async function validateUrl(url, options) {
|
|
65
|
+
const protocol = new URL(url).protocol;
|
|
66
|
+
if (protocol !== "http:" && protocol !== "https:") throw new UrlError(`Unsupported protocol: ${protocol}`);
|
|
65
67
|
if (!allowPrivateAddress) {
|
|
66
68
|
try {
|
|
67
69
|
await validatePublicUrl(url);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const { URLPattern } = require("urlpattern-polyfill");
|
|
2
|
-
const require_http = require("./http-
|
|
2
|
+
const require_http = require("./http-BFC8b7mH.cjs");
|
|
3
3
|
let _logtape_logtape = require("@logtape/logtape");
|
|
4
4
|
let _fedify_vocab_runtime = require("@fedify/vocab-runtime");
|
|
5
5
|
let es_toolkit = require("es-toolkit");
|
|
@@ -61,6 +61,8 @@ function getAuthenticatedDocumentLoader(identity, { allowPrivateAddress, maxRedi
|
|
|
61
61
|
}, options);
|
|
62
62
|
}
|
|
63
63
|
async function validateUrl(url, options) {
|
|
64
|
+
const protocol = new URL(url).protocol;
|
|
65
|
+
if (protocol !== "http:" && protocol !== "https:") throw new _fedify_vocab_runtime.UrlError(`Unsupported protocol: ${protocol}`);
|
|
64
66
|
if (!allowPrivateAddress) {
|
|
65
67
|
try {
|
|
66
68
|
await (0, _fedify_vocab_runtime.validatePublicUrl)(url);
|
|
@@ -3,8 +3,8 @@ import "urlpattern-polyfill";
|
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
4
|
import { c as isPortableActorDocument, d as isPortableUri, l as isPortableId, m as parseKeyIdString, s as isCompatibleKeyId } from "./portable-key-id-Ca30-hOa.mjs";
|
|
5
5
|
import { a as verificationObservation, i as triedKey, n as observeCheck, t as observeAttempt } from "./verification-Dpfcw8ml.mjs";
|
|
6
|
-
import { n as version, t as name } from "./deno-
|
|
7
|
-
import { _ as recordKeyLookup, n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch, t as classifyFetchError } from "./metrics-
|
|
6
|
+
import { n as version, t as name } from "./deno-BEgaR4dC.mjs";
|
|
7
|
+
import { _ as recordKeyLookup, n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch, t as classifyFetchError } from "./metrics-DAhJVtgg.mjs";
|
|
8
8
|
import { n as attachKeyOwnerEvidence, t as MAX_PORTABLE_KEY_TTL } from "./key-owner-evidence-APB6NpSk.mjs";
|
|
9
9
|
import { getLogger } from "@logtape/logtape";
|
|
10
10
|
import { Activity, CryptographicKey, Multikey, Object as Object$1, getTypeId, isActor } from "@fedify/vocab";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "temporal-polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { n as version, t as name } from "./deno-
|
|
4
|
+
import { n as version, t as name } from "./deno-BEgaR4dC.mjs";
|
|
5
5
|
import { metrics } from "@opentelemetry/api";
|
|
6
6
|
import { FetchError } from "@fedify/vocab-runtime";
|
|
7
7
|
//#region src/federation/metrics.ts
|