Files
Thomas a184c31cca Software
v0.0..1
2025-10-05 14:58:05 +02:00

396 lines
16 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?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>