Merge pull request #382 from cloudflare/sven/refactor-ui-auth

refactor UI auth
pull/383/head
Sven Sauleau 2023-03-08 10:00:15 +01:00 zatwierdzone przez GitHub
commit 097a2b5d97
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
6 zmienionych plików z 59 dodań i 56 usunięć

Wyświetl plik

@ -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,

Wyświetl plik

@ -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)
}

Wyświetl plik

@ -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)
}

Wyświetl plik

@ -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({

Wyświetl plik

@ -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'))

Wyświetl plik

@ -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"))