/** * Benutzer und Rollen. * * Beide Seiten zeigen ausschliesslich an. Anlegen und Aendern gehen weiterhin * ueber die API: Eine Maske, die Rollen bearbeitet, muss die Sonderfaelle * beherrschen — den letzten Administrator, mitgelieferte unveraenderliche Rollen * — und die gehoeren geprueft, nicht nebenbei gebaut. */ import { useCallback } from 'react'; import { requestApi } from '../../api/client'; import { useApiResource } from '../../api/useApiResource'; import { EmptyState, ErrorState, LoadingState } from '../../components/PageState'; /** Ein Benutzerkonto. */ interface UserAccount { /** Oeffentlicher Bezeichner. */ readonly id: string; /** Anmeldename. */ readonly username: string; /** Postanschrift. */ readonly email?: string; /** Zustand des Kontos. */ readonly status: string; /** Meldet einen eingerichteten zweiten Faktor. */ readonly mfa_enabled: boolean; /** Zugewiesene Rollen. */ readonly roles?: readonly string[]; /** Letzte Anmeldung in UTC. */ readonly last_login_at?: string; } /** Eine Rolle. */ interface RoleDefinition { /** Oeffentlicher Bezeichner. */ readonly id: string; /** Name der Rolle. */ readonly name: string; /** Beschreibung. */ readonly description?: string; /** Zugeordnete Berechtigungen. */ readonly permissions?: readonly string[]; } /** Zeigt die Benutzerkonten. */ export function UsersPage(): React.JSX.Element { const loadUsers = useCallback( (abortSignal: AbortSignal) => requestApi('/users', { signal: abortSignal }), [], ); const { loadState, data, loadError, reload } = useApiResource(loadUsers); return (

Benutzer

{data !== null && {data.length} Konten}
{loadState === 'loading' && } {loadState === 'failed' && loadError !== null && } {loadState === 'loaded' && data !== null && data.length === 0 && ( )} {loadState === 'loaded' && data !== null && data.length > 0 && (
{data.map((userAccount) => ( ))}
Anmeldename Rollen Zustand Zweiter Faktor
{userAccount.username} {userAccount.email !== undefined && ( {userAccount.email} )} {userAccount.roles?.join(', ') ?? '—'} {userAccount.status} {/* Ein fehlender zweiter Faktor ist ein Sicherheitsbefund und wird benannt, statt ihn zu verschweigen (PROMPT.md §90). */} {userAccount.mfa_enabled ? ( eingerichtet ) : ( fehlt )}
)}
); } /** Zeigt die Rollen samt Berechtigungen. */ export function RolesPage(): React.JSX.Element { const loadRoles = useCallback( (abortSignal: AbortSignal) => requestApi('/roles', { signal: abortSignal }), [], ); const { loadState, data, loadError, reload } = useApiResource(loadRoles); return (

Rollen

{data !== null && {data.length} Rollen}
{loadState === 'loading' && } {loadState === 'failed' && loadError !== null && } {loadState === 'loaded' && data !== null && (
    {data.map((roleDefinition) => (
  • {roleDefinition.name}

    {roleDefinition.description !== undefined && (

    {roleDefinition.description}

    )} {/* Die Berechtigungen stehen ausgeschrieben da: „Backup Operator" sagt einem Betreiber wenig, „backups.delete" alles. */}
      {(roleDefinition.permissions ?? []).map((permissionName) => (
    • {permissionName}
    • ))}
  • ))}
)}
); }