feat(perm): load capabilities + bot access onto req.user; expose via /me

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
saopig1andClaude Opus 4.8 committed 2026-05-30 13:21:24 +08:00
1 parent 7a8666efbb
commit 696b224f8d
8 files changed
+42 -12

No files matched your search

+3 -1
View File
@@ -5,6 +5,7 @@ import request from "supertest";
import { createDatabase, type BotDatabase } from "../../data/database.js";
import { createUserStore } from "../../data/users.js";
import { createSessionStore } from "../../data/sessions.js";
import { createPermissionStore } from "../../data/permissions.js";
import { createRequireAuth } from "./requireAuth.js";
import { requireAdmin } from "./requireAdmin.js";
import { SESSION_COOKIE_NAME } from "../auth/validateSession.js";
@@ -19,13 +20,14 @@ describe("requireAdmin middleware", () => {
botDb = createDatabase(":memory:");
const users = createUserStore(botDb.db);
const sessions = createSessionStore(botDb.db);
const permissions = createPermissionStore(botDb.db);
const admin = await users.createUser("admin", "pw-admin-pw", "admin");
const member = await users.createUser("member", "pw-member-pw", "member");
adminCookie = `${SESSION_COOKIE_NAME}=${sessions.createSession(admin.id).token}`;
memberCookie = `${SESSION_COOKIE_NAME}=${sessions.createSession(member.id).token}`;
app = express();
app.use(cookieParser());
app.use(createRequireAuth(sessions));
app.use(createRequireAuth(sessions, permissions));
app.use(requireAdmin);
app.get("/admin-only", (_req, res) => res.json({ ok: true }));
});
+3 -1
View File
@@ -5,6 +5,7 @@ import request from "supertest";
import { createDatabase, type BotDatabase } from "../../data/database.js";
import { createUserStore } from "../../data/users.js";
import { createSessionStore } from "../../data/sessions.js";
import { createPermissionStore } from "../../data/permissions.js";
import { createRequireAuth } from "./requireAuth.js";
import { SESSION_COOKIE_NAME } from "../auth/validateSession.js";
@@ -17,12 +18,13 @@ describe("requireAuth middleware", () => {
botDb = createDatabase(":memory:");
const users = createUserStore(botDb.db);
const sessions = createSessionStore(botDb.db);
const permissions = createPermissionStore(botDb.db);
const u = await users.createUser("alice", "pw-alice", "admin");
validToken = sessions.createSession(u.id).token;
app = express();
app.use(cookieParser());
app.use(createRequireAuth(sessions));
app.use(createRequireAuth(sessions, permissions));
app.get("/protected", (req, res) => {
res.json({ ok: true, user: (req as any).user });
});
+10 -2
View File
@@ -1,6 +1,7 @@
import type { Request, Response, NextFunction, RequestHandler } from "express";
import type { SessionStore } from "../../data/sessions.js";
import { SESSION_TTL_MS } from "../../data/sessions.js";
import { resolvePermissionContext, type PermissionStore } from "../../data/permissions.js";
import {
validateSessionFromHeaders,
extractSessionToken,
@@ -19,7 +20,7 @@ declare module "express-serve-static-core" {
}
}
export function createRequireAuth(sessions: SessionStore): RequestHandler {
export function createRequireAuth(sessions: SessionStore, permissions: PermissionStore): RequestHandler {
return function requireAuth(req: Request, res: Response, next: NextFunction) {
const result = validateSessionFromHeaders(req.headers.cookie, sessions);
if (!result) {
@@ -27,7 +28,14 @@ export function createRequireAuth(sessions: SessionStore): RequestHandler {
res.status(401).json({ error: "unauthenticated" });
return;
}
req.user = { id: result.userId, username: result.username, role: result.role };
const ctx = resolvePermissionContext(result.role, result.userId, permissions);
req.user = {
id: result.userId,
username: result.username,
role: result.role,
capabilities: ctx.capabilities,
bots: ctx.bots,
};
const token = extractSessionToken(req.headers.cookie);
if (token) {
res.cookie(SESSION_COOKIE_NAME, token, {