@fedify/fedify 2.4.0-dev.2108 → 2.4.0-dev.2115
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-BRlYEsrV.mjs → builder-CZcBWrBd.mjs} +3 -3
- 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-CHGINiJg.mjs → compound-proof-CDuP9MIX.mjs} +3 -3
- package/dist/{context-Cqx9M_zO.d.cts → context-SMNOiiY6.d.cts} +1 -1
- package/dist/{context-B4F4kGJq.d.ts → context-tOiJfBoD.d.ts} +1 -1
- package/dist/{deno-CC4RKEEp.mjs → deno-CCABdw7r.mjs} +1 -1
- package/dist/{docloader-D3_m98fe.mjs → docloader-Bby5g2iu.mjs} +2 -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 +3 -3
- package/dist/federation/handler.test.mjs +5 -5
- package/dist/federation/hashlink-media.test.mjs +2 -2
- package/dist/federation/idempotency.test.mjs +2 -2
- package/dist/federation/keycache.test.mjs +67 -1
- package/dist/federation/metrics.test.mjs +1 -1
- package/dist/federation/middleware.test.mjs +7 -7
- 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-gateway-keys.test.mjs +24 -7
- package/dist/federation/portable-inbox.test.mjs +4 -4
- 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-dqo6ZzgT.cjs → federation-DYr384xz.cjs} +71 -3
- package/dist/{http-B7wEqv49.cjs → http-B4mQvNcv.cjs} +140 -55
- package/dist/{http-CHEYA5fH.mjs → http-BcLnr2do.mjs} +6 -12
- package/dist/{http-xlG-jRdB.d.cts → http-BrQgkJay.d.ts} +12 -7
- package/dist/{http-CaUBCHYJ.js → http-Ce6_Lf7k.js} +140 -55
- package/dist/{http-n2v6DPxu.d.ts → http-D76Py0M4.d.cts} +12 -7
- package/dist/{key-B8u1vdvF.mjs → key-CkNxztNV.mjs} +107 -16
- package/dist/{keycache-BUDHB5rY.mjs → keycache-BInaECGj.mjs} +68 -0
- package/dist/{kv-cache-0pYMPrkL.cjs → kv-cache-Dqp_E-1g.cjs} +1 -1
- package/dist/{kv-cache-ibncHN1D.js → kv-cache-DwzXzchj.js} +1 -1
- package/dist/{kv-cache-CTdvf_Iq.mjs → kv-cache-iFaRIcIL.mjs} +1 -1
- package/dist/{ld-BTMJHEGV.mjs → ld-CnT0f1nU.mjs} +4 -7
- package/dist/{metrics-CF1TEvdS.mjs → metrics-em_7lwhG.mjs} +1 -1
- package/dist/{middleware-B-rZ7oE-.mjs → middleware-BpwKvOpU.mjs} +15 -15
- package/dist/{middleware-Dl-Y_ZlC.js → middleware-DhIBngkj.js} +71 -3
- package/dist/{middleware-DCMTJT3_.mjs → middleware-U9IuzvDO.mjs} +1 -1
- package/dist/{mod-4s_XgF17.d.ts → mod-COWgSo2v.d.ts} +1 -1
- package/dist/{mod-Coacostr.d.ts → mod-CTh7ijx5.d.ts} +1 -1
- package/dist/{mod-xGFCHqq_.d.cts → mod-Cr79cQlc.d.cts} +1 -1
- package/dist/{mod-CVZrXSdm.d.ts → mod-Cwdt_CWU.d.ts} +1 -1
- package/dist/{mod-C8LieBzF.d.cts → mod-DMdGFMEl.d.cts} +1 -1
- package/dist/{mod-BuFmStNT.d.cts → mod-DnElMslb.d.cts} +1 -1
- package/dist/mod.cjs +5 -5
- package/dist/mod.d.cts +5 -5
- package/dist/mod.d.ts +5 -5
- package/dist/mod.js +5 -5
- package/dist/nodeinfo/handler.test.mjs +1 -1
- package/dist/{owner-CY--JSqO.mjs → owner-9LiHmOv1.mjs} +4 -4
- package/dist/{owner-CGnHPl4k.cjs → owner-BtoMCv6Z.cjs} +1 -1
- package/dist/{owner-M8dOKEl6.js → owner-Cb3ytrlq.js} +1 -1
- package/dist/{portable-key-C0-ghkgM.mjs → portable-key-9ck61lI0.mjs} +5 -4
- package/dist/{portable-key-id-Bva5iT3W.mjs → portable-key-id-T7lecIbt.mjs} +3 -2
- package/dist/{proof-DDw0i9b1.mjs → proof-xdPYGPaA.mjs} +32 -31
- package/dist/{send-CU3sHGPt.mjs → send-Bf8rSWch.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 +2 -2
- package/dist/sig/mod.cjs +3 -3
- package/dist/sig/mod.d.cts +2 -2
- package/dist/sig/mod.d.ts +2 -2
- package/dist/sig/mod.js +3 -3
- package/dist/sig/owner.test.mjs +1 -1
- package/dist/sig/portable-collection.test.mjs +2 -2
- package/dist/sig/portable-key.test.mjs +301 -23
- package/dist/sig/proof.test.mjs +3 -3
- package/dist/{sig-C2CORtvg.cjs → sig-Bj3fHnv0.cjs} +2 -2
- package/dist/{sig-fycq7sOr.js → sig-DN9pOglI.js} +2 -2
- package/dist/{temporal-NHiSpGcW.mjs → temporal-tAtwv6vX.mjs} +1 -1
- package/dist/testing/kv.test.mjs +1 -1
- package/dist/utils/docloader.test.mjs +2 -2
- package/dist/utils/kv-cache.test.mjs +1 -1
- package/dist/utils/mod.cjs +1 -1
- package/dist/utils/mod.d.cts +1 -1
- package/dist/utils/mod.d.ts +1 -1
- package/dist/utils/mod.js +1 -1
- package/package.json +6 -6
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
import "temporal-polyfill";
|
|
1
|
+
import { Temporal } from "temporal-polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { n as
|
|
8
|
-
import {
|
|
4
|
+
import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
|
|
5
|
+
import { i as fetchKey } from "../key-CkNxztNV.mjs";
|
|
6
|
+
import { h as verifyJsonLd, p as signJsonLd } from "../ld-CnT0f1nU.mjs";
|
|
7
|
+
import { n as createProof, u as verifyProof } from "../proof-xdPYGPaA.mjs";
|
|
8
|
+
import { d as verifyRequest, f as verifyRequestDetailed, l as signRequest } from "../http-BcLnr2do.mjs";
|
|
9
|
+
import { a as rsaPrivateKey3, c as rsaPublicKey3, i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2 } from "../keys-DIJNHW-Y.mjs";
|
|
10
|
+
import { n as getKeyOwner, t as doesActorOwnKey } from "../owner-9LiHmOv1.mjs";
|
|
11
|
+
import { t as KvKeyCache } from "../keycache-BInaECGj.mjs";
|
|
12
|
+
import { Create, CryptographicKey, Multikey, Note, Person } from "@fedify/vocab";
|
|
9
13
|
import { test } from "@fedify/fixture";
|
|
10
14
|
import { FetchError, encodeMultibase, exportDidKey, exportSpki, parseIri, preloadedContexts } from "@fedify/vocab-runtime";
|
|
11
15
|
import serialize from "json-canon";
|
|
@@ -46,14 +50,15 @@ function createLoader(responses) {
|
|
|
46
50
|
};
|
|
47
51
|
return Object.assign(loader, { fetched });
|
|
48
52
|
}
|
|
49
|
-
async function sign(document, privateKey = ed25519PrivateKey, verificationMethod = didKeyId) {
|
|
53
|
+
async function sign(document, privateKey = ed25519PrivateKey, verificationMethod = didKeyId, proofOptions = {}) {
|
|
50
54
|
const proofConfig = {
|
|
51
55
|
"@context": document["@context"],
|
|
52
56
|
type: "DataIntegrityProof",
|
|
53
57
|
cryptosuite: "eddsa-jcs-2022",
|
|
54
58
|
verificationMethod,
|
|
55
59
|
proofPurpose: "assertionMethod",
|
|
56
|
-
created: "2023-02-24T23:36:38Z"
|
|
60
|
+
created: "2023-02-24T23:36:38Z",
|
|
61
|
+
...proofOptions
|
|
57
62
|
};
|
|
58
63
|
const encoder = new TextEncoder();
|
|
59
64
|
const proofDigest = await crypto.subtle.digest("SHA-256", encoder.encode(serialize(proofConfig)));
|
|
@@ -333,32 +338,305 @@ test("keys at ordinary URLs cannot belong to portable actors", async () => {
|
|
|
333
338
|
keyCache
|
|
334
339
|
})).key, null);
|
|
335
340
|
});
|
|
336
|
-
|
|
341
|
+
const specs = ["draft-cavage-http-signatures-12", "rfc9421"];
|
|
342
|
+
function fetchKeyOf(cls, options) {
|
|
343
|
+
return cls === CryptographicKey ? fetchKey(keyId, CryptographicKey, options) : fetchKey(keyId, Multikey, options);
|
|
344
|
+
}
|
|
345
|
+
function fetchCount(loader, url = keyId) {
|
|
346
|
+
const document = url.href.replace(/#.*$/, "");
|
|
347
|
+
return loader.fetched.filter((u) => u.replace(/#.*$/, "") === document).length;
|
|
348
|
+
}
|
|
349
|
+
test("verifyRequest() caches failures to fetch compatible key IDs", async () => {
|
|
350
|
+
const unreachable = {
|
|
351
|
+
"404 Not Found": createLoader({}),
|
|
352
|
+
"a network error": (url) => Promise.reject(/* @__PURE__ */ new TypeError(`${url}`))
|
|
353
|
+
};
|
|
354
|
+
for (const spec of specs) for (const [name, inner] of Object.entries(unreachable)) {
|
|
355
|
+
const fetched = [];
|
|
356
|
+
const documentLoader = Object.assign((url) => {
|
|
357
|
+
fetched.push(url);
|
|
358
|
+
return inner(url);
|
|
359
|
+
}, { fetched });
|
|
360
|
+
const keyCache = new KvKeyCache(new MemoryKvStore(), ["pk"]);
|
|
361
|
+
const options = {
|
|
362
|
+
documentLoader,
|
|
363
|
+
contextLoader,
|
|
364
|
+
keyCache,
|
|
365
|
+
spec
|
|
366
|
+
};
|
|
367
|
+
for (let i = 0; i < 3; i++) {
|
|
368
|
+
const result = await verifyRequestDetailed(await signedRequest(spec), options);
|
|
369
|
+
ok(!result.verified, `${spec}, ${name}`);
|
|
370
|
+
strictEqual(result.reason.type, "keyFetchError", `${spec}, ${name}`);
|
|
371
|
+
}
|
|
372
|
+
strictEqual(fetchCount(documentLoader), 1, `${spec}, ${name}`);
|
|
373
|
+
for (const cls of [CryptographicKey, Multikey]) {
|
|
374
|
+
const result = await fetchKeyOf(cls, options);
|
|
375
|
+
strictEqual(result.key, null);
|
|
376
|
+
ok(result.cached, `${spec}, ${name}, ${cls.name}`);
|
|
377
|
+
}
|
|
378
|
+
strictEqual(fetchCount(documentLoader), 1, `${spec}, ${name}`);
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
test("verifyRequest() caches gateway keys only for HTTP Signatures", async () => {
|
|
382
|
+
for (const spec of specs) {
|
|
383
|
+
const documentLoader = createLoader({ [compatibleId(gw1)]: await sign(await actorJson()) });
|
|
384
|
+
const keyCache = new KvKeyCache(new MemoryKvStore(), ["pk"]);
|
|
385
|
+
const options = {
|
|
386
|
+
documentLoader,
|
|
387
|
+
contextLoader,
|
|
388
|
+
keyCache,
|
|
389
|
+
spec
|
|
390
|
+
};
|
|
391
|
+
for (let i = 0; i < 3; i++) strictEqual((await verifyRequest(await signedRequest(spec), options))?.ownerId?.href, parseIri(actorId).href, spec);
|
|
392
|
+
strictEqual(fetchCount(documentLoader), 1, spec);
|
|
393
|
+
const document = {
|
|
394
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
|
395
|
+
type: "Create",
|
|
396
|
+
id: "https://gw1.example/activities/1",
|
|
397
|
+
actor: compatibleId(gw1)
|
|
398
|
+
};
|
|
399
|
+
const { signature } = await signJsonLd(document, rsaPrivateKey2, keyId, { contextLoader });
|
|
400
|
+
ok(!await verifyJsonLd({
|
|
401
|
+
...document,
|
|
402
|
+
signature
|
|
403
|
+
}, options), spec);
|
|
404
|
+
const note = new Note({
|
|
405
|
+
id: new URL("https://gw1.example/notes/1"),
|
|
406
|
+
attribution: new URL(compatibleId(gw1)),
|
|
407
|
+
content: "Hello"
|
|
408
|
+
});
|
|
409
|
+
const proof = await createProof(note, ed25519PrivateKey, keyId, { contextLoader });
|
|
410
|
+
strictEqual(await verifyProof(await note.toJsonLd({
|
|
411
|
+
format: "compact",
|
|
412
|
+
contextLoader
|
|
413
|
+
}), proof, options), null, spec);
|
|
414
|
+
for (const cls of [CryptographicKey, Multikey]) strictEqual((await fetchKeyOf(cls, options)).key, null, spec);
|
|
415
|
+
const fetched = fetchCount(documentLoader);
|
|
416
|
+
ok(await verifyRequest(await signedRequest(spec), options) != null, spec);
|
|
417
|
+
strictEqual(fetchCount(documentLoader), fetched, spec);
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
test("verifyRequest() is not rejected by other purposes' negative entries", async () => {
|
|
421
|
+
const documentLoader = createLoader({ [compatibleId(gw1)]: await sign(await actorJson()) });
|
|
422
|
+
const keyCache = new KvKeyCache(new MemoryKvStore(), ["pk"]);
|
|
423
|
+
const options = {
|
|
424
|
+
documentLoader,
|
|
425
|
+
contextLoader,
|
|
426
|
+
keyCache
|
|
427
|
+
};
|
|
428
|
+
for (const cls of [CryptographicKey, Multikey]) strictEqual((await fetchKeyOf(cls, options)).key, null);
|
|
429
|
+
ok(await verifyRequest(await signedRequest(), options) != null);
|
|
430
|
+
const fetched = fetchCount(documentLoader);
|
|
431
|
+
for (const cls of [CryptographicKey, Multikey]) {
|
|
432
|
+
const result = await fetchKeyOf(cls, options);
|
|
433
|
+
strictEqual(result.key, null);
|
|
434
|
+
ok(result.cached);
|
|
435
|
+
}
|
|
436
|
+
strictEqual(fetchCount(documentLoader), fetched);
|
|
437
|
+
});
|
|
438
|
+
test("fetchKey() does not share negative entries of compatible key IDs across key classes", async () => {
|
|
439
|
+
const ownerId = new URL("https://gw1.example/users/bob");
|
|
440
|
+
const multikey = new Multikey({
|
|
441
|
+
id: keyId,
|
|
442
|
+
controller: ownerId,
|
|
443
|
+
publicKey: ed25519PublicKey.publicKey
|
|
444
|
+
});
|
|
445
|
+
const documentLoader = createLoader({
|
|
446
|
+
[compatibleId(gw1)]: await multikey.toJsonLd({ contextLoader }),
|
|
447
|
+
[ownerId.href]: await new Person({
|
|
448
|
+
id: ownerId,
|
|
449
|
+
assertionMethods: [multikey]
|
|
450
|
+
}).toJsonLd({ contextLoader })
|
|
451
|
+
});
|
|
452
|
+
const keyCache = new KvKeyCache(new MemoryKvStore(), ["pk"]);
|
|
453
|
+
const options = {
|
|
454
|
+
documentLoader,
|
|
455
|
+
contextLoader,
|
|
456
|
+
keyCache
|
|
457
|
+
};
|
|
458
|
+
strictEqual((await fetchKey(keyId, CryptographicKey, options)).key, null);
|
|
459
|
+
strictEqual(await verifyRequest(await signedRequest(), options), null);
|
|
460
|
+
const result = await fetchKey(keyId, Multikey, options);
|
|
461
|
+
strictEqual(result.key?.id?.href, keyId.href);
|
|
462
|
+
ok(!result.cached);
|
|
463
|
+
const cached = await fetchKey(keyId, Multikey, options);
|
|
464
|
+
strictEqual(cached.key?.id?.href, keyId.href);
|
|
465
|
+
ok(cached.cached);
|
|
466
|
+
const other = await fetchKey(keyId, CryptographicKey, options);
|
|
467
|
+
strictEqual(other.key, null);
|
|
468
|
+
ok(other.cached);
|
|
469
|
+
});
|
|
470
|
+
test("verifyRequest() caches rejected portable actor documents", async () => {
|
|
471
|
+
for (const spec of specs) {
|
|
472
|
+
const documentLoader = createLoader({ [compatibleId(gw1)]: await actorJson() });
|
|
473
|
+
const keyCache = new KvKeyCache(new MemoryKvStore(), ["pk"]);
|
|
474
|
+
const options = {
|
|
475
|
+
documentLoader,
|
|
476
|
+
contextLoader,
|
|
477
|
+
keyCache,
|
|
478
|
+
spec
|
|
479
|
+
};
|
|
480
|
+
for (let i = 0; i < 3; i++) {
|
|
481
|
+
const result = await verifyRequestDetailed(await signedRequest(spec), options);
|
|
482
|
+
ok(!result.verified, spec);
|
|
483
|
+
strictEqual(result.reason.type, "invalidSignature", spec);
|
|
484
|
+
}
|
|
485
|
+
strictEqual(fetchCount(documentLoader), 1, spec);
|
|
486
|
+
}
|
|
487
|
+
});
|
|
488
|
+
test("verifyRequest() refetches a cached gateway key that fails to verify", async () => {
|
|
489
|
+
for (const spec of specs) {
|
|
490
|
+
const responses = { [compatibleId(gw1)]: await sign(await actorJson()) };
|
|
491
|
+
const documentLoader = createLoader(responses);
|
|
492
|
+
const keyCache = new KvKeyCache(new MemoryKvStore(), ["pk"]);
|
|
493
|
+
const options = {
|
|
494
|
+
documentLoader,
|
|
495
|
+
contextLoader,
|
|
496
|
+
keyCache,
|
|
497
|
+
spec
|
|
498
|
+
};
|
|
499
|
+
ok(await verifyRequest(await signedRequest(spec), options) != null);
|
|
500
|
+
responses[compatibleId(gw1)] = await sign(await actorJson({
|
|
501
|
+
publicKey: rsaPublicKey3.publicKey,
|
|
502
|
+
assertionMethod: false
|
|
503
|
+
}));
|
|
504
|
+
const rotated = async () => await signRequest(new Request("https://recipient.example/inbox", {
|
|
505
|
+
method: "POST",
|
|
506
|
+
body: "{}",
|
|
507
|
+
headers: { "Content-Type": "application/activity+json" }
|
|
508
|
+
}), rsaPrivateKey3, keyId, { spec });
|
|
509
|
+
ok(await verifyRequest(await rotated(), options) != null, spec);
|
|
510
|
+
strictEqual(fetchCount(documentLoader), 2, spec);
|
|
511
|
+
ok(await verifyRequest(await rotated(), options) != null, spec);
|
|
512
|
+
strictEqual(fetchCount(documentLoader), 2, spec);
|
|
513
|
+
}
|
|
514
|
+
});
|
|
515
|
+
test("verifyRequest() drops a cached gateway key when its refetch fails", async () => {
|
|
516
|
+
for (const spec of specs) {
|
|
517
|
+
const responses = { [compatibleId(gw1)]: await sign(await actorJson()) };
|
|
518
|
+
const documentLoader = createLoader(responses);
|
|
519
|
+
const keyCache = new KvKeyCache(new MemoryKvStore(), ["pk"]);
|
|
520
|
+
const options = {
|
|
521
|
+
documentLoader,
|
|
522
|
+
contextLoader,
|
|
523
|
+
keyCache,
|
|
524
|
+
spec
|
|
525
|
+
};
|
|
526
|
+
ok(await verifyRequest(await signedRequest(spec), options) != null);
|
|
527
|
+
delete responses[compatibleId(gw1)];
|
|
528
|
+
const result = await verifyRequestDetailed(await signRequest(new Request("https://recipient.example/inbox", {
|
|
529
|
+
method: "POST",
|
|
530
|
+
body: "{}",
|
|
531
|
+
headers: { "Content-Type": "application/activity+json" }
|
|
532
|
+
}), rsaPrivateKey3, keyId, { spec }), options);
|
|
533
|
+
ok(!result.verified, spec);
|
|
534
|
+
strictEqual(result.reason.type, "keyFetchError", spec);
|
|
535
|
+
strictEqual(fetchCount(documentLoader), 2, spec);
|
|
536
|
+
const next = await verifyRequestDetailed(await signedRequest(spec), options);
|
|
537
|
+
ok(!next.verified, spec);
|
|
538
|
+
strictEqual(next.reason.type, "keyFetchError", spec);
|
|
539
|
+
strictEqual(fetchCount(documentLoader), 2, spec);
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
test("verifyRequest() does not trust a cached gateway key for longer than its proof", async () => {
|
|
543
|
+
let now = Temporal.Now.instant();
|
|
544
|
+
const keyCache = new KvKeyCache(new MemoryKvStore(), ["pk"], { now: () => now });
|
|
545
|
+
const expires = now.add({ minutes: 30 });
|
|
546
|
+
const documentLoader = createLoader({ [compatibleId(gw1)]: await sign(await actorJson(), ed25519PrivateKey, didKeyId, { expires: expires.toString() }) });
|
|
547
|
+
const options = {
|
|
548
|
+
documentLoader,
|
|
549
|
+
contextLoader,
|
|
550
|
+
keyCache
|
|
551
|
+
};
|
|
552
|
+
ok(await verifyRequest(await signedRequest(), options) != null);
|
|
553
|
+
now = expires.subtract({ seconds: 1 });
|
|
554
|
+
ok(await verifyRequest(await signedRequest(), options) != null);
|
|
555
|
+
strictEqual(fetchCount(documentLoader), 1);
|
|
556
|
+
now = expires;
|
|
557
|
+
ok(await verifyRequest(await signedRequest(), options) != null);
|
|
558
|
+
strictEqual(fetchCount(documentLoader), 2);
|
|
559
|
+
});
|
|
560
|
+
test("verifyRequest() looks up a cached gateway key again after an hour", async () => {
|
|
561
|
+
let now = Temporal.Now.instant();
|
|
562
|
+
const keyCache = new KvKeyCache(new MemoryKvStore(), ["pk"], { now: () => now });
|
|
563
|
+
const responses = { [compatibleId(gw1)]: await sign(await actorJson()) };
|
|
564
|
+
const documentLoader = createLoader(responses);
|
|
565
|
+
const options = {
|
|
566
|
+
documentLoader,
|
|
567
|
+
contextLoader,
|
|
568
|
+
keyCache
|
|
569
|
+
};
|
|
570
|
+
const start = now;
|
|
571
|
+
ok(await verifyRequest(await signedRequest(), options) != null);
|
|
572
|
+
now = start.add({ minutes: 59 });
|
|
573
|
+
ok(await verifyRequest(await signedRequest(), options) != null);
|
|
574
|
+
strictEqual(fetchCount(documentLoader), 1);
|
|
575
|
+
responses[compatibleId(gw1)] = await sign(await actorJson({ gateways: [gw2] }));
|
|
576
|
+
now = start.add({ hours: 1 });
|
|
577
|
+
strictEqual(await verifyRequest(await signedRequest(), options), null);
|
|
578
|
+
strictEqual(fetchCount(documentLoader), 2);
|
|
579
|
+
});
|
|
580
|
+
test("verifyRequest() rejects and caches gateway keys of expired documents", async () => {
|
|
581
|
+
const documentLoader = createLoader({ [compatibleId(gw1)]: await sign(await actorJson(), ed25519PrivateKey, didKeyId, { expires: "2000-01-01T00:00:00Z" }) });
|
|
582
|
+
const keyCache = new KvKeyCache(new MemoryKvStore(), ["pk"]);
|
|
583
|
+
const options = {
|
|
584
|
+
documentLoader,
|
|
585
|
+
contextLoader,
|
|
586
|
+
keyCache
|
|
587
|
+
};
|
|
588
|
+
strictEqual(await verifyRequest(await signedRequest(), options), null);
|
|
589
|
+
strictEqual(await verifyRequest(await signedRequest(), options), null);
|
|
590
|
+
strictEqual(fetchCount(documentLoader), 1);
|
|
591
|
+
});
|
|
592
|
+
test("verifyRequest() takes the expiration of a proof whatever its context", async () => {
|
|
593
|
+
let now = Temporal.Now.instant();
|
|
594
|
+
const keyCache = new KvKeyCache(new MemoryKvStore(), ["pk"], { now: () => now });
|
|
595
|
+
const expires = now.add({ minutes: 30 });
|
|
596
|
+
const actor = await actorJson();
|
|
597
|
+
const documentLoader = createLoader({ [compatibleId(gw1)]: await sign({
|
|
598
|
+
...actor,
|
|
599
|
+
"@context": [...actor["@context"], { expires: "https://example.com/ns#notExpiration" }]
|
|
600
|
+
}, ed25519PrivateKey, didKeyId, { expires: expires.toString() }) });
|
|
601
|
+
const options = {
|
|
602
|
+
documentLoader,
|
|
603
|
+
contextLoader,
|
|
604
|
+
keyCache
|
|
605
|
+
};
|
|
606
|
+
ok(await verifyRequest(await signedRequest(), options) != null);
|
|
607
|
+
now = expires.subtract({ seconds: 1 });
|
|
608
|
+
ok(await verifyRequest(await signedRequest(), options) != null);
|
|
609
|
+
strictEqual(fetchCount(documentLoader), 1);
|
|
610
|
+
now = expires;
|
|
611
|
+
ok(await verifyRequest(await signedRequest(), options) != null);
|
|
612
|
+
strictEqual(fetchCount(documentLoader), 2);
|
|
613
|
+
});
|
|
614
|
+
test("a key cache without namespaces caches only fetch failures of compatible key IDs", async () => {
|
|
337
615
|
const calls = [];
|
|
616
|
+
const entries = /* @__PURE__ */ new Map();
|
|
338
617
|
const keyCache = {
|
|
339
618
|
get(id) {
|
|
340
619
|
calls.push(`get ${id.href}`);
|
|
341
|
-
return Promise.resolve(
|
|
620
|
+
return Promise.resolve(entries.get(id.href));
|
|
342
621
|
},
|
|
343
|
-
set(id) {
|
|
344
|
-
calls.push(`set ${id.href}`);
|
|
622
|
+
set(id, key) {
|
|
623
|
+
calls.push(`set ${id.href} ${key == null ? "null" : "key"}`);
|
|
624
|
+
entries.set(id.href, key);
|
|
345
625
|
return Promise.resolve();
|
|
346
626
|
}
|
|
347
627
|
};
|
|
348
|
-
const
|
|
349
|
-
|
|
350
|
-
documentLoader,
|
|
351
|
-
contextLoader,
|
|
352
|
-
keyCache
|
|
353
|
-
}) != null);
|
|
354
|
-
const fetchOptions = {
|
|
355
|
-
documentLoader,
|
|
628
|
+
const options = {
|
|
629
|
+
documentLoader: createLoader({ [compatibleId(gw1)]: await sign(await actorJson()) }),
|
|
356
630
|
contextLoader,
|
|
357
631
|
keyCache
|
|
358
632
|
};
|
|
359
|
-
|
|
360
|
-
strictEqual((await
|
|
361
|
-
|
|
633
|
+
ok(await verifyRequest(await signedRequest(), options) != null);
|
|
634
|
+
for (const cls of [CryptographicKey, Multikey]) strictEqual((await fetchKeyOf(cls, options)).key, null);
|
|
635
|
+
ok(await verifyRequest(await signedRequest(), options) != null);
|
|
636
|
+
deepStrictEqual(calls.filter((c) => c.startsWith("set ") && c.includes(keyId.origin)), []);
|
|
637
|
+
const unreachable = new URL(`${compatibleId(gw2)}#main-key`);
|
|
638
|
+
strictEqual((await fetchKey(unreachable, CryptographicKey, options)).key, null);
|
|
639
|
+
deepStrictEqual(calls.filter((c) => c.startsWith("set ") && c.includes(gw2)), [`set ${unreachable.href} null`]);
|
|
362
640
|
});
|
|
363
641
|
test("doesActorOwnKey() and getKeyOwner() resolve gateway keys to portable actors", async () => {
|
|
364
642
|
const options = {
|
package/dist/sig/proof.test.mjs
CHANGED
|
@@ -10,10 +10,10 @@ import "../std__assert-DvMDRPes.mjs";
|
|
|
10
10
|
import { t as assertRejects } from "../assert_rejects-CTptdYck.mjs";
|
|
11
11
|
import { t as assert } from "../assert-D1Eafmd_.mjs";
|
|
12
12
|
import { r as normalizeOutgoingActivityJsonLd } from "../outgoing-jsonld-4IJqPYos.mjs";
|
|
13
|
-
import { i as signObject, n as createProof, o as verifyObject, r as hasProofLike, s as verifyPortableObjectProof, u as verifyProof } from "../proof-
|
|
14
|
-
import { d as verifyRequest } from "../http-
|
|
13
|
+
import { i as signObject, n as createProof, o as verifyObject, r as hasProofLike, s as verifyPortableObjectProof, u as verifyProof } from "../proof-xdPYGPaA.mjs";
|
|
14
|
+
import { d as verifyRequest } from "../http-BcLnr2do.mjs";
|
|
15
15
|
import { i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-DIJNHW-Y.mjs";
|
|
16
|
-
import { t as getAuthenticatedDocumentLoader } from "../docloader-
|
|
16
|
+
import { t as getAuthenticatedDocumentLoader } from "../docloader-Bby5g2iu.mjs";
|
|
17
17
|
import { Create, DataIntegrityProof, Document, Multikey, Note, PUBLIC_COLLECTION, Place, Translation } from "@fedify/vocab";
|
|
18
18
|
import { createTestMeterProvider, mockDocumentLoader, test } from "@fedify/fixture";
|
|
19
19
|
import { decodeMultibase, encodeMultibase, exportDidKey, exportMultibaseKey, formatIri, importMultibaseKey, parseIri } from "@fedify/vocab-runtime";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const { URLPattern } = require("urlpattern-polyfill");
|
|
2
|
-
const require_http = require("./http-
|
|
3
|
-
require("./owner-
|
|
2
|
+
const require_http = require("./http-B4mQvNcv.cjs");
|
|
3
|
+
require("./owner-BtoMCv6Z.cjs");
|
|
4
4
|
let _logtape_logtape = require("@logtape/logtape");
|
|
5
5
|
let _fedify_vocab = require("@fedify/vocab");
|
|
6
6
|
let _opentelemetry_api = require("@opentelemetry/api");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Temporal } from "temporal-polyfill";
|
|
2
2
|
import { URLPattern } from "urlpattern-polyfill";
|
|
3
|
-
import { _ as verifyPortableObjectProofWithRoot, bt as name, h as verifyPortableObjectProof, xt as version } from "./http-
|
|
4
|
-
import "./owner-
|
|
3
|
+
import { _ as verifyPortableObjectProofWithRoot, bt as name, h as verifyPortableObjectProof, xt as version } from "./http-Ce6_Lf7k.js";
|
|
4
|
+
import "./owner-Cb3ytrlq.js";
|
|
5
5
|
import { getLogger } from "@logtape/logtape";
|
|
6
6
|
import { CollectionPage, Object as Object$1, isActor } from "@fedify/vocab";
|
|
7
7
|
import { trace } from "@opentelemetry/api";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Temporal } from "temporal-polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { c as getNormalizationContextLoader } from "./ld-
|
|
4
|
+
import { c as getNormalizationContextLoader } from "./ld-CnT0f1nU.mjs";
|
|
5
5
|
import jsonld from "@fedify/vocab-runtime/jsonld";
|
|
6
6
|
//#region src/federation/temporal.ts
|
|
7
7
|
function isPlainObject(value) {
|
package/dist/testing/kv.test.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
|
|
|
6
6
|
import { t as ManualClockKvStore } from "../kv-DBRWVx3Q.mjs";
|
|
7
7
|
import { n as baseOptions, o as stringSchema, t as MockQueue } from "../tasks-dbCShlCb.mjs";
|
|
8
8
|
import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
|
|
9
|
-
import { o as createFederation } from "../middleware-
|
|
9
|
+
import { o as createFederation } from "../middleware-BpwKvOpU.mjs";
|
|
10
10
|
//#region src/testing/kv.test.ts
|
|
11
11
|
test("ManualClockKvStore keeps an entry at its exact expiration instant", async () => {
|
|
12
12
|
const kv = new ManualClockKvStore();
|
|
@@ -5,9 +5,9 @@ import { t as esm_default } from "../esm-BHXXx4Wx.mjs";
|
|
|
5
5
|
import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
|
|
6
6
|
import "../std__assert-DvMDRPes.mjs";
|
|
7
7
|
import { t as assertRejects } from "../assert_rejects-CTptdYck.mjs";
|
|
8
|
-
import { d as verifyRequest } from "../http-
|
|
8
|
+
import { d as verifyRequest } from "../http-BcLnr2do.mjs";
|
|
9
9
|
import { i as rsaPrivateKey2 } from "../keys-DIJNHW-Y.mjs";
|
|
10
|
-
import { t as getAuthenticatedDocumentLoader } from "../docloader-
|
|
10
|
+
import { t as getAuthenticatedDocumentLoader } from "../docloader-Bby5g2iu.mjs";
|
|
11
11
|
import { mockDocumentLoader, test } from "@fedify/fixture";
|
|
12
12
|
import { FetchError, UrlError } from "@fedify/vocab-runtime";
|
|
13
13
|
//#region src/utils/docloader.test.ts
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Temporal } from "temporal-polyfill";
|
|
2
2
|
import { URLPattern } from "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { n as kvCache, t as MockKvStore } from "../kv-cache-
|
|
4
|
+
import { n as kvCache, t as MockKvStore } from "../kv-cache-iFaRIcIL.mjs";
|
|
5
5
|
import { deepStrictEqual, throws } from "node:assert";
|
|
6
6
|
import { createTestMeterProvider, mockDocumentLoader, test } from "@fedify/fixture";
|
|
7
7
|
import { preloadedContexts } from "@fedify/vocab-runtime";
|
package/dist/utils/mod.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const { URLPattern } = require("urlpattern-polyfill");
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
const require_kv_cache = require("../kv-cache-
|
|
3
|
+
const require_kv_cache = require("../kv-cache-Dqp_E-1g.cjs");
|
|
4
4
|
exports.getAuthenticatedDocumentLoader = require_kv_cache.getAuthenticatedDocumentLoader;
|
|
5
5
|
exports.kvCache = require_kv_cache.kvCache;
|
package/dist/utils/mod.d.cts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
/// <reference lib="esnext.temporal" />
|
|
2
|
-
import { n as getAuthenticatedDocumentLoader, t as kvCache } from "../mod-
|
|
2
|
+
import { n as getAuthenticatedDocumentLoader, t as kvCache } from "../mod-Cr79cQlc.cjs";
|
|
3
3
|
export { getAuthenticatedDocumentLoader, kvCache };
|
package/dist/utils/mod.d.ts
CHANGED
package/dist/utils/mod.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { Temporal } from "temporal-polyfill";
|
|
2
2
|
import { URLPattern } from "urlpattern-polyfill";
|
|
3
|
-
import { n as getAuthenticatedDocumentLoader, t as kvCache } from "../kv-cache-
|
|
3
|
+
import { n as getAuthenticatedDocumentLoader, t as kvCache } from "../kv-cache-DwzXzchj.js";
|
|
4
4
|
export { getAuthenticatedDocumentLoader, kvCache };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fedify/fedify",
|
|
3
|
-
"version": "2.4.0-dev.
|
|
3
|
+
"version": "2.4.0-dev.2115+e2feaf2b",
|
|
4
4
|
"description": "An ActivityPub server framework",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ActivityPub",
|
|
@@ -151,10 +151,10 @@
|
|
|
151
151
|
"structured-field-values": "^2.0.4",
|
|
152
152
|
"temporal-polyfill": "^1.0.1",
|
|
153
153
|
"urlpattern-polyfill": "^10.1.0",
|
|
154
|
-
"@fedify/uri-template": "2.4.0-dev.
|
|
155
|
-
"@fedify/vocab
|
|
156
|
-
"@fedify/vocab": "2.4.0-dev.
|
|
157
|
-
"@fedify/webfinger": "2.4.0-dev.
|
|
154
|
+
"@fedify/uri-template": "2.4.0-dev.2115+e2feaf2b",
|
|
155
|
+
"@fedify/vocab": "2.4.0-dev.2115+e2feaf2b",
|
|
156
|
+
"@fedify/vocab-runtime": "2.4.0-dev.2115+e2feaf2b",
|
|
157
|
+
"@fedify/webfinger": "2.4.0-dev.2115+e2feaf2b"
|
|
158
158
|
},
|
|
159
159
|
"devDependencies": {
|
|
160
160
|
"@std/assert": "npm:@jsr/std__assert@^0.226.0",
|
|
@@ -167,7 +167,7 @@
|
|
|
167
167
|
"typescript": "^6.0.0",
|
|
168
168
|
"wrangler": "^4.17.0",
|
|
169
169
|
"@fedify/fixture": "2.0.0",
|
|
170
|
-
"@fedify/vocab-tools": "^2.4.0-dev.
|
|
170
|
+
"@fedify/vocab-tools": "^2.4.0-dev.2115+e2feaf2b"
|
|
171
171
|
},
|
|
172
172
|
"scripts": {
|
|
173
173
|
"build:self": "tsdown",
|