fix: improve admin security and modal accessibility

- Replace Card-based modal with proper Dialog component in SecurityAlertsTable for better accessibility
- Add missing admin role check to threat-analysis endpoint for proper authorization
- Implement ARIA attributes, focus management, and semantic structure
- Ensure consistent admin security patterns across endpoints
This commit is contained in:
2025-07-13 23:27:36 +02:00
parent 04d415f2cc
commit bcb7554ffc
2 changed files with 55 additions and 45 deletions

View File

@ -29,7 +29,7 @@ export async function POST(request: NextRequest) {
try { try {
const session = await getServerSession(authOptions); const session = await getServerSession(authOptions);
if (!session?.user || !session.user.isPlatformUser) { if (!session?.user || session.user.role !== "ADMIN") {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
} }

View File

@ -4,7 +4,15 @@ import { AlertTriangle, CheckCircle, Eye, EyeOff } from "lucide-react";
import { useState } from "react"; import { useState } from "react";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Card, CardContent } from "@/components/ui/card";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { import {
Table, Table,
TableBody, TableBody,
@ -196,32 +204,32 @@ export function SecurityAlertsTable({
)} )}
{/* Alert Details Modal */} {/* Alert Details Modal */}
{selectedAlert && ( <Dialog
<Card className="fixed inset-0 z-50 bg-black/50 flex items-center justify-center p-4"> open={!!selectedAlert}
<Card className="max-w-2xl w-full max-h-[80vh] overflow-auto"> onOpenChange={() => setSelectedAlert(null)}
<CardHeader> >
<div className="flex items-center justify-between"> <DialogContent className="max-w-2xl max-h-[80vh] overflow-auto">
<div className="space-y-2"> <DialogHeader>
<CardTitle>{selectedAlert.title}</CardTitle> <DialogTitle className="flex items-center gap-2">
<div className="flex items-center gap-2"> {selectedAlert?.title}
<Badge variant={getSeverityColor(selectedAlert.severity)}> <div className="flex items-center gap-2">
{selectedAlert.severity} <Badge
</Badge> variant={getSeverityColor(selectedAlert?.severity || "")}
<Badge variant="outline">
{formatAlertType(selectedAlert.type)}
</Badge>
</div>
</div>
<Button
variant="outline"
size="sm"
onClick={() => setSelectedAlert(null)}
> >
Close {selectedAlert?.severity}
</Button> </Badge>
<Badge variant="outline">
{formatAlertType(selectedAlert?.type || "")}
</Badge>
</div> </div>
</CardHeader> </DialogTitle>
<CardContent className="space-y-4"> <DialogDescription>
Security alert details and context information
</DialogDescription>
</DialogHeader>
{selectedAlert && (
<div className="space-y-4">
<div> <div>
<h4 className="font-medium mb-2">Description</h4> <h4 className="font-medium mb-2">Description</h4>
<p className="text-sm text-muted-foreground"> <p className="text-sm text-muted-foreground">
@ -249,26 +257,28 @@ export function SecurityAlertsTable({
</div> </div>
</div> </div>
)} )}
</div>
)}
<div className="flex items-center justify-between pt-4 border-t"> <DialogFooter className="flex items-center justify-between pt-4 border-t">
<span className="text-sm text-muted-foreground"> <span className="text-sm text-muted-foreground">
{formatTimestamp(selectedAlert.timestamp)} {selectedAlert && formatTimestamp(selectedAlert.timestamp)}
</span> </span>
{!selectedAlert.acknowledged && ( <div className="flex gap-2">
<Button {selectedAlert && !selectedAlert.acknowledged && (
onClick={() => { <Button
onAcknowledge(selectedAlert.id); onClick={() => {
setSelectedAlert(null); onAcknowledge(selectedAlert.id);
}} setSelectedAlert(null);
> }}
Acknowledge Alert >
</Button> Acknowledge Alert
)} </Button>
</div> )}
</CardContent> </div>
</Card> </DialogFooter>
</Card> </DialogContent>
)} </Dialog>
</div> </div>
); );
} }