a116b30df7
[ui] registrace.php – použit společný JS pro kontrolu hesla [ui] nove_heslo.php – použit společný JS pro kontrolu hesla, opraven odkaz na reset [ui] admin.php – použit společný JS pro kontrolu hesla [FÁZE-1][install] Vícekrokový průběh instalace, použit společný JS pro kontrolu hesla
424 lines
17 KiB
PHP
424 lines
17 KiB
PHP
<?php
|
||
// ============================================================
|
||
// ADMINISTRAČNÍ ROZHRANÍ
|
||
// ============================================================
|
||
|
||
require_once __DIR__ . '/config.php';
|
||
require_once __DIR__ . '/db.php';
|
||
require_once __DIR__ . '/mail.php';
|
||
require_once __DIR__ . '/auth.php';
|
||
|
||
// ------------------------------------------------------------
|
||
// KONTROLA: Je přihlášený uživatel admin?
|
||
// ------------------------------------------------------------
|
||
|
||
if (!$auth_uzivatel['admin']) {
|
||
http_response_code(404);
|
||
die('<!DOCTYPE html><html lang="cs"><head><meta charset="UTF-8"><title>Stránka nenalezena</title></head><body><p>Stránka nebyla nalezena.</p></body></html>');
|
||
}
|
||
|
||
// ------------------------------------------------------------
|
||
// CSRF TOKEN
|
||
// ------------------------------------------------------------
|
||
|
||
if (empty($_SESSION['csrf_token'])) {
|
||
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
||
}
|
||
$csrf_token = $_SESSION['csrf_token'];
|
||
|
||
// ------------------------------------------------------------
|
||
// ZPRACOVÁNÍ AKCÍ
|
||
// ------------------------------------------------------------
|
||
|
||
$zprava = '';
|
||
$chyba = '';
|
||
|
||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||
|
||
$csrf_z_formulare = $_POST['csrf_token'] ?? '';
|
||
if (!hash_equals($csrf_token, $csrf_z_formulare)) {
|
||
$chyba = 'Neplatný požadavek. Zkuste stránku obnovit.';
|
||
}
|
||
|
||
if (empty($chyba)) {
|
||
|
||
$akce = $_POST['akce'] ?? '';
|
||
|
||
// ====================================================
|
||
// AKCE: Založení nového uživatele
|
||
// ====================================================
|
||
if ($akce === 'novy_uzivatel') {
|
||
|
||
$email = trim($_POST['email'] ?? '');
|
||
$heslo = $_POST['heslo'] ?? '';
|
||
$heslo2 = $_POST['heslo2'] ?? '';
|
||
$admin = isset($_POST['admin']) ? 1 : 0;
|
||
|
||
if (empty($email)) {
|
||
$chyba = 'Email nesmí být prázdný.';
|
||
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||
$chyba = 'Email není platná emailová adresa.';
|
||
} elseif (empty($heslo)) {
|
||
$chyba = 'Heslo nesmí být prázdné.';
|
||
} elseif (mb_strlen($heslo) < HESLO_MIN_DELKA) {
|
||
$chyba = 'Heslo musí mít alespoň ' . HESLO_MIN_DELKA . ' znaků.';
|
||
} elseif ($heslo !== $heslo2) {
|
||
$chyba = 'Hesla se neshodují.';
|
||
}
|
||
|
||
if (empty($chyba)) {
|
||
$stmt = $pdo->prepare("
|
||
SELECT COUNT(*) AS pocet
|
||
FROM `" . DB_TABULKA_UZIVATELE . "`
|
||
WHERE `email` = :email
|
||
");
|
||
$stmt->execute([':email' => $email]);
|
||
if ($stmt->fetch()['pocet'] > 0) {
|
||
$chyba = 'Uživatel s tímto emailem již existuje.';
|
||
}
|
||
}
|
||
|
||
if (empty($chyba)) {
|
||
try {
|
||
$heslo_hash = password_hash($heslo, PASSWORD_DEFAULT);
|
||
|
||
$stmt = $pdo->prepare("
|
||
INSERT INTO `" . DB_TABULKA_UZIVATELE . "`
|
||
(`email`, `heslo`, `admin`)
|
||
VALUES
|
||
(:email, :heslo, :admin)
|
||
");
|
||
$stmt->execute([
|
||
':email' => $email,
|
||
':heslo' => $heslo_hash,
|
||
':admin' => $admin,
|
||
]);
|
||
|
||
$novy_id = $pdo->lastInsertId();
|
||
|
||
if (DB_TABULKA_SLUZBA !== '') {
|
||
$stmt2 = $pdo->prepare("
|
||
INSERT INTO `" . DB_TABULKA_SLUZBA . "`
|
||
(`uzivatel_id`)
|
||
VALUES
|
||
(:uzivatel_id)
|
||
");
|
||
$stmt2->execute([':uzivatel_id' => $novy_id]);
|
||
}
|
||
|
||
$zprava = 'Uživatel ' . htmlspecialchars($email) . ' byl úspěšně vytvořen.';
|
||
|
||
} catch (PDOException $e) {
|
||
error_log('Admin – chyba při vytváření uživatele: ' . $e->getMessage());
|
||
$chyba = 'Při vytváření uživatele došlo k chybě databáze.';
|
||
}
|
||
}
|
||
}
|
||
|
||
// ====================================================
|
||
// AKCE: Změna hesla uživatele
|
||
// ====================================================
|
||
elseif ($akce === 'zmena_hesla') {
|
||
|
||
$uzivatel_id = (int) ($_POST['uzivatel_id'] ?? 0);
|
||
$heslo = $_POST['heslo'] ?? '';
|
||
$heslo2 = $_POST['heslo2'] ?? '';
|
||
|
||
if ($uzivatel_id <= 0) {
|
||
$chyba = 'Neplatné ID uživatele.';
|
||
} elseif (empty($heslo)) {
|
||
$chyba = 'Heslo nesmí být prázdné.';
|
||
} elseif (mb_strlen($heslo) < HESLO_MIN_DELKA) {
|
||
$chyba = 'Heslo musí mít alespoň ' . HESLO_MIN_DELKA . ' znaků.';
|
||
} elseif ($heslo !== $heslo2) {
|
||
$chyba = 'Hesla se neshodují.';
|
||
}
|
||
|
||
if (empty($chyba)) {
|
||
try {
|
||
$heslo_hash = password_hash($heslo, PASSWORD_DEFAULT);
|
||
|
||
$stmt = $pdo->prepare("
|
||
UPDATE `" . DB_TABULKA_UZIVATELE . "`
|
||
SET `heslo` = :heslo
|
||
WHERE `id` = :id
|
||
");
|
||
$stmt->execute([
|
||
':heslo' => $heslo_hash,
|
||
':id' => $uzivatel_id,
|
||
]);
|
||
|
||
$stmt2 = $pdo->prepare("
|
||
DELETE FROM `" . DB_TABULKA_TOKENY . "`
|
||
WHERE `uzivatel_id` = :uzivatel_id
|
||
");
|
||
$stmt2->execute([':uzivatel_id' => $uzivatel_id]);
|
||
|
||
$zprava = 'Heslo uživatele bylo úspěšně změněno. Uživatel byl odhlášen ze všech zařízení.';
|
||
|
||
} catch (PDOException $e) {
|
||
error_log('Admin – chyba při změně hesla: ' . $e->getMessage());
|
||
$chyba = 'Při změně hesla došlo k chybě databáze.';
|
||
}
|
||
}
|
||
}
|
||
|
||
// ====================================================
|
||
// AKCE: Smazání uživatele
|
||
// ====================================================
|
||
elseif ($akce === 'smazat_uzivatele') {
|
||
|
||
$uzivatel_id = (int) ($_POST['uzivatel_id'] ?? 0);
|
||
|
||
if ($uzivatel_id <= 0) {
|
||
$chyba = 'Neplatné ID uživatele.';
|
||
} elseif ($uzivatel_id === (int) $auth_uzivatel['id']) {
|
||
$chyba = 'Nemůžeš smazat svůj vlastní účet.';
|
||
}
|
||
|
||
if (empty($chyba)) {
|
||
try {
|
||
$stmt = $pdo->prepare("
|
||
DELETE FROM `" . DB_TABULKA_UZIVATELE . "`
|
||
WHERE `id` = :id
|
||
");
|
||
$stmt->execute([':id' => $uzivatel_id]);
|
||
|
||
$zprava = 'Uživatel byl úspěšně smazán.';
|
||
|
||
} catch (PDOException $e) {
|
||
error_log('Admin – chyba při mazání uživatele: ' . $e->getMessage());
|
||
$chyba = 'Při mazání uživatele došlo k chybě databáze.';
|
||
}
|
||
}
|
||
}
|
||
|
||
// ====================================================
|
||
// AKCE: Testovací email
|
||
// ====================================================
|
||
elseif ($akce === 'test_email') {
|
||
|
||
$vysledek = odesli_mail(
|
||
MAIL_TEST_ADRESA,
|
||
'Testovací email – ' . PROJEKT_NAZEV,
|
||
"Toto je testovací email ze systému přihlašování.\n\n"
|
||
. "Projekt: " . PROJEKT_NAZEV . "\n"
|
||
. "URL: " . PROJEKT_URL . "\n"
|
||
. "Čas odeslání: " . date('Y-m-d H:i:s') . "\n\n"
|
||
. "Pokud tento email vidíš, odesílání emailů funguje správně."
|
||
);
|
||
|
||
if ($vysledek) {
|
||
$zprava = 'Testovací email byl odeslán na ' . htmlspecialchars(MAIL_TEST_ADRESA) . '.';
|
||
} else {
|
||
$chyba = 'Odeslání testovacího emailu selhalo. Zkontroluj PHP error log a nastavení v config.php.';
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// ------------------------------------------------------------
|
||
// NAČTENÍ SEZNAMU UŽIVATELŮ
|
||
// ------------------------------------------------------------
|
||
|
||
$uzivatele = [];
|
||
try {
|
||
$stmt = $pdo->query("
|
||
SELECT `id`, `email`, `admin`, `vytvoreno`
|
||
FROM `" . DB_TABULKA_UZIVATELE . "`
|
||
ORDER BY `vytvoreno` ASC
|
||
");
|
||
$uzivatele = $stmt->fetchAll();
|
||
} catch (PDOException $e) {
|
||
error_log('Admin – chyba při načítání uživatelů: ' . $e->getMessage());
|
||
$chyba = 'Nepodařilo se načíst seznam uživatelů.';
|
||
}
|
||
|
||
// ------------------------------------------------------------
|
||
// HTML VÝSTUP
|
||
// ------------------------------------------------------------
|
||
?>
|
||
<!DOCTYPE html>
|
||
<html lang="cs">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<title>Administrace – <?php echo htmlspecialchars(PROJEKT_NAZEV); ?></title>
|
||
<?php if (AUTH_CSS !== ''): ?>
|
||
<link rel="stylesheet" href="<?php echo htmlspecialchars(AUTH_CSS); ?>">
|
||
<?php endif; ?>
|
||
</head>
|
||
<body>
|
||
|
||
<h1>Administrace</h1>
|
||
<h2><?php echo htmlspecialchars(PROJEKT_NAZEV); ?></h2>
|
||
|
||
<p>Přihlášen jako: <strong><?php echo htmlspecialchars($auth_uzivatel['email']); ?></strong></p>
|
||
<?php echo $auth_logout_html; ?>
|
||
|
||
<?php if (!empty($zprava)): ?>
|
||
<p><strong>OK: <?php echo $zprava; ?></strong></p>
|
||
<?php endif; ?>
|
||
<?php if (!empty($chyba)): ?>
|
||
<p><strong>Chyba: <?php echo htmlspecialchars($chyba); ?></strong></p>
|
||
<?php endif; ?>
|
||
|
||
<hr>
|
||
|
||
<h3>Seznam uživatelů</h3>
|
||
|
||
<?php if (empty($uzivatele)): ?>
|
||
<p>Žádní uživatelé.</p>
|
||
<?php else: ?>
|
||
<table border="1" cellpadding="5">
|
||
<tr>
|
||
<th>ID</th>
|
||
<th>Email</th>
|
||
<th>Admin</th>
|
||
<th>Registrován</th>
|
||
<th>Akce</th>
|
||
</tr>
|
||
<?php foreach ($uzivatele as $u): ?>
|
||
<tr>
|
||
<td><?php echo htmlspecialchars($u['id']); ?></td>
|
||
<td><?php echo htmlspecialchars($u['email']); ?></td>
|
||
<td><?php echo $u['admin'] ? 'ANO' : 'ne'; ?></td>
|
||
<td><?php echo htmlspecialchars($u['vytvoreno']); ?></td>
|
||
<td>
|
||
<form method="POST" action="" style="display:inline;">
|
||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrf_token); ?>">
|
||
<input type="hidden" name="akce" value="zmena_hesla">
|
||
<input type="hidden" name="uzivatel_id" value="<?php echo htmlspecialchars($u['id']); ?>">
|
||
<input type="password" name="heslo" placeholder="nové heslo" minlength="<?php echo (int) HESLO_MIN_DELKA; ?>" required>
|
||
<input type="password" name="heslo2" placeholder="nové heslo znovu" required>
|
||
<button type="submit">Změnit heslo</button>
|
||
</form>
|
||
|
||
<?php if ($u['id'] !== $auth_uzivatel['id']): ?>
|
||
<form method="POST" action="" style="display:inline;"
|
||
onsubmit="return confirm('Opravdu smazat uživatele <?php echo htmlspecialchars(addslashes($u['email'])); ?>? Tato akce je nevratná.');">
|
||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrf_token); ?>">
|
||
<input type="hidden" name="akce" value="smazat_uzivatele">
|
||
<input type="hidden" name="uzivatel_id" value="<?php echo htmlspecialchars($u['id']); ?>">
|
||
<button type="submit">Smazat</button>
|
||
</form>
|
||
<?php endif; ?>
|
||
</td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
</table>
|
||
<?php endif; ?>
|
||
|
||
<hr>
|
||
|
||
<h3>Přidat nového uživatele</h3>
|
||
|
||
<form method="POST" action="">
|
||
|
||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrf_token); ?>">
|
||
<input type="hidden" name="akce" value="novy_uzivatel">
|
||
|
||
<p>
|
||
<label for="novy-email">Email:</label><br>
|
||
<input type="email" id="novy-email" name="email" required autocomplete="off">
|
||
</p>
|
||
|
||
<p>
|
||
<label for="heslo">Heslo:</label><br>
|
||
<input type="password" id="heslo" name="heslo" required autocomplete="new-password">
|
||
<br>
|
||
<span id="heslo-sila">Síla hesla: zadej heslo</span>
|
||
</p>
|
||
|
||
<p>
|
||
<label for="heslo2">Heslo znovu:</label><br>
|
||
<input type="password" id="heslo2" name="heslo2" required autocomplete="new-password">
|
||
</p>
|
||
|
||
<p>
|
||
<label>
|
||
<input type="checkbox" name="admin" value="1">
|
||
Administrátor
|
||
</label>
|
||
</p>
|
||
|
||
<p>
|
||
<button type="submit" id="tlacitko-odeslat" disabled>
|
||
Vytvořit uživatele
|
||
</button>
|
||
<span id="tlacitko-duvod"> (čekám na dostatečně silné heslo)</span>
|
||
</p>
|
||
|
||
</form>
|
||
|
||
<hr>
|
||
|
||
<h3>Konfigurace systému</h3>
|
||
|
||
<p>Hodnoty jsou načteny z <code>auth/config.php</code>. Citlivé údaje jsou skryté – zobrazíš je kliknutím.</p>
|
||
|
||
<table border="1" cellpadding="5">
|
||
<tr><th>Konstanta</th><th>Hodnota</th></tr>
|
||
<tr><td>DB_HOST</td><td><?php echo htmlspecialchars(DB_HOST); ?></td></tr>
|
||
<tr><td>DB_NAME</td><td><?php echo htmlspecialchars(DB_NAME); ?></td></tr>
|
||
<tr><td>DB_USER</td><td><?php echo htmlspecialchars(DB_USER); ?></td></tr>
|
||
<tr><td>DB_PASS</td>
|
||
<td>
|
||
<span id="db-pass-skryto">[skryto]
|
||
<button type="button" onclick="
|
||
document.getElementById('db-pass-skryto').style.display='none';
|
||
document.getElementById('db-pass-hodnota').style.display='inline';
|
||
">Zobrazit</button>
|
||
</span>
|
||
<span id="db-pass-hodnota" style="display:none;"><?php echo htmlspecialchars(DB_PASS); ?></span>
|
||
</td>
|
||
</tr>
|
||
<tr><td>DB_TABULKA_UZIVATELE</td><td><?php echo htmlspecialchars(DB_TABULKA_UZIVATELE); ?></td></tr>
|
||
<tr><td>DB_TABULKA_TOKENY</td><td><?php echo htmlspecialchars(DB_TABULKA_TOKENY); ?></td></tr>
|
||
<tr><td>DB_TABULKA_BRUTE</td><td><?php echo htmlspecialchars(DB_TABULKA_BRUTE); ?></td></tr>
|
||
<tr><td>DB_TABULKA_RESET</td><td><?php echo htmlspecialchars(DB_TABULKA_RESET); ?></td></tr>
|
||
<tr><td>DB_TABULKA_SLUZBA</td><td><?php echo htmlspecialchars(DB_TABULKA_SLUZBA !== '' ? DB_TABULKA_SLUZBA : '(nevyužito)'); ?></td></tr>
|
||
<tr><td>PROJEKT_NAZEV</td><td><?php echo htmlspecialchars(PROJEKT_NAZEV); ?></td></tr>
|
||
<tr><td>PROJEKT_URL</td><td><?php echo htmlspecialchars(PROJEKT_URL); ?></td></tr>
|
||
<tr><td>AUTH_LOGIN_URL</td><td><?php echo htmlspecialchars(AUTH_LOGIN_URL); ?></td></tr>
|
||
<tr><td>AUTH_LOGOUT_URL</td><td><?php echo htmlspecialchars(AUTH_LOGOUT_URL); ?></td></tr>
|
||
<tr><td>AUTH_REGISTRACE_URL</td><td><?php echo htmlspecialchars(AUTH_REGISTRACE_URL); ?></td></tr>
|
||
<tr><td>AUTH_RESET_URL</td><td><?php echo htmlspecialchars(AUTH_RESET_URL); ?></td></tr>
|
||
<tr><td>AUTH_REDIRECT_PO_PRIHLASENI</td><td><?php echo htmlspecialchars(AUTH_REDIRECT_PO_PRIHLASENI); ?></td></tr>
|
||
<tr><td>REGISTRACE_OTEVRENA</td><td><?php echo REGISTRACE_OTEVRENA ? 'true (otevřená)' : 'false (pouze admin)'; ?></td></tr>
|
||
<tr><td>VYZADOVAT_PRIHLASENI</td><td><?php echo VYZADOVAT_PRIHLASENI ? 'true (vždy přesměrovat)' : 'false (stránka rozhoduje sama)'; ?></td></tr>
|
||
<tr><td>SESSION_NAZEV</td><td><?php echo htmlspecialchars(SESSION_NAZEV); ?></td></tr>
|
||
<tr><td>SESSION_EXPIRACE</td><td><?php echo (int) SESSION_EXPIRACE; ?> s (<?php echo round(SESSION_EXPIRACE / 3600, 1); ?> h)</td></tr>
|
||
<tr><td>REMEMBER_EXPIRACE</td><td><?php echo (int) REMEMBER_EXPIRACE; ?> s (<?php echo round(REMEMBER_EXPIRACE / 86400, 1); ?> dní)</td></tr>
|
||
<tr><td>BRUTE_MAX_POKUSU</td><td><?php echo (int) BRUTE_MAX_POKUSU; ?></td></tr>
|
||
<tr><td>BRUTE_OKNO</td><td><?php echo (int) BRUTE_OKNO; ?> s (<?php echo round(BRUTE_OKNO / 60, 1); ?> min)</td></tr>
|
||
<tr><td>HESLO_MIN_SILA</td><td><?php echo (int) HESLO_MIN_SILA; ?> / 4</td></tr>
|
||
<tr><td>HESLO_MIN_DELKA</td><td><?php echo (int) HESLO_MIN_DELKA; ?> znaků</td></tr>
|
||
<tr><td>MAIL_ODESILATEL</td><td><?php echo htmlspecialchars(MAIL_ODESILATEL); ?></td></tr>
|
||
<tr><td>MAIL_ODESILATEL_JMENO</td><td><?php echo htmlspecialchars(MAIL_ODESILATEL_JMENO); ?></td></tr>
|
||
<tr><td>MAIL_TEST_ADRESA</td><td><?php echo htmlspecialchars(MAIL_TEST_ADRESA); ?></td></tr>
|
||
<tr><td>AUTH_CSS</td><td><?php echo AUTH_CSS !== '' ? htmlspecialchars(AUTH_CSS) : '(nevyužito)'; ?></td></tr>
|
||
</table>
|
||
|
||
<hr>
|
||
|
||
<h3>Test odesílání emailů</h3>
|
||
|
||
<p>Odešle testovací email na adresu <strong><?php echo htmlspecialchars(MAIL_TEST_ADRESA); ?></strong>.</p>
|
||
|
||
<form method="POST" action="">
|
||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrf_token); ?>">
|
||
<input type="hidden" name="akce" value="test_email">
|
||
<button type="submit">Odeslat testovací email</button>
|
||
</form>
|
||
|
||
<hr>
|
||
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/zxcvbn/4.4.2/zxcvbn.js"></script>
|
||
<script>
|
||
var AUTH_HESLO_MIN_SILA = <?php echo (int) HESLO_MIN_SILA; ?>;
|
||
var AUTH_HESLO_MIN_DELKA = <?php echo (int) HESLO_MIN_DELKA; ?>;
|
||
</script>
|
||
<script src="<?php echo htmlspecialchars(PROJEKT_URL); ?>/auth/js/heslo-sila.js"></script>
|
||
|
||
</body>
|
||
</html>
|