···11import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
22-import { getSession, getSessionWithPermissions, canLockTopics, canModeratePosts, canBanUsers } from "../session.js";
22+import { getSession, getSessionWithPermissions, canLockTopics, canModeratePosts, canBanUsers, hasAnyAdminPermission } from "../session.js";
33import { logger } from "../logger.js";
4455vi.mock("../logger.js", () => ({
···325325 it("canBanUsers returns true for owner with wildcard permission", () =>
326326 expect(canBanUsers(ownerSession)).toBe(true));
327327});
328328+329329+describe("hasAnyAdminPermission", () => {
330330+ const unauthSession = { authenticated: false as const, permissions: new Set<string>() };
331331+332332+ const noPermSession = {
333333+ authenticated: true as const,
334334+ did: "did:plc:member",
335335+ handle: "member.bsky.social",
336336+ permissions: new Set<string>(),
337337+ };
338338+339339+ const makeSinglePermSession = (permission: string) => ({
340340+ authenticated: true as const,
341341+ did: "did:plc:user",
342342+ handle: "user.bsky.social",
343343+ permissions: new Set([permission]),
344344+ });
345345+346346+ it("returns false for unauthenticated session", () =>
347347+ expect(hasAnyAdminPermission(unauthSession)).toBe(false));
348348+349349+ it("returns false for authenticated user with no permissions", () =>
350350+ expect(hasAnyAdminPermission(noPermSession)).toBe(false));
351351+352352+ it("returns true for user with manageMembers permission", () =>
353353+ expect(hasAnyAdminPermission(makeSinglePermSession("space.atbb.permission.manageMembers"))).toBe(true));
354354+355355+ it("returns true for user with manageCategories permission", () =>
356356+ expect(hasAnyAdminPermission(makeSinglePermSession("space.atbb.permission.manageCategories"))).toBe(true));
357357+358358+ it("returns true for user with moderatePosts permission", () =>
359359+ expect(hasAnyAdminPermission(makeSinglePermSession("space.atbb.permission.moderatePosts"))).toBe(true));
360360+361361+ it("returns true for user with banUsers permission", () =>
362362+ expect(hasAnyAdminPermission(makeSinglePermSession("space.atbb.permission.banUsers"))).toBe(true));
363363+364364+ it("returns true for user with lockTopics permission", () =>
365365+ expect(hasAnyAdminPermission(makeSinglePermSession("space.atbb.permission.lockTopics"))).toBe(true));
366366+367367+ it("returns true for user with wildcard permission", () =>
368368+ expect(hasAnyAdminPermission(makeSinglePermSession("*"))).toBe(true));
369369+370370+ it("returns false for user with only an unrelated permission", () =>
371371+ expect(hasAnyAdminPermission(makeSinglePermSession("space.atbb.permission.someOtherThing"))).toBe(false));
372372+});
+2-2
apps/web/src/lib/session.ts
···158158] as const;
159159160160/**
161161- * Returns true if the session grants at least one admin or mod permission,
162162- * or the wildcard "*". Used to gate the /admin landing page.
161161+ * Returns true if the session grants at least one of the admin panel permissions
162162+ * listed in ADMIN_PERMISSIONS, or the wildcard "*". Used to gate the /admin landing page.
163163 */
164164export function hasAnyAdminPermission(
165165 auth: WebSessionWithPermissions