feat: Implement mandatory AAL2 for superuser actions

This commit is contained in:
gpt-engineer-app[bot]
2025-10-30 00:48:02 +00:00
parent 2829f5f491
commit 4b08836d6d
4 changed files with 1055 additions and 4 deletions

View File

@@ -1,8 +1,10 @@
import { useState, useEffect } from 'react';
import { Search, Ban, Shield, UserCheck, UserX, AlertTriangle } from 'lucide-react';
import { Search, Ban, Shield, UserCheck, UserX, AlertTriangle, Trash2 } from 'lucide-react';
import { supabase } from '@/integrations/supabase/client';
import { useAuth } from '@/hooks/useAuth';
import { useUserRole, UserRole } from '@/hooks/useUserRole';
import { useSuperuserGuard } from '@/hooks/useSuperuserGuard';
import { AdminUserDeletionDialog } from '@/components/admin/AdminUserDeletionDialog';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button';
@@ -34,6 +36,8 @@ export function ProfileManager() {
const [statusFilter, setStatusFilter] = useState<'all' | 'active' | 'banned'>('all');
const [roleFilter, setRoleFilter] = useState<'all' | UserRole>('all');
const [actionLoading, setActionLoading] = useState<string | null>(null);
const [deletionTarget, setDeletionTarget] = useState<UserProfile | null>(null);
const superuserGuard = useSuperuserGuard();
useEffect(() => {
if (!roleLoading && permissions?.can_view_all_profiles) {
@@ -193,6 +197,20 @@ export function ProfileManager() {
}
};
// Check if current superuser can delete a specific user
const canDeleteUser = (targetProfile: UserProfile) => {
if (!superuserGuard.isSuperuser) return false;
if (!superuserGuard.canPerformAction) return false;
// Cannot delete other superusers
if (targetProfile.roles.includes('superuser')) return false;
// Cannot delete self
if (targetProfile.user_id === user?.id) return false;
return true;
};
const canManageUser = (targetProfile: UserProfile) => {
if (!permissions) return false;
@@ -336,10 +354,10 @@ export function ProfileManager() {
</div>
</div>
{canManageUser(profile) && (
{(canManageUser(profile) || canDeleteUser(profile)) && (
<div className="flex items-center gap-2">
{/* Ban/Unban Button */}
{permissions.can_ban_any_user && (
{canManageUser(profile) && permissions.can_ban_any_user && (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button
@@ -383,8 +401,21 @@ export function ProfileManager() {
</AlertDialog>
)}
{/* Delete User Button - Superusers Only */}
{canDeleteUser(profile) && (
<Button
variant="destructive"
size="sm"
onClick={() => setDeletionTarget(profile)}
disabled={actionLoading === profile.user_id}
>
<Trash2 className="w-4 h-4 mr-2" />
Delete User
</Button>
)}
{/* Role Management */}
{(permissions.can_manage_moderator_roles || permissions.can_manage_admin_roles) && (
{canManageUser(profile) && (permissions.can_manage_moderator_roles || permissions.can_manage_admin_roles) && (
<Select
onValueChange={(value) => handleRoleChange(profile.user_id, value as UserRole | 'remove', profile.roles)}
disabled={actionLoading === profile.user_id}
@@ -422,6 +453,25 @@ export function ProfileManager() {
)}
</div>
)}
{/* User Deletion Dialog */}
{deletionTarget && (
<AdminUserDeletionDialog
open={!!deletionTarget}
onOpenChange={(open) => !open && setDeletionTarget(null)}
targetUser={{
userId: deletionTarget.user_id,
username: deletionTarget.username,
email: '', // Email not available in profile data
displayName: deletionTarget.display_name || undefined,
roles: deletionTarget.roles
}}
onDeletionComplete={() => {
setDeletionTarget(null);
fetchProfiles();
}}
/>
)}
</div>
);
}