Add server ownership, team access, role-based UI, and default admin role
- New migration 002_server_access.sql: created_by column + server_user table - Server model: ownership, permission checks, team management methods - Routes: /team routes, RoleMiddleware on /servers/create and /admin - ServerController: permission checks via server_user, team CRUD methods - TerminalController: server access checks - DashboardController: filter servers by accessible ones - ApiController: server access checks - Views: team.php, team_server.php, sidebar Team link, hide actions by role - Default user role changed from operator to admin on registration - Admin user form defaults to admin role
This commit is contained in:
@@ -62,7 +62,7 @@ class AdminController
|
||||
'username' => $_POST['username'],
|
||||
'email' => $_POST['email'],
|
||||
'password' => $_POST['password'],
|
||||
'role' => $_POST['role'],
|
||||
'role' => $_POST['role'] ?? 'admin',
|
||||
'is_active' => 1,
|
||||
]);
|
||||
|
||||
@@ -159,6 +159,11 @@ class AdminController
|
||||
|
||||
public function securitySettings(): void
|
||||
{
|
||||
if (Session::get('user_role') !== 'super_admin') {
|
||||
Session::setFlash('error', 'You do not have permission to access this page.');
|
||||
$this->view->redirect('/dashboard');
|
||||
}
|
||||
|
||||
$this->view->display('admin.security', [
|
||||
'title' => 'Security Settings - ServerManager',
|
||||
]);
|
||||
|
||||
@@ -76,6 +76,11 @@ class ApiController
|
||||
$filters = [];
|
||||
if ($search) $filters['search'] = $search;
|
||||
|
||||
$accessibleIds = $serverModel->getAccessibleServerIds((int) $user['id']);
|
||||
if (!empty($accessibleIds)) {
|
||||
$filters['accessible_ids'] = $accessibleIds;
|
||||
}
|
||||
|
||||
$servers = $serverModel->getAll($page, $perPage, $filters);
|
||||
|
||||
$this->view->json([
|
||||
@@ -101,6 +106,10 @@ class ApiController
|
||||
$this->view->json(['error' => 'Server not found'], 404);
|
||||
}
|
||||
|
||||
if (!$serverModel->canView($id, (int) $user['id'])) {
|
||||
$this->view->json(['error' => 'Server not found'], 404);
|
||||
}
|
||||
|
||||
$monitoringService = new MonitoringService();
|
||||
$metrics = $monitoringService->getLatestMetrics($id);
|
||||
|
||||
@@ -154,6 +163,10 @@ class ApiController
|
||||
$this->view->json(['error' => 'Server not found'], 404);
|
||||
}
|
||||
|
||||
if (!$serverModel->canManage($id, (int) $user['id'])) {
|
||||
$this->view->json(['error' => 'Forbidden'], 403);
|
||||
}
|
||||
|
||||
$serverModel->update($id, $input);
|
||||
|
||||
$this->view->json([
|
||||
@@ -173,6 +186,10 @@ class ApiController
|
||||
$this->view->json(['error' => 'Server not found'], 404);
|
||||
}
|
||||
|
||||
if (!$serverModel->canManage($id, (int) $user['id'])) {
|
||||
$this->view->json(['error' => 'Forbidden'], 403);
|
||||
}
|
||||
|
||||
$serverModel->delete($id);
|
||||
|
||||
$this->view->json([
|
||||
@@ -185,6 +202,11 @@ class ApiController
|
||||
{
|
||||
$user = $this->authenticateRequest();
|
||||
|
||||
$serverModel = new Server();
|
||||
if (!$serverModel->canView($id, (int) $user['id'])) {
|
||||
$this->view->json(['error' => 'Server not found'], 404);
|
||||
}
|
||||
|
||||
$monitoringService = new MonitoringService();
|
||||
|
||||
$hours = (int) ($_GET['hours'] ?? 24);
|
||||
@@ -202,6 +224,11 @@ class ApiController
|
||||
{
|
||||
$user = $this->authenticateRequest();
|
||||
|
||||
$serverModel = new Server();
|
||||
if (!$serverModel->canView($id, (int) $user['id'])) {
|
||||
$this->view->json(['error' => 'Server not found'], 404);
|
||||
}
|
||||
|
||||
$input = json_decode(file_get_contents('php://input'), true) ?? $_POST;
|
||||
$command = $input['command'] ?? '';
|
||||
|
||||
|
||||
@@ -126,13 +126,13 @@ class AuthController
|
||||
'username' => $username,
|
||||
'email' => $email,
|
||||
'password' => $_POST['password'],
|
||||
'role' => 'operator',
|
||||
'role' => 'admin',
|
||||
'is_active' => 1,
|
||||
]);
|
||||
|
||||
$this->auditService->log('user_registered', 'user', $id, [
|
||||
'username' => $username,
|
||||
'role' => 'operator',
|
||||
'role' => 'admin',
|
||||
]);
|
||||
|
||||
Session::setFlash('success', 'Account created. You can now log in.');
|
||||
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace ServerManager\Controllers;
|
||||
|
||||
use ServerManager\Core\App;
|
||||
use ServerManager\Core\Session;
|
||||
use ServerManager\Models\Server;
|
||||
use ServerManager\Services\MonitoringService;
|
||||
use ServerManager\Services\AuditService;
|
||||
@@ -28,7 +29,13 @@ class DashboardController
|
||||
$recentActivity = $this->auditService->getRecentActivity(10);
|
||||
|
||||
$serverModel = new Server();
|
||||
$userId = (int) Session::get('user_id');
|
||||
$accessibleIds = $serverModel->getAccessibleServerIds($userId);
|
||||
$servers = $serverModel->findAll(true);
|
||||
$servers = array_filter($servers, function ($s) use ($accessibleIds) {
|
||||
return in_array((int) $s['id'], $accessibleIds, true);
|
||||
});
|
||||
$servers = array_values($servers);
|
||||
|
||||
$this->view->display('dashboard.index', [
|
||||
'title' => 'Dashboard - ServerManager',
|
||||
|
||||
@@ -8,6 +8,7 @@ use ServerManager\Core\App;
|
||||
use ServerManager\Core\Session;
|
||||
use ServerManager\Core\Validator;
|
||||
use ServerManager\Models\Server;
|
||||
use ServerManager\Models\User;
|
||||
use ServerManager\Services\SSHService;
|
||||
use ServerManager\Services\MonitoringService;
|
||||
use ServerManager\Services\AuditService;
|
||||
@@ -26,18 +27,47 @@ class ServerController
|
||||
$this->auditService = new AuditService();
|
||||
}
|
||||
|
||||
private function requireServerAccess(int $serverId, string $minRole = 'viewer'): ?array
|
||||
{
|
||||
$userId = (int) Session::get('user_id');
|
||||
$server = $this->serverModel->findById($serverId);
|
||||
|
||||
if (!$server) {
|
||||
$this->view->error(404);
|
||||
}
|
||||
|
||||
$role = $this->serverModel->getUserRole($serverId, $userId);
|
||||
|
||||
if ($role === null) {
|
||||
$this->view->error(404);
|
||||
}
|
||||
|
||||
if ($minRole === 'manager' && $role !== 'owner' && $role !== 'manager') {
|
||||
Session::setFlash('error', 'You do not have permission to perform this action.');
|
||||
$this->view->redirect("/servers/{$serverId}");
|
||||
}
|
||||
|
||||
return $server;
|
||||
}
|
||||
|
||||
public function index(): void
|
||||
{
|
||||
$page = (int) ($_GET['page'] ?? 1);
|
||||
$search = $_GET['search'] ?? '';
|
||||
$status = $_GET['status'] ?? '';
|
||||
$group = $_GET['group'] ?? '';
|
||||
$userId = (int) Session::get('user_id');
|
||||
|
||||
$filters = [];
|
||||
if ($search) $filters['search'] = $search;
|
||||
if ($status) $filters['status'] = $status;
|
||||
if ($group) $filters['group_name'] = $group;
|
||||
|
||||
$accessibleIds = $this->serverModel->getAccessibleServerIds($userId);
|
||||
if (!empty($accessibleIds)) {
|
||||
$filters['accessible_ids'] = $accessibleIds;
|
||||
}
|
||||
|
||||
$servers = $this->serverModel->getAll($page, 15, $filters);
|
||||
$groups = $this->serverModel->getGroups();
|
||||
|
||||
@@ -107,11 +137,9 @@ class ServerController
|
||||
|
||||
public function show(int $id): void
|
||||
{
|
||||
$server = $this->serverModel->findById($id);
|
||||
$server = $this->requireServerAccess($id, 'viewer');
|
||||
|
||||
if (!$server) {
|
||||
$this->view->error(404);
|
||||
}
|
||||
$role = $this->serverModel->getUserRole($id, (int) Session::get('user_id'));
|
||||
|
||||
$monitoringService = new MonitoringService();
|
||||
$latestMetrics = $monitoringService->getLatestMetrics($id);
|
||||
@@ -122,16 +150,13 @@ class ServerController
|
||||
'server' => $server,
|
||||
'metrics' => $latestMetrics,
|
||||
'historicalMetrics' => $historicalMetrics,
|
||||
'server_role' => $role,
|
||||
]);
|
||||
}
|
||||
|
||||
public function edit(int $id): void
|
||||
{
|
||||
$server = $this->serverModel->findById($id);
|
||||
|
||||
if (!$server) {
|
||||
$this->view->error(404);
|
||||
}
|
||||
$server = $this->requireServerAccess($id, 'manager');
|
||||
|
||||
$groups = $this->serverModel->getGroups();
|
||||
|
||||
@@ -144,11 +169,7 @@ class ServerController
|
||||
|
||||
public function update(int $id): void
|
||||
{
|
||||
$server = $this->serverModel->findById($id);
|
||||
|
||||
if (!$server) {
|
||||
$this->view->error(404);
|
||||
}
|
||||
$server = $this->requireServerAccess($id, 'manager');
|
||||
|
||||
$validator = new Validator();
|
||||
$rules = [
|
||||
@@ -194,11 +215,7 @@ class ServerController
|
||||
|
||||
public function delete(int $id): void
|
||||
{
|
||||
$server = $this->serverModel->findById($id);
|
||||
|
||||
if (!$server) {
|
||||
$this->view->error(404);
|
||||
}
|
||||
$server = $this->requireServerAccess($id, 'manager');
|
||||
|
||||
$this->serverModel->delete($id);
|
||||
|
||||
@@ -218,6 +235,12 @@ class ServerController
|
||||
$this->view->json(['success' => false, 'message' => 'Server not found'], 404);
|
||||
}
|
||||
|
||||
$userId = (int) Session::get('user_id');
|
||||
$role = $this->serverModel->getUserRole($id, $userId);
|
||||
if ($role !== 'owner' && $role !== 'manager') {
|
||||
$this->view->json(['success' => false, 'message' => 'Forbidden'], 403);
|
||||
}
|
||||
|
||||
$newState = $this->serverModel->toggleActive($id);
|
||||
|
||||
$this->auditService->log(
|
||||
@@ -236,11 +259,7 @@ class ServerController
|
||||
|
||||
public function testConnection(int $id): void
|
||||
{
|
||||
$server = $this->serverModel->findById($id);
|
||||
|
||||
if (!$server) {
|
||||
$this->view->json(['success' => false, 'message' => 'Server not found'], 404);
|
||||
}
|
||||
$server = $this->requireServerAccess($id, 'viewer');
|
||||
|
||||
$ssh = new SSHService();
|
||||
$result = $ssh->testConnection($server);
|
||||
@@ -255,11 +274,7 @@ class ServerController
|
||||
|
||||
public function processes(int $id): void
|
||||
{
|
||||
$server = $this->serverModel->findById($id);
|
||||
|
||||
if (!$server) {
|
||||
$this->view->error(404);
|
||||
}
|
||||
$server = $this->requireServerAccess($id, 'viewer');
|
||||
|
||||
try {
|
||||
$ssh = new SSHService();
|
||||
@@ -306,11 +321,7 @@ class ServerController
|
||||
|
||||
public function systemInfo(int $id): void
|
||||
{
|
||||
$server = $this->serverModel->findById($id);
|
||||
|
||||
if (!$server) {
|
||||
$this->view->error(404);
|
||||
}
|
||||
$server = $this->requireServerAccess($id, 'viewer');
|
||||
|
||||
try {
|
||||
$ssh = new SSHService();
|
||||
@@ -335,11 +346,7 @@ class ServerController
|
||||
|
||||
public function logs(int $id): void
|
||||
{
|
||||
$server = $this->serverModel->findById($id);
|
||||
|
||||
if (!$server) {
|
||||
$this->view->error(404);
|
||||
}
|
||||
$server = $this->requireServerAccess($id, 'viewer');
|
||||
|
||||
$logType = $_GET['type'] ?? 'syslog';
|
||||
$lines = (int) ($_GET['lines'] ?? 100);
|
||||
@@ -393,6 +400,12 @@ class ServerController
|
||||
$this->view->json(['success' => false, 'message' => 'Server not found'], 404);
|
||||
}
|
||||
|
||||
$userId = (int) Session::get('user_id');
|
||||
$role = $this->serverModel->getUserRole($id, $userId);
|
||||
if ($role !== 'owner' && $role !== 'manager') {
|
||||
$this->view->json(['success' => false, 'message' => 'Forbidden'], 403);
|
||||
}
|
||||
|
||||
try {
|
||||
$ssh = new SSHService();
|
||||
if (!$ssh->connect($server)) {
|
||||
@@ -425,6 +438,12 @@ class ServerController
|
||||
$this->view->json(['success' => false, 'message' => 'Server not found'], 404);
|
||||
}
|
||||
|
||||
$userId = (int) Session::get('user_id');
|
||||
$role = $this->serverModel->getUserRole($id, $userId);
|
||||
if ($role !== 'owner' && $role !== 'manager') {
|
||||
$this->view->json(['success' => false, 'message' => 'Forbidden'], 403);
|
||||
}
|
||||
|
||||
try {
|
||||
$ssh = new SSHService();
|
||||
if (!$ssh->connect($server)) {
|
||||
@@ -457,6 +476,12 @@ class ServerController
|
||||
$this->view->json(['success' => false, 'message' => 'Server not found'], 404);
|
||||
}
|
||||
|
||||
$userId = (int) Session::get('user_id');
|
||||
$role = $this->serverModel->getUserRole($id, $userId);
|
||||
if ($role !== 'owner' && $role !== 'manager') {
|
||||
$this->view->json(['success' => false, 'message' => 'Forbidden'], 403);
|
||||
}
|
||||
|
||||
$service = $_POST['service'] ?? '';
|
||||
if (empty($service)) {
|
||||
$this->view->json(['success' => false, 'message' => 'Service name is required']);
|
||||
@@ -501,6 +526,12 @@ class ServerController
|
||||
$this->view->json(['success' => false, 'message' => 'Server not found'], 404);
|
||||
}
|
||||
|
||||
$userId = (int) Session::get('user_id');
|
||||
$role = $this->serverModel->getUserRole($id, $userId);
|
||||
if ($role !== 'owner' && $role !== 'manager') {
|
||||
$this->view->json(['success' => false, 'message' => 'Forbidden'], 403);
|
||||
}
|
||||
|
||||
$pid = (int) ($_POST['pid'] ?? 0);
|
||||
$signal = (int) ($_POST['signal'] ?? 15);
|
||||
|
||||
@@ -542,11 +573,7 @@ class ServerController
|
||||
|
||||
public function nginx(int $id): void
|
||||
{
|
||||
$server = $this->serverModel->findById($id);
|
||||
|
||||
if (!$server) {
|
||||
$this->view->error(404);
|
||||
}
|
||||
$server = $this->requireServerAccess($id, 'viewer');
|
||||
|
||||
try {
|
||||
$ssh = new SSHService();
|
||||
@@ -601,11 +628,7 @@ class ServerController
|
||||
|
||||
public function nginxGetFile(int $id): void
|
||||
{
|
||||
$server = $this->serverModel->findById($id);
|
||||
|
||||
if (!$server) {
|
||||
$this->view->json(['success' => false, 'message' => 'Server not found'], 404);
|
||||
}
|
||||
$server = $this->requireServerAccess($id, 'viewer');
|
||||
|
||||
$file = $_GET['file'] ?? '';
|
||||
if (empty($file) || str_contains($file, '..')) {
|
||||
@@ -658,6 +681,12 @@ class ServerController
|
||||
$this->view->json(['success' => false, 'message' => 'Server not found'], 404);
|
||||
}
|
||||
|
||||
$userId = (int) Session::get('user_id');
|
||||
$role = $this->serverModel->getUserRole($id, $userId);
|
||||
if ($role !== 'owner' && $role !== 'manager') {
|
||||
$this->view->json(['success' => false, 'message' => 'Forbidden'], 403);
|
||||
}
|
||||
|
||||
$file = $_POST['file'] ?? '';
|
||||
$content = $_POST['content'] ?? '';
|
||||
|
||||
@@ -712,6 +741,12 @@ class ServerController
|
||||
$this->view->json(['success' => false, 'message' => 'Server not found'], 404);
|
||||
}
|
||||
|
||||
$userId = (int) Session::get('user_id');
|
||||
$role = $this->serverModel->getUserRole($id, $userId);
|
||||
if ($role !== 'owner' && $role !== 'manager') {
|
||||
$this->view->json(['success' => false, 'message' => 'Forbidden'], 403);
|
||||
}
|
||||
|
||||
$site = $_POST['site'] ?? '';
|
||||
if (empty($site) || str_contains($site, '..') || str_contains($site, '/')) {
|
||||
$this->view->json(['success' => false, 'message' => 'Invalid site name']);
|
||||
@@ -750,6 +785,12 @@ class ServerController
|
||||
$this->view->json(['success' => false, 'message' => 'Server not found'], 404);
|
||||
}
|
||||
|
||||
$userId = (int) Session::get('user_id');
|
||||
$role = $this->serverModel->getUserRole($id, $userId);
|
||||
if ($role !== 'owner' && $role !== 'manager') {
|
||||
$this->view->json(['success' => false, 'message' => 'Forbidden'], 403);
|
||||
}
|
||||
|
||||
$siteName = trim($_POST['site_name'] ?? '');
|
||||
$siteRoot = trim($_POST['site_root'] ?? '');
|
||||
$serverName = trim($_POST['server_name'] ?? '');
|
||||
@@ -836,6 +877,12 @@ class ServerController
|
||||
$this->view->json(['success' => false, 'message' => 'Server not found'], 404);
|
||||
}
|
||||
|
||||
$userId = (int) Session::get('user_id');
|
||||
$role = $this->serverModel->getUserRole($id, $userId);
|
||||
if ($role !== 'owner' && $role !== 'manager') {
|
||||
$this->view->json(['success' => false, 'message' => 'Forbidden'], 403);
|
||||
}
|
||||
|
||||
$site = $_POST['site'] ?? '';
|
||||
$enable = (bool) ($_POST['enable'] ?? false);
|
||||
|
||||
@@ -879,6 +926,12 @@ class ServerController
|
||||
$this->view->json(['success' => false, 'message' => 'Server not found'], 404);
|
||||
}
|
||||
|
||||
$userId = (int) Session::get('user_id');
|
||||
$role = $this->serverModel->getUserRole($id, $userId);
|
||||
if ($role !== 'owner' && $role !== 'manager') {
|
||||
$this->view->json(['success' => false, 'message' => 'Forbidden'], 403);
|
||||
}
|
||||
|
||||
$action = $_POST['action'] ?? '';
|
||||
$allowed = ['restart', 'reload', 'test', 'start', 'stop'];
|
||||
if (!in_array($action, $allowed, true)) {
|
||||
@@ -921,11 +974,7 @@ class ServerController
|
||||
|
||||
public function ssl(int $id): void
|
||||
{
|
||||
$server = $this->serverModel->findById($id);
|
||||
|
||||
if (!$server) {
|
||||
$this->view->error(404);
|
||||
}
|
||||
$server = $this->requireServerAccess($id, 'viewer');
|
||||
|
||||
try {
|
||||
$ssh = new SSHService();
|
||||
@@ -994,6 +1043,12 @@ class ServerController
|
||||
$this->view->json(['success' => false, 'message' => 'Server not found'], 404);
|
||||
}
|
||||
|
||||
$userId = (int) Session::get('user_id');
|
||||
$role = $this->serverModel->getUserRole($id, $userId);
|
||||
if ($role !== 'owner' && $role !== 'manager') {
|
||||
$this->view->json(['success' => false, 'message' => 'Forbidden'], 403);
|
||||
}
|
||||
|
||||
$action = $_POST['action'] ?? '';
|
||||
$allowed = ['install', 'request', 'renew', 'delete'];
|
||||
if (!in_array($action, $allowed, true)) {
|
||||
@@ -1086,11 +1141,7 @@ class ServerController
|
||||
|
||||
public function database(int $id): void
|
||||
{
|
||||
$server = $this->serverModel->findById($id);
|
||||
|
||||
if (!$server) {
|
||||
$this->view->error(404);
|
||||
}
|
||||
$server = $this->requireServerAccess($id, 'viewer');
|
||||
|
||||
try {
|
||||
$ssh = new SSHService();
|
||||
@@ -1198,6 +1249,12 @@ class ServerController
|
||||
$this->view->json(['success' => false, 'message' => 'Server not found'], 404);
|
||||
}
|
||||
|
||||
$userId = (int) Session::get('user_id');
|
||||
$role = $this->serverModel->getUserRole($id, $userId);
|
||||
if ($role !== 'owner' && $role !== 'manager') {
|
||||
$this->view->json(['success' => false, 'message' => 'Forbidden'], 403);
|
||||
}
|
||||
|
||||
$action = $_POST['action'] ?? '';
|
||||
$allowed = ['query', 'tables', 'structure', 'browse', 'users', 'create_user', 'grant', 'revoke', 'drop_user', 'change_pass'];
|
||||
if (!in_array($action, $allowed, true)) {
|
||||
@@ -1462,9 +1519,15 @@ class ServerController
|
||||
|
||||
public function sidebarServers(): void
|
||||
{
|
||||
$userId = (int) Session::get('user_id');
|
||||
$accessibleIds = $this->serverModel->getAccessibleServerIds($userId);
|
||||
|
||||
$servers = $this->serverModel->findAll();
|
||||
$result = [];
|
||||
foreach ($servers as $s) {
|
||||
if (!in_array((int) $s['id'], $accessibleIds, true)) {
|
||||
continue;
|
||||
}
|
||||
$result[] = [
|
||||
'id' => $s['id'],
|
||||
'name' => $s['name'],
|
||||
@@ -1477,8 +1540,7 @@ class ServerController
|
||||
|
||||
public function services(int $id): void
|
||||
{
|
||||
$server = $this->serverModel->findById($id);
|
||||
if (!$server) { $this->view->error(404); }
|
||||
$server = $this->requireServerAccess($id, 'viewer');
|
||||
|
||||
try {
|
||||
$ssh = new SSHService();
|
||||
@@ -1527,6 +1589,12 @@ class ServerController
|
||||
$this->view->json(['success' => false, 'message' => 'Server not found'], 404);
|
||||
}
|
||||
|
||||
$userId = (int) Session::get('user_id');
|
||||
$role = $this->serverModel->getUserRole($id, $userId);
|
||||
if ($role !== 'owner' && $role !== 'manager') {
|
||||
$this->view->json(['success' => false, 'message' => 'Forbidden'], 403);
|
||||
}
|
||||
|
||||
$action = $_POST['action'] ?? '';
|
||||
$service = $_POST['service'] ?? '';
|
||||
$allowed = ['start', 'stop', 'restart', 'reload', 'enable', 'disable'];
|
||||
@@ -1558,8 +1626,7 @@ class ServerController
|
||||
|
||||
public function systemUsers(int $id): void
|
||||
{
|
||||
$server = $this->serverModel->findById($id);
|
||||
if (!$server) { $this->view->error(404); }
|
||||
$server = $this->requireServerAccess($id, 'viewer');
|
||||
|
||||
try {
|
||||
$ssh = new SSHService();
|
||||
@@ -1614,6 +1681,12 @@ class ServerController
|
||||
$this->view->json(['success' => false, 'message' => 'Server not found'], 404);
|
||||
}
|
||||
|
||||
$userId = (int) Session::get('user_id');
|
||||
$role = $this->serverModel->getUserRole($id, $userId);
|
||||
if ($role !== 'owner' && $role !== 'manager') {
|
||||
$this->view->json(['success' => false, 'message' => 'Forbidden'], 403);
|
||||
}
|
||||
|
||||
$action = $_POST['action'] ?? '';
|
||||
$allowed = ['add', 'delete', 'lock', 'unlock', 'addgroup'];
|
||||
if (!in_array($action, $allowed, true)) {
|
||||
@@ -1723,4 +1796,141 @@ class ServerController
|
||||
'data' => $metrics,
|
||||
]);
|
||||
}
|
||||
|
||||
public function team(): void
|
||||
{
|
||||
$userId = (int) Session::get('user_id');
|
||||
$servers = $this->serverModel->getOwnedServers($userId);
|
||||
|
||||
$result = [];
|
||||
foreach ($servers as $server) {
|
||||
$memberCount = count($this->serverModel->getTeam((int) $server['id']));
|
||||
$result[] = [
|
||||
'id' => $server['id'],
|
||||
'name' => $server['name'],
|
||||
'ip_address' => $server['ip_address'],
|
||||
'member_count' => $memberCount,
|
||||
'status' => $server['current_status'] ?? 'unknown',
|
||||
];
|
||||
}
|
||||
|
||||
$this->view->display('servers.team', [
|
||||
'title' => 'Team Management - ServerManager',
|
||||
'servers' => $result,
|
||||
]);
|
||||
}
|
||||
|
||||
public function teamServer(int $id): void
|
||||
{
|
||||
$userId = (int) Session::get('user_id');
|
||||
$server = $this->serverModel->findById($id);
|
||||
|
||||
if (!$server || (int) $server['created_by'] !== $userId) {
|
||||
$this->view->error(404);
|
||||
}
|
||||
|
||||
$members = $this->serverModel->getTeam($id);
|
||||
|
||||
$userModel = new User();
|
||||
$users = $userModel->getAll(1, 999);
|
||||
$availableUsers = [];
|
||||
$memberIds = array_map(fn($m) => $m['user_id'], $members);
|
||||
foreach ($users['data'] as $u) {
|
||||
if (!in_array((int) $u['id'], $memberIds, true)) {
|
||||
$availableUsers[] = $u;
|
||||
}
|
||||
}
|
||||
|
||||
$this->view->display('servers.team_server', [
|
||||
'title' => 'Team - ' . $server['name'] . ' - ServerManager',
|
||||
'server' => $server,
|
||||
'members' => $members,
|
||||
'availableUsers' => $availableUsers,
|
||||
]);
|
||||
}
|
||||
|
||||
public function addTeamUser(int $id): void
|
||||
{
|
||||
$userId = (int) Session::get('user_id');
|
||||
$server = $this->serverModel->findById($id);
|
||||
|
||||
if (!$server || (int) $server['created_by'] !== $userId) {
|
||||
$this->view->json(['success' => false, 'message' => 'Server not found'], 404);
|
||||
}
|
||||
|
||||
$targetUserId = (int) ($_POST['user_id'] ?? 0);
|
||||
$role = $_POST['role'] ?? 'viewer';
|
||||
|
||||
if ($targetUserId <= 0) {
|
||||
$this->view->json(['success' => false, 'message' => 'Invalid user']);
|
||||
}
|
||||
|
||||
if (!in_array($role, ['viewer', 'manager'], true)) {
|
||||
$this->view->json(['success' => false, 'message' => 'Invalid role']);
|
||||
}
|
||||
|
||||
if ($this->serverModel->addUser($id, $targetUserId, $role)) {
|
||||
$this->auditService->log('team_user_added', 'server', $id, [
|
||||
'target_user_id' => $targetUserId,
|
||||
'role' => $role,
|
||||
]);
|
||||
$this->view->json(['success' => true, 'message' => 'User added to server.']);
|
||||
}
|
||||
|
||||
$this->view->json(['success' => false, 'message' => 'User is already assigned to this server.']);
|
||||
}
|
||||
|
||||
public function removeTeamUser(int $id): void
|
||||
{
|
||||
$userId = (int) Session::get('user_id');
|
||||
$server = $this->serverModel->findById($id);
|
||||
|
||||
if (!$server || (int) $server['created_by'] !== $userId) {
|
||||
$this->view->json(['success' => false, 'message' => 'Server not found'], 404);
|
||||
}
|
||||
|
||||
$targetUserId = (int) ($_POST['user_id'] ?? 0);
|
||||
|
||||
if ($targetUserId <= 0) {
|
||||
$this->view->json(['success' => false, 'message' => 'Invalid user']);
|
||||
}
|
||||
|
||||
$this->serverModel->removeUser($id, $targetUserId);
|
||||
|
||||
$this->auditService->log('team_user_removed', 'server', $id, [
|
||||
'target_user_id' => $targetUserId,
|
||||
]);
|
||||
|
||||
$this->view->json(['success' => true, 'message' => 'User removed from server.']);
|
||||
}
|
||||
|
||||
public function updateTeamUserRole(int $id): void
|
||||
{
|
||||
$userId = (int) Session::get('user_id');
|
||||
$server = $this->serverModel->findById($id);
|
||||
|
||||
if (!$server || (int) $server['created_by'] !== $userId) {
|
||||
$this->view->json(['success' => false, 'message' => 'Server not found'], 404);
|
||||
}
|
||||
|
||||
$targetUserId = (int) ($_POST['user_id'] ?? 0);
|
||||
$role = $_POST['role'] ?? 'viewer';
|
||||
|
||||
if ($targetUserId <= 0) {
|
||||
$this->view->json(['success' => false, 'message' => 'Invalid user']);
|
||||
}
|
||||
|
||||
if (!in_array($role, ['viewer', 'manager'], true)) {
|
||||
$this->view->json(['success' => false, 'message' => 'Invalid role']);
|
||||
}
|
||||
|
||||
$this->serverModel->updateUserRole($id, $targetUserId, $role);
|
||||
|
||||
$this->auditService->log('team_user_role_updated', 'server', $id, [
|
||||
'target_user_id' => $targetUserId,
|
||||
'role' => $role,
|
||||
]);
|
||||
|
||||
$this->view->json(['success' => true, 'message' => 'User role updated.']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,11 @@ class TerminalController
|
||||
$this->view->error(404);
|
||||
}
|
||||
|
||||
$userId = (int) Session::get('user_id');
|
||||
if (!$this->serverModel->canView($id, $userId)) {
|
||||
$this->view->error(404);
|
||||
}
|
||||
|
||||
$commandHistory = new CommandHistory();
|
||||
$history = $commandHistory->getByServer($id, 50);
|
||||
|
||||
@@ -50,6 +55,11 @@ class TerminalController
|
||||
$this->view->json(['success' => false, 'message' => 'Server not found'], 404);
|
||||
}
|
||||
|
||||
$userId = (int) Session::get('user_id');
|
||||
if (!$this->serverModel->canView($id, $userId)) {
|
||||
$this->view->json(['success' => false, 'message' => 'Forbidden'], 403);
|
||||
}
|
||||
|
||||
$command = $_POST['command'] ?? '';
|
||||
|
||||
if (empty($command)) {
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace ServerManager\Models;
|
||||
use ServerManager\Core\Database;
|
||||
use ServerManager\Core\App;
|
||||
use ServerManager\Core\Encryption;
|
||||
use ServerManager\Core\Session;
|
||||
|
||||
class Server
|
||||
{
|
||||
@@ -74,6 +75,13 @@ class Server
|
||||
$params[] = $filters['status'];
|
||||
}
|
||||
|
||||
if (!empty($filters['accessible_ids'])) {
|
||||
$ids = array_map('intval', $filters['accessible_ids']);
|
||||
$placeholders = implode(',', array_fill(0, count($ids), '?'));
|
||||
$where[] = "s.id IN ({$placeholders})";
|
||||
$params = array_merge($params, $ids);
|
||||
}
|
||||
|
||||
$whereClause = !empty($where) ? 'WHERE ' . implode(' AND ', $where) : '';
|
||||
$offset = ($page - 1) * $perPage;
|
||||
|
||||
@@ -121,6 +129,11 @@ class Server
|
||||
$data['current_status'] = 'unknown';
|
||||
}
|
||||
|
||||
$userId = Session::get('user_id');
|
||||
if ($userId && empty($data['created_by'])) {
|
||||
$data['created_by'] = $userId;
|
||||
}
|
||||
|
||||
return $this->db->insert('servers', $data);
|
||||
}
|
||||
|
||||
@@ -186,4 +199,155 @@ class Server
|
||||
);
|
||||
return (int) ($result['total'] ?? 0);
|
||||
}
|
||||
|
||||
public function getUserRole(int $serverId, int $userId): ?string
|
||||
{
|
||||
$server = $this->db->fetch('SELECT created_by FROM servers WHERE id = ?', [$serverId]);
|
||||
if (!$server) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ((int) $server['created_by'] === $userId) {
|
||||
return 'owner';
|
||||
}
|
||||
|
||||
$assignment = $this->db->fetch(
|
||||
'SELECT role FROM server_user WHERE server_id = ? AND user_id = ?',
|
||||
[$serverId, $userId]
|
||||
);
|
||||
|
||||
return $assignment ? $assignment['role'] : null;
|
||||
}
|
||||
|
||||
public function getAccessibleServerIds(int $userId): array
|
||||
{
|
||||
$owned = $this->db->fetchAll(
|
||||
'SELECT id FROM servers WHERE created_by = ?',
|
||||
[$userId]
|
||||
);
|
||||
|
||||
$assigned = $this->db->fetchAll(
|
||||
'SELECT server_id FROM server_user WHERE user_id = ?',
|
||||
[$userId]
|
||||
);
|
||||
|
||||
$ids = [];
|
||||
foreach ($owned as $row) {
|
||||
$ids[] = (int) $row['id'];
|
||||
}
|
||||
foreach ($assigned as $row) {
|
||||
$ids[] = (int) $row['server_id'];
|
||||
}
|
||||
|
||||
return array_unique($ids);
|
||||
}
|
||||
|
||||
public function isOwner(int $serverId, int $userId): bool
|
||||
{
|
||||
$server = $this->db->fetch('SELECT created_by FROM servers WHERE id = ?', [$serverId]);
|
||||
return $server && (int) $server['created_by'] === $userId;
|
||||
}
|
||||
|
||||
public function canManage(int $serverId, int $userId): bool
|
||||
{
|
||||
$role = $this->getUserRole($serverId, $userId);
|
||||
return $role === 'owner' || $role === 'manager';
|
||||
}
|
||||
|
||||
public function canView(int $serverId, int $userId): bool
|
||||
{
|
||||
return $this->getUserRole($serverId, $userId) !== null;
|
||||
}
|
||||
|
||||
public function getTeam(int $serverId): array
|
||||
{
|
||||
$members = $this->db->fetchAll(
|
||||
'SELECT su.*, u.username, u.email
|
||||
FROM server_user su
|
||||
JOIN users u ON su.user_id = u.id
|
||||
WHERE su.server_id = ?
|
||||
ORDER BY su.created_at ASC',
|
||||
[$serverId]
|
||||
);
|
||||
|
||||
$owner = $this->db->fetch(
|
||||
'SELECT u.id, u.username, u.email
|
||||
FROM servers s
|
||||
JOIN users u ON s.created_by = u.id
|
||||
WHERE s.id = ?',
|
||||
[$serverId]
|
||||
);
|
||||
|
||||
$result = [];
|
||||
if ($owner) {
|
||||
$result[] = [
|
||||
'user_id' => (int) $owner['id'],
|
||||
'username' => $owner['username'],
|
||||
'email' => $owner['email'],
|
||||
'role' => 'owner',
|
||||
];
|
||||
}
|
||||
foreach ($members as $m) {
|
||||
$result[] = [
|
||||
'user_id' => (int) $m['user_id'],
|
||||
'username' => $m['username'],
|
||||
'email' => $m['email'],
|
||||
'role' => $m['role'],
|
||||
];
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function addUser(int $serverId, int $userId, string $role): bool
|
||||
{
|
||||
if (!in_array($role, ['viewer', 'manager'], true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$existing = $this->db->fetch(
|
||||
'SELECT id FROM server_user WHERE server_id = ? AND user_id = ?',
|
||||
[$serverId, $userId]
|
||||
);
|
||||
if ($existing) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->db->insert('server_user', [
|
||||
'server_id' => $serverId,
|
||||
'user_id' => $userId,
|
||||
'role' => $role,
|
||||
]);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function removeUser(int $serverId, int $userId): bool
|
||||
{
|
||||
return $this->db->delete(
|
||||
'server_user',
|
||||
'server_id = ? AND user_id = ?',
|
||||
[$serverId, $userId]
|
||||
) > 0;
|
||||
}
|
||||
|
||||
public function updateUserRole(int $serverId, int $userId, string $role): bool
|
||||
{
|
||||
if (!in_array($role, ['viewer', 'manager'], true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->db->update(
|
||||
'server_user',
|
||||
['role' => $role],
|
||||
'server_id = ? AND user_id = ?',
|
||||
[$serverId, $userId]
|
||||
) > 0;
|
||||
}
|
||||
|
||||
public function getOwnedServers(int $userId): array
|
||||
{
|
||||
return $this->db->fetchAll(
|
||||
'SELECT * FROM servers WHERE created_by = ? ORDER BY name ASC',
|
||||
[$userId]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user