Skip to content

Commit

Permalink
Rename handle to identifier for consistency
Browse files Browse the repository at this point in the history
  • Loading branch information
dahlia committed Sep 20, 2024
1 parent 448c7c5 commit 276c412
Show file tree
Hide file tree
Showing 29 changed files with 1,910 additions and 946 deletions.
66 changes: 66 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,72 @@ Version 1.0.0

To be released.

- The term `handle` for dispatching actors is deprecated in favor of
`identifier`.

- The URI template for the following methods now accepts variable
`{identifier}` instead of `{handle}`:

- `Federation.setActorDispatcher()`
- `Federation.setInboxDispatcher()`
- `Federation.setOutboxDispatcher()`
- `Federation.setFollowingDispatcher()`
- `Federation.setFollowersDispatcher()`
- `Federation.setLikedDispatcher()`
- `Federation.setFeaturedDispatcher()`
- `Federation.setFeaturedTagsDispatcher()`
- `Federation.setInboxListeners()`

The `{handle}` variable is deprecated, and it will be removed in
the future.
- The type of `Federation.setActorDispatcher()` method's first parameter
became ```${string}{identifier}${string}` |
`${string}{handle}${string}``` (was ```${string}{handle}${string}```).
- The type of `Federation.setInboxDispatcher()` method's first parameter
became ```${string}{identifier}${string}` |
`${string}{handle}${string}``` (was ```${string}{handle}${string}```).
- The type of `Federation.setOutboxDispatcher()` method's first parameter
became ```${string}{identifier}${string}` |
`${string}{handle}${string}``` (was ```${string}{handle}${string}```).
- The type of `Federation.setFollowingDispatcher()` method's first
parameter became ```${string}{identifier}${string}` |
`${string}{handle}${string}``` (was ```${string}{handle}${string}```).
- The type of `Federation.setFollowersDispatcher()` method's first
parameter became ```${string}{identifier}${string}` |
`${string}{handle}${string}``` (was ```${string}{handle}${string}```).
- The type of `Federation.setLikedDispatcher()` method's first parameter
became ```${string}{identifier}${string}` |
`${string}{handle}${string}``` (was ```${string}{handle}${string}```).
- The type of `Federation.setFeaturedDispatcher()` method's first
parameter became ```${string}{identifier}${string}` |
`${string}{handle}${string}``` (was ```${string}{handle}${string}```).
- The type of `Federation.setFeaturedTagsDispatcher()` method's first
parameter became ```${string}{identifier}${string}` |
`${string}{handle}${string}``` (was ```${string}{handle}${string}```).
- The type of `Federation.setInboxListeners()` method's first parameter
became ```${string}{identifier}${string}` |
`${string}{handle}${string}``` (was ```${string}{handle}${string}```).
- The type of `Context.getDocumentLoader()` method's first parameter
became `{ identifier: string } | { username: string } | { handle:
string } | { keyId: URL; privateKey: CryptoKey }` (was `{ handle:
string } | { keyId: URL; privateKey: CryptoKey }`).
- Passing `{ handle: string }` to `Context.getDocumentLoader()` method is
deprecated in favor of `{ username: string }`.
- The type of `Context.sendActivity()` method's first parameter became
`SenderKeyPair | SenderKeyPair[] | { identifier: string } | {
username: string } | { handle: string }` (was `SenderKeyPair | SenderKeyPair[] | { handle: string }`).
- All properties of `ParseUriResult` type became readonly.
- Added `identifier` properties next to `handle` properties in
`ParseUriResult` type.
- The `handle` properties of `ParseUriResult` type are deprecated in favor
of `identifier` properties.
- The return type of `SharedInboxKeyDispatcher` callback type became
`SenderKeyPair | { identifier: string } | { username: string } |
{ handle: string } | null | Promise<SenderKeyPair | { identifier:
string } | { username: string } | { handle: string } | null>`
(was `SenderKeyPair | { handle: string } | null |
Promise<SenderKeyPair | { handle: string } | null>`).

- Fedify now supports [Linked Data Signatures], which is outdated but still
widely used in the fediverse.

