feat(PROJ-13): add SYSTEM_ADMIN platform-wide role
users.isSystemAdmin (boolean, default false), DB-only to grant (no UI in MVP scope, per spec). requesterIsSystemAdmin()/requireSystemAdmin() in authz.ts — checked fresh from the DB on every request, not cached in the JWT, so revoking takes effect immediately. GET /api/auth/me returns the flag; DashboardPage shows a "System-Admin" nav button when set (convenience only, not the access control — PROJ-14/15/16/18's own routes enforce it). /system-admin route doesn't exist yet (comes with PROJ-14) — the nav button currently bounces back to / via the catch-all route, no stub page added. Verified end-to-end on talkhub: /api/auth/me flips true immediately after a direct DB update, no re-login required. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0143yYvjesjHndBACoweyK67
This commit is contained in:
1 parent
c1c1ae9c88
commit
0f15432045
10 files changed
+1599
-13
No files matched your search
@@ -4,6 +4,9 @@ import { clearCachedChatCredentials } from "@/lib/matrix";
|
||||
interface AuthUser {
|
||||
id: string;
|
||||
email: string;
|
||||
/** PROJ-13: platform-wide admin. Only drives nav visibility — every
|
||||
* system-admin API route re-checks this server-side. */
|
||||
isSystemAdmin: boolean;
|
||||
}
|
||||
|
||||
interface AuthContextValue {
|
||||
@@ -46,11 +49,12 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
if (!meRes.ok) {
|
||||
throw new Error(await parseError(meRes));
|
||||
}
|
||||
const me = (await meRes.json()) as AuthUser;
|
||||
const me = (await meRes.json()) as Partial<AuthUser> & { id: string; email: string };
|
||||
|
||||
localStorage.setItem(STORAGE_KEY, newToken);
|
||||
setTokenState(newToken);
|
||||
setUser(me);
|
||||
// Default to false — never let a missing/unexpected field grant admin nav.
|
||||
setUser({ id: me.id, email: me.email, isSystemAdmin: me.isSystemAdmin === true });
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -29,6 +29,13 @@ export function DashboardPage() {
|
||||
<Button variant="outline" asChild>
|
||||
<Link to="/audit-log">Aktivitäten</Link>
|
||||
</Button>
|
||||
{/* PROJ-13: only a convenience — the panel's APIs enforce SYSTEM_ADMIN
|
||||
themselves, hiding the link is not the access control. */}
|
||||
{user?.isSystemAdmin && (
|
||||
<Button variant="outline" asChild>
|
||||
<Link to="/system-admin">System-Admin</Link>
|
||||
</Button>
|
||||
)}
|
||||
<Button variant="outline" onClick={logout}>
|
||||
Abmelden
|
||||
</Button>
|
||||
|
||||
Reference in new issue
Block a user