From 781b46050f5581db9aa38f617c609769d1ed049c Mon Sep 17 00:00:00 2001 From: kelvin Date: Thu, 2 Mar 2023 00:42:08 +0900 Subject: [PATCH 01/10] allow website to be undefined --- backend/src/mastodon/client.ts | 6 +++--- backend/test/mastodon/apps.spec.ts | 27 +++++++++++++++++++++++++++ backend/test/utils.ts | 2 +- functions/api/v1/apps.ts | 13 +++++++++++-- 4 files changed, 42 insertions(+), 6 deletions(-) diff --git a/backend/src/mastodon/client.ts b/backend/src/mastodon/client.ts index 2c2df3d..db8472b 100644 --- a/backend/src/mastodon/client.ts +++ b/backend/src/mastodon/client.ts @@ -6,16 +6,16 @@ export interface Client { secret: string name: string redirect_uris: string - website: string scopes: string + website?: string } export async function createClient( db: Database, name: string, redirect_uris: string, - website: string, - scopes: string + scopes: string, + website?: string ): Promise { const id = crypto.randomUUID() diff --git a/backend/test/mastodon/apps.spec.ts b/backend/test/mastodon/apps.spec.ts index 998a2fa..17896f5 100644 --- a/backend/test/mastodon/apps.spec.ts +++ b/backend/test/mastodon/apps.spec.ts @@ -36,6 +36,33 @@ describe('Mastodon APIs', () => { assert.deepEqual(rest, {}) }) + test('POST /apps registers client without website', async () => { + const db = await makeDB() + const vapidKeys = await generateVAPIDKeys() + const request = new Request('https://example.com', { + method: 'POST', + body: '{"redirect_uris":"mastodon://example.com/oauth","client_name":"Example mastodon client","scopes":"read write follow push"}', + headers: { + 'content-type': 'application/json', + }, + }) + + const res = await apps.handleRequest(db, request, vapidKeys) + assert.equal(res.status, 200) + assertCORS(res) + assertJSON(res) + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { name, redirect_uri, client_id, client_secret, vapid_key, id, ...rest } = await res.json< + Record + >() + + assert.equal(name, 'Example mastodon client') + assert.equal(redirect_uri, 'mastodon://example.com/oauth') + assert.equal(id, '20') + assert.deepEqual(rest, {}) + }) + test('POST /apps returns 422 for malformed requests', async () => { // client_name and redirect_uris are required according to https://docs.joinmastodon.org/methods/apps/#form-data-parameters const db = await makeDB() diff --git a/backend/test/utils.ts b/backend/test/utils.ts index 7220ca7..418607e 100644 --- a/backend/test/utils.ts +++ b/backend/test/utils.ts @@ -73,7 +73,7 @@ export async function createTestClient( redirectUri: string = 'https://localhost', scopes: string = 'read follow' ): Promise { - return createClient(db, 'test client', redirectUri, 'https://cloudflare.com', scopes) + return createClient(db, 'test client', redirectUri, scopes, 'https://cloudflare.com') } type TestQueue = Queue & { messages: Array } diff --git a/functions/api/v1/apps.ts b/functions/api/v1/apps.ts index 32d9f17..ef19120 100644 --- a/functions/api/v1/apps.ts +++ b/functions/api/v1/apps.ts @@ -11,7 +11,7 @@ import { type Database, getDatabase } from 'wildebeest/backend/src/database' type AppsPost = { redirect_uris: string - website: string + website?: string client_name: string scopes: string } @@ -42,9 +42,18 @@ export async function handleRequest(db: Database, request: Request, vapidKeys: J } catch { return errors.unprocessableEntity('redirect_uris must be a valid URI') } + } else if (body.website) { + if (body.website.length > 2000) { + return errors.unprocessableEntity('website cannot exceed 2000 characters') + } + try { + new URL('', body.website) + } catch { + return errors.unprocessableEntity('website is invalid URI') + } } - const client = await createClient(db, body.client_name, body.redirect_uris, body.website, body.scopes) + const client = await createClient(db, body.client_name, body.redirect_uris, body.scopes, body.website) const vapidKey = VAPIDPublicKey(vapidKeys) const res = { From bfdbc16dba3a039dcd0c5e5afc37c943e7e007d6 Mon Sep 17 00:00:00 2001 From: kelvin Date: Thu, 2 Mar 2023 01:58:35 +0900 Subject: [PATCH 02/10] fix query --- backend/src/mastodon/client.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/backend/src/mastodon/client.ts b/backend/src/mastodon/client.ts index db8472b..1f96606 100644 --- a/backend/src/mastodon/client.ts +++ b/backend/src/mastodon/client.ts @@ -28,7 +28,10 @@ export async function createClient( INSERT INTO clients (id, secret, name, redirect_uris, website, scopes) VALUES (?, ?, ?, ?, ?, ?) ` - const { success, error } = await db.prepare(query).bind(id, secret, name, redirect_uris, website, scopes).run() + const { success, error } = await db + .prepare(query) + .bind(id, secret, name, redirect_uris, website === undefined ? null : website, scopes) + .run() if (!success) { throw new Error('SQL error: ' + error) } From acd4a5e5b2a80f643db1bf1bc18f419a7e25518c Mon Sep 17 00:00:00 2001 From: Sven Sauleau Date: Mon, 6 Mar 2023 09:01:26 +0000 Subject: [PATCH 03/10] mastodon -> wildebeest --- frontend/src/routes/(frontend)/about/index.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/frontend/src/routes/(frontend)/about/index.tsx b/frontend/src/routes/(frontend)/about/index.tsx index fe9164d..b370b24 100644 --- a/frontend/src/routes/(frontend)/about/index.tsx +++ b/frontend/src/routes/(frontend)/about/index.tsx @@ -84,9 +84,13 @@ export default component$(() => {

- Decentralised social media powered by{' '} - - Mastodon + Decentralized social network powered by{' '} + + Wildebeest

@@ -142,7 +146,7 @@ export const head: DocumentHead = ({ resolveValue, head }) => { return getDocumentHead( { title: `About - ${instance.title}`, - description: `About page for the ${instance.title} Mastodon instance`, + description: `About page for the ${instance.title}`, og: { type: 'website', image: instance.thumbnail, From 41a9d11634089dffff1e268e1b43e406f4b8af78 Mon Sep 17 00:00:00 2001 From: CyberFlame Date: Mon, 6 Mar 2023 23:03:09 +1300 Subject: [PATCH 04/10] [minor] get rid of 'the' --- frontend/src/routes/(frontend)/about/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/routes/(frontend)/about/index.tsx b/frontend/src/routes/(frontend)/about/index.tsx index b370b24..a6fda02 100644 --- a/frontend/src/routes/(frontend)/about/index.tsx +++ b/frontend/src/routes/(frontend)/about/index.tsx @@ -146,7 +146,7 @@ export const head: DocumentHead = ({ resolveValue, head }) => { return getDocumentHead( { title: `About - ${instance.title}`, - description: `About page for the ${instance.title}`, + description: `About page for ${instance.title}`, og: { type: 'website', image: instance.thumbnail, From db7f1bf13ab13d39fc60e0f616a21eb85dd6c543 Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Mon, 6 Mar 2023 10:08:15 +0000 Subject: [PATCH 05/10] add missing auth loader to settings page --- frontend/src/routes/(admin)/settings/layout.tsx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/frontend/src/routes/(admin)/settings/layout.tsx b/frontend/src/routes/(admin)/settings/layout.tsx index db131f0..7b3b1ce 100644 --- a/frontend/src/routes/(admin)/settings/layout.tsx +++ b/frontend/src/routes/(admin)/settings/layout.tsx @@ -1,5 +1,19 @@ import { component$, Slot } from '@builder.io/qwik' +import { loader$ } from '@builder.io/qwik-city' +import { getDatabase } from 'wildebeest/backend/src/database' import { WildebeestLogo } from '~/components/MastodonLogo' +import { getErrorHtml } from '~/utils/getErrorHtml/getErrorHtml' +import { isUserAdmin } from '~/utils/isUserAdmin' + +export const authLoader = loader$(async ({ cookie, platform, html }) => { + const database = await getDatabase(platform) + const jwt = cookie.get('CF_Authorization')?.value ?? '' + const isAdmin = await isUserAdmin(jwt, database) + + if (!isAdmin) { + return html(401, getErrorHtml("You're unauthorized to view this page")) + } +}) export default component$(() => { return ( From d505ed278e2cb80a7980132ab4e1ff3196476c47 Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Mon, 6 Mar 2023 11:14:10 +0000 Subject: [PATCH 06/10] add access check to isUserAdmin function --- .../routes/(admin)/settings/aliases/index.tsx | 5 +++-- frontend/src/routes/(admin)/settings/layout.tsx | 8 +++++--- .../routes/(admin)/settings/migration/index.tsx | 13 ------------- frontend/src/routes/layout.tsx | 5 +++-- frontend/src/utils/checkAuth.ts | 15 ++++----------- frontend/src/utils/isUserAdmin.ts | 17 +++++++++++++++-- functions/api/wb/settings/server/rules.ts | 14 +++++++------- functions/api/wb/settings/server/server.ts | 8 ++++---- functions/oauth/authorize.ts | 13 +++++-------- 9 files changed, 46 insertions(+), 52 deletions(-) diff --git a/frontend/src/routes/(admin)/settings/aliases/index.tsx b/frontend/src/routes/(admin)/settings/aliases/index.tsx index cb0863c..dd5cc46 100644 --- a/frontend/src/routes/(admin)/settings/aliases/index.tsx +++ b/frontend/src/routes/(admin)/settings/aliases/index.tsx @@ -2,8 +2,9 @@ import { component$, useStore, useSignal, $ } from '@builder.io/qwik' import { loader$ } from '@builder.io/qwik-city' import { checkAuth } from '~/utils/checkAuth' -export const loader = loader$(async ({ request, platform, redirect }) => { - const isAuthorized = await checkAuth(request, platform) +export const loader = loader$(async ({ cookie, request, platform, redirect }) => { + const jwt = cookie.get('CF_Authorization')?.value ?? '' + const isAuthorized = await checkAuth(request, jwt, platform.ACCESS_AUTH_DOMAIN, platform.ACCESS_AUD) if (!isAuthorized) { redirect(303, '/explore') diff --git a/frontend/src/routes/(admin)/settings/layout.tsx b/frontend/src/routes/(admin)/settings/layout.tsx index 7b3b1ce..a56cdd1 100644 --- a/frontend/src/routes/(admin)/settings/layout.tsx +++ b/frontend/src/routes/(admin)/settings/layout.tsx @@ -1,14 +1,16 @@ import { component$, Slot } from '@builder.io/qwik' import { loader$ } from '@builder.io/qwik-city' +import { parse } from 'cookie' import { getDatabase } from 'wildebeest/backend/src/database' import { WildebeestLogo } from '~/components/MastodonLogo' import { getErrorHtml } from '~/utils/getErrorHtml/getErrorHtml' import { isUserAdmin } from '~/utils/isUserAdmin' -export const authLoader = loader$(async ({ cookie, platform, html }) => { +export const authLoader = loader$(async ({ request, platform, html }) => { const database = await getDatabase(platform) - const jwt = cookie.get('CF_Authorization')?.value ?? '' - const isAdmin = await isUserAdmin(jwt, database) + const cookie = parse(request.headers.get('Cookie') || '') + const jwtCookie = cookie.CF_Authorization ?? '' + const isAdmin = await isUserAdmin(request, jwtCookie, platform.ACCESS_AUTH_DOMAIN, platform.ACCESS_AUD, database) if (!isAdmin) { return html(401, getErrorHtml("You're unauthorized to view this page")) diff --git a/frontend/src/routes/(admin)/settings/migration/index.tsx b/frontend/src/routes/(admin)/settings/migration/index.tsx index 6180b41..e1c0e6b 100644 --- a/frontend/src/routes/(admin)/settings/migration/index.tsx +++ b/frontend/src/routes/(admin)/settings/migration/index.tsx @@ -1,17 +1,4 @@ import { component$ } from '@builder.io/qwik' -import { loader$ } from '@builder.io/qwik-city' -// import { checkAuth } from '~/utils/checkAuth' - -export const loader = loader$(async ({ redirect }) => { - // Hiding this page for now - redirect(303, '/explore') - - // const isAuthorized = await checkAuth(request, platform) - - // if (!isAuthorized) { - // redirect(303, '/explore') - // } -}) export default component$(() => { return ( diff --git a/frontend/src/routes/layout.tsx b/frontend/src/routes/layout.tsx index e4027ac..3dba437 100644 --- a/frontend/src/routes/layout.tsx +++ b/frontend/src/routes/layout.tsx @@ -8,8 +8,9 @@ type AccessLoaderData = { isAuthorized: boolean } -export const accessLoader = loader$>(async ({ platform, request }) => { - const isAuthorized = await checkAuth(request, platform) +export const accessLoader = loader$>(async ({ platform, request, cookie }) => { + const jwt = cookie.get('CF_Authorization')?.value ?? '' + const isAuthorized = await checkAuth(request, jwt, platform.ACCESS_AUTH_DOMAIN, platform.ACCESS_AUD) return { isAuthorized, diff --git a/frontend/src/utils/checkAuth.ts b/frontend/src/utils/checkAuth.ts index 23fbe14..3e447b4 100644 --- a/frontend/src/utils/checkAuth.ts +++ b/frontend/src/utils/checkAuth.ts @@ -1,27 +1,20 @@ -import { RequestContext } from '@builder.io/qwik-city/middleware/request-handler' import * as access from 'wildebeest/backend/src/access' -type Env = { - ACCESS_AUTH_DOMAIN: string - ACCESS_AUD: string -} - -export const checkAuth = async (request: RequestContext, platform: Env) => { - const jwt = request.headers.get('Cf-Access-Jwt-Assertion') || '' +export const checkAuth = async (request: Request, jwt: string, accessAuthDomain: string, accessAud: string) => { if (!jwt) return false try { const validate = access.generateValidator({ jwt, - domain: platform.ACCESS_AUTH_DOMAIN, - aud: platform.ACCESS_AUD, + domain: accessAuthDomain, + aud: accessAud, }) await validate(new Request(request.url)) } catch { return false } - const identity = await access.getIdentity({ jwt, domain: platform.ACCESS_AUTH_DOMAIN }) + const identity = await access.getIdentity({ jwt, domain: accessAuthDomain }) if (identity) { return true } diff --git a/frontend/src/utils/isUserAdmin.ts b/frontend/src/utils/isUserAdmin.ts index a247d96..a14a1f2 100644 --- a/frontend/src/utils/isUserAdmin.ts +++ b/frontend/src/utils/isUserAdmin.ts @@ -1,12 +1,25 @@ import { emailSymbol } from 'wildebeest/backend/src/activitypub/actors' import { Database } from 'wildebeest/backend/src/database' import { getAdmins } from 'wildebeest/functions/api/wb/settings/server/admins' +import { checkAuth } from './checkAuth' import { getJwtEmail } from './getJwtEmail' -export async function isUserAdmin(jwtCookie: string, database: Database): Promise { +export async function isUserAdmin( + request: Request, + jwt: string, + accessAuthDomain: string, + accessAud: string, + database: Database +): Promise { let email: string + try { - email = getJwtEmail(jwtCookie) + const authenticated = await checkAuth(request, jwt, accessAuthDomain, accessAud) + if (!authenticated) { + return false + } + + email = getJwtEmail(jwt) } catch { return false } diff --git a/functions/api/wb/settings/server/rules.ts b/functions/api/wb/settings/server/rules.ts index 1a66b30..98e2bd3 100644 --- a/functions/api/wb/settings/server/rules.ts +++ b/functions/api/wb/settings/server/rules.ts @@ -2,11 +2,11 @@ import type { Env } from 'wildebeest/backend/src/types/env' import type { ContextData } from 'wildebeest/backend/src/types/context' import * as errors from 'wildebeest/backend/src/errors' import { type Database, getDatabase } from 'wildebeest/backend/src/database' -import { parse } from 'cookie' import { isUserAdmin } from 'wildebeest/frontend/src/utils/isUserAdmin' +import { parse } from 'cookie' export const onRequestGet: PagesFunction = async ({ env, request }) => { - return handleRequestPost(await getDatabase(env), request) + return handleRequestPost(await getDatabase(env), request, env.ACCESS_AUTH_DOMAIN, env.ACCESS_AUD) } export async function handleRequestGet(db: Database) { @@ -21,13 +21,13 @@ export async function handleRequestGet(db: Database) { } export const onRequestPost: PagesFunction = async ({ env, request }) => { - return handleRequestPost(await getDatabase(env), request) + return handleRequestPost(await getDatabase(env), request, env.ACCESS_AUTH_DOMAIN, env.ACCESS_AUD) } -export async function handleRequestPost(db: Database, request: Request) { +export async function handleRequestPost(db: Database, request: Request, accessAuthDomain: string, accessAud: string) { const cookie = parse(request.headers.get('Cookie') || '') const jwt = cookie['CF_Authorization'] - const isAdmin = await isUserAdmin(jwt, db) + const isAdmin = await isUserAdmin(request, jwt, accessAuthDomain, accessAud, db) if (!isAdmin) { return errors.notAuthorized('Lacking authorization rights to edit server rules') @@ -56,10 +56,10 @@ export async function upsertRule(db: Database, rule: { id?: number; text: string .run() } -export async function handleRequestDelete(db: Database, request: Request) { +export async function handleRequestDelete(db: Database, request: Request, accessAuthDomain: string, accessAud: string) { const cookie = parse(request.headers.get('Cookie') || '') const jwt = cookie['CF_Authorization'] - const isAdmin = await isUserAdmin(jwt, db) + const isAdmin = await isUserAdmin(request, jwt, accessAuthDomain, accessAud, db) if (!isAdmin) { return errors.notAuthorized('Lacking authorization rights to edit server rules') diff --git a/functions/api/wb/settings/server/server.ts b/functions/api/wb/settings/server/server.ts index f286d49..2e4b2f3 100644 --- a/functions/api/wb/settings/server/server.ts +++ b/functions/api/wb/settings/server/server.ts @@ -7,7 +7,7 @@ import { isUserAdmin } from 'wildebeest/frontend/src/utils/isUserAdmin' import { ServerSettingsData } from 'wildebeest/frontend/src/routes/(admin)/settings/server-settings/layout' export const onRequestGet: PagesFunction = async ({ env, request }) => { - return handleRequestPost(await getDatabase(env), request) + return handleRequestPost(await getDatabase(env), request, env.ACCESS_AUTH_DOMAIN, env.ACCESS_AUD) } export async function handleRequestGet(db: Database) { @@ -30,13 +30,13 @@ export async function handleRequestGet(db: Database) { } export const onRequestPost: PagesFunction = async ({ env, request }) => { - return handleRequestPost(await getDatabase(env), request) + return handleRequestPost(await getDatabase(env), request, env.ACCESS_AUTH_DOMAIN, env.ACCESS_AUD) } -export async function handleRequestPost(db: Database, request: Request) { +export async function handleRequestPost(db: Database, request: Request, accessAuthDomain: string, accessAud: string) { const cookie = parse(request.headers.get('Cookie') || '') const jwt = cookie['CF_Authorization'] - const isAdmin = await isUserAdmin(jwt, db) + const isAdmin = await isUserAdmin(request, jwt, accessAuthDomain, accessAud, db) if (!isAdmin) { return errors.notAuthorized('Lacking authorization rights to edit server settings') diff --git a/functions/oauth/authorize.ts b/functions/oauth/authorize.ts index f0ed5bd..f3ba56f 100644 --- a/functions/oauth/authorize.ts +++ b/functions/oauth/authorize.ts @@ -8,6 +8,7 @@ import { getClientById } from 'wildebeest/backend/src/mastodon/client' import * as access from 'wildebeest/backend/src/access' import { getPersonByEmail } from 'wildebeest/backend/src/activitypub/actors' import { type Database, getDatabase } from 'wildebeest/backend/src/database' +import { checkAuth } from 'wildebeest/frontend/src/utils/checkAuth' // Extract the JWT token sent by Access (running before us). const extractJWTFromRequest = (request: Request) => request.headers.get('Cf-Access-Jwt-Assertion') || '' @@ -79,18 +80,14 @@ export async function handleRequestPost( } const jwt = extractJWTFromRequest(request) - if (!jwt) { + const isAuthenticated = await checkAuth(request, jwt, accessDomain, accessAud) + + if (!isAuthenticated) { return new Response('', { status: 401 }) } - const validate = access.generateValidator({ jwt, domain: accessDomain, aud: accessAud }) - await validate(request) const identity = await access.getIdentity({ jwt, domain: accessDomain }) - if (!identity) { - return new Response('', { status: 401 }) - } - - const isFirstLogin = (await getPersonByEmail(db, identity.email)) === null + const isFirstLogin = (await getPersonByEmail(db, identity!.email)) === null return buildRedirect(db, request, isFirstLogin, jwt) } From 5704849170c7a4fb081b1cf76d61a953e77ce710 Mon Sep 17 00:00:00 2001 From: Sven Sauleau Date: Mon, 6 Mar 2023 16:24:39 +0000 Subject: [PATCH 07/10] use OAuth endpoint for UI --- .../layout/RightColumn/RightColumn.tsx | 10 +++++----- .../routes/(admin)/oauth/authorize/index.tsx | 2 +- frontend/src/routes/layout.tsx | 20 +++++++++++-------- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/frontend/src/components/layout/RightColumn/RightColumn.tsx b/frontend/src/components/layout/RightColumn/RightColumn.tsx index 73a8659..e7a12a8 100644 --- a/frontend/src/components/layout/RightColumn/RightColumn.tsx +++ b/frontend/src/components/layout/RightColumn/RightColumn.tsx @@ -1,7 +1,7 @@ import { component$ } from '@builder.io/qwik' import { Link, useLocation } from '@builder.io/qwik-city' import { WildebeestLogo } from '~/components/MastodonLogo' -import { accessLoader } from '~/routes/layout' +import { authLoader } from '~/routes/layout' type LinkConfig = { iconName: string @@ -11,7 +11,7 @@ type LinkConfig = { } export default component$(() => { - const accessData = accessLoader().value + const { isAuthorized, loginUrl } = authLoader().value const location = useLocation() const renderNavLink = ({ iconName, linkText, linkTarget, linkActiveRegex }: LinkConfig) => { @@ -55,15 +55,15 @@ export default component$(() => { {renderNavLink(aboutLink)} */} - {!accessData.isAuthorized && ( + {!isAuthorized && ( Sign in )} - {accessData.isAuthorized && ( + {isAuthorized && ( Preferences diff --git a/frontend/src/routes/(admin)/oauth/authorize/index.tsx b/frontend/src/routes/(admin)/oauth/authorize/index.tsx index 027ce5d..86f7d19 100644 --- a/frontend/src/routes/(admin)/oauth/authorize/index.tsx +++ b/frontend/src/routes/(admin)/oauth/authorize/index.tsx @@ -21,7 +21,7 @@ export const clientLoader = loader$>(async ({ platform, query, h throw html(500, getErrorHtml('An error occurred while trying to fetch the client data, please try again later')) } if (client === null) { - throw new Error('client not found') + throw html(500, getErrorHtml('client not found')) } return client }) diff --git a/frontend/src/routes/layout.tsx b/frontend/src/routes/layout.tsx index 3dba437..8ad88c9 100644 --- a/frontend/src/routes/layout.tsx +++ b/frontend/src/routes/layout.tsx @@ -1,24 +1,28 @@ import { component$, Slot } from '@builder.io/qwik' import { loader$ } from '@builder.io/qwik-city' -import * as access from 'wildebeest/backend/src/access' import { checkAuth } from '~/utils/checkAuth' -type AccessLoaderData = { +type AuthLoaderData = { loginUrl: string isAuthorized: boolean } -export const accessLoader = loader$>(async ({ platform, request, cookie }) => { +export const authLoader = loader$>(async ({ platform, request, cookie }) => { const jwt = cookie.get('CF_Authorization')?.value ?? '' const isAuthorized = await checkAuth(request, jwt, platform.ACCESS_AUTH_DOMAIN, platform.ACCESS_AUD) + // FIXME(sven): remove hardcoded value + const UI_CLIENT_ID = '924801be-d211-495d-8cac-e73503413af8' + const params = new URLSearchParams({ + redirect_uri: request.url, + response_type: 'code', + client_id: UI_CLIENT_ID, + scope: 'all', + }) + const loginUrl = new URL('/oauth/authorize?' + params, 'https://' + platform.DOMAIN) return { isAuthorized, - loginUrl: access.generateLoginURL({ - redirectURL: request.url, - domain: platform.ACCESS_AUTH_DOMAIN, - aud: platform.ACCESS_AUD, - }), + loginUrl, } }) From f8206182f3b9eb5599e44309cfbd13580c0a8545 Mon Sep 17 00:00:00 2001 From: Sven Sauleau Date: Mon, 6 Mar 2023 16:47:21 +0000 Subject: [PATCH 08/10] fix SQL is_admin type The `is_admin` column is an `INTEGER`. While SQLite doesn't mind converting `TRUE` to an integer Postgresql is throwing an error. Switch to `1` which is consistent with the column type. --- functions/api/wb/settings/server/admins.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/api/wb/settings/server/admins.ts b/functions/api/wb/settings/server/admins.ts index 51a7dec..143b004 100644 --- a/functions/api/wb/settings/server/admins.ts +++ b/functions/api/wb/settings/server/admins.ts @@ -15,7 +15,7 @@ export async function handleRequestGet(db: Database) { export async function getAdmins(db: Database): Promise { let rows: unknown[] = [] try { - const stmt = db.prepare('SELECT * FROM actors WHERE is_admin=TRUE') + const stmt = db.prepare('SELECT * FROM actors WHERE is_admin=1') const result = await stmt.all() rows = result.success ? (result.results as unknown[]) : [] } catch { From 65c4924cdb29532e97631a22ee06ff413349668d Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Mon, 6 Mar 2023 18:25:30 +0000 Subject: [PATCH 09/10] improve/fix frontend authenticated vs admin settings --- .../src/utils/auth}/getJwtEmail.ts | 0 .../src/utils/auth}/isUserAdmin.ts | 6 +++--- .../src/utils/auth/isUserAuthenticated.ts | 2 +- .../routes/(admin)/oauth/authorize/index.tsx | 2 +- .../(admin)/settings/(admin)/layout.tsx | 11 +++++++++++ .../{ => (admin)}/migration/index.tsx | 0 .../server-settings/about/index.tsx | 4 +++- .../server-settings/branding/index.tsx | 4 +++- .../{ => (admin)}/server-settings/index.tsx | 0 .../{ => (admin)}/server-settings/layout.tsx | 0 .../server-settings/rules/edit/[id]/index.tsx | 0 .../server-settings/rules/index.tsx | 0 .../settings/{ => (auth)}/aliases/index.tsx | 11 ----------- .../routes/(admin)/settings/(auth)/layout.tsx | 11 +++++++++++ .../src/routes/(admin)/settings/layout.tsx | 16 ---------------- .../src/routes/(frontend)/about/index.tsx | 2 +- frontend/src/routes/layout.tsx | 6 +++--- frontend/src/utils/adminLoader.ts | 16 ++++++++++++++++ frontend/src/utils/authLoader.ts | 19 +++++++++++++++++++ functions/api/wb/settings/server/rules.ts | 2 +- functions/api/wb/settings/server/server.ts | 4 ++-- functions/first-login.ts | 2 +- functions/oauth/authorize.ts | 4 ++-- 23 files changed, 78 insertions(+), 44 deletions(-) rename {frontend/src/utils => backend/src/utils/auth}/getJwtEmail.ts (100%) rename {frontend/src/utils => backend/src/utils/auth}/isUserAdmin.ts (72%) rename frontend/src/utils/checkAuth.ts => backend/src/utils/auth/isUserAuthenticated.ts (76%) create mode 100644 frontend/src/routes/(admin)/settings/(admin)/layout.tsx rename frontend/src/routes/(admin)/settings/{ => (admin)}/migration/index.tsx (100%) rename frontend/src/routes/(admin)/settings/{ => (admin)}/server-settings/about/index.tsx (95%) rename frontend/src/routes/(admin)/settings/{ => (admin)}/server-settings/branding/index.tsx (95%) rename frontend/src/routes/(admin)/settings/{ => (admin)}/server-settings/index.tsx (100%) rename frontend/src/routes/(admin)/settings/{ => (admin)}/server-settings/layout.tsx (100%) rename frontend/src/routes/(admin)/settings/{ => (admin)}/server-settings/rules/edit/[id]/index.tsx (100%) rename frontend/src/routes/(admin)/settings/{ => (admin)}/server-settings/rules/index.tsx (100%) rename frontend/src/routes/(admin)/settings/{ => (auth)}/aliases/index.tsx (89%) create mode 100644 frontend/src/routes/(admin)/settings/(auth)/layout.tsx create mode 100644 frontend/src/utils/adminLoader.ts create mode 100644 frontend/src/utils/authLoader.ts diff --git a/frontend/src/utils/getJwtEmail.ts b/backend/src/utils/auth/getJwtEmail.ts similarity index 100% rename from frontend/src/utils/getJwtEmail.ts rename to backend/src/utils/auth/getJwtEmail.ts diff --git a/frontend/src/utils/isUserAdmin.ts b/backend/src/utils/auth/isUserAdmin.ts similarity index 72% rename from frontend/src/utils/isUserAdmin.ts rename to backend/src/utils/auth/isUserAdmin.ts index a14a1f2..bdecb2c 100644 --- a/frontend/src/utils/isUserAdmin.ts +++ b/backend/src/utils/auth/isUserAdmin.ts @@ -1,8 +1,8 @@ import { emailSymbol } from 'wildebeest/backend/src/activitypub/actors' import { Database } from 'wildebeest/backend/src/database' +import { getJwtEmail } from 'wildebeest/backend/src/utils/auth/getJwtEmail' import { getAdmins } from 'wildebeest/functions/api/wb/settings/server/admins' -import { checkAuth } from './checkAuth' -import { getJwtEmail } from './getJwtEmail' +import { isUserAuthenticated } from './isUserAuthenticated' export async function isUserAdmin( request: Request, @@ -14,7 +14,7 @@ export async function isUserAdmin( let email: string try { - const authenticated = await checkAuth(request, jwt, accessAuthDomain, accessAud) + const authenticated = await isUserAuthenticated(request, jwt, accessAuthDomain, accessAud) if (!authenticated) { return false } diff --git a/frontend/src/utils/checkAuth.ts b/backend/src/utils/auth/isUserAuthenticated.ts similarity index 76% rename from frontend/src/utils/checkAuth.ts rename to backend/src/utils/auth/isUserAuthenticated.ts index 3e447b4..190580d 100644 --- a/frontend/src/utils/checkAuth.ts +++ b/backend/src/utils/auth/isUserAuthenticated.ts @@ -1,6 +1,6 @@ import * as access from 'wildebeest/backend/src/access' -export const checkAuth = async (request: Request, jwt: string, accessAuthDomain: string, accessAud: string) => { +export async function isUserAuthenticated(request: Request, jwt: string, accessAuthDomain: string, accessAud: string) { if (!jwt) return false try { diff --git a/frontend/src/routes/(admin)/oauth/authorize/index.tsx b/frontend/src/routes/(admin)/oauth/authorize/index.tsx index 86f7d19..2c4e711 100644 --- a/frontend/src/routes/(admin)/oauth/authorize/index.tsx +++ b/frontend/src/routes/(admin)/oauth/authorize/index.tsx @@ -8,7 +8,7 @@ import { getPersonByEmail } from 'wildebeest/backend/src/activitypub/actors' import { getErrorHtml } from '~/utils/getErrorHtml/getErrorHtml' import { buildRedirect } from 'wildebeest/functions/oauth/authorize' import { getDatabase } from 'wildebeest/backend/src/database' -import { getJwtEmail } from '~/utils/getJwtEmail' +import { getJwtEmail } from 'wildebeest/backend/src/utils/auth/getJwtEmail' export const clientLoader = loader$>(async ({ platform, query, html }) => { const client_id = query.get('client_id') || '' diff --git a/frontend/src/routes/(admin)/settings/(admin)/layout.tsx b/frontend/src/routes/(admin)/settings/(admin)/layout.tsx new file mode 100644 index 0000000..5ee4079 --- /dev/null +++ b/frontend/src/routes/(admin)/settings/(admin)/layout.tsx @@ -0,0 +1,11 @@ +import { component$, Slot } from '@builder.io/qwik' + +export { adminLoader } from '~/utils/adminLoader' + +export default component$(() => { + return ( + <> + + + ) +}) diff --git a/frontend/src/routes/(admin)/settings/migration/index.tsx b/frontend/src/routes/(admin)/settings/(admin)/migration/index.tsx similarity index 100% rename from frontend/src/routes/(admin)/settings/migration/index.tsx rename to frontend/src/routes/(admin)/settings/(admin)/migration/index.tsx diff --git a/frontend/src/routes/(admin)/settings/server-settings/about/index.tsx b/frontend/src/routes/(admin)/settings/(admin)/server-settings/about/index.tsx similarity index 95% rename from frontend/src/routes/(admin)/settings/server-settings/about/index.tsx rename to frontend/src/routes/(admin)/settings/(admin)/server-settings/about/index.tsx index 4aad8cc..c5e515d 100644 --- a/frontend/src/routes/(admin)/settings/server-settings/about/index.tsx +++ b/frontend/src/routes/(admin)/settings/(admin)/server-settings/about/index.tsx @@ -17,7 +17,9 @@ export const action = action$(async (data, { request, platform }) => { try { const response = await handleRequestPost( await getDatabase(platform), - new Request(request, { body: JSON.stringify(data) }) + new Request(request, { body: JSON.stringify(data) }), + platform.ACCESS_AUTH_DOMAIN, + platform.ACCESS_AUD ) success = response.ok } catch (e: unknown) { diff --git a/frontend/src/routes/(admin)/settings/server-settings/branding/index.tsx b/frontend/src/routes/(admin)/settings/(admin)/server-settings/branding/index.tsx similarity index 95% rename from frontend/src/routes/(admin)/settings/server-settings/branding/index.tsx rename to frontend/src/routes/(admin)/settings/(admin)/server-settings/branding/index.tsx index 59c6059..a6e8961 100644 --- a/frontend/src/routes/(admin)/settings/server-settings/branding/index.tsx +++ b/frontend/src/routes/(admin)/settings/(admin)/server-settings/branding/index.tsx @@ -18,7 +18,9 @@ export const action = action$(async (data, { request, platform }) => { try { const response = await handleRequestPost( await getDatabase(platform), - new Request(request, { body: JSON.stringify(data) }) + new Request(request, { body: JSON.stringify(data) }), + platform.ACCESS_AUTH_DOMAIN, + platform.ACCESS_AUD ) success = response.ok } catch (e: unknown) { diff --git a/frontend/src/routes/(admin)/settings/server-settings/index.tsx b/frontend/src/routes/(admin)/settings/(admin)/server-settings/index.tsx similarity index 100% rename from frontend/src/routes/(admin)/settings/server-settings/index.tsx rename to frontend/src/routes/(admin)/settings/(admin)/server-settings/index.tsx diff --git a/frontend/src/routes/(admin)/settings/server-settings/layout.tsx b/frontend/src/routes/(admin)/settings/(admin)/server-settings/layout.tsx similarity index 100% rename from frontend/src/routes/(admin)/settings/server-settings/layout.tsx rename to frontend/src/routes/(admin)/settings/(admin)/server-settings/layout.tsx diff --git a/frontend/src/routes/(admin)/settings/server-settings/rules/edit/[id]/index.tsx b/frontend/src/routes/(admin)/settings/(admin)/server-settings/rules/edit/[id]/index.tsx similarity index 100% rename from frontend/src/routes/(admin)/settings/server-settings/rules/edit/[id]/index.tsx rename to frontend/src/routes/(admin)/settings/(admin)/server-settings/rules/edit/[id]/index.tsx diff --git a/frontend/src/routes/(admin)/settings/server-settings/rules/index.tsx b/frontend/src/routes/(admin)/settings/(admin)/server-settings/rules/index.tsx similarity index 100% rename from frontend/src/routes/(admin)/settings/server-settings/rules/index.tsx rename to frontend/src/routes/(admin)/settings/(admin)/server-settings/rules/index.tsx diff --git a/frontend/src/routes/(admin)/settings/aliases/index.tsx b/frontend/src/routes/(admin)/settings/(auth)/aliases/index.tsx similarity index 89% rename from frontend/src/routes/(admin)/settings/aliases/index.tsx rename to frontend/src/routes/(admin)/settings/(auth)/aliases/index.tsx index dd5cc46..6d24fea 100644 --- a/frontend/src/routes/(admin)/settings/aliases/index.tsx +++ b/frontend/src/routes/(admin)/settings/(auth)/aliases/index.tsx @@ -1,15 +1,4 @@ import { component$, useStore, useSignal, $ } from '@builder.io/qwik' -import { loader$ } from '@builder.io/qwik-city' -import { checkAuth } from '~/utils/checkAuth' - -export const loader = loader$(async ({ cookie, request, platform, redirect }) => { - const jwt = cookie.get('CF_Authorization')?.value ?? '' - const isAuthorized = await checkAuth(request, jwt, platform.ACCESS_AUTH_DOMAIN, platform.ACCESS_AUD) - - if (!isAuthorized) { - redirect(303, '/explore') - } -}) export default component$(() => { const ref = useSignal() diff --git a/frontend/src/routes/(admin)/settings/(auth)/layout.tsx b/frontend/src/routes/(admin)/settings/(auth)/layout.tsx new file mode 100644 index 0000000..9e4da41 --- /dev/null +++ b/frontend/src/routes/(admin)/settings/(auth)/layout.tsx @@ -0,0 +1,11 @@ +import { component$, Slot } from '@builder.io/qwik' + +export { authLoader } from '~/utils/authLoader' + +export default component$(() => { + return ( + <> + + + ) +}) diff --git a/frontend/src/routes/(admin)/settings/layout.tsx b/frontend/src/routes/(admin)/settings/layout.tsx index a56cdd1..db131f0 100644 --- a/frontend/src/routes/(admin)/settings/layout.tsx +++ b/frontend/src/routes/(admin)/settings/layout.tsx @@ -1,21 +1,5 @@ import { component$, Slot } from '@builder.io/qwik' -import { loader$ } from '@builder.io/qwik-city' -import { parse } from 'cookie' -import { getDatabase } from 'wildebeest/backend/src/database' import { WildebeestLogo } from '~/components/MastodonLogo' -import { getErrorHtml } from '~/utils/getErrorHtml/getErrorHtml' -import { isUserAdmin } from '~/utils/isUserAdmin' - -export const authLoader = loader$(async ({ request, platform, html }) => { - const database = await getDatabase(platform) - const cookie = parse(request.headers.get('Cookie') || '') - const jwtCookie = cookie.CF_Authorization ?? '' - const isAdmin = await isUserAdmin(request, jwtCookie, platform.ACCESS_AUTH_DOMAIN, platform.ACCESS_AUD, database) - - if (!isAdmin) { - return html(401, getErrorHtml("You're unauthorized to view this page")) - } -}) export default component$(() => { return ( diff --git a/frontend/src/routes/(frontend)/about/index.tsx b/frontend/src/routes/(frontend)/about/index.tsx index a6fda02..aafe8b3 100644 --- a/frontend/src/routes/(frontend)/about/index.tsx +++ b/frontend/src/routes/(frontend)/about/index.tsx @@ -6,7 +6,7 @@ import { handleRequestGet as settingsHandleRequestGet } from 'wildebeest/functio import { handleRequestGet as rulesHandleRequestGet } from 'wildebeest/functions/api/v1/instance/rules' import { Accordion } from '~/components/Accordion/Accordion' import { HtmlContent } from '~/components/HtmlContent/HtmlContent' -import { ServerSettingsData } from '~/routes/(admin)/settings/server-settings/layout' +import { ServerSettingsData } from '~/routes/(admin)/settings/(admin)/server-settings/layout' import { Account } from '~/types' import { getDocumentHead } from '~/utils/getDocumentHead' import { instanceLoader } from '../layout' diff --git a/frontend/src/routes/layout.tsx b/frontend/src/routes/layout.tsx index 8ad88c9..81b8e56 100644 --- a/frontend/src/routes/layout.tsx +++ b/frontend/src/routes/layout.tsx @@ -1,15 +1,15 @@ import { component$, Slot } from '@builder.io/qwik' import { loader$ } from '@builder.io/qwik-city' -import { checkAuth } from '~/utils/checkAuth' +import { isUserAuthenticated } from 'wildebeest/backend/src/utils/auth/isUserAuthenticated' type AuthLoaderData = { - loginUrl: string + loginUrl: URL isAuthorized: boolean } export const authLoader = loader$>(async ({ platform, request, cookie }) => { const jwt = cookie.get('CF_Authorization')?.value ?? '' - const isAuthorized = await checkAuth(request, jwt, platform.ACCESS_AUTH_DOMAIN, platform.ACCESS_AUD) + const isAuthorized = await isUserAuthenticated(request, jwt, platform.ACCESS_AUTH_DOMAIN, platform.ACCESS_AUD) // FIXME(sven): remove hardcoded value const UI_CLIENT_ID = '924801be-d211-495d-8cac-e73503413af8' const params = new URLSearchParams({ diff --git a/frontend/src/utils/adminLoader.ts b/frontend/src/utils/adminLoader.ts new file mode 100644 index 0000000..4ed3606 --- /dev/null +++ b/frontend/src/utils/adminLoader.ts @@ -0,0 +1,16 @@ +import { loader$ } from '@builder.io/qwik-city' +import { parse } from 'cookie' +import { getDatabase } from 'wildebeest/backend/src/database' +import { isUserAdmin } from 'wildebeest/backend/src/utils/auth/isUserAdmin' +import { getErrorHtml } from './getErrorHtml/getErrorHtml' + +export const adminLoader = loader$(async ({ request, platform, html }) => { + const database = await getDatabase(platform) + const cookie = parse(request.headers.get('Cookie') || '') + const jwtCookie = cookie.CF_Authorization ?? '' + const isAdmin = await isUserAdmin(request, jwtCookie, platform.ACCESS_AUTH_DOMAIN, platform.ACCESS_AUD, database) + + if (!isAdmin) { + return html(401, getErrorHtml('You need to be an admin to view this page')) + } +}) diff --git a/frontend/src/utils/authLoader.ts b/frontend/src/utils/authLoader.ts new file mode 100644 index 0000000..92d4374 --- /dev/null +++ b/frontend/src/utils/authLoader.ts @@ -0,0 +1,19 @@ +import { loader$ } from '@builder.io/qwik-city' +import { parse } from 'cookie' +import { isUserAuthenticated } from 'wildebeest/backend/src/utils/auth/isUserAuthenticated' +import { getErrorHtml } from './getErrorHtml/getErrorHtml' + +export const authLoader = loader$(async ({ request, platform, html }) => { + const cookie = parse(request.headers.get('Cookie') || '') + const jwtCookie = cookie.CF_Authorization ?? '' + const isAuthenticated = await isUserAuthenticated( + request, + jwtCookie, + platform.ACCESS_AUTH_DOMAIN, + platform.ACCESS_AUD + ) + + if (!isAuthenticated) { + return html(401, getErrorHtml("You're not authorized to view this page")) + } +}) diff --git a/functions/api/wb/settings/server/rules.ts b/functions/api/wb/settings/server/rules.ts index 98e2bd3..99fc679 100644 --- a/functions/api/wb/settings/server/rules.ts +++ b/functions/api/wb/settings/server/rules.ts @@ -2,8 +2,8 @@ import type { Env } from 'wildebeest/backend/src/types/env' import type { ContextData } from 'wildebeest/backend/src/types/context' import * as errors from 'wildebeest/backend/src/errors' import { type Database, getDatabase } from 'wildebeest/backend/src/database' -import { isUserAdmin } from 'wildebeest/frontend/src/utils/isUserAdmin' import { parse } from 'cookie' +import { isUserAdmin } from 'wildebeest/backend/src/utils/auth/isUserAdmin' export const onRequestGet: PagesFunction = async ({ env, request }) => { return handleRequestPost(await getDatabase(env), request, env.ACCESS_AUTH_DOMAIN, env.ACCESS_AUD) diff --git a/functions/api/wb/settings/server/server.ts b/functions/api/wb/settings/server/server.ts index 2e4b2f3..e81e839 100644 --- a/functions/api/wb/settings/server/server.ts +++ b/functions/api/wb/settings/server/server.ts @@ -3,8 +3,8 @@ import type { ContextData } from 'wildebeest/backend/src/types/context' import * as errors from 'wildebeest/backend/src/errors' import { type Database, getDatabase } from 'wildebeest/backend/src/database' import { parse } from 'cookie' -import { isUserAdmin } from 'wildebeest/frontend/src/utils/isUserAdmin' -import { ServerSettingsData } from 'wildebeest/frontend/src/routes/(admin)/settings/server-settings/layout' +import { ServerSettingsData } from 'wildebeest/frontend/src/routes/(admin)/settings/(admin)/server-settings/layout' +import { isUserAdmin } from 'wildebeest/backend/src/utils/auth/isUserAdmin' export const onRequestGet: PagesFunction = async ({ env, request }) => { return handleRequestPost(await getDatabase(env), request, env.ACCESS_AUTH_DOMAIN, env.ACCESS_AUD) diff --git a/functions/first-login.ts b/functions/first-login.ts index 9a69ade..abccf82 100644 --- a/functions/first-login.ts +++ b/functions/first-login.ts @@ -7,7 +7,7 @@ import { parse } from 'cookie' import * as errors from 'wildebeest/backend/src/errors' import * as access from 'wildebeest/backend/src/access' import { type Database, getDatabase } from 'wildebeest/backend/src/database' -import { getJwtEmail } from 'wildebeest/frontend/src/utils/getJwtEmail' +import { getJwtEmail } from 'wildebeest/backend/src/utils/auth/getJwtEmail' export const onRequestPost: PagesFunction = async ({ request, env }) => { return handlePostRequest(request, await getDatabase(env), env.userKEK, env.ACCESS_AUTH_DOMAIN, env.ACCESS_AUD) diff --git a/functions/oauth/authorize.ts b/functions/oauth/authorize.ts index f3ba56f..612d856 100644 --- a/functions/oauth/authorize.ts +++ b/functions/oauth/authorize.ts @@ -8,7 +8,7 @@ import { getClientById } from 'wildebeest/backend/src/mastodon/client' import * as access from 'wildebeest/backend/src/access' import { getPersonByEmail } from 'wildebeest/backend/src/activitypub/actors' import { type Database, getDatabase } from 'wildebeest/backend/src/database' -import { checkAuth } from 'wildebeest/frontend/src/utils/checkAuth' +import { isUserAuthenticated } from 'wildebeest/backend/src/utils/auth/isUserAuthenticated' // Extract the JWT token sent by Access (running before us). const extractJWTFromRequest = (request: Request) => request.headers.get('Cf-Access-Jwt-Assertion') || '' @@ -80,7 +80,7 @@ export async function handleRequestPost( } const jwt = extractJWTFromRequest(request) - const isAuthenticated = await checkAuth(request, jwt, accessDomain, accessAud) + const isAuthenticated = await isUserAuthenticated(request, jwt, accessDomain, accessAud) if (!isAuthenticated) { return new Response('', { status: 401 }) From 75c8b7ff0edd21b4521205a20764d949bcdf5962 Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Mon, 6 Mar 2023 19:06:32 +0000 Subject: [PATCH 10/10] remove admins endpoint --- backend/src/utils/auth/getAdmins.ts | 15 +++++++++++ backend/src/utils/auth/isUserAdmin.ts | 2 +- .../src/routes/(frontend)/about/index.tsx | 2 +- functions/api/wb/settings/server/admins.ts | 26 ------------------- 4 files changed, 17 insertions(+), 28 deletions(-) create mode 100644 backend/src/utils/auth/getAdmins.ts diff --git a/backend/src/utils/auth/getAdmins.ts b/backend/src/utils/auth/getAdmins.ts new file mode 100644 index 0000000..0232cac --- /dev/null +++ b/backend/src/utils/auth/getAdmins.ts @@ -0,0 +1,15 @@ +import { type Database } from 'wildebeest/backend/src/database' +import { Person, personFromRow } from 'wildebeest/backend/src/activitypub/actors' + +export async function getAdmins(db: Database): Promise { + let rows: unknown[] = [] + try { + const stmt = db.prepare('SELECT * FROM actors WHERE is_admin=1') + const result = await stmt.all() + rows = result.success ? (result.results as unknown[]) : [] + } catch { + /* empty */ + } + + return rows.map(personFromRow) +} diff --git a/backend/src/utils/auth/isUserAdmin.ts b/backend/src/utils/auth/isUserAdmin.ts index bdecb2c..704959f 100644 --- a/backend/src/utils/auth/isUserAdmin.ts +++ b/backend/src/utils/auth/isUserAdmin.ts @@ -1,7 +1,7 @@ import { emailSymbol } from 'wildebeest/backend/src/activitypub/actors' import { Database } from 'wildebeest/backend/src/database' import { getJwtEmail } from 'wildebeest/backend/src/utils/auth/getJwtEmail' -import { getAdmins } from 'wildebeest/functions/api/wb/settings/server/admins' +import { getAdmins } from './getAdmins' import { isUserAuthenticated } from './isUserAuthenticated' export async function isUserAdmin( diff --git a/frontend/src/routes/(frontend)/about/index.tsx b/frontend/src/routes/(frontend)/about/index.tsx index aafe8b3..ea43c82 100644 --- a/frontend/src/routes/(frontend)/about/index.tsx +++ b/frontend/src/routes/(frontend)/about/index.tsx @@ -10,10 +10,10 @@ import { ServerSettingsData } from '~/routes/(admin)/settings/(admin)/server-set import { Account } from '~/types' import { getDocumentHead } from '~/utils/getDocumentHead' import { instanceLoader } from '../layout' -import { getAdmins } from 'wildebeest/functions/api/wb/settings/server/admins' import { emailSymbol } from 'wildebeest/backend/src/activitypub/actors' import { loadLocalMastodonAccount } from 'wildebeest/backend/src/mastodon/account' import { AccountCard } from '~/components/AccountCard/AccountCard' +import { getAdmins } from 'wildebeest/backend/src/utils/auth/getAdmins' type AboutInfo = { image: string diff --git a/functions/api/wb/settings/server/admins.ts b/functions/api/wb/settings/server/admins.ts index 143b004..e69de29 100644 --- a/functions/api/wb/settings/server/admins.ts +++ b/functions/api/wb/settings/server/admins.ts @@ -1,26 +0,0 @@ -import type { Env } from 'wildebeest/backend/src/types/env' -import type { ContextData } from 'wildebeest/backend/src/types/context' -import { type Database, getDatabase } from 'wildebeest/backend/src/database' -import { Person, personFromRow } from 'wildebeest/backend/src/activitypub/actors' - -export const onRequestGet: PagesFunction = async ({ env }) => { - return handleRequestGet(await getDatabase(env)) -} - -export async function handleRequestGet(db: Database) { - const admins = await getAdmins(db) - return new Response(JSON.stringify(admins), { status: 200 }) -} - -export async function getAdmins(db: Database): Promise { - let rows: unknown[] = [] - try { - const stmt = db.prepare('SELECT * FROM actors WHERE is_admin=1') - const result = await stmt.all() - rows = result.success ? (result.results as unknown[]) : [] - } catch { - /* empty */ - } - - return rows.map(personFromRow) -}