Add system users management

This commit is contained in:
2026-06-06 18:08:47 -04:00
parent c3009fbbf7
commit a55542564e
4 changed files with 323 additions and 0 deletions

View File

@@ -1475,6 +1475,157 @@ class ServerController
$this->view->json(['success' => true, 'data' => $result]);
}
public function systemUsers(int $id): void
{
$server = $this->serverModel->findById($id);
if (!$server) { $this->view->error(404); }
try {
$ssh = new SSHService();
if (!$ssh->connect($server)) {
throw new \RuntimeException('Could not establish SSH connection');
}
$this->auditService->logCredentialAccess($id, 'View system users');
$data = [];
$usersResult = $ssh->exec("awk -F: '\$3>=1000||\$3==0{printf \"%s|%s|%s|%s\\n\",\$1,\$3,\$6,\$7}' /etc/passwd 2>/dev/null | sort -t: -k2 -n | head -100");
$users = [];
foreach (explode("\n", trim($usersResult['output'] ?? '')) as $line) {
$parts = explode("|", $line);
if (count($parts) >= 4 && is_numeric($parts[1])) {
$users[] = [
'username' => $parts[0],
'uid' => (int) $parts[1],
'home' => $parts[2],
'shell' => $parts[3],
];
}
}
$data['users'] = $users;
$groupsResult = $ssh->exec("awk -F: '{print \$1}' /etc/group 2>/dev/null | sort | head -50");
$data['groups'] = array_filter(explode("\n", trim($groupsResult['output'] ?? '')));
$totalResult = $ssh->exec("awk -F: '\$3>=1000||\$3==0{print \$1}' /etc/passwd 2>/dev/null | wc -l");
$data['total_users'] = (int) trim($totalResult['output'] ?? '0');
$lastResult = $ssh->exec("last -5 -w 2>/dev/null | head -10 || echo 'No login history'");
$data['last_logins'] = trim($lastResult['output'] ?? '');
$ssh->disconnect();
$this->view->display('servers.system_users', [
'title' => 'System Users - ' . $server['name'] . ' - ServerManager',
'server' => $server,
'sysusers' => $data,
]);
} catch (\Throwable $e) {
Session::setFlash('error', 'Failed to get users: ' . $e->getMessage());
$this->view->redirect("/servers/{$id}");
}
}
public function systemUsersAction(int $id): void
{
$server = $this->serverModel->findById($id);
if (!$server) {
$this->view->json(['success' => false, 'message' => 'Server not found'], 404);
}
$action = $_POST['action'] ?? '';
$allowed = ['add', 'delete', 'lock', 'unlock', 'addgroup'];
if (!in_array($action, $allowed, true)) {
$this->view->json(['success' => false, 'message' => 'Invalid action']);
}
try {
$ssh = new SSHService();
if (!$ssh->connect($server)) {
throw new \RuntimeException('Could not establish SSH connection');
}
$this->auditService->logServerAction($id, "sysuser_{$action}", $action);
if ($action === 'add') {
$username = trim($_POST['username'] ?? '');
$password = trim($_POST['password'] ?? '');
$groups = trim($_POST['groups'] ?? '');
$createHome = !empty($_POST['create_home']);
if (empty($username) || empty($password)) {
$this->view->json(['success' => false, 'message' => 'Username and password required']);
}
if (!preg_match('/^[a-z_][a-z0-9_-]*$/', $username)) {
$this->view->json(['success' => false, 'message' => 'Invalid username']);
}
$homeFlag = $createHome ? '-m' : '-M';
$cmd = "sudo useradd {$homeFlag} -p \$(sudo openssl passwd -6 '{$password}')";
if (!empty($groups)) {
$sanitized = preg_replace('/[^a-z0-9_,-]/', '', $groups);
$cmd .= " -G {$sanitized}";
}
$cmd .= " {$username} 2>&1; echo EXIT:\$?";
$result = $ssh->exec($cmd);
$exitCode = (int) trim(explode('EXIT:', $result['output'] ?? 'EXIT:-1')[1] ?? '-1');
$ssh->disconnect();
$this->view->json([
'success' => $exitCode === 0,
'message' => $exitCode === 0 ? "User '{$username}' created." : 'Failed to create user.',
'output' => trim($result['output']),
]);
} elseif ($action === 'delete') {
$username = trim($_POST['username'] ?? '');
$removeHome = !empty($_POST['remove_home']);
if (empty($username)) {
$this->view->json(['success' => false, 'message' => 'Username required']);
}
$rmFlag = $removeHome ? '-r' : '';
$result = $ssh->exec("sudo userdel {$rmFlag} {$username} 2>&1; echo EXIT:\$?");
$exitCode = (int) trim(explode('EXIT:', $result['output'] ?? 'EXIT:-1')[1] ?? '-1');
$ssh->disconnect();
$this->view->json([
'success' => $exitCode === 0,
'message' => $exitCode === 0 ? "User '{$username}' deleted." : 'Failed to delete user.',
]);
} elseif ($action === 'lock') {
$username = trim($_POST['username'] ?? '');
if (empty($username)) $this->view->json(['success' => false, 'message' => 'Username required']);
$result = $ssh->exec("sudo passwd -l {$username} 2>&1; echo EXIT:\$?");
$exitCode = (int) trim(explode('EXIT:', $result['output'] ?? 'EXIT:-1')[1] ?? '-1');
$ssh->disconnect();
$this->view->json([
'success' => $exitCode === 0,
'message' => $exitCode === 0 ? "User '{$username}' locked." : 'Failed to lock user.',
]);
} elseif ($action === 'unlock') {
$username = trim($_POST['username'] ?? '');
if (empty($username)) $this->view->json(['success' => false, 'message' => 'Username required']);
$result = $ssh->exec("sudo passwd -u {$username} 2>&1; echo EXIT:\$?");
$exitCode = (int) trim(explode('EXIT:', $result['output'] ?? 'EXIT:-1')[1] ?? '-1');
$ssh->disconnect();
$this->view->json([
'success' => $exitCode === 0,
'message' => $exitCode === 0 ? "User '{$username}' unlocked." : 'Failed to unlock user.',
]);
} elseif ($action === 'addgroup') {
$group = trim($_POST['group'] ?? '');
if (empty($group)) $this->view->json(['success' => false, 'message' => 'Group name required']);
$result = $ssh->exec("sudo groupadd -f {$group} 2>&1; echo EXIT:\$?");
$exitCode = (int) trim(explode('EXIT:', $result['output'] ?? 'EXIT:-1')[1] ?? '-1');
$ssh->disconnect();
$this->view->json([
'success' => $exitCode === 0,
'message' => $exitCode === 0 ? "Group '{$group}' created." : 'Failed to create group.',
]);
}
} catch (\Throwable $e) {
$this->view->json(['success' => false, 'message' => 'Failed: ' . $e->getMessage()]);
}
}
public function metrics(int $id): void
{
$monitoringService = new MonitoringService();