From 7c2a0f207da37fa1e9a7b1cb13e46b8baf90fd21 Mon Sep 17 00:00:00 2001 From: Sven Sauleau Date: Mon, 20 Feb 2023 16:57:39 +0000 Subject: [PATCH] use Actor properties for inbox URL Previously the inbox URL would be guessed from Mastodon URL scheme, however this doesn't work with non Mastodon Actors. This change relies on Actor's properties to get the inbox URL, ie the proper way. --- backend/src/activitypub/actors/index.ts | 49 ++++++++++++++----------- backend/src/activitypub/objects/note.ts | 3 +- backend/test/activitypub.spec.ts | 8 +++- 3 files changed, 36 insertions(+), 24 deletions(-) diff --git a/backend/src/activitypub/actors/index.ts b/backend/src/activitypub/actors/index.ts index 2ec74ef..3f5a04d 100644 --- a/backend/src/activitypub/actors/index.ts +++ b/backend/src/activitypub/actors/index.ts @@ -11,22 +11,6 @@ export function actorURL(domain: string, id: string): URL { return new URL(`/ap/users/${id}`, 'https://' + domain) } -function inboxURL(id: URL): URL { - return new URL(id + '/inbox') -} - -function outboxURL(id: URL): URL { - return new URL(id + '/outbox') -} - -function followingURL(id: URL): URL { - return new URL(id + '/following') -} - -export function followersURL(id: URL): URL { - return new URL(id + '/followers') -} - // https://www.w3.org/TR/activitystreams-vocabulary/#actor-types export interface Actor extends APObject { inbox: URL @@ -143,8 +127,14 @@ type PersonProperties = { icon?: { url: string } image?: { url: string } preferredUsername?: string + + inbox?: string + outbox?: string + following?: string + followers?: string } +// Create a local user export async function createPerson( domain: string, db: D1Database, @@ -178,6 +168,23 @@ export async function createPerson( } const id = actorURL(domain, properties.preferredUsername).toString() + + if (properties.inbox === undefined) { + properties.inbox = id + '/inbox' + } + + if (properties.outbox === undefined) { + properties.outbox = id + '/outbox' + } + + if (properties.following === undefined) { + properties.following = id + '/following' + } + + if (properties.followers === undefined) { + properties.followers = id + '/followers' + } + const row = await db .prepare( ` @@ -256,6 +263,10 @@ export function personFromRow(row: any): Person { domain = new URL(row.original_actor_id).hostname } + if (properties.inbox === undefined) { + console.warn('malformed Actor: missing inbox') + } + return { // Hidden values [emailSymbol]: row.email, @@ -270,11 +281,7 @@ export function personFromRow(row: any): Person { type: PERSON, id, published: new Date(row.cdate).toISOString(), - inbox: inboxURL(row.id), - outbox: outboxURL(row.id), - following: followingURL(row.id), - followers: followersURL(row.id), url: new URL('@' + preferredUsername, 'https://' + domain), - } as Person + } as unknown as Person } diff --git a/backend/src/activitypub/objects/note.ts b/backend/src/activitypub/objects/note.ts index 1986bbc..0fc92b0 100644 --- a/backend/src/activitypub/objects/note.ts +++ b/backend/src/activitypub/objects/note.ts @@ -2,7 +2,6 @@ import type { Actor } from 'wildebeest/backend/src/activitypub/actors' import type { Link } from 'wildebeest/backend/src/activitypub/objects/link' -import { followersURL } from 'wildebeest/backend/src/activitypub/actors' import { PUBLIC_GROUP } from 'wildebeest/backend/src/activitypub/activities' import * as objects from '.' @@ -36,7 +35,7 @@ export async function createPublicNote( attributedTo: actorId, content, to: [PUBLIC_GROUP], - cc: [followersURL(actorId)], + cc: [actor.followers.toString()], // FIXME: stub values replies: null, diff --git a/backend/test/activitypub.spec.ts b/backend/test/activitypub.spec.ts index 47a460c..6a46173 100644 --- a/backend/test/activitypub.spec.ts +++ b/backend/test/activitypub.spec.ts @@ -31,7 +31,13 @@ describe('ActivityPub', () => { test('fetch user by id', async () => { const db = await makeDB() - const properties = { summary: 'test summary' } + const properties = { + summary: 'test summary', + inbox: 'https://example.com/inbox', + outbox: 'https://example.com/outbox', + following: 'https://example.com/following', + followers: 'https://example.com/followers', + } const pubKey = '-----BEGIN PUBLIC KEY-----MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEApnI8FHJQXqqAdM87YwVseRUqbNLiw8nQ0zHBUyLylzaORhI4LfW4ozguiw8cWYgMbCufXMoITVmdyeTMGbQ3Q1sfQEcEjOZZXEeCCocmnYjK6MFSspjFyNw6GP0a5A/tt1tAcSlgALv8sg1RqMhSE5Kv+6lSblAYXcIzff7T2jh9EASnimaoAAJMaRH37+HqSNrouCxEArcOFhmFETadXsv+bHZMozEFmwYSTugadr4WD3tZd+ONNeimX7XZ3+QinMzFGOW19ioVHyjt3yCDU1cPvZIDR17dyEjByNvx/4N4Zly7puwBn6Ixy/GkIh5BWtL5VOFDJm/S+zcf1G1WsOAXMwKL4Nc5UWKfTB7Wd6voId7vF7nI1QYcOnoyh0GqXWhTPMQrzie4nVnUrBedxW0s/0vRXeR63vTnh5JrTVu06JGiU2pq2kvwqoui5VU6rtdImITybJ8xRkAQ2jo4FbbkS6t49PORIuivxjS9wPl7vWYazZtDVa5g/5eL7PnxOG3HsdIJWbGEh1CsG83TU9burHIepxXuQ+JqaSiKdCVc8CUiO++acUqKp7lmbYR9E/wRmvxXDFkxCZzA0UL2mRoLLLOe4aHvRSTsqiHC5Wwxyew5bb+eseJz3wovid9ZSt/tfeMAkCDmaCxEK+LGEbJ9Ik8ihis8Esm21N0A54sCAwEAAQ==-----END PUBLIC KEY-----' await db