import { get_session, get_user, PRECHECK_TABLE, require_user } from '../../../../utils/prechecks.ts'; import { PASSWORD_ENTRIES, PASSWORD_ENTRY } from '../../../../models/password_entry.ts'; import { SESSIONS } from '../../../../models/session.ts'; import { USER, USERS } from '../../../../models/user.ts'; import parse_body from '../../../../utils/bodyparser.ts'; import * as CANNED_RESPONSES from '../../../../utils/canned_responses.ts'; export const PRECHECKS: PRECHECK_TABLE = {}; // GET /api/users/:id - Get single user PRECHECKS.GET = [get_session, get_user, require_user, (_req: Request, meta: Record): Response | undefined => { const user_is_self = !!meta.user && !!meta.params && meta.user.id === meta.params.user_id; const can_read_self = meta.user?.permissions.includes('self.read'); const can_read_others = meta.user?.permissions?.includes('users.read'); const has_permission = can_read_others || (can_read_self && user_is_self); if (!has_permission) { return CANNED_RESPONSES.permission_denied(); } }]; export async function GET(_req: Request, meta: Record): Promise { const user_id: string = meta.params?.user_id?.toLowerCase().trim() ?? ''; const user: USER | null = user_id.length === 49 ? await USERS.get(user_id) : null; // lurid is 49 chars as we use them, eg: "also-play-flow-want-form-wide-thus-work-burn-same" if (!user) { return Response.json({ error: { message: `Could not locate a user with id: "${user_id}"`, cause: 'unknown_user' } }, { status: 404 }); } return Response.json(user, { status: 200 }); } // PUT /api/users/:id - Update user PRECHECKS.PUT = [get_session, get_user, require_user, (req: Request, meta: Record): Response | undefined => { const user_is_self = !!meta.user && !!meta.params && meta.user.id === meta.params.user_id; const can_write_self = meta.user?.permissions.includes('self.write'); const can_write_others = meta.user?.permissions?.includes('users.write'); const is_a_test_override = Deno.env.get('DENO_ENV') === 'test' && !!req.headers.get('x-test-override'); const has_permission = is_a_test_override || can_write_others || (can_write_self && user_is_self); if (!has_permission) { return CANNED_RESPONSES.permission_denied(); } }]; export async function PUT(req: Request, meta: Record): Promise { const now = new Date().toISOString(); const id: string = meta.params.user_id ?? ''; const existing = await USERS.get(id); if (!existing) { return Response.json({ error: { message: 'User not found', cause: 'unknown_user' } }, { status: 404 }); } try { const body = await parse_body(req); const updated: USER = { ...existing, ...body, timestamps: { created: existing.timestamps.created, updated: now } }; if (Array.isArray(body.permissions) && body.permissions.join(':') !== existing.permissions.join(':')) { const user_can_write_others = meta.user.permissions.includes('users.write'); const is_a_test_override = Deno.env.get('DENO_ENV') === 'test' && req.headers.get('x-test-override'); const is_allowed = user_can_write_others || is_a_test_override; if (!is_allowed) { return CANNED_RESPONSES.permission_denied(); } } await USERS.update(updated); return Response.json(updated, { status: 200 }); } catch (err) { return Response.json({ error: { message: (err as Error)?.message ?? 'Unknown error due to invalid user data.', cause: (err as Error)?.cause ?? 'invalid_user_data' } }, { status: 400 }); } } // DELETE /api/users/:id - Delete user PRECHECKS.DELETE = [get_session, get_user, require_user, (_req: Request, meta: Record): Response | undefined => { const user_is_self = !!meta.user && !!meta.params && meta.user.id === meta.params.user_id; const can_write_self = meta.user?.permissions.includes('self.write'); const can_write_others = meta.user?.permissions?.includes('users.write'); const has_permission = can_write_others || (can_write_self && user_is_self); if (!has_permission) { return CANNED_RESPONSES.permission_denied(); } }]; export async function DELETE(_req: Request, meta: Record): Promise { const user_id: string = meta.params.user_id ?? ''; const user: USER | null = await USERS.get(user_id); if (!user) { return Response.json({ error: { message: 'Error deleting user.', cause: 'unknown_user' } }, { status: 404 }); } const password_entry: PASSWORD_ENTRY | null = await PASSWORD_ENTRIES.get(user_id); if (password_entry) { await PASSWORD_ENTRIES.delete(password_entry); } const sessions = await SESSIONS.find({ user_id }); for (const session of sessions) { await SESSIONS.delete(session); } await USERS.delete(user); return Response.json({ deleted: true }, { status: 200 }); }