diff --git a/api.php b/api.php index 4a472ce..5bf5795 100644 --- a/api.php +++ b/api.php @@ -38,13 +38,32 @@ function requireAuth() { function requireAdmin() { requireAuth(); - if (($_SESSION['role'] ?? '') !== 'admin') { + $role = $_SESSION['role'] ?? ''; + if ($role !== 'admin' && $role !== 'superadmin') { http_response_code(403); echo json_encode(['error' => 'Vain ylläpitäjä voi tehdä tämän']); exit; } } +function requireSuperAdmin() { + requireAuth(); + if (($_SESSION['role'] ?? '') !== 'superadmin') { + http_response_code(403); + echo json_encode(['error' => 'Vain pääkäyttäjä voi tehdä tämän']); + exit; + } +} + +function isSuperAdmin(): bool { + return ($_SESSION['role'] ?? '') === 'superadmin'; +} + +function isCompanyAdmin(): bool { + $role = $_SESSION['role'] ?? ''; + return $role === 'admin' || $role === 'superadmin'; +} + function currentUser(): string { return $_SESSION['username'] ?? 'tuntematon'; } @@ -884,7 +903,7 @@ switch ($action) { $domainCompany = dbGetCompanyByDomain($host); $domainCompanyId = $domainCompany ? $domainCompany['id'] : ''; // Jos domain kuuluu tietylle yritykselle, vain sen yrityksen käyttäjät + adminit pääsevät sisään - if ($domainCompanyId && $u['role'] !== 'admin' && !in_array($domainCompanyId, $userCompanies)) { + if ($domainCompanyId && $u['role'] !== 'superadmin' && !in_array($domainCompanyId, $userCompanies)) { dbRecordLoginAttempt($ip); http_response_code(403); echo json_encode(['error' => 'Sinulla ei ole oikeutta kirjautua tälle sivustolle.']); @@ -956,6 +975,15 @@ switch ($action) { // Brändäystiedot domain-pohjaisesti (sama kuin branding-endpoint) $host = strtolower(explode(':', $_SERVER['HTTP_HOST'] ?? '')[0]); $branding = dbGetBranding($host); + // Aktiivisen yrityksen enabled_modules + $activeCompanyId = $_SESSION['company_id'] ?? ''; + $enabledModules = []; + foreach ($allCompanies as $comp) { + if ($comp['id'] === $activeCompanyId) { + $enabledModules = $comp['enabled_modules'] ?? []; + break; + } + } echo json_encode([ 'authenticated' => true, 'user_id' => $_SESSION['user_id'], @@ -966,6 +994,7 @@ switch ($action) { 'company_id' => $_SESSION['company_id'] ?? '', 'signatures' => $userSignatures, 'branding' => $branding, + 'enabled_modules' => $enabledModules, ]); } else { echo json_encode(['authenticated' => false]); @@ -1035,7 +1064,7 @@ switch ($action) { // ---------- USERS ---------- case 'users': - requireAdmin(); + requireSuperAdmin(); $users = dbLoadUsers(); $safe = array_map(function($u) { unset($u['password_hash']); @@ -1045,14 +1074,15 @@ switch ($action) { break; case 'user_create': - requireAdmin(); + requireSuperAdmin(); if ($method !== 'POST') break; $input = json_decode(file_get_contents('php://input'), true); $username = trim($input['username'] ?? ''); $password = $input['password'] ?? ''; $nimi = trim($input['nimi'] ?? ''); $email = trim($input['email'] ?? ''); - $role = ($input['role'] ?? 'user') === 'admin' ? 'admin' : 'user'; + $validRoles = ['superadmin', 'admin', 'user']; + $role = in_array($input['role'] ?? '', $validRoles) ? $input['role'] : 'user'; if (empty($username) || empty($password)) { http_response_code(400); echo json_encode(['error' => 'Käyttäjätunnus ja salasana vaaditaan']); @@ -1099,7 +1129,7 @@ switch ($action) { break; case 'user_update': - requireAdmin(); + requireSuperAdmin(); if ($method !== 'POST') break; $input = json_decode(file_get_contents('php://input'), true); $id = $input['id'] ?? ''; @@ -1111,7 +1141,10 @@ switch ($action) { } if (isset($input['nimi'])) $u['nimi'] = trim($input['nimi']); if (isset($input['email'])) $u['email'] = trim($input['email']); - if (isset($input['role'])) $u['role'] = $input['role'] === 'admin' ? 'admin' : 'user'; + if (isset($input['role'])) { + $validRoles = ['superadmin', 'admin', 'user']; + $u['role'] = in_array($input['role'], $validRoles) ? $input['role'] : 'user'; + } if (isset($input['companies'])) { $allCompanies = dbLoadCompanies(); $validIds = array_column($allCompanies, 'id'); @@ -1146,7 +1179,7 @@ switch ($action) { break; case 'user_delete': - requireAdmin(); + requireSuperAdmin(); if ($method !== 'POST') break; $input = json_decode(file_get_contents('php://input'), true); $id = $input['id'] ?? ''; @@ -2297,7 +2330,15 @@ switch ($action) { case 'companies_all': requireAdmin(); - echo json_encode(dbLoadCompanies()); + $all = dbLoadCompanies(); + if (isSuperAdmin()) { + echo json_encode($all); + } else { + // Yritysadmin näkee vain omat yrityksensä + $userCompanyIds = $_SESSION['companies'] ?? []; + $filtered = array_values(array_filter($all, fn($c) => in_array($c['id'], $userCompanyIds))); + echo json_encode($filtered); + } break; case 'all_mailboxes': @@ -2322,7 +2363,7 @@ switch ($action) { break; case 'company_create': - requireAdmin(); + requireSuperAdmin(); if ($method !== 'POST') break; $input = json_decode(file_get_contents('php://input'), true); $id = preg_replace('/[^a-z0-9-]/', '', strtolower(trim($input['id'] ?? ''))); @@ -2374,6 +2415,15 @@ switch ($action) { if ($method !== 'POST') break; $input = json_decode(file_get_contents('php://input'), true); $id = $input['id'] ?? ''; + // Yritysadmin saa muokata vain omia yrityksiään + if (!isSuperAdmin()) { + $userCompanyIds = $_SESSION['companies'] ?? []; + if (!in_array($id, $userCompanyIds)) { + http_response_code(403); + echo json_encode(['error' => 'Ei oikeuksia muokata tätä yritystä']); + break; + } + } $companies = dbLoadCompanies(); $found = false; foreach ($companies as $c) { @@ -2385,6 +2435,9 @@ switch ($action) { } if (isset($input['primary_color'])) $c['primary_color'] = trim($input['primary_color']); if (isset($input['subtitle'])) $c['subtitle'] = trim($input['subtitle']); + if (isset($input['enabled_modules']) && is_array($input['enabled_modules'])) { + $c['enabled_modules'] = array_values($input['enabled_modules']); + } dbSaveCompany($c); $found = true; echo json_encode($c); @@ -2398,7 +2451,7 @@ switch ($action) { break; case 'company_delete': - requireAdmin(); + requireSuperAdmin(); if ($method !== 'POST') break; $input = json_decode(file_get_contents('php://input'), true); $id = $input['id'] ?? ''; diff --git a/db.php b/db.php index 359e7f9..f394261 100644 --- a/db.php +++ b/db.php @@ -137,7 +137,7 @@ function initDatabase(): void { username VARCHAR(100) NOT NULL UNIQUE, password_hash VARCHAR(255) NOT NULL, nimi VARCHAR(255) NOT NULL, - role ENUM('admin','user') DEFAULT 'user', + role ENUM('superadmin','admin','user') DEFAULT 'user', email VARCHAR(255) DEFAULT '', luotu DATETIME ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4", @@ -375,10 +375,21 @@ function initDatabase(): void { "ALTER TABLE tickets ADD COLUMN cc TEXT DEFAULT '' AFTER mailbox_id", "ALTER TABLE tickets ADD COLUMN priority VARCHAR(20) DEFAULT 'normaali' AFTER cc", "ALTER TABLE customers ADD COLUMN priority_emails TEXT DEFAULT '' AFTER lisatiedot", + "ALTER TABLE companies ADD COLUMN enabled_modules TEXT DEFAULT '' AFTER cors_origins", + "ALTER TABLE users MODIFY COLUMN role ENUM('superadmin','admin','user') DEFAULT 'user'", ]; foreach ($alters as $sql) { - try { $db->query($sql); } catch (\Throwable $e) { /* sarake on jo olemassa */ } + try { $db->query($sql); } catch (\Throwable $e) { /* sarake on jo olemassa / jo ajettu */ } } + // Kertaluontoinen migraatio: päivitä vanhat admin-käyttäjät superadminiksi + // (vain jos yhtään superadminia ei vielä ole) + try { + $result = $db->query("SELECT COUNT(*) AS cnt FROM users WHERE role = 'superadmin'"); + $row = $result->fetch_assoc(); + if ((int)($row['cnt'] ?? 0) === 0) { + $db->query("UPDATE users SET role = 'superadmin' WHERE role = 'admin'"); + } + } catch (\Throwable $e) { /* ohitetaan */ } } // ==================== YRITYKSET ==================== @@ -389,6 +400,9 @@ function dbLoadCompanies(): array { foreach ($companies as &$c) { $c['domains'] = _dbFetchColumn("SELECT domain FROM company_domains WHERE company_id = ?", [$c['id']]); $c['aktiivinen'] = (bool)$c['aktiivinen']; + // enabled_modules: JSON-array tai tyhjä (= kaikki päällä) + $raw = $c['enabled_modules'] ?? ''; + $c['enabled_modules'] = $raw ? (json_decode($raw, true) ?: []) : []; } return $companies; } @@ -397,23 +411,27 @@ function dbSaveCompany(array $company): void { $db = getDb(); $db->begin_transaction(); try { + $enabledModules = $company['enabled_modules'] ?? []; + $enabledModulesJson = is_array($enabledModules) ? json_encode($enabledModules) : ($enabledModules ?: ''); _dbExecute(" - INSERT INTO companies (id, nimi, luotu, aktiivinen, primary_color, subtitle, logo_file, api_key, cors_origins) - VALUES (:id, :nimi, :luotu, :aktiivinen, :primary_color, :subtitle, :logo_file, :api_key, :cors_origins) + INSERT INTO companies (id, nimi, luotu, aktiivinen, primary_color, subtitle, logo_file, api_key, cors_origins, enabled_modules) + VALUES (:id, :nimi, :luotu, :aktiivinen, :primary_color, :subtitle, :logo_file, :api_key, :cors_origins, :enabled_modules) ON DUPLICATE KEY UPDATE nimi = VALUES(nimi), aktiivinen = VALUES(aktiivinen), primary_color = VALUES(primary_color), subtitle = VALUES(subtitle), - logo_file = VALUES(logo_file), api_key = VALUES(api_key), cors_origins = VALUES(cors_origins) + logo_file = VALUES(logo_file), api_key = VALUES(api_key), cors_origins = VALUES(cors_origins), + enabled_modules = VALUES(enabled_modules) ", [ - 'id' => $company['id'], - 'nimi' => $company['nimi'], - 'luotu' => $company['luotu'] ?? date('Y-m-d H:i:s'), - 'aktiivinen' => $company['aktiivinen'] ?? true, - 'primary_color' => $company['primary_color'] ?? '#0f3460', - 'subtitle' => $company['subtitle'] ?? '', - 'logo_file' => $company['logo_file'] ?? '', - 'api_key' => $company['api_key'] ?? '', - 'cors_origins' => $company['cors_origins'] ?? '', + 'id' => $company['id'], + 'nimi' => $company['nimi'], + 'luotu' => $company['luotu'] ?? date('Y-m-d H:i:s'), + 'aktiivinen' => $company['aktiivinen'] ?? true, + 'primary_color' => $company['primary_color'] ?? '#0f3460', + 'subtitle' => $company['subtitle'] ?? '', + 'logo_file' => $company['logo_file'] ?? '', + 'api_key' => $company['api_key'] ?? '', + 'cors_origins' => $company['cors_origins'] ?? '', + 'enabled_modules' => $enabledModulesJson, ]); // Päivitä domainit @@ -451,23 +469,27 @@ function dbGetBranding(string $host): array { $logoUrl = !empty($company['logo_file']) ? "api.php?action=company_logo&company_id=" . urlencode($company['id']) : ''; + $rawModules = $company['enabled_modules'] ?? ''; + $enabledModules = $rawModules ? (json_decode($rawModules, true) ?: []) : []; return [ - 'found' => true, - 'company_id' => $company['id'], - 'nimi' => $company['nimi'], - 'primary_color' => $company['primary_color'] ?? '#0f3460', - 'subtitle' => $company['subtitle'] ?? '', - 'logo_url' => $logoUrl, + 'found' => true, + 'company_id' => $company['id'], + 'nimi' => $company['nimi'], + 'primary_color' => $company['primary_color'] ?? '#0f3460', + 'subtitle' => $company['subtitle'] ?? '', + 'logo_url' => $logoUrl, + 'enabled_modules' => $enabledModules, ]; } return [ - 'found' => false, - 'company_id' => '', - 'nimi' => 'Noxus Intra', - 'primary_color' => '#0f3460', - 'subtitle' => 'Hallintapaneeli', - 'logo_url' => '', + 'found' => false, + 'company_id' => '', + 'nimi' => 'Noxus Intra', + 'primary_color' => '#0f3460', + 'subtitle' => 'Hallintapaneeli', + 'logo_url' => '', + 'enabled_modules' => [], ]; } diff --git a/index.html b/index.html index f39e20b..3841068 100644 --- a/index.html +++ b/index.html @@ -606,6 +606,31 @@ + +
Valitse mitkä välilehdet ovat käytössä tässä yrityksessä.
+${esc(c.id)}