kopia lustrzana https://github.com/cloudflare/wildebeest
commit
097a2b5d97
|
@ -8,6 +8,7 @@ import { Buffer } from 'buffer'
|
|||
const PERSON = 'Person'
|
||||
const isTesting = typeof jest !== 'undefined'
|
||||
export const emailSymbol = Symbol()
|
||||
export const isAdminSymbol = Symbol()
|
||||
|
||||
export function actorURL(domain: string, id: string): URL {
|
||||
return new URL(`/ap/users/${id}`, 'https://' + domain)
|
||||
|
@ -23,6 +24,7 @@ export interface Actor extends APObject {
|
|||
alsoKnownAs?: string
|
||||
|
||||
[emailSymbol]: string
|
||||
[isAdminSymbol]: boolean
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/activitystreams-vocabulary/#dfn-person
|
||||
|
@ -298,6 +300,7 @@ export function personFromRow(row: any): Person {
|
|||
return {
|
||||
// Hidden values
|
||||
[emailSymbol]: row.email,
|
||||
[isAdminSymbol]: row.is_admin === 1,
|
||||
|
||||
...properties,
|
||||
name,
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
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 './getAdmins'
|
||||
import { isUserAuthenticated } from './isUserAuthenticated'
|
||||
|
||||
export async function isUserAdmin(
|
||||
request: Request,
|
||||
jwt: string,
|
||||
accessAuthDomain: string,
|
||||
accessAud: string,
|
||||
database: Database
|
||||
): Promise<boolean> {
|
||||
let email: string
|
||||
|
||||
try {
|
||||
const authenticated = await isUserAuthenticated(request, jwt, accessAuthDomain, accessAud)
|
||||
if (!authenticated) {
|
||||
return false
|
||||
}
|
||||
|
||||
email = getJwtEmail(jwt)
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
|
||||
const admins = await getAdmins(database)
|
||||
|
||||
return admins.some((admin) => admin[emailSymbol] === email)
|
||||
}
|
|
@ -4,13 +4,58 @@
|
|||
* It's the entry point for cloudflare-pages when building for production.
|
||||
*
|
||||
* Learn more about the cloudflare integration here:
|
||||
* - https://qwik.builder.io/qwikcity/adaptors/cloudflare-pages/
|
||||
* - https://qwik.builder.io/integrations/deployments/cloudflare-pages/#cloudflare-pages-entry-middleware
|
||||
*
|
||||
*/
|
||||
import { createQwikCity } from '@builder.io/qwik-city/middleware/cloudflare-pages'
|
||||
import qwikCityPlan from '@qwik-city-plan'
|
||||
import render from './entry.ssr'
|
||||
import type { Env } from 'wildebeest/backend/src/types/env'
|
||||
import type { ContextData } from 'wildebeest/backend/src/types/context'
|
||||
import { parse } from 'cookie'
|
||||
import * as access from 'wildebeest/backend/src/access'
|
||||
import { getJwtEmail } from 'wildebeest/backend/src/utils/auth/getJwtEmail'
|
||||
import * as errors from 'wildebeest/backend/src/errors'
|
||||
import * as actors from 'wildebeest/backend/src/activitypub/actors'
|
||||
import { getDatabase } from 'wildebeest/backend/src/database'
|
||||
import type { Person } from 'wildebeest/backend/src/activitypub/actors'
|
||||
|
||||
const onRequest = createQwikCity({ render, qwikCityPlan })
|
||||
const qwikHandler = createQwikCity({ render, qwikCityPlan })
|
||||
|
||||
export { onRequest }
|
||||
type QwikContextData = {
|
||||
connectedActor: Person | null,
|
||||
}
|
||||
|
||||
// eslint-disable-next-line
|
||||
export const onRequest: PagesFunction<Env, any, ContextData> = async (ctx) => {
|
||||
const cookie = parse(ctx.request.headers.get('Cookie') || '')
|
||||
const jwt = cookie['CF_Authorization']
|
||||
|
||||
const data: QwikContextData = {
|
||||
connectedActor: null,
|
||||
}
|
||||
|
||||
if (jwt) {
|
||||
const validate = access.generateValidator({
|
||||
jwt,
|
||||
domain: ctx.env.ACCESS_AUTH_DOMAIN,
|
||||
aud: ctx.env.ACCESS_AUD,
|
||||
})
|
||||
await validate(ctx.request)
|
||||
|
||||
let email = ''
|
||||
try {
|
||||
email = getJwtEmail(jwt ?? '')
|
||||
} catch (e) {
|
||||
return errors.notAuthorized((e as Error)?.message)
|
||||
}
|
||||
|
||||
const db = await getDatabase(ctx.env)
|
||||
data.connectedActor = await actors.getPersonByEmail(db, email)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line
|
||||
;(ctx.env as any).data = data
|
||||
|
||||
return qwikHandler(ctx)
|
||||
}
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
import { component$, Slot } from '@builder.io/qwik'
|
||||
import { loader$ } from '@builder.io/qwik-city'
|
||||
import { isUserAuthenticated } from 'wildebeest/backend/src/utils/auth/isUserAuthenticated'
|
||||
|
||||
type AuthLoaderData = {
|
||||
loginUrl: URL
|
||||
isAuthorized: boolean
|
||||
}
|
||||
|
||||
export const authLoader = loader$<Promise<AuthLoaderData>>(async ({ platform, request, cookie }) => {
|
||||
const jwt = cookie.get('CF_Authorization')?.value ?? ''
|
||||
const isAuthorized = await isUserAuthenticated(request, jwt, platform.ACCESS_AUTH_DOMAIN, platform.ACCESS_AUD)
|
||||
export const authLoader = loader$<Promise<AuthLoaderData>>(async ({ platform, request }) => {
|
||||
const isAuthorized = platform.data.connectedActor !== null
|
||||
// FIXME(sven): remove hardcoded value
|
||||
const UI_CLIENT_ID = '924801be-d211-495d-8cac-e73503413af8'
|
||||
const params = new URLSearchParams({
|
||||
|
|
|
@ -1,14 +1,10 @@
|
|||
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 { isAdminSymbol } from 'wildebeest/backend/src/activitypub/actors'
|
||||
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)
|
||||
export const adminLoader = loader$(async ({ platform, html }) => {
|
||||
const isAuthorized = platform.data.connectedActor !== null
|
||||
const isAdmin = isAuthorized && platform.data.connectedActor[isAdminSymbol]
|
||||
|
||||
if (!isAdmin) {
|
||||
return html(401, getErrorHtml('You need to be an admin to view this page'))
|
||||
|
|
|
@ -1,17 +1,8 @@
|
|||
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
|
||||
)
|
||||
export const authLoader = loader$(async ({ platform, html }) => {
|
||||
const isAuthenticated = platform.data.connectedActor !== null
|
||||
|
||||
if (!isAuthenticated) {
|
||||
return html(401, getErrorHtml("You're not authorized to view this page"))
|
||||
|
|
Ładowanie…
Reference in New Issue