Skip to content

Commit

Permalink
Follow actor on migration
Browse files Browse the repository at this point in the history
Follow the "moving from" actor to force Mastodon to deliver the Move
activity.
  • Loading branch information
xtuc committed Mar 27, 2023
1 parent cd423b8 commit 6fad405
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 4 deletions.
13 changes: 12 additions & 1 deletion backend/src/accounts/alias.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { setActorAlias } from 'wildebeest/backend/src/activitypub/actors'
import { deliverToActor } from 'wildebeest/backend/src/activitypub/deliver'
import { getSigningKey } from 'wildebeest/backend/src/mastodon/account'
import * as follow from 'wildebeest/backend/src/activitypub/activities/follow'
import type { Actor } from 'wildebeest/backend/src/activitypub/actors'
import { parseHandle } from 'wildebeest/backend/src/utils/parse'
import { queryAcct } from 'wildebeest/backend/src/webfinger'
import { type Database } from 'wildebeest/backend/src/database'

export async function addAlias(db: Database, alias: string, connectedActor: Actor) {
export async function addAlias(db: Database, alias: string, connectedActor: Actor, userKEK: string, domain: string) {
const handle = parseHandle(alias)
const acct = `${handle.localPart}@${handle.domain}`
if (handle.domain === null) {
Expand All @@ -17,4 +20,12 @@ export async function addAlias(db: Database, alias: string, connectedActor: Acto
}

await setActorAlias(db, connectedActor.id, actor.id)

// For Mastodon to deliver the Move Activity we need to be following the
// "moving from" actor.
{
const activity = follow.create(connectedActor, actor)
const signingKey = await getSigningKey(userKEK, db, connectedActor)
await deliverToActor(signingKey, connectedActor, actor, activity, domain)
}
}
2 changes: 2 additions & 0 deletions backend/src/activitypub/activities/handle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,8 @@ export async function handle(
break
}

// FIXME: Requires alsoKnownAs to be set in both directions

// move followers
{
const collection = await getMetadata(fromActor.followers)
Expand Down
16 changes: 15 additions & 1 deletion backend/test/wildebeest/settings.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ describe('Wildebeest', () => {
const db = await makeDB()
const actor = await createPerson(domain, db, userKEK, 'sven@cloudflare.com')

let receivedActivity: any = null

globalThis.fetch = async (input: RequestInfo) => {
if (input.toString() === 'https://example.com/.well-known/webfinger?resource=acct%3Atest%40example.com') {
return new Response(
Expand All @@ -32,21 +34,33 @@ describe('Wildebeest', () => {
JSON.stringify({
id: 'https://social.com/someone',
type: 'Person',
inbox: 'https://social.com/someone/inbox',
})
)
}

const request = new Request(input)
if (request.url === 'https://social.com/someone/inbox') {
assert.equal(request.method, 'POST')
const data = await request.json()
receivedActivity = data
return new Response('')
}

throw new Error('unexpected request to ' + input)
}

await alias.addAlias(db, 'test@example.com', actor)
await alias.addAlias(db, 'test@example.com', actor, userKEK, domain)

// Ensure the actor has the alias set
const newActor = await getActorById(db, actor.id)
assert(newActor)
assert(newActor.alsoKnownAs)
assert.equal(newActor.alsoKnownAs.length, 1)
assert.equal(newActor.alsoKnownAs[0], 'https://social.com/someone')

assert(receivedActivity)
assert.equal(receivedActivity.type, 'Follow')
})
})
})
6 changes: 4 additions & 2 deletions frontend/src/routes/(admin)/settings/(auth)/aliases/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,17 @@ const zodSchema = zod$({
alias: z.string().min(1),
})

export const action = action$(async (data, { platform, json }) => {
export const action = action$(async (data, { platform, json, request }) => {
const url = new URL(request.url)
const domain = url.hostname
const db = await getDatabase(platform)
const connectedActor = platform.data.connectedActor
if (connectedActor === null) {
throw json(500, { error: 'user not present in context' })
}

try {
await addAlias(db, data.alias, connectedActor)
await addAlias(db, data.alias, connectedActor, platform.userKEK, domain)
} catch (e: unknown) {
const error = e as { stack: string; cause: string }
console.error(error.stack, error.cause)
Expand Down

0 comments on commit 6fad405

Please sign in to comment.