396 lines
16 KiB
PHP
396 lines
16 KiB
PHP
<?php
|
||
require_once __DIR__ . '/guard.php';
|
||
$me = require_login(); // frigiver sessionlåsen
|
||
require_once __DIR__ . '/db.php';
|
||
|
||
function is_admin(array $me): bool { return isset($me['role']) && $me['role'] === 'admin'; }
|
||
function is_mod(array $me): bool { return isset($me['role']) && $me['role'] === 'mod'; }
|
||
|
||
$settingsFile = dirname(__DIR__) . '/data/settings.json';
|
||
$settings = file_exists($settingsFile) ? (json_decode(file_get_contents($settingsFile), true) ?: []) : ['watchdog'=>true,'timeout'=>120];
|
||
|
||
$db = db();
|
||
|
||
$msg_settings = '';
|
||
$msg_self = '';
|
||
$msg_admin = '';
|
||
$err_self = '';
|
||
$err_admin = '';
|
||
|
||
/** Helper: hent bruger efter id */
|
||
function fetch_user(PDO $db, int $id): ?array {
|
||
$st = $db->prepare('SELECT * FROM users WHERE id = ? LIMIT 1');
|
||
$st->execute([$id]);
|
||
$u = $st->fetch(PDO::FETCH_ASSOC);
|
||
return $u ?: null;
|
||
}
|
||
|
||
/** Helper: antal admins (bruges til at undgå at fjerne sidste admin) */
|
||
function count_admins(PDO $db): int {
|
||
$st = $db->query("SELECT COUNT(*) FROM users WHERE role = 'admin'");
|
||
return (int)$st->fetchColumn();
|
||
}
|
||
|
||
/* ============================================================
|
||
1) System-indstillinger (watchdog / timeout)
|
||
============================================================ */
|
||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['__action'] ?? '') === 'save_settings') {
|
||
$settings['watchdog'] = isset($_POST['watchdog']);
|
||
$settings['timeout'] = max(30, (int)($_POST['timeout'] ?? 120));
|
||
file_put_contents($settingsFile, json_encode($settings, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
||
$msg_settings = 'Indstillinger gemt.';
|
||
}
|
||
|
||
/* ============================================================
|
||
2) Egen profil – alle roller (user/mod/admin)
|
||
- Skift brugernavn
|
||
- Skift password (kræver nuværende)
|
||
============================================================ */
|
||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['__action'] ?? '') === 'self_update') {
|
||
$newUser = trim($_POST['self_username'] ?? '');
|
||
$curPass = $_POST['self_current'] ?? '';
|
||
$new1 = $_POST['self_new1'] ?? '';
|
||
$new2 = $_POST['self_new2'] ?? '';
|
||
|
||
// Valider brugernavn hvis ændres
|
||
if ($newUser === '') {
|
||
$err_self = 'Brugernavn må ikke være tomt.';
|
||
} elseif (!preg_match('/^[A-Za-z0-9_][A-Za-z0-9_.-]{2,31}$/', $newUser)) {
|
||
$err_self = 'Ugyldigt brugernavn (tilladte tegn: A-Z, 0-9, _, ., - og min. længde 3).';
|
||
} else {
|
||
// Hent nuværende bruger
|
||
$user = fetch_user($db, (int)$me['id']);
|
||
if (!$user) {
|
||
$err_self = 'Bruger ikke fundet.';
|
||
} else {
|
||
// Ændr brugernavn hvis ændret
|
||
if ($user['username'] !== $newUser) {
|
||
try {
|
||
$upd = $db->prepare('UPDATE users SET username = ? WHERE id = ?');
|
||
$upd->execute([$newUser, (int)$me['id']]);
|
||
|
||
// Opdater sessionens brugernavn
|
||
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
|
||
if (isset($_SESSION['user'])) {
|
||
$_SESSION['user']['username'] = $newUser;
|
||
}
|
||
session_write_close();
|
||
|
||
$msg_self .= ' Brugernavn opdateret.';
|
||
} catch (Throwable $e) {
|
||
if (strpos($e->getMessage(), 'UNIQUE') !== false) {
|
||
$err_self = 'Brugernavn findes allerede.';
|
||
} else {
|
||
$err_self = 'Fejl (brugernavn): ' . $e->getMessage();
|
||
}
|
||
}
|
||
}
|
||
|
||
// Skift password hvis felterne er udfyldt
|
||
$wantsPasswordChange = ($curPass !== '' || $new1 !== '' || $new2 !== '');
|
||
if (!$err_self && $wantsPasswordChange) {
|
||
if ($curPass === '' || $new1 === '' || $new2 === '') {
|
||
$err_self = 'Udfyld alle password-felter.';
|
||
} elseif ($new1 !== $new2) {
|
||
$err_self = 'De nye adgangskoder matcher ikke.';
|
||
} elseif (strlen($new1) < 6) {
|
||
$err_self = 'Vælg en adgangskode på mindst 6 tegn.';
|
||
} else {
|
||
// Verificer nuværende password (understøt både 'password' og legacy 'password_hash')
|
||
$validCurrent = false;
|
||
if (!empty($user['password']) && password_verify($curPass, $user['password'])) {
|
||
$validCurrent = true;
|
||
} elseif (!empty($user['password_hash'])) {
|
||
$ph = $user['password_hash'];
|
||
if (strpos($ph, '$') !== false) {
|
||
[$salt, $hash] = explode('$', $ph, 2);
|
||
$validCurrent = (hash('sha256', $salt . $curPass) === $hash);
|
||
} else {
|
||
$validCurrent = (hash('sha256', $curPass) === $ph);
|
||
}
|
||
}
|
||
if (!$validCurrent) {
|
||
$err_self = 'Nuværende adgangskode er forkert.';
|
||
} else {
|
||
$newHash = password_hash($new1, PASSWORD_DEFAULT);
|
||
$upd = $db->prepare('UPDATE users SET password = ?, password_hash = NULL WHERE id = ?');
|
||
$upd->execute([$newHash, (int)$me['id']]);
|
||
$msg_self .= ' Adgangskode opdateret.';
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/* ============================================================
|
||
3) Admin-only: Opret, Rediger, Slet bruger
|
||
- Opret: username + role + password
|
||
- Rediger: username, role, (valgfrit) reset password
|
||
- Slet: ikke sig selv, og efterlad altid mindst 1 admin
|
||
============================================================ */
|
||
if (is_admin($me)) {
|
||
$adminAction = $_POST['__action'] ?? '';
|
||
|
||
// Opret bruger
|
||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && $adminAction === 'create_user') {
|
||
$u = trim($_POST['new_username'] ?? '');
|
||
$p1 = $_POST['new_password1'] ?? '';
|
||
$p2 = $_POST['new_password2'] ?? '';
|
||
$role = $_POST['new_role'] ?? 'user';
|
||
if (!in_array($role, ['admin', 'mod', 'user'], true)) $role = 'user';
|
||
|
||
if ($u === '' || $p1 === '' || $p2 === '') {
|
||
$err_admin = 'Udfyld alle felter.';
|
||
} elseif (!preg_match('/^[A-Za-z0-9_][A-Za-z0-9_.-]{2,31}$/', $u)) {
|
||
$err_admin = 'Ugyldigt brugernavn.';
|
||
} elseif ($p1 !== $p2) {
|
||
$err_admin = 'Adgangskoder matcher ikke.';
|
||
} elseif (strlen($p1) < 6) {
|
||
$err_admin = 'Adgangskode skal være mindst 6 tegn.';
|
||
} else {
|
||
try {
|
||
$hash = password_hash($p1, PASSWORD_DEFAULT);
|
||
$st = $db->prepare('INSERT INTO users(username, password, role) VALUES(?,?,?)');
|
||
$st->execute([$u, $hash, $role]);
|
||
$msg_admin = "Bruger @$u oprettet.";
|
||
} catch (Throwable $e) {
|
||
if (strpos($e->getMessage(), 'UNIQUE') !== false) {
|
||
$err_admin = 'Brugernavn findes allerede.';
|
||
} else {
|
||
$err_admin = 'Fejl: ' . $e->getMessage();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Redigér bruger
|
||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && $adminAction === 'update_user') {
|
||
$id = (int)($_POST['edit_id'] ?? 0);
|
||
$u = trim($_POST['edit_username'] ?? '');
|
||
$role = $_POST['edit_role'] ?? 'user';
|
||
$p1 = $_POST['edit_password1'] ?? '';
|
||
$p2 = $_POST['edit_password2'] ?? '';
|
||
|
||
if ($id <= 0) {
|
||
$err_admin = 'Ugyldigt bruger-ID.';
|
||
} else {
|
||
$user = fetch_user($db, $id);
|
||
if (!$user) {
|
||
$err_admin = 'Bruger findes ikke.';
|
||
} else {
|
||
if (!in_array($role, ['admin', 'mod', 'user'], true)) $role = 'user';
|
||
|
||
// Forhindre at man fjerner sidste admin-rolle
|
||
$isChangingRole = ($user['role'] !== $role);
|
||
if ($isChangingRole && $user['role'] === 'admin') {
|
||
if (count_admins($db) <= 1) {
|
||
$err_admin = 'Du kan ikke fjerne rollen fra den sidste admin.';
|
||
}
|
||
}
|
||
|
||
if (!$err_admin) {
|
||
// Opdatér brugernavn + rolle
|
||
if ($u === '' || !preg_match('/^[A-Za-z0-9_][A-Za-z0-9_.-]{2,31}$/', $u)) {
|
||
$err_admin = 'Ugyldigt brugernavn.';
|
||
} else {
|
||
try {
|
||
$upd = $db->prepare('UPDATE users SET username = ?, role = ? WHERE id = ?');
|
||
$upd->execute([$u, $role, $id]);
|
||
|
||
// Opdatér password hvis udfyldt
|
||
if ($p1 !== '' || $p2 !== '') {
|
||
if ($p1 !== $p2) {
|
||
$err_admin = 'Nye adgangskoder matcher ikke.';
|
||
} elseif (strlen($p1) < 6) {
|
||
$err_admin = 'Adgangskode skal være mindst 6 tegn.';
|
||
} else {
|
||
$hash = password_hash($p1, PASSWORD_DEFAULT);
|
||
$db->prepare('UPDATE users SET password = ?, password_hash = NULL WHERE id = ?')
|
||
->execute([$hash, $id]);
|
||
$msg_admin .= ' Password opdateret for @' . htmlspecialchars($u) . '.';
|
||
}
|
||
}
|
||
|
||
if (!$err_admin) {
|
||
$msg_admin = ($msg_admin ?: 'Bruger opdateret.');
|
||
}
|
||
} catch (Throwable $e) {
|
||
if (strpos($e->getMessage(), 'UNIQUE') !== false) {
|
||
$err_admin = 'Brugernavn findes allerede.';
|
||
} else {
|
||
$err_admin = 'Fejl: ' . $e->getMessage();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Slet bruger
|
||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && $adminAction === 'delete_user') {
|
||
$id = (int)($_POST['del_id'] ?? 0);
|
||
if ($id <= 0) {
|
||
$err_admin = 'Ugyldigt bruger-ID.';
|
||
} elseif ($id === (int)$me['id']) {
|
||
$err_admin = 'Du kan ikke slette dig selv.';
|
||
} else {
|
||
$user = fetch_user($db, $id);
|
||
if (!$user) {
|
||
$err_admin = 'Bruger findes ikke.';
|
||
} else {
|
||
// Undgå at slette sidste admin
|
||
if ($user['role'] === 'admin' && count_admins($db) <= 1) {
|
||
$err_admin = 'Du kan ikke slette den sidste admin.';
|
||
} else {
|
||
$db->prepare('DELETE FROM users WHERE id = ?')->execute([$id]);
|
||
$msg_admin = 'Bruger @' . htmlspecialchars($user['username']) . ' slettet.';
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Hent liste over brugere (til visning for admin)
|
||
$users = [];
|
||
if (is_admin($me)) {
|
||
$q = $db->query('SELECT id, username, role FROM users ORDER BY username COLLATE NOCASE ASC');
|
||
$users = $q->fetchAll(PDO::FETCH_ASSOC);
|
||
}
|
||
?>
|
||
<!DOCTYPE html>
|
||
<html lang="da">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<title>Indstillinger</title>
|
||
<link rel="stylesheet" href="style.css">
|
||
<script>
|
||
function confirmDelete(u){
|
||
return confirm("Slet bruger @" + u + "? Dette kan ikke fortrydes.");
|
||
}
|
||
</script>
|
||
</head>
|
||
<body>
|
||
<div class="wrap">
|
||
|
||
<!-- Systemindstillinger -->
|
||
<div class="card">
|
||
<h2>⚙️ Indstillinger</h2>
|
||
<p><a class="btn" href="index.php">← Tilbage</a></p>
|
||
|
||
<?php if ($msg_settings): ?><p class="notice"><?php echo htmlspecialchars($msg_settings); ?></p><?php endif; ?>
|
||
|
||
<form method="post" class="small" style="margin-bottom:1rem">
|
||
<input type="hidden" name="__action" value="save_settings">
|
||
<label>
|
||
<input type="checkbox" name="watchdog" <?php echo !empty($settings['watchdog']) ? 'checked' : ''; ?>>
|
||
Aktivér watchdog (genopretter forbindelse ved inaktivitet)
|
||
</label>
|
||
<br>
|
||
<label>Timeout (sekunder)
|
||
<input type="number" name="timeout" min="30" value="<?php echo (int)($settings['timeout'] ?? 120); ?>">
|
||
</label>
|
||
<br><br>
|
||
<button class="btn" type="submit">Gem indstillinger</button>
|
||
</form>
|
||
</div>
|
||
|
||
<!-- Egen profil -->
|
||
<div class="card">
|
||
<h3>👤 Min profil</h3>
|
||
<?php if ($msg_self): ?><p class="notice"><?php echo htmlspecialchars($msg_self); ?></p><?php endif; ?>
|
||
<?php if ($err_self): ?><p class="error"><?php echo htmlspecialchars($err_self); ?></p><?php endif; ?>
|
||
|
||
<form method="post">
|
||
<input type="hidden" name="__action" value="self_update">
|
||
<label>Brugernavn
|
||
<input type="text" name="self_username" value="<?php echo htmlspecialchars($me['username']); ?>" required>
|
||
</label><br>
|
||
<label>Nuværende adgangskode
|
||
<input type="password" name="self_current" placeholder="Udfyld for at skifte password">
|
||
</label><br>
|
||
<label>Ny adgangskode
|
||
<input type="password" name="self_new1" placeholder="Min. 6 tegn">
|
||
</label><br>
|
||
<label>Gentag ny adgangskode
|
||
<input type="password" name="self_new2">
|
||
</label><br>
|
||
<button class="btn" type="submit">Opdater min profil</button>
|
||
</form>
|
||
<p class="small">Rolle: <strong><?php echo htmlspecialchars($me['role'] ?? 'user'); ?></strong></p>
|
||
</div>
|
||
|
||
<?php if (is_admin($me)): ?>
|
||
<!-- Admin: Opret / Redigér / Slet -->
|
||
<div class="card">
|
||
<h3>🛠️ Brugerstyring (Admin)</h3>
|
||
<?php if ($msg_admin): ?><p class="notice"><?php echo $msg_admin; ?></p><?php endif; ?>
|
||
<?php if ($err_admin): ?><p class="error"><?php echo $err_admin; ?></p><?php endif; ?>
|
||
|
||
<!-- Opret ny bruger -->
|
||
<form method="post" style="margin-bottom:1rem">
|
||
<input type="hidden" name="__action" value="create_user">
|
||
<h4>Opret ny bruger</h4>
|
||
<label>Brugernavn
|
||
<input type="text" name="new_username" placeholder="fx mod_jens" required>
|
||
</label><br>
|
||
<label>Adgangskode
|
||
<input type="password" name="new_password1" required>
|
||
</label><br>
|
||
<label>Gentag adgangskode
|
||
<input type="password" name="new_password2" required>
|
||
</label><br>
|
||
<label>Rolle
|
||
<select name="new_role">
|
||
<option value="user">User</option>
|
||
<option value="mod">Mod</option>
|
||
<option value="admin">Admin</option>
|
||
</select>
|
||
</label><br>
|
||
<button class="btn" type="submit">Opret bruger</button>
|
||
</form>
|
||
|
||
<!-- Liste og redigér/slet eksisterende brugere -->
|
||
<h4>Eksisterende brugere</h4>
|
||
<table>
|
||
<tr><th>ID</th><th>Brugernavn</th><th>Rolle</th><th>Nyt password (valgfrit)</th><th>Handling</th></tr>
|
||
<?php foreach ($users as $u): ?>
|
||
<tr>
|
||
<form method="post">
|
||
<input type="hidden" name="__action" value="update_user">
|
||
<input type="hidden" name="edit_id" value="<?php echo (int)$u['id']; ?>">
|
||
<td><?php echo (int)$u['id']; ?></td>
|
||
<td><input name="edit_username" value="<?php echo htmlspecialchars($u['username']); ?>" required></td>
|
||
<td>
|
||
<select name="edit_role">
|
||
<option value="user" <?php echo ($u['role']==='user'?'selected':''); ?>>User</option>
|
||
<option value="mod" <?php echo ($u['role']==='mod'?'selected':''); ?>>Mod</option>
|
||
<option value="admin" <?php echo ($u['role']==='admin'?'selected':''); ?>>Admin</option>
|
||
</select>
|
||
</td>
|
||
<td>
|
||
<input type="password" name="edit_password1" placeholder="nyt password">
|
||
<br>
|
||
<input type="password" name="edit_password2" placeholder="gentag">
|
||
</td>
|
||
<td>
|
||
<button class="btn" type="submit">Gem</button>
|
||
</form>
|
||
<form method="post" style="display:inline" onsubmit="return confirmDelete('<?php echo htmlspecialchars($u['username']); ?>')">
|
||
<input type="hidden" name="__action" value="delete_user">
|
||
<input type="hidden" name="del_id" value="<?php echo (int)$u['id']; ?>">
|
||
<button class="btn" type="submit">Slet</button>
|
||
</form>
|
||
</td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
</table>
|
||
<p class="small">Begrænsninger: Du kan ikke slette dig selv, og systemet forhindrer at stå uden en eneste admin.</p>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
</div>
|
||
</body>
|
||
</html>
|