diff --git a/api.php b/api.php index d1bcc00..b616dc8 100644 --- a/api.php +++ b/api.php @@ -38,8 +38,7 @@ function requireAuth() { function requireAdmin() { requireAuth(); - $role = $_SESSION['role'] ?? ''; - if ($role !== 'admin' && $role !== 'superadmin') { + if (!isCompanyAdmin()) { http_response_code(403); echo json_encode(['error' => 'Vain ylläpitäjä voi tehdä tämän']); exit; @@ -60,8 +59,8 @@ function isSuperAdmin(): bool { } function isCompanyAdmin(): bool { - $role = $_SESSION['role'] ?? ''; - return $role === 'admin' || $role === 'superadmin'; + if (($_SESSION['role'] ?? '') === 'superadmin') return true; + return ($_SESSION['company_role'] ?? '') === 'admin'; } function currentUser(): string { @@ -103,6 +102,20 @@ function requireCompany(): string { echo json_encode(['error' => 'Ei oikeutta tähän yritykseen']); exit; } + // IP-rajoitus: tarkista vasta kun yrityksen dataa käytetään (superadmin ohittaa) + if (($_SESSION['role'] ?? '') !== 'superadmin') { + $allCompanies = dbLoadCompanies(); + foreach ($allCompanies as $comp) { + if ($comp['id'] === $companyId) { + if (!isIpAllowed(getClientIp(), $comp['allowed_ips'] ?? '')) { + http_response_code(403); + echo json_encode(['error' => 'IP-osoitteesi ei ole sallittu tälle yritykselle.']); + exit; + } + break; + } + } + } return $companyId; } @@ -1329,45 +1342,33 @@ switch ($action) { echo json_encode(['error' => 'Sinulla ei ole oikeutta kirjautua tälle sivustolle.']); break; } - // IP-rajoitus: superadmin ohittaa aina + // IP-rajoitus EI enää estä kirjautumista — tarkistetaan vasta requireCompany():ssa $allCompanies = dbLoadCompanies(); - if ($u['role'] !== 'superadmin') { - $allowedCompanies = []; - foreach ($userCompanies as $ucId) { - foreach ($allCompanies as $comp) { - if ($comp['id'] === $ucId) { - if (isIpAllowed($ip, $comp['allowed_ips'] ?? '')) { - $allowedCompanies[] = $ucId; - } - break; - } - } - } - if (empty($allowedCompanies)) { - dbRecordLoginAttempt($ip); - http_response_code(403); - echo json_encode(['error' => 'IP-osoitteesi (' . $ip . ') ei ole sallittu.']); - break; - } - $userCompanies = $allowedCompanies; - } session_regenerate_id(true); $_SESSION['user_id'] = $u['id']; $_SESSION['username'] = $u['username']; $_SESSION['nimi'] = $u['nimi']; $_SESSION['role'] = $u['role']; $_SESSION['companies'] = $userCompanies; + $_SESSION['company_roles'] = $u['company_roles'] ?? []; // Jos domain matchaa ja käyttäjällä on oikeus -> käytä sitä if ($domainCompanyId && in_array($domainCompanyId, $userCompanies)) { $_SESSION['company_id'] = $domainCompanyId; } else { $_SESSION['company_id'] = !empty($userCompanies) ? $userCompanies[0] : ''; } - // Hae yritysten nimet + // Aseta aktiivisen yrityksen rooli + $_SESSION['company_role'] = $_SESSION['company_roles'][$_SESSION['company_id']] ?? 'user'; + // Hae yritysten nimet + IP-status $companyList = []; foreach ($allCompanies as $comp) { if (in_array($comp['id'], $userCompanies)) { - $companyList[] = ['id' => $comp['id'], 'nimi' => $comp['nimi']]; + $entry = ['id' => $comp['id'], 'nimi' => $comp['nimi']]; + // Merkitse IP-estetyt yritykset (superadmin ohittaa) + if ($u['role'] !== 'superadmin' && !isIpAllowed($ip, $comp['allowed_ips'] ?? '')) { + $entry['ip_blocked'] = true; + } + $companyList[] = $entry; } } echo json_encode([ @@ -1375,6 +1376,7 @@ switch ($action) { 'username' => $u['username'], 'nimi' => $u['nimi'], 'role' => $u['role'], + 'company_role' => $_SESSION['company_role'], 'companies' => $companyList, 'company_id' => $_SESSION['company_id'], 'signatures' => buildSignaturesWithDefaults($u, $u['companies'] ?? []), @@ -1397,37 +1399,29 @@ switch ($action) { $u = dbGetUser($_SESSION['user_id']); if ($u) { $_SESSION['companies'] = $u['companies'] ?? []; + $_SESSION['company_roles'] = $u['company_roles'] ?? []; // Varmista aktiivinen yritys on sallittu if (!in_array($_SESSION['company_id'] ?? '', $_SESSION['companies'])) { $_SESSION['company_id'] = !empty($_SESSION['companies']) ? $_SESSION['companies'][0] : ''; } + // Päivitä aktiivisen yrityksen rooli + $_SESSION['company_role'] = $_SESSION['company_roles'][$_SESSION['company_id'] ?? ''] ?? 'user'; } - // Hae yritysten nimet + IP-rajoitus + // Hae yritysten nimet — EI suodata IP:n perusteella pois, vaan merkitään ip_blocked $userCompanyIds = $_SESSION['companies'] ?? []; $allCompanies = dbLoadCompanies(); $ip = getClientIp(); $companyList = []; foreach ($allCompanies as $comp) { if (in_array($comp['id'], $userCompanyIds)) { - // IP-rajoitus: superadmin ohittaa aina + $entry = ['id' => $comp['id'], 'nimi' => $comp['nimi']]; + // Merkitse IP-estetyt yritykset (superadmin ohittaa) if (($_SESSION['role'] ?? '') !== 'superadmin' && !isIpAllowed($ip, $comp['allowed_ips'] ?? '')) { - continue; + $entry['ip_blocked'] = true; } - $companyList[] = ['id' => $comp['id'], 'nimi' => $comp['nimi']]; + $companyList[] = $entry; } } - // Jos IP-rajoitus poistaa kaikki yritykset (ei superadmin) → kirjaa ulos - if (empty($companyList) && ($_SESSION['role'] ?? '') !== 'superadmin') { - session_destroy(); - echo json_encode(['authenticated' => false]); - break; - } - // Päivitä session companies IP-suodatuksen mukaan - $allowedIds = array_column($companyList, 'id'); - $_SESSION['companies'] = $allowedIds; - if (!in_array($_SESSION['company_id'] ?? '', $allowedIds) && !empty($allowedIds)) { - $_SESSION['company_id'] = $allowedIds[0]; - } // Hae allekirjoitukset (oletus generoituna jos omaa ei ole) $userSignatures = $u ? buildSignaturesWithDefaults($u, $u['companies'] ?? []) : []; // Brändäystiedot domain-pohjaisesti (sama kuin branding-endpoint) @@ -1449,6 +1443,7 @@ switch ($action) { 'nimi' => $_SESSION['nimi'], 'email' => $u['email'] ?? '', 'role' => $_SESSION['role'], + 'company_role' => $_SESSION['company_role'] ?? 'user', 'companies' => $companyList, 'company_id' => $_SESSION['company_id'] ?? '', 'signatures' => $userSignatures, @@ -1549,7 +1544,8 @@ switch ($action) { $nimi = trim($input['nimi'] ?? ''); $email = trim($input['email'] ?? ''); $isSA = ($_SESSION['role'] ?? '') === 'superadmin'; - $validRoles = $isSA ? ['superadmin', 'admin', 'user'] : ['admin', 'user']; + // Globaali rooli: user tai superadmin (admin on nyt yrityskohtainen) + $validRoles = $isSA ? ['superadmin', 'user'] : ['user']; $role = in_array($input['role'] ?? '', $validRoles) ? $input['role'] : 'user'; if (empty($username) || empty($password)) { http_response_code(400); @@ -1583,6 +1579,19 @@ switch ($action) { $signatures[(string)$mbId] = (string)$sig; } } + // Yrityskohtaiset roolit + $companyRoles = []; + if ($isSA && isset($input['company_roles']) && is_array($input['company_roles'])) { + foreach ($input['company_roles'] as $cid => $crole) { + if (in_array($cid, $companies) && in_array($crole, ['admin', 'user'])) { + $companyRoles[$cid] = $crole; + } + } + } elseif (!$isSA) { + // Admin luo käyttäjiä vain omaan yritykseensä -> oletusrooli user + $myCompanyId = $_SESSION['company_id'] ?? ''; + $companyRoles[$myCompanyId] = 'user'; + } $newUser = [ 'id' => generateId(), 'username' => $username, @@ -1591,6 +1600,7 @@ switch ($action) { 'email' => $email, 'role' => $role, 'companies' => $companies, + 'company_roles' => $companyRoles, 'signatures' => $signatures, 'luotu' => date('Y-m-d H:i:s'), ]; @@ -1623,7 +1633,8 @@ switch ($action) { if (isset($input['nimi'])) $u['nimi'] = trim($input['nimi']); if (isset($input['email'])) $u['email'] = trim($input['email']); if (isset($input['role'])) { - $validRoles = $isSA ? ['superadmin', 'admin', 'user'] : ['admin', 'user']; + // Globaali rooli: user tai superadmin (admin on nyt yrityskohtainen) + $validRoles = $isSA ? ['superadmin', 'user'] : ['user']; // Admin ei voi muuttaa superadminia if (!$isSA && ($u['role'] === 'superadmin')) { // Älä muuta roolia @@ -1636,6 +1647,16 @@ switch ($action) { $validIds = array_column($allCompanies, 'id'); $u['companies'] = array_values(array_filter($input['companies'], fn($c) => in_array($c, $validIds))); } + // Yrityskohtaiset roolit + if (isset($input['company_roles']) && is_array($input['company_roles'])) { + $companyRoles = $u['company_roles'] ?? []; + foreach ($input['company_roles'] as $cid => $crole) { + if (in_array($cid, $u['companies'] ?? []) && in_array($crole, ['admin', 'user'])) { + $companyRoles[$cid] = $crole; + } + } + $u['company_roles'] = $companyRoles; + } if (!empty($input['password'])) { $u['password_hash'] = password_hash($input['password'], PASSWORD_DEFAULT); } @@ -1652,12 +1673,15 @@ switch ($action) { // Päivitä sessio jos muokattiin kirjautunutta käyttäjää if ($u['id'] === $_SESSION['user_id']) { $_SESSION['companies'] = $u['companies'] ?? []; + $_SESSION['company_roles'] = $u['company_roles'] ?? []; if (!empty($u['companies']) && !in_array($_SESSION['company_id'] ?? '', $u['companies'])) { $_SESSION['company_id'] = $u['companies'][0]; } if (empty($u['companies'])) { $_SESSION['company_id'] = ''; } + // Päivitä aktiivisen yrityksen rooli + $_SESSION['company_role'] = ($_SESSION['company_roles'][$_SESSION['company_id'] ?? '']) ?? 'user'; } $safe = $u; unset($safe['password_hash']); @@ -3755,7 +3779,13 @@ switch ($action) { } } $_SESSION['company_id'] = $companyId; - echo json_encode(['success' => true, 'company_id' => $companyId]); + // Päivitä aktiivisen yrityksen rooli + $_SESSION['company_role'] = ($_SESSION['company_roles'] ?? [])[$companyId] ?? 'user'; + echo json_encode([ + 'success' => true, + 'company_id' => $companyId, + 'company_role' => $_SESSION['company_role'], + ]); break; case 'company_config': diff --git a/db.php b/db.php index 030febd..20d0e9f 100644 --- a/db.php +++ b/db.php @@ -591,6 +591,7 @@ function initDatabase(): void { "ALTER TABLE mailboxes ADD COLUMN auto_reply_body TEXT AFTER auto_reply_enabled", "ALTER TABLE companies ADD COLUMN allowed_ips TEXT DEFAULT '' AFTER enabled_modules", "ALTER TABLE todos ADD COLUMN category VARCHAR(30) DEFAULT '' AFTER priority", + "ALTER TABLE user_companies ADD COLUMN role VARCHAR(20) DEFAULT 'user' AFTER company_id", ]; foreach ($alters as $sql) { try { $db->query($sql); } catch (\Throwable $e) { /* sarake on jo olemassa / jo ajettu */ } @@ -604,6 +605,17 @@ function initDatabase(): void { $db->query("UPDATE users SET role = 'superadmin' WHERE role = 'admin'"); } } catch (\Throwable $e) { /* ohitetaan */ } + + // Migraatio: kopioi admin-käyttäjien rooli user_companies-tauluun + // (kun role-sarake lisätty, olemassa olevat admin-käyttäjät saavat admin-roolin kaikkiin yrityksiinsä) + try { + $db->query("UPDATE user_companies uc JOIN users u ON u.id = uc.user_id SET uc.role = 'admin' WHERE u.role = 'admin' AND uc.role = 'user'"); + } catch (\Throwable $e) { /* ohitetaan */ } + + // Migraatio: muuta globaali 'admin' → 'user' (admin on nyt yrityskohtainen user_companies.role) + try { + $db->query("UPDATE users SET role = 'user' WHERE role = 'admin'"); + } catch (\Throwable $e) { /* ohitetaan */ } } // ==================== YRITYKSET ==================== @@ -730,6 +742,12 @@ function dbLoadUsers(): array { foreach ($users as &$u) { $u['companies'] = _dbFetchColumn("SELECT company_id FROM user_companies WHERE user_id = ?", [$u['id']]); + // Yrityskohtaiset roolit + $roleRows = _dbFetchAll("SELECT company_id, role FROM user_companies WHERE user_id = ?", [$u['id']]); + $companyRoles = []; + foreach ($roleRows as $rr) { $companyRoles[$rr['company_id']] = $rr['role'] ?? 'user'; } + $u['company_roles'] = $companyRoles; + $sigRows = _dbFetchAll("SELECT mailbox_id, signature FROM user_signatures WHERE user_id = ?", [$u['id']]); $sigs = []; foreach ($sigRows as $row) { @@ -746,6 +764,11 @@ function dbGetUser(string $id): ?array { $u['companies'] = _dbFetchColumn("SELECT company_id FROM user_companies WHERE user_id = ?", [$id]); + $roleRows = _dbFetchAll("SELECT company_id, role FROM user_companies WHERE user_id = ?", [$id]); + $companyRoles = []; + foreach ($roleRows as $rr) { $companyRoles[$rr['company_id']] = $rr['role'] ?? 'user'; } + $u['company_roles'] = $companyRoles; + $sigRows = _dbFetchAll("SELECT mailbox_id, signature FROM user_signatures WHERE user_id = ?", [$id]); $sigs = []; foreach ($sigRows as $row) { @@ -762,6 +785,11 @@ function dbGetUserByUsername(string $username): ?array { $u['companies'] = _dbFetchColumn("SELECT company_id FROM user_companies WHERE user_id = ?", [$u['id']]); + $roleRows = _dbFetchAll("SELECT company_id, role FROM user_companies WHERE user_id = ?", [$u['id']]); + $companyRoles = []; + foreach ($roleRows as $rr) { $companyRoles[$rr['company_id']] = $rr['role'] ?? 'user'; } + $u['company_roles'] = $companyRoles; + $sigRows = _dbFetchAll("SELECT mailbox_id, signature FROM user_signatures WHERE user_id = ?", [$u['id']]); $sigs = []; foreach ($sigRows as $row) { @@ -792,11 +820,13 @@ function dbSaveUser(array $user): void { 'luotu' => $user['luotu'] ?? date('Y-m-d H:i:s'), ]); - // Yritykset + // Yritykset + yrityskohtaiset roolit _dbExecute("DELETE FROM user_companies WHERE user_id = ?", [$user['id']]); if (!empty($user['companies'])) { + $companyRoles = $user['company_roles'] ?? []; foreach ($user['companies'] as $cid) { - _dbExecute("INSERT IGNORE INTO user_companies (user_id, company_id) VALUES (?, ?)", [$user['id'], $cid]); + $role = $companyRoles[$cid] ?? 'user'; + _dbExecute("INSERT IGNORE INTO user_companies (user_id, company_id, role) VALUES (?, ?, ?)", [$user['id'], $cid, $role]); } } diff --git a/index.html b/index.html index 1b47dcb..3b69301 100644 --- a/index.html +++ b/index.html @@ -1943,17 +1943,16 @@ -