This release is 23 versions behind 1.4.9 — the latest version of @fedify/fedify. Jump to latest
Built and signed on GitHub ActionsBuilt and signed on GitHub Actions
Built and signed on GitHub Actions
An ActivityPub/fediverse server framework
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278import { assertEquals } from "jsr:@std/assert@^0.226.0"; import type { ActorDispatcher, ActorHandleMapper, } from "../federation/callback.ts"; import { createRequestContext } from "../testing/context.ts"; import { test } from "../testing/mod.ts"; import type { Actor } from "../vocab/actor.ts"; import { Image, Link, Person } from "../vocab/vocab.ts"; import { handleWebFinger } from "./handler.ts"; test("handleWebFinger()", async () => { const url = new URL("https://example.com/.well-known/webfinger"); const context = createRequestContext<void>({ url, data: undefined, getActorUri(identifier) { return new URL(`https://example.com/users/${identifier}`); }, async getActor(handle): Promise<Actor | null> { return await actorDispatcher( context, handle, ); }, parseUri(uri) { if (uri == null) return null; if (uri.protocol === "acct:") return null; const paths = uri.pathname.split("/"); const identifier = paths[paths.length - 1]; return { type: "actor", identifier, get handle(): string { throw new Error("ParseUriResult.handle is deprecated!"); }, }; }, }); const actorDispatcher: ActorDispatcher<void> = (ctx, handle) => { if (handle !== "someone" && handle !== "someone2") return null; return new Person({ id: ctx.getActorUri(handle), name: handle === "someone" ? "Someone" : "Someone 2", preferredUsername: handle === "someone" ? null : handle, icon: new Image({ url: new URL("https://example.com/icon.jpg"), mediaType: "image/jpeg", }), urls: [ new URL("https://example.com/@" + handle), new Link({ href: new URL("https://example.org/@" + handle), rel: "alternate", mediaType: "text/html", }), ], }); }; let onNotFoundCalled: Request | null = null; const onNotFound = (request: Request) => { onNotFoundCalled = request; return new Response("Not found", { status: 404 }); }; let request = context.request; let response = await handleWebFinger(request, { context, onNotFound, }); assertEquals(response.status, 404); assertEquals(onNotFoundCalled, request); onNotFoundCalled = null; response = await handleWebFinger(request, { context, actorDispatcher, onNotFound, }); assertEquals(response.status, 400); assertEquals(await response.text(), "Missing resource parameter."); assertEquals(onNotFoundCalled, null); url.searchParams.set("resource", " invalid "); request = new Request(url); response = await handleWebFinger(request, { context, actorDispatcher, onNotFound, }); assertEquals(response.status, 400); assertEquals(await response.text(), "Invalid resource URL."); assertEquals(onNotFoundCalled, null); url.searchParams.set("resource", "acct:someone@example.com"); request = new Request(url); response = await handleWebFinger(request, { context, actorDispatcher, onNotFound, }); assertEquals(response.status, 200); assertEquals(response.headers.get("Content-Type"), "application/jrd+json"); assertEquals(response.headers.get("Access-Control-Allow-Origin"), "*"); const expected = { subject: "acct:someone@example.com", aliases: [ "https://example.com/users/someone", ], links: [ { href: "https://example.com/users/someone", rel: "self", type: "application/activity+json", }, { href: "https://example.com/@someone", rel: "http://webfinger.net/rel/profile-page", }, { href: "https://example.org/@someone", rel: "alternate", type: "text/html", }, { href: "https://example.com/icon.jpg", rel: "http://webfinger.net/rel/avatar", type: "image/jpeg", }, ], }; assertEquals(await response.json(), expected); url.searchParams.set("resource", "https://example.com/users/someone"); request = new Request(url); response = await handleWebFinger(request, { context, actorDispatcher, onNotFound, }); assertEquals(response.status, 200); assertEquals(await response.json(), { ...expected, aliases: [], subject: "https://example.com/users/someone", }); url.searchParams.set("resource", "https://example.com/users/someone2"); request = new Request(url); response = await handleWebFinger(request, { context, actorDispatcher, onNotFound, }); assertEquals(response.status, 200); const expected2 = { subject: "https://example.com/users/someone2", aliases: [ "acct:someone2@example.com", ], links: [ { href: "https://example.com/users/someone2", rel: "self", type: "application/activity+json", }, { href: "https://example.com/@someone2", rel: "http://webfinger.net/rel/profile-page", }, { href: "https://example.org/@someone2", rel: "alternate", type: "text/html", }, { href: "https://example.com/icon.jpg", rel: "http://webfinger.net/rel/avatar", type: "image/jpeg", }, ], }; assertEquals(await response.json(), expected2); url.searchParams.set("resource", "acct:no-one@example.com"); request = new Request(url); response = await handleWebFinger(request, { context, actorDispatcher, onNotFound, }); assertEquals(response.status, 404); assertEquals(onNotFoundCalled, request); onNotFoundCalled = null; url.searchParams.set("resource", "https://example.com/users/no-one"); request = new Request(url); response = await handleWebFinger(request, { context, actorDispatcher, onNotFound, }); assertEquals(response.status, 404); assertEquals(onNotFoundCalled, request); onNotFoundCalled = null; url.searchParams.set("resource", "https://google.com/"); request = new Request(url); response = await handleWebFinger(request, { context, actorDispatcher, onNotFound, }); assertEquals(response.status, 404); assertEquals(onNotFoundCalled, request); const actorHandleMapper: ActorHandleMapper<void> = (_ctx, username) => { return username === "foo" ? "someone" : username === "bar" ? "someone2" : null; }; url.searchParams.set("resource", "acct:foo@example.com"); request = new Request(url); response = await handleWebFinger(request, { context, actorDispatcher, actorHandleMapper, onNotFound, }); assertEquals(response.status, 200); assertEquals(await response.json(), { ...expected, aliases: ["https://example.com/users/someone"], subject: "acct:foo@example.com", }); url.searchParams.set("resource", "acct:bar@example.com"); request = new Request(url); response = await handleWebFinger(request, { context, actorDispatcher, actorHandleMapper, onNotFound, }); assertEquals(response.status, 200); assertEquals(await response.json(), { ...expected2, aliases: ["https://example.com/users/someone2"], subject: "acct:bar@example.com", }); url.searchParams.set("resource", "https://example.com/users/someone"); request = new Request(url); response = await handleWebFinger(request, { context, actorDispatcher, actorHandleMapper, onNotFound, }); assertEquals(response.status, 200); assertEquals(await response.json(), { ...expected, aliases: [], subject: "https://example.com/users/someone", }); url.searchParams.set("resource", "acct:baz@example.com"); request = new Request(url); response = await handleWebFinger(request, { context, actorDispatcher, actorHandleMapper, onNotFound, }); assertEquals(response.status, 404); });