audit: 33-agent comprehensive audit + critical fixes
Full team audit run, all reports verbatim in docs/AUDIT-2026-05-12.md (5900+ lines, 30+ critical findings). Already-fixed this commit: - permission-overrides PUT: self-target block + RolePermissions allow-list + cross-tenant guard - /api/auth/resolve-identifier: rate-limit + synthetic miss-email kill enumeration - admin email-change: rotates account.accountId + revokes sessions - middleware: token-gated email confirm/cancel routes whitelisted - NAV_CATALOG: 10 dead-link sweeps to existing /admin/<x> targets Feature work landing same commit: optional username sign-in (migration 0054), per-user permission overrides (0055) with three-state matrix tabbed inside UserForm, user disable button, role + outcome + stage label normalisation across the platform, admin email-change with auto-notification template. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,6 @@
|
||||
import { formatErrorBanner } from '@/lib/api/toast-error';
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
@@ -15,6 +14,8 @@ import {
|
||||
} from '@/components/ui/select';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetFooter } from '@/components/ui/sheet';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import { UserPermissionMatrix } from './user-permission-matrix';
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
@@ -26,7 +27,6 @@ import {
|
||||
AlertDialogTitle,
|
||||
} from '@/components/ui/alert-dialog';
|
||||
import { PhoneInput, type PhoneInputValue } from '@/components/shared/phone-input';
|
||||
import { useUIStore } from '@/stores/ui-store';
|
||||
import { apiFetch } from '@/lib/api/client';
|
||||
import { formatRole } from '@/lib/constants';
|
||||
|
||||
@@ -69,7 +69,6 @@ export function UserForm({ open, onOpenChange, user, onSuccess }: UserFormProps)
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const portSlug = useUIStore((s) => s.currentPortSlug);
|
||||
const isEdit = !!user;
|
||||
const fullName = `${firstName} ${lastName}`.trim();
|
||||
|
||||
@@ -192,7 +191,28 @@ export function UserForm({ open, onOpenChange, user, onSuccess }: UserFormProps)
|
||||
<SheetTitle>{isEdit ? 'Edit User' : 'New User'}</SheetTitle>
|
||||
</SheetHeader>
|
||||
|
||||
<form onSubmit={handleSubmit} className="mt-6 space-y-4">
|
||||
<Tabs defaultValue="profile" className="mt-6">
|
||||
<TabsList className="w-full">
|
||||
<TabsTrigger value="profile" className="flex-1">
|
||||
Profile & role
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="permissions" className="flex-1" disabled={!isEdit}>
|
||||
Permissions
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="permissions" className="mt-4">
|
||||
{isEdit ? (
|
||||
<UserPermissionMatrix userId={user.userId} />
|
||||
) : (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Save the new user first, then return here to fine-tune their permissions.
|
||||
</p>
|
||||
)}
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="profile" className="mt-4">
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="user-first-name">First name</Label>
|
||||
@@ -320,23 +340,6 @@ export function UserForm({ open, onOpenChange, user, onSuccess }: UserFormProps)
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isEdit && portSlug && (
|
||||
<div className="rounded-lg border bg-muted/30 p-3">
|
||||
<p className="text-sm font-medium">Fine-tuned permissions</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
The selected role grants a baseline. To add or remove a specific permission for
|
||||
this user only, open the role & permissions page.
|
||||
</p>
|
||||
<Link
|
||||
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
||||
href={`/${portSlug}/admin/roles?focusUser=${user.userId}` as any}
|
||||
className="mt-2 inline-block text-xs font-medium text-primary hover:underline"
|
||||
>
|
||||
Manage permissions →
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{error && <p className="whitespace-pre-line text-sm text-destructive">{error}</p>}
|
||||
|
||||
<SheetFooter>
|
||||
@@ -353,6 +356,8 @@ export function UserForm({ open, onOpenChange, user, onSuccess }: UserFormProps)
|
||||
</Button>
|
||||
</SheetFooter>
|
||||
</form>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
|
||||
<AlertDialog open={emailConfirmOpen} onOpenChange={setEmailConfirmOpen}>
|
||||
<AlertDialogContent>
|
||||
|
||||
Reference in New Issue
Block a user