dashboard-project-auth.ts64 lines · main
| 1 | /** |
| 2 | * Project-scoped Auth dashboard access. |
| 3 | * |
| 4 | * Platform session alone is not enough for user list / GDPR export / |
| 5 | * session revoke / roles / migration — must be admin of the target project. |
| 6 | */ |
| 7 | |
| 8 | import type { Context } from 'hono'; |
| 9 | |
| 10 | import { hasRoleAtLeast } from '../access.js'; |
| 11 | import { getProjectAccessForUser } from '../projects.js'; |
| 12 | import type { User } from '../../lib/auth.js'; |
| 13 | |
| 14 | export async function requireDashboardProjectAdmin( |
| 15 | c: Context, |
| 16 | projectId: string | null | undefined, |
| 17 | ): Promise<{ projectId: string } | Response> { |
| 18 | const user = c.get('user') as User | null; |
| 19 | if (!user) { |
| 20 | return c.json( |
| 21 | { |
| 22 | engine: 'briven-engine', |
| 23 | code: 'unauthorized', |
| 24 | message: 'authentication required', |
| 25 | }, |
| 26 | 401, |
| 27 | ); |
| 28 | } |
| 29 | const id = projectId?.trim() ?? ''; |
| 30 | if (!id.startsWith('p_')) { |
| 31 | return c.json( |
| 32 | { |
| 33 | engine: 'briven-engine', |
| 34 | code: 'project_id_required', |
| 35 | message: |
| 36 | 'projectId query/body is required (Auth admin is project-scoped)', |
| 37 | }, |
| 38 | 400, |
| 39 | ); |
| 40 | } |
| 41 | try { |
| 42 | const access = await getProjectAccessForUser(id, user.id); |
| 43 | if (!hasRoleAtLeast(access.role, 'admin')) { |
| 44 | return c.json( |
| 45 | { |
| 46 | engine: 'briven-engine', |
| 47 | code: 'forbidden', |
| 48 | message: 'project admin role required', |
| 49 | }, |
| 50 | 403, |
| 51 | ); |
| 52 | } |
| 53 | } catch { |
| 54 | return c.json( |
| 55 | { |
| 56 | engine: 'briven-engine', |
| 57 | code: 'forbidden', |
| 58 | message: 'no access to this project', |
| 59 | }, |
| 60 | 403, |
| 61 | ); |
| 62 | } |
| 63 | return { projectId: id }; |
| 64 | } |