harden admin api authorization and add authz regression tests

This commit is contained in:
dimitar committed 2026-03-29 11:23:49 +02:00
1 parent 272c9b36dd
commit 10b58245f5
28 files changed
+1336 -240

No files matched your search

+27 -55
View File
@@ -3,6 +3,7 @@ import { auth } from "@clerk/nextjs/server";
import { eq, sql } from "@fitai/database";
import { db, users as usersTable, gyms as gymsTable } from "@fitai/database";
import { ensureUserSynced } from "@/lib/sync-user";
import { getDatabase } from "@/lib/database";
import log from "@/lib/logger";
async function ensureGymsTable() {
@@ -23,14 +24,37 @@ async function ensureGymsTable() {
// Lists active gyms for selection (grid)
export async function GET() {
try {
const { userId } = await auth();
if (!userId) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
const appDb = await getDatabase();
const currentUser = await ensureUserSynced(userId, appDb);
if (!currentUser) {
return NextResponse.json({ error: "Forbidden" }, { status: 403 });
}
if (currentUser.role !== "admin" && currentUser.role !== "superAdmin") {
return NextResponse.json({ error: "Forbidden" }, { status: 403 });
}
await ensureGymsTable();
const rows = await db
let rows = await db
.select()
.from(gymsTable)
.where(eq(gymsTable.status, "active"))
.orderBy(sql`created_at DESC`)
.all();
if (currentUser.role !== "superAdmin") {
if (!currentUser.gymId) {
return NextResponse.json({ error: "No gym assigned" }, { status: 403 });
}
rows = rows.filter((row) => row.id === currentUser.gymId);
}
return NextResponse.json(rows);
} catch (error) {
log.error("Failed to get gyms", error);
@@ -48,60 +72,8 @@ export async function POST(req: Request) {
const { userId } = await auth();
if (!userId) return new NextResponse("Unauthorized", { status: 401 });
// Ensure our local DB has the user synced (role, etc.)
const currentUser = await ensureUserSynced(userId, {
// minimal facade for ensureUserSynced to work: it expects an object implementing part of IDatabase
getUserById: async (id: string) => {
const row = await db
.select()
.from(usersTable)
.where(eq(usersTable.id, id))
.get();
return row
? {
id: row.id,
email: row.email,
firstName: row.firstName,
lastName: row.lastName,
password: row.password ?? "",
phone: row.phone ?? undefined,
role: row.role,
imageUrl: undefined,
createdAt: new Date(row.createdAt),
updatedAt: new Date(row.updatedAt),
}
: null;
},
updateUser: async (id: string, updates: any) => {
await db
.update(usersTable)
.set({
...updates,
updatedAt: new Date(),
})
.where(eq(usersTable.id, id))
.run();
const row = await db
.select()
.from(usersTable)
.where(eq(usersTable.id, id))
.get();
return row
? {
id: row.id,
email: row.email,
firstName: row.firstName,
lastName: row.lastName,
password: row.password ?? "",
phone: row.phone ?? undefined,
role: row.role,
imageUrl: undefined,
createdAt: new Date(row.createdAt),
updatedAt: new Date(row.updatedAt),
}
: null;
},
} as any);
const appDb = await getDatabase();
const currentUser = await ensureUserSynced(userId, appDb);
if (
!currentUser ||