Expand Down
65 changes: 35 additions & 30 deletions cli/inbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,9 @@ export const command = new Command()
}
if (options.follow != null && options.follow.length > 0) {
spinner.text = "Following actors...";
const documentLoader = await fedCtx.getDocumentLoader({ handle: "i" });
const documentLoader = await fedCtx.getDocumentLoader({
identifier: "i",
});
for (const uri of options.follow) {
spinner.text = `Following ${colors.green(uri)}...`;
const actor = await lookupObject(uri, { documentLoader });
Expand All @@ -107,7 +109,7 @@ export const command = new Command()
}
if (actor.id != null) peers[actor.id?.href] = actor;
await fedCtx.sendActivity(
{ handle: "i" },
{ identifier: "i" },
actor,
new Follow({
id: new URL(`#follows/${actor.id?.href}`, fedCtx.getActorUri("i")),
Expand All @@ -132,34 +134,34 @@ const time = Temporal.Now.instant();
let actorKeyPairs: CryptoKeyPair[] | undefined = undefined;

federation
.setActorDispatcher("/{handle}", async (ctx, handle) => {
if (handle !== "i") return null;
.setActorDispatcher("/{identifier}", async (ctx, identifier) => {
if (identifier !== "i") return null;
return new Application({
id: ctx.getActorUri(handle),
preferredUsername: handle,
id: ctx.getActorUri(identifier),
preferredUsername: identifier,
name: "Fedify Ephemeral Inbox",
summary: "An ephemeral ActivityPub inbox for testing purposes.",
inbox: ctx.getInboxUri(handle),
inbox: ctx.getInboxUri(identifier),
endpoints: new Endpoints({
sharedInbox: ctx.getInboxUri(),
}),
followers: ctx.getFollowersUri(handle),
following: ctx.getFollowingUri(handle),
outbox: ctx.getOutboxUri(handle),
followers: ctx.getFollowersUri(identifier),
following: ctx.getFollowingUri(identifier),
outbox: ctx.getOutboxUri(identifier),
manuallyApprovesFollowers: true,
published: time,
icon: new Image({
url: new URL("https://fedify.dev/logo.png"),
mediaType: "image/png",
}),
publicKey: (await ctx.getActorKeyPairs(handle))[0].cryptographicKey,
assertionMethods: (await ctx.getActorKeyPairs(handle))
publicKey: (await ctx.getActorKeyPairs(identifier))[0].cryptographicKey,
assertionMethods: (await ctx.getActorKeyPairs(identifier))
.map((pair) => pair.multikey),
url: ctx.getActorUri(handle),
url: ctx.getActorUri(identifier),
});
})
.setKeyPairsDispatcher(async (_ctxData, handle) => {
if (handle !== "i") return [];
.setKeyPairsDispatcher(async (_ctxData, identifier) => {
if (identifier !== "i") return [];
if (actorKeyPairs == null) {
actorKeyPairs = [
await generateCryptoKeyPair("RSASSA-PKCS1-v1_5"),
Expand Down Expand Up @@ -195,7 +197,7 @@ async function sendDeleteToPeers(server: TemporaryServer): Promise<void> {
const ctx = federation.createContext(server.url, -1);
const actorId = ctx.getActorUri("i");
await ctx.sendActivity(
{ handle: "i" },
{ identifier: "i" },
Object.values(peers),
new Delete({
id: new URL(`#delete`, actorId),
Expand All @@ -209,8 +211,8 @@ async function sendDeleteToPeers(server: TemporaryServer): Promise<void> {
const followers: Record<string, Actor> = {};

federation
.setInboxListeners("/{handle}/inbox", "/inbox")
.setSharedKeyDispatcher((_) => ({ handle: "i" }))
.setInboxListeners("/{identifier}/inbox", "/inbox")
.setSharedKeyDispatcher((_) => ({ identifier: "i" }))
.on(Activity, async (ctx, activity) => {
activities[ctx.data].activity = activity;
for await (const actor of activity.getActors()) {
Expand All @@ -224,8 +226,8 @@ federation
const objectId = activity.objectId;
if (objectId == null) return;
const parsed = ctx.parseUri(objectId);
if (parsed?.type !== "actor" || parsed.handle !== "i") return;
const { handle } = parsed;
if (parsed?.type !== "actor" || parsed.identifier !== "i") return;
const { identifier } = parsed;
const follower = await activity.getActor();
if (!isActor(follower)) return;
const accepts = await acceptsFollowFrom(follower);
Expand All @@ -240,39 +242,42 @@ federation
});
followers[activity.id.href] = follower;
await ctx.sendActivity(
{ handle },
{ identifier },
follower,
new Accept({
id: new URL(`#accepts/${follower.id?.href}`, ctx.getActorUri("i")),
actor: ctx.getActorUri(handle),
actor: ctx.getActorUri(identifier),
object: activity.id,
}),
);
}
});

federation
.setFollowersDispatcher("/{handle}/followers", (_ctx, handle) => {
if (handle !== "i") return null;
.setFollowersDispatcher("/{identifier}/followers", (_ctx, identifier) => {
if (identifier !== "i") return null;
const items: Recipient[] = [];
for (const follower of Object.values(followers)) {
if (follower.id == null) continue;
items.push(follower);
}
return { items };
})
.setCounter((_ctx, handle) => {
if (handle !== "i") return null;
.setCounter((_ctx, identifier) => {
if (identifier !== "i") return null;
return Object.keys(followers).length;
});

federation
.setFollowingDispatcher("/{handle}/following", (_ctx, _handle) => null)
.setCounter((_ctx, _handle) => 0);
.setFollowingDispatcher(
"/{identifier}/following",
(_ctx, _identifier) => null,
)
.setCounter((_ctx, _identifier) => 0);

federation
.setOutboxDispatcher("/{handle}/outbox", (_ctx, _handle) => null)
.setCounter((_ctx, _handle) => 0);
.setOutboxDispatcher("/{identifier}/outbox", (_ctx, _identifier) => null)
.setCounter((_ctx, _identifier) => 0);

federation.setNodeInfoDispatcher("/nodeinfo/2.1", (_ctx) => {
return {
Expand Down
8 changes: 4 additions & 4 deletions cli/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -845,11 +845,11 @@ const federation = createFederation({
queue: ${mqDesc.object},
});
federation.setActorDispatcher("/users/{handle}", async (ctx, handle) => {
federation.setActorDispatcher("/users/{identifier}", async (ctx, identifier) => {
return new Person({
id: ctx.getActorUri(handle),
preferredUsername: handle,
name: handle,
id: ctx.getActorUri(identifier),
preferredUsername: identifier,
name: identifier,
});
});
Expand Down
30 changes: 16 additions & 14 deletions docs/manual/access-control.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,24 +42,24 @@ import type { Actor, Federation } from "@fedify/fedify";
const federation = null as unknown as Federation<void>;
/**
* A hypothetical function that checks if the user blocks the actor.
* @param handle The handle of the user to check if the actor is blocked.
* @param userId The ID of the user to check if the actor is blocked.
* @param signedKeyOwner The actor who signed the request.
* @returns `true` if the actor is blocked; otherwise, `false`.
*/
async function isBlocked(handle: string, signedKeyOwner: Actor): Promise<boolean> {
async function isBlocked(userId: string, signedKeyOwner: Actor): Promise<boolean> {
return false;
}
// ---cut-before---
import { federation } from "./your-federation.ts";
import { isBlocked } from "./your-blocklist.ts";
federation
.setActorDispatcher("/users/{handle}", async (ctx, handle) => {
.setActorDispatcher("/users/{identifier}", async (ctx, identifier) => {
// Omitted for brevity; see the related section for details.
})
.authorize(async (ctx, handle, signedKey, signedKeyOwner) => {
.authorize(async (ctx, identifier, signedKey, signedKeyOwner) => {
if (signedKeyOwner == null) return false;
return !await isBlocked(handle, signedKeyOwner);
return !await isBlocked(identifier, signedKeyOwner);
});
~~~~

Expand All @@ -74,24 +74,24 @@ import type { Actor, Federation } from "@fedify/fedify";
const federation = null as unknown as Federation<void>;
/**
* A hypothetical function that checks if the user blocks the actor.
* @param handle The handle of the user to check if the actor is blocked.
* @param userId The ID of the user to check if the actor is blocked.
* @param signedKeyOwner The actor who signed the request.
* @returns `true` if the actor is blocked; otherwise, `false`.
*/
async function isBlocked(handle: string, signedKeyOwner: Actor): Promise<boolean> {
async function isBlocked(userId: string, signedKeyOwner: Actor): Promise<boolean> {
return false;
}
// ---cut-before---
import { federation } from "./your-federation.ts";
import { isBlocked } from "./your-blocklist.ts";
federation
.setOutboxDispatcher("/users/{handle}/outbox", async (ctx, handle) => {
.setOutboxDispatcher("/users/{identifier}/outbox", async (ctx, identifier) => {
// Omitted for brevity; see the related section for details.
})
.authorize(async (ctx, handle, signedKey, signedKeyOwner) => {
.authorize(async (ctx, identifier, signedKey, signedKeyOwner) => {
if (signedKeyOwner == null) return false;
return !await isBlocked(handle, signedKeyOwner);
return !await isBlocked(identifier, signedKeyOwner);
});
~~~~

Expand Down Expand Up @@ -126,10 +126,10 @@ interface Post {
}
/**
* A hypothetical function that gets posts from the database.
* @param handle The handle of the user to get posts.
* @param userId The ID of the user to get posts.
* @returns The posts of the user.
*/
async function getPosts(handle: string): Promise<Post[]> {
async function getPosts(userId: string): Promise<Post[]> {
return [];
}
/**
Expand All @@ -145,8 +145,8 @@ import { federation } from "./your-federation.ts";
import { getPosts, toCreate } from "./your-model.ts";
federation
.setOutboxDispatcher("/users/{handle}/outbox", async (ctx, handle) => {
const posts = await getPosts(handle); // Get posts from the database
.setOutboxDispatcher("/users/{identifier}/outbox", async (ctx, identifier) => {
const posts = await getPosts(identifier); // Get posts from the database
const keyOwner = await ctx.getSignedKeyOwner(); // Get the actor who signed the request
if (keyOwner == null) return { items: [] }; // Return an empty array if the actor is not found
const items = posts
Expand All @@ -155,3 +155,5 @@ federation
return { items };
});
~~~~

<!-- cSpell: ignore blocklist -->
Loading

0 comments on commit 276c412

Please sign in to comment.