From 1dc19ef422bbb3ee6b61a4d56b2b89df96a1b72e Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 25 Nov 2024 22:36:32 -0600 Subject: [PATCH 1/2] Support Bluesky's !no-unauthenticated self-label --- src/controllers/api/accounts.ts | 13 +++++++++++-- src/controllers/api/statuses.ts | 15 ++++++++++----- src/utils/api.ts | 14 ++++++++++++++ 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/controllers/api/accounts.ts b/src/controllers/api/accounts.ts index 1ae6a4f6..035a4837 100644 --- a/src/controllers/api/accounts.ts +++ b/src/controllers/api/accounts.ts @@ -9,7 +9,7 @@ import { booleanParamSchema, fileSchema } from '@/schema.ts'; import { Storages } from '@/storages.ts'; import { uploadFile } from '@/utils/upload.ts'; import { nostrNow } from '@/utils.ts'; -import { createEvent, paginated, parseBody, updateEvent, updateListEvent } from '@/utils/api.ts'; +import { assertAuthenticated, createEvent, paginated, parseBody, updateEvent, updateListEvent } from '@/utils/api.ts'; import { extractIdentifier, lookupAccount, lookupPubkey } from '@/utils/lookup.ts'; import { renderAccounts, renderEventAccounts, renderStatuses } from '@/views.ts'; import { accountFromPubkey, renderAccount } from '@/views/mastodon/accounts.ts'; @@ -82,6 +82,7 @@ const accountController: AppController = async (c) => { const event = await getAuthor(pubkey); if (event) { + assertAuthenticated(c, event); return c.json(await renderAccount(event)); } else { return c.json(await accountFromPubkey(pubkey)); @@ -204,7 +205,15 @@ const accountStatusesController: AppController = async (c) => { const store = await Storages.db(); - const [user] = await store.query([{ kinds: [30382], authors: [Conf.pubkey], '#d': [pubkey], limit: 1 }], { signal }); + const [[author], [user]] = await Promise.all([ + store.query([{ kinds: [0], authors: [pubkey], limit: 1 }], { signal }), + store.query([{ kinds: [30382], authors: [Conf.pubkey], '#d': [pubkey], limit: 1 }], { signal }), + ]); + + if (author) { + assertAuthenticated(c, author); + } + const names = getTagSet(user?.tags ?? [], 'n'); if (names.has('disabled')) { diff --git a/src/controllers/api/statuses.ts b/src/controllers/api/statuses.ts index 8e66f7cb..98337777 100644 --- a/src/controllers/api/statuses.ts +++ b/src/controllers/api/statuses.ts @@ -16,7 +16,7 @@ import { lookupPubkey } from '@/utils/lookup.ts'; import { languageSchema } from '@/schema.ts'; import { Storages } from '@/storages.ts'; import { hydrateEvents } from '@/storages/hydrate.ts'; -import { createEvent, paginated, paginatedList, parseBody, updateListEvent } from '@/utils/api.ts'; +import { assertAuthenticated, createEvent, paginated, paginatedList, parseBody, updateListEvent } from '@/utils/api.ts'; import { getInvoice, getLnurl } from '@/utils/lnurl.ts'; import { purifyEvent } from '@/utils/purify.ts'; import { getZapSplits } from '@/utils/zap-split.ts'; @@ -48,13 +48,18 @@ const createStatusSchema = z.object({ const statusController: AppController = async (c) => { const id = c.req.param('id'); + const signal = AbortSignal.any([c.req.raw.signal, AbortSignal.timeout(1500)]); - const event = await getEvent(id, { - signal: AbortSignal.timeout(1500), - }); + const event = await getEvent(id, { signal }); + + if (event?.author) { + assertAuthenticated(c, event.author); + } if (event) { - return c.json(await renderStatus(event, { viewerPubkey: await c.get('signer')?.getPublicKey() })); + const viewerPubkey = await c.get('signer')?.getPublicKey(); + const status = await renderStatus(event, { viewerPubkey }); + return c.json(status); } return c.json({ error: 'Event not found.' }, 404); diff --git a/src/utils/api.ts b/src/utils/api.ts index 829a2cce..e47779d2 100644 --- a/src/utils/api.ts +++ b/src/utils/api.ts @@ -286,8 +286,22 @@ function localRequest(c: Context): Request { }); } +/** Actors with Bluesky's `!no-unauthenticated` self-label should require authorization to view. */ +function assertAuthenticated(c: AppContext, author: NostrEvent): void { + if ( + !c.get('signer') && author.tags.some(([name, value, ns]) => + name === 'l' && + value === '!no-unauthenticated' && + ns === 'com.atproto.label.defs#selfLabels' + ) + ) { + throw new HTTPException(401, { message: 'Sign-in required.' }); + } +} + export { activityJson, + assertAuthenticated, createAdminEvent, createEvent, type EventStub, From 4ed66b8fe597157fc0e26b9505a3f9938d30d08b Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 25 Nov 2024 22:44:14 -0600 Subject: [PATCH 2/2] accountLookupController: support !no-unauthenticated --- src/controllers/api/accounts.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/controllers/api/accounts.ts b/src/controllers/api/accounts.ts index 035a4837..d5beac03 100644 --- a/src/controllers/api/accounts.ts +++ b/src/controllers/api/accounts.ts @@ -98,6 +98,7 @@ const accountLookupController: AppController = async (c) => { const event = await lookupAccount(decodeURIComponent(acct)); if (event) { + assertAuthenticated(c, event); return c.json(await renderAccount(event)); } try {