Files
MSPPPPaM/auth/registrace.php
T

289 lines
9.4 KiB
PHP
Raw 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
// ============================================================
// 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__ . '/db.php';
// Spuštění session (stejné nastavení jako v auth.php)
session_name(SESSION_NAZEV);
session_set_cookie_params([
'lifetime' => 0,
'path' => '/',
'httponly' => true,
'samesite' => 'Strict',
// 'secure' => true,
]);
session_start();
// ------------------------------------------------------------
// KONTROLA: Je registrace povolena?
// ------------------------------------------------------------
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>');
}
// Pokud je uživatel již přihlášen, přesměrujeme ho
if (isset($_SESSION['uzivatel_id'])) {
header('Location: ' . AUTH_REDIRECT_PO_PRIHLASENI);
exit;
}
// ------------------------------------------------------------
// CSRF TOKEN
// ------------------------------------------------------------
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
$csrf_token = $_SESSION['csrf_token'];
// ------------------------------------------------------------
// ZPRACOVÁNÍ FORMULÁŘE
// ------------------------------------------------------------
$chyba = '';
$uspech = false;
$email_hodnota = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// -- Ověření CSRF tokenu ----------------------------------
$csrf_z_formulare = $_POST['csrf_token'] ?? '';
if (!hash_equals($csrf_token, $csrf_z_formulare)) {
$chyba = 'Neplatný požadavek. Zkuste stránku obnovit a zkusit znovu.';
}
if (empty($chyba)) {
// -- Načtení hodnot z formuláře -----------------------
$email = trim($_POST['email'] ?? '');
$heslo = $_POST['heslo'] ?? '';
$heslo2 = $_POST['heslo2'] ?? '';
$email_hodnota = htmlspecialchars($email);
// -- Validace emailu ----------------------------------
if (empty($email)) {
$chyba = 'Email nesmí být prázdný.';
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$chyba = 'Email není platná emailová adresa.';
}
}
if (empty($chyba)) {
// -- Validace hesla -----------------------------------
if (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)) {
// -- 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("
SELECT COUNT(*) AS pocet
FROM `" . DB_TABULKA_UZIVATELE . "`
WHERE `email` = :email
");
$stmt->execute([':email' => $email]);
$pocet = $stmt->fetch()['pocet'];
if ($pocet > 0) {
$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)) {
// -- Vše v pořádku zaregistrujeme uživatele ---------
try {
// Zahashujeme heslo (nikdy neukládáme plaintext!)
$heslo_hash = password_hash($heslo, PASSWORD_DEFAULT);
// Vložíme nového uživatele do tabulky auth_users
$stmt = $pdo->prepare("
INSERT INTO `" . DB_TABULKA_UZIVATELE . "`
(`email`, `heslo`, `admin`)
VALUES
(:email, :heslo, 0)
");
$stmt->execute([
':email' => $email,
':heslo' => $heslo_hash,
]);
$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 !== '') {
$stmt2 = $pdo->prepare("
INSERT INTO `" . DB_TABULKA_SLUZBA . "`
(`uzivatel_id`)
VALUES
(:uzivatel_id)
");
$stmt2->execute([':uzivatel_id' => $novy_uzivatel_id]);
}
$uspech = true;
} catch (PDOException $e) {
error_log('Chyba při registraci: ' . $e->getMessage());
$chyba = 'Při registraci došlo k chybě. Zkuste to prosím znovu.';
}
}
}
// ------------------------------------------------------------
// HTML VÝSTUP
// ------------------------------------------------------------
?>
<!DOCTYPE html>
<html lang="cs">
<head>
<meta charset="UTF-8">
<title>Registrace <?php echo htmlspecialchars(PROJEKT_NAZEV); ?></title>
<?php if (AUTH_CSS !== ''): ?>
<link rel="stylesheet" href="<?php echo htmlspecialchars(AUTH_CSS); ?>">
<?php endif; ?>
</head>
<body>
<h1>Registrace</h1>
<h2><?php echo htmlspecialchars(PROJEKT_NAZEV); ?></h2>
<?php if ($uspech): ?>
<p><strong>Registrace proběhla úspěšně!</strong></p>
<p>Můžeš se nyní <a href="<?php echo htmlspecialchars(AUTH_LOGIN_URL); ?>">přihlásit</a>.</p>
<?php else: ?>
<?php if (!empty($chyba)): ?>
<p><strong>Chyba: <?php echo htmlspecialchars($chyba); ?></strong></p>
<?php endif; ?>
<form method="POST" action="">
<!-- CSRF token -->
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrf_token); ?>">
<p>
<label for="email">Email:</label><br>
<input
type="email"
id="email"
name="email"
value="<?php echo $email_hodnota; ?>"
required
autocomplete="email"
>
</p>
<p>
<label for="heslo">Heslo:</label><br>
<input
type="password"
id="heslo"
name="heslo"
required
autocomplete="new-password"
>
<br>
<!-- Indikátor síly hesla vyplní ho zxcvbn přes JavaScript -->
<span id="heslo-sila">Síla hesla: zadej heslo</span>
</p>
<p>
<label for="heslo2">Heslo znovu (pro ověření):</label><br>
<input
type="password"
id="heslo2"
name="heslo2"
required
autocomplete="new-password"
>
</p>
<p>
<!-- Tlačítko je zakázané dokud heslo nedosáhne požadované síly -->
<button type="submit" id="tlacitko-odeslat" disabled>
Zaregistrovat se
</button>
<span id="tlacitko-duvod"> (čekám na dostatečně silné heslo)</span>
</p>
</form>
<p><a href="<?php echo htmlspecialchars(AUTH_LOGIN_URL); ?>">Už máš účet? Přihlas se.</a></p>
<?php endif; ?>
<script src="https://cdnjs.cloudflare.com/ajax/libs/zxcvbn/4.4.2/zxcvbn.js"></script>
<script>
const MIN_SILA = <?php echo (int) HESLO_MIN_SILA; ?>;
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');
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>
</body>
</html>