mirror of
https://github.com/ZHANGTIANYAO1/teamspeak-music-bot.git
synced 2026-10-02 04:52:50 +08:00
feat(auth): two-role permission system (admin/member)
Adds an admin/member role to WebUI auth. /api/users and /api/audit are now gated by a requireAdmin middleware; all other authenticated endpoints accept both roles. Schema migration defaults all existing users to admin to preserve access. POST /api/users defaults new users to member; first-run setup always creates an admin. Adds PATCH /api/users/:id/role with last-admin demotion and deletion guards. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
b0b61f8fce
commit
a73f797bcb
18 files changed
+292
-60
No files matched your search
@@ -0,0 +1,50 @@
|
||||
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
||||
import express from "express";
|
||||
import cookieParser from "cookie-parser";
|
||||
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 { createRequireAuth } from "./requireAuth.js";
|
||||
import { requireAdmin } from "./requireAdmin.js";
|
||||
import { SESSION_COOKIE_NAME } from "../auth/validateSession.js";
|
||||
|
||||
describe("requireAdmin middleware", () => {
|
||||
let botDb: BotDatabase;
|
||||
let app: express.Express;
|
||||
let adminCookie: string;
|
||||
let memberCookie: string;
|
||||
|
||||
beforeEach(async () => {
|
||||
botDb = createDatabase(":memory:");
|
||||
const users = createUserStore(botDb.db);
|
||||
const sessions = createSessionStore(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(requireAdmin);
|
||||
app.get("/admin-only", (_req, res) => res.json({ ok: true }));
|
||||
});
|
||||
|
||||
afterEach(() => botDb.close());
|
||||
|
||||
it("rejects unauthenticated requests with 401", async () => {
|
||||
const res = await request(app).get("/admin-only");
|
||||
expect(res.status).toBe(401);
|
||||
});
|
||||
|
||||
it("rejects member with 403", async () => {
|
||||
const res = await request(app).get("/admin-only").set("Cookie", memberCookie);
|
||||
expect(res.status).toBe(403);
|
||||
expect(res.body).toEqual({ error: "forbidden" });
|
||||
});
|
||||
|
||||
it("allows admin", async () => {
|
||||
const res = await request(app).get("/admin-only").set("Cookie", adminCookie);
|
||||
expect(res.status).toBe(200);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user