Loading CHANGES.md +2 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,8 @@ To be released. - Added `Context.hostname` property. - Added `Context.host` property. - Added `Context.origin` property. - The type of `ActorKeyPairsDispatcher<TContextData>`'s first parameter became `Context` (was `TContextData`). [#66]: https://github.com/dahlia/fedify/issues/66 [#85]: https://github.com/dahlia/fedify/issues/85 Loading docs/manual/actor.md +2 −2 Original line number Diff line number Diff line Loading @@ -195,7 +195,7 @@ federation.setActorDispatcher("/users/{handle}", async (ctx, handle) => { // Many more properties; see the previous section for details. }); }) .setKeyPairsDispatcher(async (ctxData, handle) => { .setKeyPairsDispatcher(async (ctx, handle) => { // Work with the database to find the key pair by the handle. if (user == null) return []; // Return null if the key pair is not found. // Return the loaded key pair. See the below example for details. Loading Loading @@ -256,7 +256,7 @@ federation .setActorDispatcher("/users/{handle}", async (ctx, handle) => { // Omitted for brevity; see the previous example for details. }) .setKeyPairsDispatcher(async (ctxData, handle) => { .setKeyPairsDispatcher(async (ctx, handle) => { const kv = await Deno.openKv(); const entry = await kv.get<{ privateKey: JsonWebKey; publicKey: JsonWebKey }>( ["keypair", handle], Loading examples/blog/federation/mod.ts +1 −1 Original line number Diff line number Diff line Loading @@ -80,7 +80,7 @@ federation.setActorDispatcher("/users/{handle}", async (ctx, handle) => { assertionMethods: keyPairs.map((keyPair) => keyPair.multikey), }); }) .setKeyPairsDispatcher(async (_ctxData, handle) => { .setKeyPairsDispatcher(async (_ctx, handle) => { const blog = await getBlog(); if (blog == null) return []; else if (blog.handle !== handle) return []; Loading federation/callback.ts +3 −3 Original line number Diff line number Diff line Loading @@ -3,7 +3,7 @@ import type { Actor } from "../vocab/actor.ts"; import type { Activity, CryptographicKey } from "../vocab/mod.ts"; import type { Object } from "../vocab/vocab.ts"; import type { PageItems } from "./collection.ts"; import type { RequestContext } from "./context.ts"; import type { Context, RequestContext } from "./context.ts"; import type { SenderKeyPair } from "./send.ts"; /** Loading Loading @@ -35,13 +35,13 @@ export type ActorDispatcher<TContextData> = ( * A callback that dispatches key pairs for an actor. * * @typeParam TContextData The context data to pass to the {@link Context}. * @param contextData The context data. * @param context The context. * @param handle The actor's handle. * @returns The key pairs. * @since 0.10.0 */ export type ActorKeyPairsDispatcher<TContextData> = ( contextData: TContextData, context: Context<TContextData>, handle: string, ) => CryptoKeyPair[] | Promise<CryptoKeyPair[]>; Loading federation/middleware.ts +41 −25 Original line number Diff line number Diff line Loading @@ -748,8 +748,8 @@ export class Federation<TContextData> { "deprecated. Use the ActorCallbackSetters.setKeyPairsDispatcher() " + "instead.", ); callbacks.keyPairsDispatcher = async (ctxData, handle) => { const key = await dispatcher(ctxData, handle); callbacks.keyPairsDispatcher = async (ctx, handle) => { const key = await dispatcher(ctx.data, handle); if (key == null) return []; return [key]; }; Loading Loading @@ -1737,6 +1737,7 @@ interface ContextOptions<TContextData> { documentLoader: DocumentLoader; contextLoader: DocumentLoader; authenticatedDocumentLoaderFactory: AuthenticatedDocumentLoaderFactory; invokedFromActorKeyPairsDispatcher?: { handle: string }; } class ContextImpl<TContextData> implements Context<TContextData> { Loading @@ -1758,6 +1759,7 @@ class ContextImpl<TContextData> implements Context<TContextData> { readonly contextLoader: DocumentLoader; readonly #authenticatedDocumentLoaderFactory: AuthenticatedDocumentLoaderFactory; readonly #invokedFromActorKeyPairsDispatcher?: { handle: string }; constructor( { Loading @@ -1771,6 +1773,7 @@ class ContextImpl<TContextData> implements Context<TContextData> { documentLoader, contextLoader, authenticatedDocumentLoaderFactory, invokedFromActorKeyPairsDispatcher, }: ContextOptions<TContextData>, ) { this.#url = url; Loading @@ -1784,6 +1787,8 @@ class ContextImpl<TContextData> implements Context<TContextData> { this.contextLoader = contextLoader; this.#authenticatedDocumentLoaderFactory = authenticatedDocumentLoaderFactory; this.#invokedFromActorKeyPairsDispatcher = invokedFromActorKeyPairsDispatcher; } get hostname(): string { Loading Loading @@ -1945,16 +1950,24 @@ class ContextImpl<TContextData> implements Context<TContextData> { } async getActorKeyPairs(handle: string): Promise<ActorKeyPair[]> { const logger = getLogger(["fedify", "federation", "actor"]); if (this.#invokedFromActorKeyPairsDispatcher != null) { logger.warn( "Context.getActorKeyPairs({getActorKeyPairsHandle}) method is " + "invoked from the actor key pairs dispatcher " + "({actorKeyPairsDispatcherHandle}); this may cause an infinite loop.", { getActorKeyPairsHandle: handle, actorKeyPairsDispatcherHandle: this.#invokedFromActorKeyPairsDispatcher.handle, }, ); } let keyPairs: (CryptoKeyPair & { keyId: URL })[]; try { keyPairs = await this.getKeyPairsFromHandle( this.#url, this.data, handle, ); keyPairs = await this.getKeyPairsFromHandle(handle); } catch (_) { getLogger(["fedify", "federation", "actor"]) .warn("No actor key pairs dispatcher registered."); logger.warn("No actor key pairs dispatcher registered."); return []; } const owner = this.getActorUri(handle); Loading @@ -1979,8 +1992,6 @@ class ContextImpl<TContextData> implements Context<TContextData> { } protected async getKeyPairsFromHandle( url: URL | string, contextData: TContextData, handle: string, ): Promise<(CryptoKeyPair & { keyId: URL })[]> { const logger = getLogger(["fedify", "federation", "actor"]); Loading @@ -1992,9 +2003,22 @@ class ContextImpl<TContextData> implements Context<TContextData> { logger.warn("No actor dispatcher registered."); return []; } const actorUri = new URL(path, url); const actorUri = new URL(path, this.#url); const keyPairs = await this.actorCallbacks?.keyPairsDispatcher( contextData, new ContextImpl({ url: this.#url, federation: this.#federation, router: this.#router, objectTypeIds: this.#objectTypeIds, objectCallbacks: this.objectCallbacks, actorCallbacks: this.actorCallbacks, data: this.data, documentLoader: this.documentLoader, contextLoader: this.contextLoader, authenticatedDocumentLoaderFactory: this.#authenticatedDocumentLoaderFactory, invokedFromActorKeyPairsDispatcher: { handle }, }), handle, ); if (keyPairs.length < 1) { Loading Loading @@ -2038,11 +2062,7 @@ class ContextImpl<TContextData> implements Context<TContextData> { protected async getRsaKeyPairFromHandle( handle: string, ): Promise<CryptoKeyPair & { keyId: URL } | null> { const keyPairs = await this.getKeyPairsFromHandle( this.#url, this.data, handle, ); const keyPairs = await this.getKeyPairsFromHandle(handle); for (const keyPair of keyPairs) { const { privateKey } = keyPair; if ( Loading Loading @@ -2085,11 +2105,7 @@ class ContextImpl<TContextData> implements Context<TContextData> { ): Promise<void> { let keys: SenderKeyPair[]; if ("handle" in sender) { keys = await this.getKeyPairsFromHandle( this.#url, this.data, sender.handle, ); keys = await this.getKeyPairsFromHandle(sender.handle); if (keys.length < 1) { throw new Error( `No key pair found for actor ${JSON.stringify(sender.handle)}.`, Loading Loading @@ -2223,7 +2239,7 @@ class RequestContextImpl<TContextData> extends ContextImpl<TContextData> throw new Error("No actor dispatcher registered."); } if (this.#invokedFromActorDispatcher != null) { getLogger(["fedify", "federation"]).warn( getLogger(["fedify", "federation", "actor"]).warn( "RequestContext.getActor({getActorHandle}) is invoked from " + "the actor dispatcher ({actorDispatcherHandle}); " + "this may cause an infinite loop.", Loading Loading @@ -2385,7 +2401,7 @@ export interface ActorCallbackSetters<TContextData> { * Use {@link ActorCallbackSetters.setKeyPairsDispatcher} instead. * @param dispatcher A callback that returns the key pair for an actor. * @returns The setters object so that settings can be chained. * @deprecated * @deprecated Use {@link ActorCallbackSetters.setKeyPairsDispatcher} instead. */ setKeyPairDispatcher( dispatcher: ActorKeyPairDispatcher<TContextData>, Loading Loading
CHANGES.md +2 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,8 @@ To be released. - Added `Context.hostname` property. - Added `Context.host` property. - Added `Context.origin` property. - The type of `ActorKeyPairsDispatcher<TContextData>`'s first parameter became `Context` (was `TContextData`). [#66]: https://github.com/dahlia/fedify/issues/66 [#85]: https://github.com/dahlia/fedify/issues/85 Loading
docs/manual/actor.md +2 −2 Original line number Diff line number Diff line Loading @@ -195,7 +195,7 @@ federation.setActorDispatcher("/users/{handle}", async (ctx, handle) => { // Many more properties; see the previous section for details. }); }) .setKeyPairsDispatcher(async (ctxData, handle) => { .setKeyPairsDispatcher(async (ctx, handle) => { // Work with the database to find the key pair by the handle. if (user == null) return []; // Return null if the key pair is not found. // Return the loaded key pair. See the below example for details. Loading Loading @@ -256,7 +256,7 @@ federation .setActorDispatcher("/users/{handle}", async (ctx, handle) => { // Omitted for brevity; see the previous example for details. }) .setKeyPairsDispatcher(async (ctxData, handle) => { .setKeyPairsDispatcher(async (ctx, handle) => { const kv = await Deno.openKv(); const entry = await kv.get<{ privateKey: JsonWebKey; publicKey: JsonWebKey }>( ["keypair", handle], Loading
examples/blog/federation/mod.ts +1 −1 Original line number Diff line number Diff line Loading @@ -80,7 +80,7 @@ federation.setActorDispatcher("/users/{handle}", async (ctx, handle) => { assertionMethods: keyPairs.map((keyPair) => keyPair.multikey), }); }) .setKeyPairsDispatcher(async (_ctxData, handle) => { .setKeyPairsDispatcher(async (_ctx, handle) => { const blog = await getBlog(); if (blog == null) return []; else if (blog.handle !== handle) return []; Loading
federation/callback.ts +3 −3 Original line number Diff line number Diff line Loading @@ -3,7 +3,7 @@ import type { Actor } from "../vocab/actor.ts"; import type { Activity, CryptographicKey } from "../vocab/mod.ts"; import type { Object } from "../vocab/vocab.ts"; import type { PageItems } from "./collection.ts"; import type { RequestContext } from "./context.ts"; import type { Context, RequestContext } from "./context.ts"; import type { SenderKeyPair } from "./send.ts"; /** Loading Loading @@ -35,13 +35,13 @@ export type ActorDispatcher<TContextData> = ( * A callback that dispatches key pairs for an actor. * * @typeParam TContextData The context data to pass to the {@link Context}. * @param contextData The context data. * @param context The context. * @param handle The actor's handle. * @returns The key pairs. * @since 0.10.0 */ export type ActorKeyPairsDispatcher<TContextData> = ( contextData: TContextData, context: Context<TContextData>, handle: string, ) => CryptoKeyPair[] | Promise<CryptoKeyPair[]>; Loading
federation/middleware.ts +41 −25 Original line number Diff line number Diff line Loading @@ -748,8 +748,8 @@ export class Federation<TContextData> { "deprecated. Use the ActorCallbackSetters.setKeyPairsDispatcher() " + "instead.", ); callbacks.keyPairsDispatcher = async (ctxData, handle) => { const key = await dispatcher(ctxData, handle); callbacks.keyPairsDispatcher = async (ctx, handle) => { const key = await dispatcher(ctx.data, handle); if (key == null) return []; return [key]; }; Loading Loading @@ -1737,6 +1737,7 @@ interface ContextOptions<TContextData> { documentLoader: DocumentLoader; contextLoader: DocumentLoader; authenticatedDocumentLoaderFactory: AuthenticatedDocumentLoaderFactory; invokedFromActorKeyPairsDispatcher?: { handle: string }; } class ContextImpl<TContextData> implements Context<TContextData> { Loading @@ -1758,6 +1759,7 @@ class ContextImpl<TContextData> implements Context<TContextData> { readonly contextLoader: DocumentLoader; readonly #authenticatedDocumentLoaderFactory: AuthenticatedDocumentLoaderFactory; readonly #invokedFromActorKeyPairsDispatcher?: { handle: string }; constructor( { Loading @@ -1771,6 +1773,7 @@ class ContextImpl<TContextData> implements Context<TContextData> { documentLoader, contextLoader, authenticatedDocumentLoaderFactory, invokedFromActorKeyPairsDispatcher, }: ContextOptions<TContextData>, ) { this.#url = url; Loading @@ -1784,6 +1787,8 @@ class ContextImpl<TContextData> implements Context<TContextData> { this.contextLoader = contextLoader; this.#authenticatedDocumentLoaderFactory = authenticatedDocumentLoaderFactory; this.#invokedFromActorKeyPairsDispatcher = invokedFromActorKeyPairsDispatcher; } get hostname(): string { Loading Loading @@ -1945,16 +1950,24 @@ class ContextImpl<TContextData> implements Context<TContextData> { } async getActorKeyPairs(handle: string): Promise<ActorKeyPair[]> { const logger = getLogger(["fedify", "federation", "actor"]); if (this.#invokedFromActorKeyPairsDispatcher != null) { logger.warn( "Context.getActorKeyPairs({getActorKeyPairsHandle}) method is " + "invoked from the actor key pairs dispatcher " + "({actorKeyPairsDispatcherHandle}); this may cause an infinite loop.", { getActorKeyPairsHandle: handle, actorKeyPairsDispatcherHandle: this.#invokedFromActorKeyPairsDispatcher.handle, }, ); } let keyPairs: (CryptoKeyPair & { keyId: URL })[]; try { keyPairs = await this.getKeyPairsFromHandle( this.#url, this.data, handle, ); keyPairs = await this.getKeyPairsFromHandle(handle); } catch (_) { getLogger(["fedify", "federation", "actor"]) .warn("No actor key pairs dispatcher registered."); logger.warn("No actor key pairs dispatcher registered."); return []; } const owner = this.getActorUri(handle); Loading @@ -1979,8 +1992,6 @@ class ContextImpl<TContextData> implements Context<TContextData> { } protected async getKeyPairsFromHandle( url: URL | string, contextData: TContextData, handle: string, ): Promise<(CryptoKeyPair & { keyId: URL })[]> { const logger = getLogger(["fedify", "federation", "actor"]); Loading @@ -1992,9 +2003,22 @@ class ContextImpl<TContextData> implements Context<TContextData> { logger.warn("No actor dispatcher registered."); return []; } const actorUri = new URL(path, url); const actorUri = new URL(path, this.#url); const keyPairs = await this.actorCallbacks?.keyPairsDispatcher( contextData, new ContextImpl({ url: this.#url, federation: this.#federation, router: this.#router, objectTypeIds: this.#objectTypeIds, objectCallbacks: this.objectCallbacks, actorCallbacks: this.actorCallbacks, data: this.data, documentLoader: this.documentLoader, contextLoader: this.contextLoader, authenticatedDocumentLoaderFactory: this.#authenticatedDocumentLoaderFactory, invokedFromActorKeyPairsDispatcher: { handle }, }), handle, ); if (keyPairs.length < 1) { Loading Loading @@ -2038,11 +2062,7 @@ class ContextImpl<TContextData> implements Context<TContextData> { protected async getRsaKeyPairFromHandle( handle: string, ): Promise<CryptoKeyPair & { keyId: URL } | null> { const keyPairs = await this.getKeyPairsFromHandle( this.#url, this.data, handle, ); const keyPairs = await this.getKeyPairsFromHandle(handle); for (const keyPair of keyPairs) { const { privateKey } = keyPair; if ( Loading Loading @@ -2085,11 +2105,7 @@ class ContextImpl<TContextData> implements Context<TContextData> { ): Promise<void> { let keys: SenderKeyPair[]; if ("handle" in sender) { keys = await this.getKeyPairsFromHandle( this.#url, this.data, sender.handle, ); keys = await this.getKeyPairsFromHandle(sender.handle); if (keys.length < 1) { throw new Error( `No key pair found for actor ${JSON.stringify(sender.handle)}.`, Loading Loading @@ -2223,7 +2239,7 @@ class RequestContextImpl<TContextData> extends ContextImpl<TContextData> throw new Error("No actor dispatcher registered."); } if (this.#invokedFromActorDispatcher != null) { getLogger(["fedify", "federation"]).warn( getLogger(["fedify", "federation", "actor"]).warn( "RequestContext.getActor({getActorHandle}) is invoked from " + "the actor dispatcher ({actorDispatcherHandle}); " + "this may cause an infinite loop.", Loading Loading @@ -2385,7 +2401,7 @@ export interface ActorCallbackSetters<TContextData> { * Use {@link ActorCallbackSetters.setKeyPairsDispatcher} instead. * @param dispatcher A callback that returns the key pair for an actor. * @returns The setters object so that settings can be chained. * @deprecated * @deprecated Use {@link ActorCallbackSetters.setKeyPairsDispatcher} instead. */ setKeyPairDispatcher( dispatcher: ActorKeyPairDispatcher<TContextData>, Loading