refactor(housekeeping): reuse request capability context

This commit is contained in:
Simo committed 2026-08-26 21:20:12 +02:00
1 parent cc6bb9a9a3
commit edc165ba8d
5 files changed
+134 -8

No files matched your search

+22
View File
@@ -1,9 +1,14 @@
// @ts-nocheck
import { beforeEach, describe, expect, it, vi } from "vitest";
import {
anyCapability,
type HousekeepingCapabilityContext,
} from "@/features/housekeeping/foundation/contracts";
import { redirectSafe } from "@/lib/foundation/security";
import { canAccess, getAdminContext } from "@/lib/permissions";
import { clientIp, rateLimit } from "@/lib/rate-limit";
import {
requireHousekeepingCapability,
requireMod,
requireModPermission,
requirePermission,
@@ -70,6 +75,23 @@ describe("requirePermission", () => {
});
});
describe("requireHousekeepingCapability", () => {
it("reuses an already-created capability context without reloading permissions", async () => {
const context: HousekeepingCapabilityContext = {
actor: { id: 1, username: "admin", rank: 0 },
isSuperAdmin: false,
has: (slug) => slug === "admin.users.view",
hasAny: (...slugs) => slugs.includes("admin.users.view"),
hasAll: (...slugs) => slugs.every((slug) => slug === "admin.users.view"),
};
await expect(
requireHousekeepingCapability(anyCapability("admin.users.view"), context),
).resolves.toBe(context);
expect(getAdminContext).not.toHaveBeenCalled();
});
});
describe("requireMod", () => {
it("allows with MOD_DASHBOARD", async () => {
vi.mocked(getAdminContext).mockResolvedValue({
+18
View File
@@ -1,3 +1,9 @@
import { authorizeHousekeeping } from "@/features/housekeeping/foundation/authorization";
import type {
CapabilityRequirement,
HousekeepingCapabilityContext,
} from "@/features/housekeeping/foundation/contracts";
import { getHousekeepingCapabilityContext } from "@/features/housekeeping/foundation/server-capability-context";
import { redirectSafe } from "@/lib/foundation/security";
import { canAccess, getAdminContext, PERMS } from "@/lib/permissions";
import { clientIp, rateLimit } from "@/lib/rate-limit";
@@ -7,6 +13,18 @@ export interface StaffUser {
rank: number;
username: string;
}
export async function requireHousekeepingCapability(
requirement: CapabilityRequirement,
context?: HousekeepingCapabilityContext,
): Promise<HousekeepingCapabilityContext> {
const capabilityContext =
context ?? (await getHousekeepingCapabilityContext());
const authorization = authorizeHousekeeping(capabilityContext, requirement);
if (!authorization.ok) redirectSafe("/admin", "/admin");
return capabilityContext;
}
export async function requireStaff(): Promise<StaffUser> {
const { session, permissions } = await getAdminContext();