[ui] Extrahována kontrola síly hesla do samostatného JS souboru, přidána kontrola minimální délky

[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
This commit is contained in:
stepan
2026-03-17 10:06:44 +01:00
parent 47ae07946d
commit a116b30df7
5 changed files with 167 additions and 411 deletions
+52 -230
View File
@@ -2,15 +2,6 @@
// ============================================================ // ============================================================
// ADMINISTRAČNÍ ROZHRANÍ // ADMINISTRAČNÍ ROZHRANÍ
// ============================================================ // ============================================================
// Tato stránka je dostupná pouze přihlášenému adminovi.
// Umožňuje:
// - zobrazit seznam uživatelů
// - založit nového uživatele
// - změnit heslo uživatele
// - smazat uživatele
// - zobrazit konfiguraci systému (citlivé údaje skryté)
// - odeslat testovací email
// ============================================================
require_once __DIR__ . '/config.php'; require_once __DIR__ . '/config.php';
require_once __DIR__ . '/db.php'; require_once __DIR__ . '/db.php';
@@ -20,12 +11,8 @@ require_once __DIR__ . '/auth.php';
// ------------------------------------------------------------ // ------------------------------------------------------------
// KONTROLA: Je přihlášený uživatel admin? // KONTROLA: Je přihlášený uživatel admin?
// ------------------------------------------------------------ // ------------------------------------------------------------
// auth.php již zajistil, že uživatel je přihlášen (nebo přesměroval).
// Zde navíc kontrolujeme příznak admin.
if (!$auth_uzivatel['admin']) { if (!$auth_uzivatel['admin']) {
// Uživatel je přihlášen, ale není admin
// zobrazíme obecnou chybu (nechceme prozradit, že stránka existuje)
http_response_code(404); 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>'); 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>');
} }
@@ -40,15 +27,14 @@ if (empty($_SESSION['csrf_token'])) {
$csrf_token = $_SESSION['csrf_token']; $csrf_token = $_SESSION['csrf_token'];
// ------------------------------------------------------------ // ------------------------------------------------------------
// ZPRACOVÁNÍ AKCÍ (POST požadavky) // ZPRACOVÁNÍ AKCÍ
// ------------------------------------------------------------ // ------------------------------------------------------------
$zprava = ''; // zpráva o úspěchu $zprava = '';
$chyba = ''; // chybová hláška $chyba = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') { if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// -- Ověření CSRF tokenu ----------------------------------
$csrf_z_formulare = $_POST['csrf_token'] ?? ''; $csrf_z_formulare = $_POST['csrf_token'] ?? '';
if (!hash_equals($csrf_token, $csrf_z_formulare)) { if (!hash_equals($csrf_token, $csrf_z_formulare)) {
$chyba = 'Neplatný požadavek. Zkuste stránku obnovit.'; $chyba = 'Neplatný požadavek. Zkuste stránku obnovit.';
@@ -68,25 +54,19 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$heslo2 = $_POST['heslo2'] ?? ''; $heslo2 = $_POST['heslo2'] ?? '';
$admin = isset($_POST['admin']) ? 1 : 0; $admin = isset($_POST['admin']) ? 1 : 0;
// Validace emailu
if (empty($email)) { if (empty($email)) {
$chyba = 'Email nesmí být prázdný.'; $chyba = 'Email nesmí být prázdný.';
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) { } elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$chyba = 'Email není platná emailová adresa.'; $chyba = 'Email není platná emailová adresa.';
} } elseif (empty($heslo)) {
// Validace hesla
if (empty($chyba) && empty($heslo)) {
$chyba = 'Heslo nesmí být prázdné.'; $chyba = 'Heslo nesmí být prázdné.';
} elseif (empty($chyba) && mb_strlen($heslo) < HESLO_MIN_DELKA) { } elseif (mb_strlen($heslo) < HESLO_MIN_DELKA) {
$chyba = 'Heslo musí mít alespoň ' . HESLO_MIN_DELKA . ' znaků.'; $chyba = 'Heslo musí mít alespoň ' . HESLO_MIN_DELKA . ' znaků.';
} elseif (empty($chyba) && $heslo !== $heslo2) { } elseif ($heslo !== $heslo2) {
$chyba = 'Hesla se neshodují.'; $chyba = 'Hesla se neshodují.';
} }
if (empty($chyba)) { if (empty($chyba)) {
// Kontrola, zda email již neexistuje
$stmt = $pdo->prepare(" $stmt = $pdo->prepare("
SELECT COUNT(*) AS pocet SELECT COUNT(*) AS pocet
FROM `" . DB_TABULKA_UZIVATELE . "` FROM `" . DB_TABULKA_UZIVATELE . "`
@@ -99,7 +79,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
} }
if (empty($chyba)) { if (empty($chyba)) {
try { try {
$heslo_hash = password_hash($heslo, PASSWORD_DEFAULT); $heslo_hash = password_hash($heslo, PASSWORD_DEFAULT);
@@ -117,7 +96,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$novy_id = $pdo->lastInsertId(); $novy_id = $pdo->lastInsertId();
// Vytvoříme prázdný řádek v tabulce služby
if (DB_TABULKA_SLUZBA !== '') { if (DB_TABULKA_SLUZBA !== '') {
$stmt2 = $pdo->prepare(" $stmt2 = $pdo->prepare("
INSERT INTO `" . DB_TABULKA_SLUZBA . "` INSERT INTO `" . DB_TABULKA_SLUZBA . "`
@@ -157,7 +135,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
} }
if (empty($chyba)) { if (empty($chyba)) {
try { try {
$heslo_hash = password_hash($heslo, PASSWORD_DEFAULT); $heslo_hash = password_hash($heslo, PASSWORD_DEFAULT);
@@ -171,8 +148,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
':id' => $uzivatel_id, ':id' => $uzivatel_id,
]); ]);
// Po změně hesla smažeme všechny remember me tokeny
// tohoto uživatele odhlásíme ho ze všech zařízení
$stmt2 = $pdo->prepare(" $stmt2 = $pdo->prepare("
DELETE FROM `" . DB_TABULKA_TOKENY . "` DELETE FROM `" . DB_TABULKA_TOKENY . "`
WHERE `uzivatel_id` = :uzivatel_id WHERE `uzivatel_id` = :uzivatel_id
@@ -197,19 +172,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if ($uzivatel_id <= 0) { if ($uzivatel_id <= 0) {
$chyba = 'Neplatné ID uživatele.'; $chyba = 'Neplatné ID uživatele.';
} } elseif ($uzivatel_id === (int) $auth_uzivatel['id']) {
// Admin nesmí smazat sám sebe
if (empty($chyba) && $uzivatel_id === (int) $auth_uzivatel['id']) {
$chyba = 'Nemůžeš smazat svůj vlastní účet.'; $chyba = 'Nemůžeš smazat svůj vlastní účet.';
} }
if (empty($chyba)) { if (empty($chyba)) {
try { try {
// Díky FOREIGN KEY s ON DELETE CASCADE se automaticky
// smažou i záznamy v auth_remember_tokens a auth_password_resets
// a v tabulce služby (users)
$stmt = $pdo->prepare(" $stmt = $pdo->prepare("
DELETE FROM `" . DB_TABULKA_UZIVATELE . "` DELETE FROM `" . DB_TABULKA_UZIVATELE . "`
WHERE `id` = :id WHERE `id` = :id
@@ -290,17 +258,12 @@ try {
<?php if (!empty($zprava)): ?> <?php if (!empty($zprava)): ?>
<p><strong>OK: <?php echo $zprava; ?></strong></p> <p><strong>OK: <?php echo $zprava; ?></strong></p>
<?php endif; ?> <?php endif; ?>
<?php if (!empty($chyba)): ?> <?php if (!empty($chyba)): ?>
<p><strong>Chyba: <?php echo htmlspecialchars($chyba); ?></strong></p> <p><strong>Chyba: <?php echo htmlspecialchars($chyba); ?></strong></p>
<?php endif; ?> <?php endif; ?>
<hr> <hr>
<!-- ============================================================
SEZNAM UŽIVATELŮ
============================================================ -->
<h3>Seznam uživatelů</h3> <h3>Seznam uživatelů</h3>
<?php if (empty($uzivatele)): ?> <?php if (empty($uzivatele)): ?>
@@ -321,35 +284,16 @@ try {
<td><?php echo $u['admin'] ? 'ANO' : 'ne'; ?></td> <td><?php echo $u['admin'] ? 'ANO' : 'ne'; ?></td>
<td><?php echo htmlspecialchars($u['vytvoreno']); ?></td> <td><?php echo htmlspecialchars($u['vytvoreno']); ?></td>
<td> <td>
<!-- Formulář pro změnu hesla tohoto uživatele -->
<form method="POST" action="" style="display:inline;"> <form method="POST" action="" style="display:inline;">
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrf_token); ?>"> <input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrf_token); ?>">
<input type="hidden" name="akce" value="zmena_hesla"> <input type="hidden" name="akce" value="zmena_hesla">
<input type="hidden" name="uzivatel_id" value="<?php echo htmlspecialchars($u['id']); ?>"> <input type="hidden" name="uzivatel_id" value="<?php echo htmlspecialchars($u['id']); ?>">
<input <input type="password" name="heslo" placeholder="nové heslo" minlength="<?php echo (int) HESLO_MIN_DELKA; ?>" required>
type="password" <input type="password" name="heslo2" placeholder="nové heslo znovu" required>
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> <button type="submit">Změnit heslo</button>
</form> </form>
&nbsp; &nbsp;
<?php if ($u['id'] !== $auth_uzivatel['id']): ?>
<?php
// Admin nemůže smazat sám sebe skryjeme tlačítko
if ($u['id'] !== $auth_uzivatel['id']):
?>
<!-- Formulář pro smazání tohoto uživatele -->
<form method="POST" action="" style="display:inline;" <form method="POST" action="" style="display:inline;"
onsubmit="return confirm('Opravdu smazat uživatele <?php echo htmlspecialchars(addslashes($u['email'])); ?>? Tato akce je nevratná.');"> 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="csrf_token" value="<?php echo htmlspecialchars($csrf_token); ?>">
@@ -358,7 +302,6 @@ try {
<button type="submit">Smazat</button> <button type="submit">Smazat</button>
</form> </form>
<?php endif; ?> <?php endif; ?>
</td> </td>
</tr> </tr>
<?php endforeach; ?> <?php endforeach; ?>
@@ -367,10 +310,6 @@ try {
<hr> <hr>
<!-- ============================================================
FORMULÁŘ: Nový uživatel
============================================================ -->
<h3>Přidat nového uživatele</h3> <h3>Přidat nového uživatele</h3>
<form method="POST" action=""> <form method="POST" action="">
@@ -380,37 +319,19 @@ try {
<p> <p>
<label for="novy-email">Email:</label><br> <label for="novy-email">Email:</label><br>
<input <input type="email" id="novy-email" name="email" required autocomplete="off">
type="email"
id="novy-email"
name="email"
required
autocomplete="off"
>
</p> </p>
<p> <p>
<label for="novy-heslo">Heslo:</label><br> <label for="heslo">Heslo:</label><br>
<input <input type="password" id="heslo" name="heslo" required autocomplete="new-password">
type="password"
id="novy-heslo"
name="heslo"
required
autocomplete="new-password"
>
<br> <br>
<span id="novy-heslo-sila">Síla hesla: zadej heslo</span> <span id="heslo-sila">Síla hesla: zadej heslo</span>
</p> </p>
<p> <p>
<label for="novy-heslo2">Heslo znovu:</label><br> <label for="heslo2">Heslo znovu:</label><br>
<input <input type="password" id="heslo2" name="heslo2" required autocomplete="new-password">
type="password"
id="novy-heslo2"
name="heslo2"
required
autocomplete="new-password"
>
</p> </p>
<p> <p>
@@ -421,129 +342,68 @@ try {
</p> </p>
<p> <p>
<button type="submit" id="novy-tlacitko" disabled> <button type="submit" id="tlacitko-odeslat" disabled>
Vytvořit uživatele Vytvořit uživatele
</button> </button>
<span id="novy-tlacitko-duvod"> (čekám na dostatečně silné heslo)</span> <span id="tlacitko-duvod"> (čekám na dostatečně silné heslo)</span>
</p> </p>
</form> </form>
<hr> <hr>
<!-- ============================================================
KONFIGURACE SYSTÉMU
============================================================ -->
<h3>Konfigurace systému</h3> <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> <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"> <table border="1" cellpadding="5">
<tr> <tr><th>Konstanta</th><th>Hodnota</th></tr>
<th>Konstanta</th> <tr><td>DB_HOST</td><td><?php echo htmlspecialchars(DB_HOST); ?></td></tr>
<th>Hodnota</th> <tr><td>DB_NAME</td><td><?php echo htmlspecialchars(DB_NAME); ?></td></tr>
</tr> <tr><td>DB_USER</td><td><?php echo htmlspecialchars(DB_USER); ?></td></tr>
<!-- Databáze -->
<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> <tr><td>DB_PASS</td>
<td> <td>
<!-- Heslo je skryté, zobrazí se až po kliknutí -->
<span id="db-pass-skryto">[skryto] <span id="db-pass-skryto">[skryto]
<button type="button" onclick=" <button type="button" onclick="
document.getElementById('db-pass-skryto').style.display='none'; document.getElementById('db-pass-skryto').style.display='none';
document.getElementById('db-pass-hodnota').style.display='inline'; document.getElementById('db-pass-hodnota').style.display='inline';
">Zobrazit</button> ">Zobrazit</button>
</span> </span>
<span id="db-pass-hodnota" style="display:none;"> <span id="db-pass-hodnota" style="display:none;"><?php echo htmlspecialchars(DB_PASS); ?></span>
<?php echo htmlspecialchars(DB_PASS); ?>
</span>
</td> </td>
</tr> </tr>
<tr><td>DB_TABULKA_UZIVATELE</td> <tr><td>DB_TABULKA_UZIVATELE</td><td><?php echo htmlspecialchars(DB_TABULKA_UZIVATELE); ?></td></tr>
<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_TOKENY</td> <tr><td>DB_TABULKA_BRUTE</td><td><?php echo htmlspecialchars(DB_TABULKA_BRUTE); ?></td></tr>
<td><?php echo htmlspecialchars(DB_TABULKA_TOKENY); ?></td></tr> <tr><td>DB_TABULKA_RESET</td><td><?php echo htmlspecialchars(DB_TABULKA_RESET); ?></td></tr>
<tr><td>DB_TABULKA_BRUTE</td> <tr><td>DB_TABULKA_SLUZBA</td><td><?php echo htmlspecialchars(DB_TABULKA_SLUZBA !== '' ? DB_TABULKA_SLUZBA : '(nevyužito)'); ?></td></tr>
<td><?php echo htmlspecialchars(DB_TABULKA_BRUTE); ?></td></tr> <tr><td>PROJEKT_NAZEV</td><td><?php echo htmlspecialchars(PROJEKT_NAZEV); ?></td></tr>
<tr><td>DB_TABULKA_RESET</td> <tr><td>PROJEKT_URL</td><td><?php echo htmlspecialchars(PROJEKT_URL); ?></td></tr>
<td><?php echo htmlspecialchars(DB_TABULKA_RESET); ?></td></tr> <tr><td>AUTH_LOGIN_URL</td><td><?php echo htmlspecialchars(AUTH_LOGIN_URL); ?></td></tr>
<tr><td>DB_TABULKA_SLUZBA</td> <tr><td>AUTH_LOGOUT_URL</td><td><?php echo htmlspecialchars(AUTH_LOGOUT_URL); ?></td></tr>
<td><?php echo htmlspecialchars(DB_TABULKA_SLUZBA !== '' ? DB_TABULKA_SLUZBA : '(nevyužito)'); ?></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>
<!-- Projekt --> <tr><td>AUTH_REDIRECT_PO_PRIHLASENI</td><td><?php echo htmlspecialchars(AUTH_REDIRECT_PO_PRIHLASENI); ?></td></tr>
<tr><td>PROJEKT_NAZEV</td> <tr><td>REGISTRACE_OTEVRENA</td><td><?php echo REGISTRACE_OTEVRENA ? 'true (otevřená)' : 'false (pouze admin)'; ?></td></tr>
<td><?php echo htmlspecialchars(PROJEKT_NAZEV); ?></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>PROJEKT_URL</td> <tr><td>SESSION_NAZEV</td><td><?php echo htmlspecialchars(SESSION_NAZEV); ?></td></tr>
<td><?php echo htmlspecialchars(PROJEKT_URL); ?></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>AUTH_LOGIN_URL</td> <tr><td>REMEMBER_EXPIRACE</td><td><?php echo (int) REMEMBER_EXPIRACE; ?> s (<?php echo round(REMEMBER_EXPIRACE / 86400, 1); ?> dní)</td></tr>
<td><?php echo htmlspecialchars(AUTH_LOGIN_URL); ?></td></tr> <tr><td>BRUTE_MAX_POKUSU</td><td><?php echo (int) BRUTE_MAX_POKUSU; ?></td></tr>
<tr><td>AUTH_LOGOUT_URL</td> <tr><td>BRUTE_OKNO</td><td><?php echo (int) BRUTE_OKNO; ?> s (<?php echo round(BRUTE_OKNO / 60, 1); ?> min)</td></tr>
<td><?php echo htmlspecialchars(AUTH_LOGOUT_URL); ?></td></tr> <tr><td>HESLO_MIN_SILA</td><td><?php echo (int) HESLO_MIN_SILA; ?> / 4</td></tr>
<tr><td>AUTH_REDIRECT_PO_PRIHLASENI</td> <tr><td>HESLO_MIN_DELKA</td><td><?php echo (int) HESLO_MIN_DELKA; ?> znaků</td></tr>
<td><?php echo htmlspecialchars(AUTH_REDIRECT_PO_PRIHLASENI); ?></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>
<!-- Registrace a přístup --> <tr><td>MAIL_TEST_ADRESA</td><td><?php echo htmlspecialchars(MAIL_TEST_ADRESA); ?></td></tr>
<tr><td>REGISTRACE_OTEVRENA</td> <tr><td>AUTH_CSS</td><td><?php echo AUTH_CSS !== '' ? htmlspecialchars(AUTH_CSS) : '(nevyužito)'; ?></td></tr>
<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>
<!-- Session -->
<tr><td>SESSION_NAZEV</td>
<td><?php echo htmlspecialchars(SESSION_NAZEV); ?></td></tr>
<tr><td>SESSION_EXPIRACE</td>
<td><?php echo (int) SESSION_EXPIRACE; ?> sekund
(<?php echo round(SESSION_EXPIRACE / 3600, 1); ?> hodin)</td></tr>
<!-- Remember me -->
<tr><td>REMEMBER_EXPIRACE</td>
<td><?php echo (int) REMEMBER_EXPIRACE; ?> sekund
(<?php echo round(REMEMBER_EXPIRACE / 86400, 1); ?> dní)</td></tr>
<!-- Brute force -->
<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; ?> sekund
(<?php echo round(BRUTE_OKNO / 60, 1); ?> minut)</td></tr>
<!-- Heslo -->
<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>
<!-- Email -->
<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>
<!-- Styl -->
<tr><td>AUTH_CSS</td>
<td><?php echo AUTH_CSS !== '' ? htmlspecialchars(AUTH_CSS) : '(nevyužito)'; ?></td></tr>
</table> </table>
<hr> <hr>
<!-- ============================================================
TESTOVACÍ EMAIL
============================================================ -->
<h3>Test odesílání emailů</h3> <h3>Test odesílání emailů</h3>
<p>Odešle testovací email na adresu <strong><?php echo htmlspecialchars(MAIL_TEST_ADRESA); ?></strong> <p>Odešle testovací email na adresu <strong><?php echo htmlspecialchars(MAIL_TEST_ADRESA); ?></strong>.</p>
(nastaveno v config.php jako MAIL_TEST_ADRESA).</p>
<form method="POST" action=""> <form method="POST" action="">
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrf_token); ?>"> <input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrf_token); ?>">
@@ -553,50 +413,12 @@ try {
<hr> <hr>
<!-- ============================================================
zxcvbn pro formulář nového uživatele
============================================================ -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/zxcvbn/4.4.2/zxcvbn.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/zxcvbn/4.4.2/zxcvbn.js"></script>
<script> <script>
const MIN_SILA = <?php echo (int) HESLO_MIN_SILA; ?>; var AUTH_HESLO_MIN_SILA = <?php echo (int) HESLO_MIN_SILA; ?>;
var AUTH_HESLO_MIN_DELKA = <?php echo (int) HESLO_MIN_DELKA; ?>;
const POPISKY_SILY = [
'Velmi slabé',
'Slabé',
'Průměrné',
'Silné',
'Velmi silné'
];
const inputHeslo = document.getElementById('novy-heslo');
const spanSila = document.getElementById('novy-heslo-sila');
const tlacitko = document.getElementById('novy-tlacitko');
const spanDuvod = document.getElementById('novy-tlacitko-duvod');
inputHeslo.addEventListener('input', function () {
const hodnota = this.value;
if (hodnota.length === 0) {
spanSila.textContent = 'Síla hesla: zadej heslo';
tlacitko.disabled = true;
spanDuvod.textContent = ' (čekám na dostatečně silné heslo)';
return;
}
const vysledek = zxcvbn(hodnota);
const skore = vysledek.score;
spanSila.textContent = 'Síla hesla: ' + POPISKY_SILY[skore] + ' (' + skore + '/4)';
if (skore >= MIN_SILA) {
tlacitko.disabled = false;
spanDuvod.textContent = '';
} else {
tlacitko.disabled = true;
spanDuvod.textContent = ' (heslo je příliš slabé, potřebuji alespoň ' + MIN_SILA + '/4)';
}
});
</script> </script>
<script src="<?php echo htmlspecialchars(PROJEKT_URL); ?>/auth/js/heslo-sila.js"></script>
</body> </body>
</html> </html>
+6 -33
View File
@@ -11,6 +11,7 @@
// Krok 2 pokud selže, zobrazí SQL pro ruční zadání do phpMyAdmin // Krok 2 pokud selže, zobrazí SQL pro ruční zadání do phpMyAdmin
// Krok 3 ověření, že tabulky existují a mají správnou strukturu // Krok 3 ověření, že tabulky existují a mají správnou strukturu
// Krok 4 formulář pro vytvoření prvního admina + zámek // Krok 4 formulář pro vytvoření prvního admina + zámek
// Krok 5 instalace úspěšně dokončena
// //
// Po úspěšné instalaci se vytvoří soubor install.lock // Po úspěšné instalaci se vytvoří soubor install.lock
// a tento skript již nepůjde spustit znovu. // a tento skript již nepůjde spustit znovu.
@@ -132,9 +133,8 @@ function zkontroluj_tabulky(PDO $pdo): array
// URČENÍ AKTUÁLNÍHO KROKU // URČENÍ AKTUÁLNÍHO KROKU
// ------------------------------------------------------------ // ------------------------------------------------------------
$krok = 1; // výchozí krok $krok = 1;
$chyba = ''; $chyba = '';
$zprava = '';
// Krok se přenáší přes skryté pole ve formuláři // Krok se přenáší přes skryté pole ve formuláři
$krok_z_post = (int) ($_POST['krok'] ?? 0); $krok_z_post = (int) ($_POST['krok'] ?? 0);
@@ -250,7 +250,7 @@ elseif ($krok_z_post === 4) {
} }
// ------------------------------------------------------------ // ------------------------------------------------------------
// SESTAVENÍ SQL TEXTU PRO ZOBRAZENÍ UŽIVATELI (krok 2) // SESTAVENÍ SQL TEXTU PRO ZOBRAZENÍ UŽIVATELI (krok 2 a 3)
// ------------------------------------------------------------ // ------------------------------------------------------------
$sql_pro_zobrazeni = implode(";\n\n", $sql_tabulky) . ';'; $sql_pro_zobrazeni = implode(";\n\n", $sql_tabulky) . ';';
@@ -393,37 +393,10 @@ elseif ($krok === 5): ?>
<script src="https://cdnjs.cloudflare.com/ajax/libs/zxcvbn/4.4.2/zxcvbn.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/zxcvbn/4.4.2/zxcvbn.js"></script>
<script> <script>
const MIN_SILA = <?php echo (int) HESLO_MIN_SILA; ?>; var AUTH_HESLO_MIN_SILA = <?php echo (int) HESLO_MIN_SILA; ?>;
const POPISKY_SILY = ['Velmi slabé','Slabé','Průměrné','Silné','Velmi silné']; var AUTH_HESLO_MIN_DELKA = <?php echo (int) HESLO_MIN_DELKA; ?>;
const inputHeslo = document.getElementById('heslo');
const spanSila = document.getElementById('heslo-sila');
const tlacitko = document.getElementById('tlacitko-odeslat');
const spanDuvod = document.getElementById('tlacitko-duvod');
// Prvky existují jen na kroku 4
if (inputHeslo) {
inputHeslo.addEventListener('input', function () {
const hodnota = this.value;
if (hodnota.length === 0) {
spanSila.textContent = 'Síla hesla: zadej heslo';
tlacitko.disabled = true;
spanDuvod.textContent = ' (čekám na dostatečně silné heslo)';
return;
}
const vysledek = zxcvbn(hodnota);
const skore = vysledek.score;
spanSila.textContent = 'Síla hesla: ' + POPISKY_SILY[skore] + ' (' + skore + '/4)';
if (skore >= MIN_SILA) {
tlacitko.disabled = false;
spanDuvod.textContent = '';
} else {
tlacitko.disabled = true;
spanDuvod.textContent = ' (heslo je příliš slabé, potřebuji alespoň ' + MIN_SILA + '/4)';
}
});
}
</script> </script>
<script src="<?php echo htmlspecialchars(PROJEKT_URL); ?>/auth/js/heslo-sila.js"></script>
</body> </body>
</html> </html>
+90
View File
@@ -0,0 +1,90 @@
// ============================================================
// KONTROLA SÍLY HESLA
// ============================================================
// Tento skript se stará o indikátor síly hesla a blokování
// tlačítka odeslání formuláře.
//
// Předpoklady (musí být v HTML před načtením tohoto skriptu):
// - načtena knihovna zxcvbn
// - definovány proměnné AUTH_HESLO_MIN_SILA a AUTH_HESLO_MIN_DELKA
// (vkládá je PHP přímo do stránky)
//
// Skript hledá na stránce prvky podle ID:
// - input#heslo ... pole pro zadání hesla
// - span#heslo-sila ... zobrazení síly hesla
// - button#tlacitko-odeslat ... tlačítko pro odeslání
// - span#tlacitko-duvod ... vysvětlení proč je tlačítko zakázané
// ============================================================
(function () {
// Popisky síly hesla (index = skóre zxcvbn 04)
var POPISKY_SILY = [
'Velmi slabé',
'Slabé',
'Průměrné',
'Silné',
'Velmi silné'
];
var inputHeslo = document.getElementById('heslo');
var spanSila = document.getElementById('heslo-sila');
var tlacitko = document.getElementById('tlacitko-odeslat');
var spanDuvod = document.getElementById('tlacitko-duvod');
// Pokud prvky na stránce nejsou (např. zobrazení úspěchu),
// skript tiše skončí
if (!inputHeslo || !spanSila || !tlacitko || !spanDuvod) {
return;
}
function zkontrolujHeslo() {
var hodnota = inputHeslo.value;
// Prázdné heslo
if (hodnota.length === 0) {
spanSila.textContent = 'Síla hesla: zadej heslo';
tlacitko.disabled = true;
spanDuvod.textContent = ' (čekám na zadání hesla)';
return;
}
// Kontrola minimální délky
// AUTH_HESLO_MIN_DELKA je definována PHP přímo ve stránce
if (hodnota.length < AUTH_HESLO_MIN_DELKA) {
spanSila.textContent = 'Heslo je příliš krátké ('
+ hodnota.length + ' / '
+ AUTH_HESLO_MIN_DELKA + ' znaků)';
tlacitko.disabled = true;
spanDuvod.textContent = ' (heslo musí mít alespoň '
+ AUTH_HESLO_MIN_DELKA + ' znaků)';
return;
}
// Kontrola síly přes zxcvbn
var vysledek = zxcvbn(hodnota);
var skore = vysledek.score; // 04
spanSila.textContent = 'Síla hesla: '
+ POPISKY_SILY[skore]
+ ' (' + skore + '/4)'
+ ' · délka: ' + hodnota.length + ' znaků';
// Tlačítko je dostupné jen pokud jsou splněny obě podmínky:
// 1) dostatečná délka (zkontrolována výše)
// 2) dostatečná síla (zxcvbn skóre)
if (skore >= AUTH_HESLO_MIN_SILA) {
tlacitko.disabled = false;
spanDuvod.textContent = '';
} else {
tlacitko.disabled = true;
spanDuvod.textContent = ' (heslo je příliš slabé, '
+ 'potřebuji alespoň '
+ AUTH_HESLO_MIN_SILA + '/4)';
}
}
// Spustíme kontrolu při každé změně hesla
inputHeslo.addEventListener('input', zkontrolujHeslo);
})();
+8 -72
View File
@@ -2,11 +2,6 @@
// ============================================================ // ============================================================
// OBNOVA HESLA KROK 2 // OBNOVA HESLA KROK 2
// ============================================================ // ============================================================
// Na tuto stránku přichází uživatel z odkazu v emailu.
// Odkaz obsahuje ID záznamu a plaintext token.
//
// Stránka ověří platnost tokenu a umožní nastavit nové heslo.
// ============================================================
require_once __DIR__ . '/config.php'; require_once __DIR__ . '/config.php';
require_once __DIR__ . '/db.php'; require_once __DIR__ . '/db.php';
@@ -21,7 +16,6 @@ session_set_cookie_params([
]); ]);
session_start(); session_start();
// Pokud je uživatel přihlášen, přesměrujeme ho
if (isset($_SESSION['uzivatel_id'])) { if (isset($_SESSION['uzivatel_id'])) {
header('Location: ' . AUTH_REDIRECT_PO_PRIHLASENI); header('Location: ' . AUTH_REDIRECT_PO_PRIHLASENI);
exit; exit;
@@ -40,16 +34,15 @@ $csrf_token = $_SESSION['csrf_token'];
// OVĚŘENÍ ODKAZU (parametry z URL) // OVĚŘENÍ ODKAZU (parametry z URL)
// ------------------------------------------------------------ // ------------------------------------------------------------
$token_ok = false; // true pokud je token platný $token_ok = false;
$zaznam_id = 0; // ID záznamu v auth_password_resets $zaznam_id = 0;
$uzivatel_id = 0; // ID uživatele $uzivatel_id = 0;
$id_z_url = $_GET['id'] ?? ''; $id_z_url = $_GET['id'] ?? '';
$token_z_url = $_GET['token'] ?? ''; $token_z_url = $_GET['token'] ?? '';
if (!empty($id_z_url) && !empty($token_z_url)) { if (!empty($id_z_url) && !empty($token_z_url)) {
// Vyhledáme záznam podle ID
$stmt = $pdo->prepare(" $stmt = $pdo->prepare("
SELECT `id`, `uzivatel_id`, `token_hash`, `expiruje` SELECT `id`, `uzivatel_id`, `token_hash`, `expiruje`
FROM `" . DB_TABULKA_RESET . "` FROM `" . DB_TABULKA_RESET . "`
@@ -60,12 +53,7 @@ if (!empty($id_z_url) && !empty($token_z_url)) {
$zaznam = $stmt->fetch(); $zaznam = $stmt->fetch();
if ($zaznam) { if ($zaznam) {
// Zkontrolujeme expiraci
if (time() < strtotime($zaznam['expiruje'])) { if (time() < strtotime($zaznam['expiruje'])) {
// Ověříme token (password_verify = konstantní čas,
// ochrana před timing útoky)
if (password_verify($token_z_url, $zaznam['token_hash'])) { if (password_verify($token_z_url, $zaznam['token_hash'])) {
$token_ok = true; $token_ok = true;
$zaznam_id = $zaznam['id']; $zaznam_id = $zaznam['id'];
@@ -76,7 +64,7 @@ if (!empty($id_z_url) && !empty($token_z_url)) {
} }
// ------------------------------------------------------------ // ------------------------------------------------------------
// ZPRACOVÁNÍ FORMULÁŘE (nastavení nového hesla) // ZPRACOVÁNÍ FORMULÁŘE
// ------------------------------------------------------------ // ------------------------------------------------------------
$chyba = ''; $chyba = '';
@@ -84,7 +72,6 @@ $uspech = false;
if ($_SERVER['REQUEST_METHOD'] === 'POST' && $token_ok) { if ($_SERVER['REQUEST_METHOD'] === 'POST' && $token_ok) {
// -- Ověření CSRF tokenu ----------------------------------
$csrf_z_formulare = $_POST['csrf_token'] ?? ''; $csrf_z_formulare = $_POST['csrf_token'] ?? '';
if (!hash_equals($csrf_token, $csrf_z_formulare)) { if (!hash_equals($csrf_token, $csrf_z_formulare)) {
$chyba = 'Neplatný požadavek. Zkuste stránku obnovit a zkusit znovu.'; $chyba = 'Neplatný požadavek. Zkuste stránku obnovit a zkusit znovu.';
@@ -95,7 +82,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && $token_ok) {
$heslo = $_POST['heslo'] ?? ''; $heslo = $_POST['heslo'] ?? '';
$heslo2 = $_POST['heslo2'] ?? ''; $heslo2 = $_POST['heslo2'] ?? '';
// Validace hesla
if (empty($heslo)) { if (empty($heslo)) {
$chyba = 'Heslo nesmí být prázdné.'; $chyba = 'Heslo nesmí být prázdné.';
} elseif (mb_strlen($heslo) < HESLO_MIN_DELKA) { } elseif (mb_strlen($heslo) < HESLO_MIN_DELKA) {
@@ -108,11 +94,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && $token_ok) {
if (empty($chyba)) { if (empty($chyba)) {
try { try {
// Zahashujeme nové heslo
$heslo_hash = password_hash($heslo, PASSWORD_DEFAULT); $heslo_hash = password_hash($heslo, PASSWORD_DEFAULT);
// Aktualizujeme heslo uživatele v DB
$stmt = $pdo->prepare(" $stmt = $pdo->prepare("
UPDATE `" . DB_TABULKA_UZIVATELE . "` UPDATE `" . DB_TABULKA_UZIVATELE . "`
SET `heslo` = :heslo SET `heslo` = :heslo
@@ -123,15 +106,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && $token_ok) {
':id' => $uzivatel_id, ':id' => $uzivatel_id,
]); ]);
// Smažeme použitý reset token je jednorázový
$stmt2 = $pdo->prepare(" $stmt2 = $pdo->prepare("
DELETE FROM `" . DB_TABULKA_RESET . "` DELETE FROM `" . DB_TABULKA_RESET . "`
WHERE `id` = :id WHERE `id` = :id
"); ");
$stmt2->execute([':id' => $zaznam_id]); $stmt2->execute([':id' => $zaznam_id]);
// Smažeme všechny remember me tokeny tohoto uživatele
// po změně hesla jsou všechna zařízení odhlášena
$stmt3 = $pdo->prepare(" $stmt3 = $pdo->prepare("
DELETE FROM `" . DB_TABULKA_TOKENY . "` DELETE FROM `" . DB_TABULKA_TOKENY . "`
WHERE `uzivatel_id` = :uzivatel_id WHERE `uzivatel_id` = :uzivatel_id
@@ -174,16 +154,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && $token_ok) {
<?php elseif (!$token_ok): ?> <?php elseif (!$token_ok): ?>
<!-- Token je neplatný nebo vypršel -->
<p><strong>Tento odkaz pro obnovu hesla je neplatný nebo vypršel.</strong></p> <p><strong>Tento odkaz pro obnovu hesla je neplatný nebo vypršel.</strong></p>
<p>Požádej o <p>Požádej o <a href="<?php echo htmlspecialchars(AUTH_RESET_URL); ?>">nový odkaz pro obnovu hesla</a>.</p>
<a href="<?php echo htmlspecialchars(dirname($_SERVER['PHP_SELF'])); ?>/reset_hesla.php">
nový odkaz pro obnovu hesla</a>.</p>
<?php else: ?> <?php else: ?>
<!-- Token je platný zobrazíme formulář -->
<?php if (!empty($chyba)): ?> <?php if (!empty($chyba)): ?>
<p><strong>Chyba: <?php echo htmlspecialchars($chyba); ?></strong></p> <p><strong>Chyba: <?php echo htmlspecialchars($chyba); ?></strong></p>
<?php endif; ?> <?php endif; ?>
@@ -231,49 +206,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && $token_ok) {
<script src="https://cdnjs.cloudflare.com/ajax/libs/zxcvbn/4.4.2/zxcvbn.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/zxcvbn/4.4.2/zxcvbn.js"></script>
<script> <script>
const MIN_SILA = <?php echo (int) HESLO_MIN_SILA; ?>; var AUTH_HESLO_MIN_SILA = <?php echo (int) HESLO_MIN_SILA; ?>;
var AUTH_HESLO_MIN_DELKA = <?php echo (int) HESLO_MIN_DELKA; ?>;
const POPISKY_SILY = [
'Velmi slabé',
'Slabé',
'Průměrné',
'Silné',
'Velmi silné'
];
const inputHeslo = document.getElementById('heslo');
const spanSila = document.getElementById('heslo-sila');
const tlacitko = document.getElementById('tlacitko-odeslat');
const spanDuvod = document.getElementById('tlacitko-duvod');
// Prvky existují jen pokud je zobrazen formulář
// (ne při úspěchu nebo neplatném tokenu)
if (inputHeslo) {
inputHeslo.addEventListener('input', function () {
const hodnota = this.value;
if (hodnota.length === 0) {
spanSila.textContent = 'Síla hesla: zadej heslo';
tlacitko.disabled = true;
spanDuvod.textContent = ' (čekám na dostatečně silné heslo)';
return;
}
const vysledek = zxcvbn(hodnota);
const skore = vysledek.score;
spanSila.textContent = 'Síla hesla: ' + POPISKY_SILY[skore] + ' (' + skore + '/4)';
if (skore >= MIN_SILA) {
tlacitko.disabled = false;
spanDuvod.textContent = '';
} else {
tlacitko.disabled = true;
spanDuvod.textContent = ' (heslo je příliš slabé, potřebuji alespoň ' + MIN_SILA + '/4)';
}
});
}
</script> </script>
<script src="<?php echo htmlspecialchars(PROJEKT_URL); ?>/auth/js/heslo-sila.js"></script>
</body> </body>
</html> </html>
+5 -70
View File
@@ -2,15 +2,10 @@
// ============================================================ // ============================================================
// REGISTRACE NOVÉHO UŽIVATELE // REGISTRACE NOVÉHO UŽIVATELE
// ============================================================ // ============================================================
// Tento soubor je dostupný pouze pokud je v config.php
// nastaveno REGISTRACE_OTEVRENA = true.
// Jinak registraci provádí pouze admin přes admin.php.
// ============================================================
require_once __DIR__ . '/config.php'; require_once __DIR__ . '/config.php';
require_once __DIR__ . '/db.php'; require_once __DIR__ . '/db.php';
// Spuštění session (stejné nastavení jako v auth.php)
session_name(SESSION_NAZEV); session_name(SESSION_NAZEV);
session_set_cookie_params([ session_set_cookie_params([
'lifetime' => 0, 'lifetime' => 0,
@@ -26,8 +21,6 @@ session_start();
// ------------------------------------------------------------ // ------------------------------------------------------------
if (!REGISTRACE_OTEVRENA) { if (!REGISTRACE_OTEVRENA) {
// Registrace je uzavřena zobrazíme informaci a skončíme
// Neposíláme HTTP 403, aby útočník nezjistil, že stránka existuje
die('<!DOCTYPE html><html lang="cs"><head><meta charset="UTF-8"><title>Registrace</title></head><body><p>Registrace nových uživatelů není momentálně dostupná.</p><p><a href="' . htmlspecialchars(AUTH_LOGIN_URL) . '">Zpět na přihlášení</a></p></body></html>'); die('<!DOCTYPE html><html lang="cs"><head><meta charset="UTF-8"><title>Registrace</title></head><body><p>Registrace nových uživatelů není momentálně dostupná.</p><p><a href="' . htmlspecialchars(AUTH_LOGIN_URL) . '">Zpět na přihlášení</a></p></body></html>');
} }
@@ -56,7 +49,6 @@ $email_hodnota = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') { if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// -- Ověření CSRF tokenu ----------------------------------
$csrf_z_formulare = $_POST['csrf_token'] ?? ''; $csrf_z_formulare = $_POST['csrf_token'] ?? '';
if (!hash_equals($csrf_token, $csrf_z_formulare)) { if (!hash_equals($csrf_token, $csrf_z_formulare)) {
$chyba = 'Neplatný požadavek. Zkuste stránku obnovit a zkusit znovu.'; $chyba = 'Neplatný požadavek. Zkuste stránku obnovit a zkusit znovu.';
@@ -64,14 +56,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (empty($chyba)) { if (empty($chyba)) {
// -- Načtení hodnot z formuláře -----------------------
$email = trim($_POST['email'] ?? ''); $email = trim($_POST['email'] ?? '');
$heslo = $_POST['heslo'] ?? ''; $heslo = $_POST['heslo'] ?? '';
$heslo2 = $_POST['heslo2'] ?? ''; $heslo2 = $_POST['heslo2'] ?? '';
$email_hodnota = htmlspecialchars($email); $email_hodnota = htmlspecialchars($email);
// -- Validace emailu ----------------------------------
if (empty($email)) { if (empty($email)) {
$chyba = 'Email nesmí být prázdný.'; $chyba = 'Email nesmí být prázdný.';
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) { } elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
@@ -81,7 +71,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (empty($chyba)) { if (empty($chyba)) {
// -- Validace hesla -----------------------------------
if (empty($heslo)) { if (empty($heslo)) {
$chyba = 'Heslo nesmí být prázdné.'; $chyba = 'Heslo nesmí být prázdné.';
} elseif (mb_strlen($heslo) < HESLO_MIN_DELKA) { } elseif (mb_strlen($heslo) < HESLO_MIN_DELKA) {
@@ -93,37 +82,22 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (empty($chyba)) { if (empty($chyba)) {
// -- Kontrola, zda email již neexistuje ---------------
// Používáme stejnou obecnou chybovou hlášku jako při
// neúspěšném přihlášení nezveřejňujeme, které emaily
// jsou v systému registrovány (ochrana soukromí).
$stmt = $pdo->prepare(" $stmt = $pdo->prepare("
SELECT COUNT(*) AS pocet SELECT COUNT(*) AS pocet
FROM `" . DB_TABULKA_UZIVATELE . "` FROM `" . DB_TABULKA_UZIVATELE . "`
WHERE `email` = :email WHERE `email` = :email
"); ");
$stmt->execute([':email' => $email]); $stmt->execute([':email' => $email]);
$pocet = $stmt->fetch()['pocet']; if ($stmt->fetch()['pocet'] > 0) {
if ($pocet > 0) {
$chyba = 'Tento email je již registrován.'; $chyba = 'Tento email je již registrován.';
// Poznámka: Tato hláška záměrně říká, že email existuje.
// Pokud by ti vadilo zveřejňování existence emailů,
// změň hlášku na obecnější (např. "Registrace se nezdařila,
// zkontroluj zadané údaje."). Pro většinu projektů je ale
// přímá hláška přijatelná a uživatelsky přívětivější.
} }
} }
if (empty($chyba)) { if (empty($chyba)) {
// -- Vše v pořádku zaregistrujeme uživatele ---------
try { try {
// Zahashujeme heslo (nikdy neukládáme plaintext!)
$heslo_hash = password_hash($heslo, PASSWORD_DEFAULT); $heslo_hash = password_hash($heslo, PASSWORD_DEFAULT);
// Vložíme nového uživatele do tabulky auth_users
$stmt = $pdo->prepare(" $stmt = $pdo->prepare("
INSERT INTO `" . DB_TABULKA_UZIVATELE . "` INSERT INTO `" . DB_TABULKA_UZIVATELE . "`
(`email`, `heslo`, `admin`) (`email`, `heslo`, `admin`)
@@ -137,8 +111,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$novy_uzivatel_id = $pdo->lastInsertId(); $novy_uzivatel_id = $pdo->lastInsertId();
// Pokud existuje tabulka služby, vytvoříme prázdný řádek
// (ostatní data si uživatel doplní později v nastavení)
if (DB_TABULKA_SLUZBA !== '') { if (DB_TABULKA_SLUZBA !== '') {
$stmt2 = $pdo->prepare(" $stmt2 = $pdo->prepare("
INSERT INTO `" . DB_TABULKA_SLUZBA . "` INSERT INTO `" . DB_TABULKA_SLUZBA . "`
@@ -189,7 +161,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<form method="POST" action=""> <form method="POST" action="">
<!-- CSRF token -->
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrf_token); ?>"> <input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrf_token); ?>">
<p> <p>
@@ -214,7 +185,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
autocomplete="new-password" autocomplete="new-password"
> >
<br> <br>
<!-- Indikátor síly hesla vyplní ho zxcvbn přes JavaScript -->
<span id="heslo-sila">Síla hesla: zadej heslo</span> <span id="heslo-sila">Síla hesla: zadej heslo</span>
</p> </p>
@@ -230,7 +200,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
</p> </p>
<p> <p>
<!-- Tlačítko je zakázané dokud heslo nedosáhne požadované síly -->
<button type="submit" id="tlacitko-odeslat" disabled> <button type="submit" id="tlacitko-odeslat" disabled>
Zaregistrovat se Zaregistrovat se
</button> </button>
@@ -245,45 +214,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<script src="https://cdnjs.cloudflare.com/ajax/libs/zxcvbn/4.4.2/zxcvbn.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/zxcvbn/4.4.2/zxcvbn.js"></script>
<script> <script>
const MIN_SILA = <?php echo (int) HESLO_MIN_SILA; ?>; // Předáme PHP konstanty do JavaScriptu
var AUTH_HESLO_MIN_SILA = <?php echo (int) HESLO_MIN_SILA; ?>;
const POPISKY_SILY = [ var AUTH_HESLO_MIN_DELKA = <?php echo (int) HESLO_MIN_DELKA; ?>;
'Velmi slabé',
'Slabé',
'Průměrné',
'Silné',
'Velmi silné'
];
const inputHeslo = document.getElementById('heslo');
const spanSila = document.getElementById('heslo-sila');
const tlacitko = document.getElementById('tlacitko-odeslat');
const spanDuvod = document.getElementById('tlacitko-duvod');
inputHeslo.addEventListener('input', function () {
const hodnota = this.value;
if (hodnota.length === 0) {
spanSila.textContent = 'Síla hesla: zadej heslo';
tlacitko.disabled = true;
spanDuvod.textContent = ' (čekám na dostatečně silné heslo)';
return;
}
const vysledek = zxcvbn(hodnota);
const skore = vysledek.score;
spanSila.textContent = 'Síla hesla: ' + POPISKY_SILY[skore] + ' (' + skore + '/4)';
if (skore >= MIN_SILA) {
tlacitko.disabled = false;
spanDuvod.textContent = '';
} else {
tlacitko.disabled = true;
spanDuvod.textContent = ' (heslo je příliš slabé, potřebuji alespoň ' + MIN_SILA + '/4)';
}
});
</script> </script>
<script src="<?php echo htmlspecialchars(PROJEKT_URL); ?>/auth/js/heslo-sila.js"></script>
</body> </body>
</html> </html>