feat(15-08): serverseitige Modulsperre mit 403-Seite
- checkModuleAccess (module-access-actions.ts) fragt GET /modules/active serverseitig ab, Cookie-Weiterleitung nach fetchCurrentUser-Muster, schliesst im Zweifel (fehlendes Cookie, nicht-ok, Fehler -> false) - page.tsx zur async Server Component umgebaut, rendert bei fehlender Freigabe das 403-Markup direkt (kein notFound(), kein Redirect, D-07) - bisheriger Client-Inhalt (Whitelist-Pruefung, Nicht-gefunden-Zustand, Ruecknavigation) unveraendert nach module-shell.tsx ausgelagert - 5 Tests in module-access.test.tsx: 403-Zustand, Shell-Rendering, fehlendes Cookie, nicht-ok-Antwort, unregistrierter Slug trotz Zugriff
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
'use server';
|
||||
|
||||
import { cookies } from 'next/headers';
|
||||
|
||||
const API_URL = process.env.API_INTERNAL_URL || process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001';
|
||||
|
||||
/**
|
||||
* Server-side module access check for the module page route (D-07, PERM-04).
|
||||
*
|
||||
* Reads the session cookie, forwards it to `GET /modules/active` (the same
|
||||
* ModuleAccessService.getAccessibleModuleIds resolution ModuleGuard and the
|
||||
* sidebar use — D-01), and checks whether `moduleSlug` is present in the
|
||||
* response. Mirrors `fetchCurrentUser()` in auth-actions.ts exactly: same
|
||||
* cookie-forwarding, `credentials: 'include'`, `cache: 'no-store'`.
|
||||
*
|
||||
* Fails closed (T-15-29): a missing session cookie, a non-ok API response,
|
||||
* or a thrown network error all resolve to `false`. A broken network path
|
||||
* must never open access.
|
||||
*/
|
||||
export async function checkModuleAccess(moduleSlug: string): Promise<boolean> {
|
||||
const cookieStore = await cookies();
|
||||
const session = cookieStore.get('session')?.value;
|
||||
|
||||
if (!session) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_URL}/modules/active`, {
|
||||
headers: {
|
||||
Cookie: `session=${session}`,
|
||||
},
|
||||
credentials: 'include',
|
||||
cache: 'no-store',
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const modules: Array<{ slug: string }> = await response.json();
|
||||
return modules.some((module) => module.slug === moduleSlug);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user