import React, { useState, useEffect } from 'react';
import {
Card,
Table,
Button,
Modal,
Form,
Input,
Select,
Switch,
Space,
message,
Tabs,
Tag,
Popconfirm,
Tooltip,
Row,
Col,
Badge,
Tree,
Typography,
Divider
} from 'antd';
import {
UserOutlined,
TeamOutlined,
SecurityScanOutlined,
PlusOutlined,
EditOutlined,
DeleteOutlined,
HistoryOutlined,
UserAddOutlined,
KeyOutlined,
DownloadOutlined
} from '@ant-design/icons';
import axios from 'axios';
import { useAuth } from '../contexts/AuthContext';
const { TabPane } = Tabs;
const { Option } = Select;
const { Text, Title } = Typography;
const { TextArea } = Input;
// Comprehensive Permission tree data structure - Enterprise RBAC
const PERMISSION_TREE = [
{
title: '📊 Dashboard',
key: 'dashboard',
children: [
{ title: 'View Dashboard', key: 'dashboard.read' },
{ title: 'View Statistics', key: 'dashboard.statistics' },
{ title: 'View Performance Metrics', key: 'dashboard.metrics' }
]
},
{
title: '🌐 Frontend Management',
key: 'frontends',
children: [
{ title: 'View Frontends', key: 'frontends.read' },
{ title: 'Create Frontend', key: 'frontends.create' },
{ title: 'Edit Frontend', key: 'frontends.update' },
{ title: 'Delete Frontend', key: 'frontends.delete' },
{ title: 'Toggle Frontend', key: 'frontends.toggle' },
{ title: 'View Frontend History', key: 'frontends.history' }
]
},
{
title: '🔧 Backend Management',
key: 'backends',
children: [
{ title: 'View Backends', key: 'backends.read' },
{ title: 'Create Backend', key: 'backends.create' },
{ title: 'Edit Backend', key: 'backends.update' },
{ title: 'Delete Backend', key: 'backends.delete' },
{ title: 'Toggle Backend', key: 'backends.toggle' },
{ title: 'Manage Servers', key: 'backends.servers' },
{ title: 'View Backend History', key: 'backends.history' }
]
},
{
title: '🛡️ WAF Management',
key: 'waf',
children: [
{ title: 'View WAF Rules', key: 'waf.read' },
{ title: 'Create WAF Rule', key: 'waf.create' },
{ title: 'Edit WAF Rule', key: 'waf.update' },
{ title: 'Delete WAF Rule', key: 'waf.delete' },
{ title: 'Toggle WAF Rule', key: 'waf.toggle' },
{ title: 'View WAF History', key: 'waf.history' }
]
},
{
title: '🔐 SSL Management',
key: 'ssl',
children: [
{ title: 'View SSL Certificates', key: 'ssl.read' },
{ title: 'Upload SSL Certificate', key: 'ssl.create' },
{ title: 'Edit SSL Certificate', key: 'ssl.update' },
{ title: 'Delete SSL Certificate', key: 'ssl.delete' },
{ title: 'Download SSL Certificate', key: 'ssl.download' },
{ title: 'View SSL History', key: 'ssl.history' }
]
},
{
title: '🚀 Apply Management',
key: 'apply',
children: [
{ title: 'View Pending Changes', key: 'apply.read' },
{ title: 'Apply Changes', key: 'apply.execute' },
{ title: 'Reject Changes', key: 'apply.reject' },
{ title: 'View Apply History', key: 'apply.history' },
{ title: 'Bulk Apply', key: 'apply.bulk' },
{ title: 'Emergency Apply', key: 'apply.emergency' }
]
},
{
title: '🤖 Agent Management',
key: 'agents',
children: [
{ title: 'View Agents', key: 'agents.read' },
{ title: 'Create Agent', key: 'agents.create' },
{ title: 'Edit Agent', key: 'agents.update' },
{ title: 'Delete Agent', key: 'agents.delete' },
{ title: 'Generate Agent Script', key: 'agents.script' },
{ title: 'Toggle Agent', key: 'agents.toggle' },
{ title: 'Upgrade Agent', key: 'agents.upgrade' },
{ title: 'View Agent Version Info', key: 'agents.version' },
{ title: 'View Agent Logs', key: 'agents.logs' }
]
},
{
title: '🏢 Cluster Management',
key: 'clusters',
children: [
{ title: 'View Clusters', key: 'clusters.read' },
{ title: 'Create Cluster', key: 'clusters.create' },
{ title: 'Edit Cluster', key: 'clusters.update' },
{ title: 'Delete Cluster', key: 'clusters.delete' },
{ title: 'Switch Cluster', key: 'clusters.switch' },
{ title: 'View Cluster Config', key: 'clusters.config' }
]
},
{
title: '📝 Configuration',
key: 'config',
children: [
{ title: 'View Configuration', key: 'config.read' },
{ title: 'Edit Configuration', key: 'config.update' },
{ title: 'Download Config', key: 'config.download' },
{ title: 'Upload Config', key: 'config.upload' },
{ title: 'Backup Config', key: 'config.backup' },
{ title: 'Restore Config', key: 'config.restore' },
{ title: 'View Config History', key: 'config.history' },
{ title: 'Bulk Import Configurations', key: 'config.bulk_import' },
{ title: 'View Config Request', key: 'config.view_request' },
{ title: 'Download Config Request', key: 'config.download_request' }
]
},
{
title: '👥 User Management',
key: 'users',
children: [
{ title: 'View Users', key: 'users.read' },
{ title: 'Create User', key: 'users.create' },
{ title: 'Edit User', key: 'users.update' },
{ title: 'Delete User', key: 'users.delete' },
{ title: 'Change Password', key: 'users.password' },
{ title: 'Assign Roles', key: 'users.roles' }
]
},
{
title: '🎭 Role Management',
key: 'roles',
children: [
{ title: 'View Roles', key: 'roles.read' },
{ title: 'Create Role', key: 'roles.create' },
{ title: 'Edit Role', key: 'roles.update' },
{ title: 'Delete Role', key: 'roles.delete' },
{ title: 'Manage Permissions', key: 'roles.permissions' }
]
},
{
title: '📈 Statistics & Monitoring',
key: 'statistics',
children: [
{ title: 'View Statistics', key: 'statistics.read' },
{ title: 'View Performance Metrics', key: 'statistics.performance' },
{ title: 'View Agent Status', key: 'statistics.agents' },
{ title: 'View System Health', key: 'statistics.health' },
{ title: 'Export Statistics', key: 'statistics.export' }
]
},
{
title: '📋 Activity Logs',
key: 'activity',
children: [
{ title: 'View Activity Logs', key: 'activity.read' },
{ title: 'View All User Activities', key: 'activity.all' },
{ title: 'Export Activity Logs', key: 'activity.export' }
]
},
{
title: '⚙️ Settings',
key: 'settings',
children: [
{ title: 'View Settings', key: 'settings.read' },
{ title: 'Update Settings', key: 'settings.update' },
{ title: 'System Settings', key: 'settings.system' },
{ title: 'Security Settings', key: 'settings.security' }
]
},
{
title: '🔧 System Administration',
key: 'system',
children: [
{ title: 'System Restart', key: 'system.restart' },
{ title: 'View System Logs', key: 'system.logs' },
{ title: 'Database Management', key: 'system.database' },
{ title: 'Service Management', key: 'system.services' },
{ title: 'Emergency Mode', key: 'system.emergency' }
]
}
];
const UserManagement = () => {
const { isAdmin } = useAuth(); // Get admin status from auth context
const [activeTab, setActiveTab] = useState('users');
// Users state
const [users, setUsers] = useState([]);
const [usersLoading, setUsersLoading] = useState(false);
const [userModalVisible, setUserModalVisible] = useState(false);
const [userForm] = Form.useForm();
const [editingUser, setEditingUser] = useState(null);
const [passwordModalVisible, setPasswordModalVisible] = useState(false);
const [passwordForm] = Form.useForm();
// Roles state
const [roles, setRoles] = useState([]);
const [rolesLoading, setRolesLoading] = useState(false);
const [roleModalVisible, setRoleModalVisible] = useState(false);
const [roleForm] = Form.useForm();
const [editingRole, setEditingRole] = useState(null);
const [checkedPermissions, setCheckedPermissions] = useState([]);
// Role assignment state
const [roleAssignmentModalVisible, setRoleAssignmentModalVisible] = useState(false);
const [assignmentForm] = Form.useForm();
const [selectedUser, setSelectedUser] = useState(null);
// Activity logs state
const [activities, setActivities] = useState([]);
const [activitiesLoading, setActivitiesLoading] = useState(false);
// Cluster state for role assignment
const [clusters, setClusters] = useState([]);
const [selectedClusters, setSelectedClusters] = useState([]);
// Search states
const [userSearchText, setUserSearchText] = useState('');
const [filteredUsers, setFilteredUsers] = useState([]);
const [roleSearchText, setRoleSearchText] = useState('');
const [filteredRoles, setFilteredRoles] = useState([]);
const [activitySearchText, setActivitySearchText] = useState('');
const [filteredActivities, setFilteredActivities] = useState([]);
// Fetch data on component mount
useEffect(() => {
fetchUsers();
fetchRoles();
fetchUserActivity();
fetchClusters();
}, []);
// Update filtered data when original data changes
useEffect(() => {
setFilteredUsers(users);
}, [users]);
useEffect(() => {
setFilteredRoles(roles);
}, [roles]);
useEffect(() => {
setFilteredActivities(activities);
}, [activities]);
// Search handlers
const handleUserSearch = (value) => {
setUserSearchText(value);
if (!value) {
setFilteredUsers(users);
} else {
const filtered = users.filter(user =>
user.username.toLowerCase().includes(value.toLowerCase()) ||
user.email.toLowerCase().includes(value.toLowerCase()) ||
user.full_name?.toLowerCase().includes(value.toLowerCase())
);
setFilteredUsers(filtered);
}
};
const handleRoleSearch = (value) => {
setRoleSearchText(value);
if (!value) {
setFilteredRoles(roles);
} else {
const filtered = roles.filter(role =>
role.name.toLowerCase().includes(value.toLowerCase()) ||
role.description?.toLowerCase().includes(value.toLowerCase())
);
setFilteredRoles(filtered);
}
};
const handleActivitySearch = (value) => {
setActivitySearchText(value);
if (!value) {
setFilteredActivities(activities);
} else {
const filtered = activities.filter(activity => {
const searchLower = value.toLowerCase();
// Search in all text fields
const actionMatch = activity.action?.toLowerCase().includes(searchLower);
const usernameMatch = activity.username?.toLowerCase().includes(searchLower);
const resourceMatch = activity.resource?.toLowerCase().includes(searchLower);
const resourceTypeMatch = activity.resource_type?.toLowerCase().includes(searchLower);
const ipMatch = activity.ip_address?.toLowerCase().includes(searchLower);
// Search in timestamp (formatted date)
let timestampMatch = false;
const dateValue = activity.timestamp || activity.created_at;
if (dateValue) {
try {
let parsedDate;
if (typeof dateValue === 'string') {
parsedDate = new Date(dateValue.includes('T') && !dateValue.endsWith('Z') ? dateValue + 'Z' : dateValue);
} else {
parsedDate = new Date(dateValue);
}
if (!isNaN(parsedDate.getTime())) {
timestampMatch = parsedDate.toLocaleString().toLowerCase().includes(searchLower);
}
} catch (e) {
// Ignore date parsing errors for search
}
}
// Search in details JSON
let detailsMatch = false;
if (activity.details) {
try {
const detailsString = typeof activity.details === 'string'
? activity.details
: JSON.stringify(activity.details);
detailsMatch = detailsString.toLowerCase().includes(searchLower);
} catch (e) {
// Ignore JSON parsing errors for search
}
}
return actionMatch || usernameMatch || resourceMatch || resourceTypeMatch ||
ipMatch || timestampMatch || detailsMatch;
});
setFilteredActivities(filtered);
}
};
// Export activities to CSV
const exportActivitiesToCSV = () => {
try {
// Prepare CSV headers
const headers = ['User', 'Email', 'Action', 'Resource Type', 'Resource ID', 'IP Address', 'Time', 'Details'];
// Prepare CSV rows
const rows = filteredActivities.map(activity => {
const dateValue = activity.timestamp || activity.created_at;
let formattedDate = 'N/A';
if (dateValue) {
try {
let parsedDate;
if (typeof dateValue === 'string') {
parsedDate = new Date(dateValue.includes('T') && !dateValue.endsWith('Z') ? dateValue + 'Z' : dateValue);
} else {
parsedDate = new Date(dateValue);
}
if (!isNaN(parsedDate.getTime())) {
formattedDate = parsedDate.toLocaleString();
}
} catch (e) {
// Keep default N/A
}
}
// Format details as readable string
let detailsString = '';
if (activity.details) {
try {
const details = typeof activity.details === 'string'
? JSON.parse(activity.details)
: activity.details;
detailsString = Object.entries(details)
.map(([key, value]) => `${key}: ${value}`)
.join('; ');
} catch (e) {
detailsString = String(activity.details);
}
}
return [
activity.username || 'N/A',
activity.email || 'N/A',
activity.action || 'N/A',
activity.resource_type || 'N/A',
activity.resource_id || 'N/A',
activity.ip_address || 'N/A',
formattedDate,
detailsString
];
});
// Create CSV content
const csvContent = [
headers.join(','),
...rows.map(row => row.map(cell => `"${String(cell).replace(/"/g, '""')}"`).join(','))
].join('\n');
// Create download link
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
const link = document.createElement('a');
const url = URL.createObjectURL(blob);
link.setAttribute('href', url);
link.setAttribute('download', `user_activities_${new Date().toISOString().split('T')[0]}.csv`);
link.style.visibility = 'hidden';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
message.success(`Exported ${filteredActivities.length} activities to CSV`);
} catch (error) {
console.error('Error exporting activities:', error);
message.error('Failed to export activities to CSV');
}
};
// ==== CLUSTERS MANAGEMENT ====
const fetchClusters = async () => {
try {
const response = await axios.get('/api/clusters');
setClusters(response.data.clusters || []);
} catch (error) {
console.error('Failed to fetch clusters:', error);
// Don't show error message for clusters as it's not critical
}
};
// ==== USERS MANAGEMENT ====
const fetchUsers = async () => {
setUsersLoading(true);
try {
const response = await axios.get('/api/users');
setUsers(response.data.users);
} catch (error) {
message.error('Failed to fetch users: ' + error.message);
} finally {
setUsersLoading(false);
}
};
const handleCreateUser = () => {
setEditingUser(null);
userForm.resetFields();
setUserModalVisible(true);
};
const handleEditUser = (user) => {
setEditingUser(user);
userForm.setFieldsValue({
username: user.username,
email: user.email,
full_name: user.full_name,
phone: user.phone,
is_active: user.is_active,
is_verified: user.is_verified,
role_ids: user.roles ? user.roles.map(role => role.id) : []
});
setUserModalVisible(true);
};
const handleUserSubmit = async (values) => {
try {
if (editingUser) {
await axios.put(`/api/users/${editingUser.id}`, values);
message.success('User updated successfully');
} else {
await axios.post('/api/users', values);
message.success('User created successfully');
}
setUserModalVisible(false);
fetchUsers();
} catch (error) {
message.error(error.response?.data?.detail || 'Operation failed');
}
};
const handleDeleteUser = async (user) => {
try {
await axios.delete(`/api/users/${user.id}`);
message.success(`User '${user.username}' deleted successfully`);
fetchUsers();
} catch (error) {
message.error(error.response?.data?.detail || 'Failed to delete user');
}
};
const handleChangePassword = (user) => {
setSelectedUser(user);
passwordForm.resetFields();
setPasswordModalVisible(true);
};
const handlePasswordSubmit = async (values) => {
try {
await axios.put(`/api/users/${selectedUser.id}/password`, values);
message.success('Password changed successfully');
setPasswordModalVisible(false);
} catch (error) {
message.error(error.response?.data?.detail || 'Failed to change password');
}
};
const handleAssignRoles = (user) => {
setSelectedUser(user);
assignmentForm.setFieldsValue({
role_ids: user.roles.map(role => role.id)
});
setRoleAssignmentModalVisible(true);
};
const handleRoleAssignmentSubmit = async (values) => {
try {
await axios.post('/api/user-roles', {
user_id: selectedUser.id,
role_ids: values.role_ids || []
});
message.success('Roles assigned successfully');
setRoleAssignmentModalVisible(false);
fetchUsers();
} catch (error) {
message.error(error.response?.data?.detail || 'Failed to assign roles');
}
};
// ==== ROLES MANAGEMENT ====
const fetchRoles = async () => {
setRolesLoading(true);
try {
const response = await axios.get('/api/roles');
// Parse JSON strings for permissions and cluster_ids
const parsedRoles = response.data.roles.map(role => ({
...role,
permissions: typeof role.permissions === 'string' ? JSON.parse(role.permissions) : (role.permissions || []),
cluster_ids: typeof role.cluster_ids === 'string' ? JSON.parse(role.cluster_ids) : (role.cluster_ids || null)
}));
setRoles(parsedRoles);
} catch (error) {
message.error('Failed to fetch roles: ' + error.message);
} finally {
setRolesLoading(false);
}
};
const handleCreateRole = () => {
setEditingRole(null);
roleForm.resetFields();
setCheckedPermissions([]);
setRoleModalVisible(true);
};
const handleEditRole = (role) => {
setEditingRole(role);
roleForm.setFieldsValue({
name: role.name,
display_name: role.display_name,
description: role.description,
is_active: role.is_active,
cluster_ids: role.cluster_ids || []
});
// Convert permissions array to checked keys array
const permissions = role.permissions || [];
setCheckedPermissions(Array.isArray(permissions) ? permissions : []);
setRoleModalVisible(true);
};
const handleRoleSubmit = async (values) => {
// Use checked permissions as array (new format)
const roleData = {
...values,
permissions: checkedPermissions,
cluster_ids: values.cluster_ids || null
};
try {
if (editingRole) {
await axios.put(`/api/roles/${editingRole.id}`, roleData);
message.success('Role updated successfully');
} else {
await axios.post('/api/roles', roleData);
message.success('Role created successfully');
}
setRoleModalVisible(false);
roleForm.resetFields();
setCheckedPermissions([]);
fetchRoles();
} catch (error) {
message.error(error.response?.data?.detail || 'Operation failed');
}
};
const handleDeleteRole = async (role) => {
try {
await axios.delete(`/api/roles/${role.id}`);
message.success(`Role '${role.display_name}' deleted successfully`);
fetchRoles();
} catch (error) {
message.error(error.response?.data?.detail || 'Failed to delete role');
}
};
// ==== ACTIVITY LOGS ====
const fetchUserActivity = async () => {
setActivitiesLoading(true);
try {
const token = localStorage.getItem('token');
console.log('UserManagement token check:', token ? `Token exists (${token.length} chars)` : 'Token is null/empty');
// Check for null, empty, or 'null' string
if (!token || token === 'null' || token === 'undefined' || token.trim() === '') {
console.error('No valid authentication token found for UserManagement');
message.error('Authentication required. Please login again.');
window.location.href = '/login';
return;
}
// Fetch user activity from backend
console.log('🔍 ACTIVITY DEBUG: Fetching user activities with token:', token ? 'Token exists' : 'No token');
const response = await axios.get('/api/user-activity', {
headers: { Authorization: `Bearer ${token}` }
});
console.log('🔍 ACTIVITY DEBUG: API Response:', response.data);
console.log('🔍 ACTIVITY DEBUG: Activities count:', response.data.activities?.length || 0);
console.log('🔍 ACTIVITY DEBUG: Total from API:', response.data.total || 0);
setActivities(response.data.activities || []);
} catch (error) {
console.error('🔍 ACTIVITY DEBUG: Error in fetchUserActivity:', error);
console.error('🔍 ACTIVITY DEBUG: Error response:', error.response?.data);
console.error('🔍 ACTIVITY DEBUG: Error status:', error.response?.status);
setActivities([]);
} finally {
setActivitiesLoading(false);
}
};
// Table columns definitions
const userColumns = [
{
title: 'Username',
dataIndex: 'username',
key: 'username',
render: (text, record) => (