Files
MSPPPPaM/auth/install.php
T
stepan c4ec313eeb [FÁZE-1][config] Přidán konfigurační soubor systému přihlašování
[FÁZE-1][db] Přidáno připojení k databázi přes PDO
[FÁZE-1][install] Přidán instalační skript s tvorbou tabulek a prvního admina
2026-03-16 23:08:28 +01:00

323 lines
12 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
// ============================================================
// INSTALAČNÍ SKRIPT
// ============================================================
// Tento skript spusť JEDNOU po nahrání souborů na server.
// Vytvoří všechny potřebné tabulky v databázi a prvního
// administrátora.
//
// Po úspěšné instalaci se vytvoří soubor install.lock
// a tento skript již nepůjde spustit znovu.
//
// BEZPEČNOST: Po instalaci můžeš install.php smazat,
// nebo ho nechat install.lock ho zablokuje.
// ============================================================
require_once __DIR__ . '/config.php';
require_once __DIR__ . '/db.php';
// ------------------------------------------------------------
// KONTROLA: Proběhla už instalace?
// ------------------------------------------------------------
$lock_soubor = __DIR__ . '/install.lock';
if (file_exists($lock_soubor)) {
die('<p>Instalace již proběhla. Pokud chceš instalaci opakovat, smaž soubor <code>auth/install.lock</code>. Pozor tím přijdeš o všechna data!</p>');
}
// ------------------------------------------------------------
// ZPRACOVÁNÍ FORMULÁŘE (pokud byl odeslán)
// ------------------------------------------------------------
$chyba = '';
$uspech = false;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Načtení hodnot z formuláře
$email = trim($_POST['email'] ?? '');
$heslo = $_POST['heslo'] ?? '';
$heslo2 = $_POST['heslo2'] ?? '';
// Základní 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.';
}
// Validace hesla
if (empty($chyba) && empty($heslo)) {
$chyba = 'Heslo nesmí být prázdné.';
} elseif (empty($chyba) && mb_strlen($heslo) < HESLO_MIN_DELKA) {
$chyba = 'Heslo musí mít alespoň ' . HESLO_MIN_DELKA . ' znaků.';
} elseif (empty($chyba) && $heslo !== $heslo2) {
$chyba = 'Hesla se neshodují.';
}
// Pokud není chyba, spustíme instalaci
if (empty($chyba)) {
try {
// ------------------------------------------------
// VYTVOŘENÍ TABULEK
// ------------------------------------------------
// Tabulka uživatelů přihlašovacího systému
// utf8mb4 = plná podpora Unicode (diakritika, emoji)
$pdo->exec("
CREATE TABLE IF NOT EXISTS `" . DB_TABULKA_UZIVATELE . "` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`email` VARCHAR(255) NOT NULL,
`heslo` VARCHAR(255) NOT NULL,
`admin` TINYINT(1) NOT NULL DEFAULT 0,
`vytvoreno` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
");
// Tabulka remember me tokenů
// selector = veřejný identifikátor pro vyhledání záznamu v DB
// token_hash = bcrypt hash tajného tokenu (samotný token je jen v cookie)
$pdo->exec("
CREATE TABLE IF NOT EXISTS `" . DB_TABULKA_TOKENY . "` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`uzivatel_id` INT UNSIGNED NOT NULL,
`selector` VARCHAR(32) NOT NULL,
`token_hash` VARCHAR(255) NOT NULL,
`expiruje` DATETIME NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `selector` (`selector`),
FOREIGN KEY (`uzivatel_id`) REFERENCES `" . DB_TABULKA_UZIVATELE . "` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
");
// Tabulka brute force záznamů
// Ukládají se neúspěšné pokusy o přihlášení
$pdo->exec("
CREATE TABLE IF NOT EXISTS `" . DB_TABULKA_BRUTE . "` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`ip_adresa` VARCHAR(45) NOT NULL,
`email` VARCHAR(255) NOT NULL,
`cas` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `ip_adresa` (`ip_adresa`),
KEY `email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
");
// Tabulka tokenů pro obnovu hesla
// Token je platný jen omezenou dobu (viz config.php)
$pdo->exec("
CREATE TABLE IF NOT EXISTS `" . DB_TABULKA_RESET . "` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`uzivatel_id` INT UNSIGNED NOT NULL,
`token_hash` VARCHAR(255) NOT NULL,
`expiruje` DATETIME NOT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY (`uzivatel_id`) REFERENCES `" . DB_TABULKA_UZIVATELE . "` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
");
// Tabulka uživatelů konkrétní služby (pokud je nastavena)
// Tato tabulka slouží pro data specifická pro tvůj projekt
// (jméno, nastavení, kontaktní údaje apod.)
if (DB_TABULKA_SLUZBA !== '') {
$pdo->exec("
CREATE TABLE IF NOT EXISTS `" . DB_TABULKA_SLUZBA . "` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`uzivatel_id` INT UNSIGNED NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uzivatel_id` (`uzivatel_id`),
FOREIGN KEY (`uzivatel_id`) REFERENCES `" . DB_TABULKA_UZIVATELE . "` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
");
}
// ------------------------------------------------
// VYTVOŘENÍ PRVNÍHO ADMIN UŽIVATELE
// ------------------------------------------------
$heslo_hash = password_hash($heslo, PASSWORD_DEFAULT);
$stmt = $pdo->prepare("
INSERT INTO `" . DB_TABULKA_UZIVATELE . "`
(`email`, `heslo`, `admin`)
VALUES
(:email, :heslo, 1)
");
$stmt->execute([
':email' => $email,
':heslo' => $heslo_hash,
]);
$novy_uzivatel_id = $pdo->lastInsertId();
// Pokud existuje tabulka služby, vytvoříme prázdný řádek
if (DB_TABULKA_SLUZBA !== '') {
$stmt2 = $pdo->prepare("
INSERT INTO `" . DB_TABULKA_SLUZBA . "`
(`uzivatel_id`)
VALUES
(:uzivatel_id)
");
$stmt2->execute([':uzivatel_id' => $novy_uzivatel_id]);
}
// ------------------------------------------------
// VYTVOŘENÍ ZÁMKU zabránění opakované instalaci
// ------------------------------------------------
file_put_contents($lock_soubor, 'Instalace proběhla: ' . date('Y-m-d H:i:s'));
$uspech = true;
} catch (PDOException $e) {
// Chybu zalogujeme, uživateli zobrazíme obecnou hlášku
error_log('Chyba při instalaci: ' . $e->getMessage());
$chyba = 'Při instalaci došlo k chybě databáze. Zkontroluj nastavení v config.php a PHP error log.';
} catch (Exception $e) {
error_log('Chyba při instalaci: ' . $e->getMessage());
$chyba = 'Při instalaci došlo k neočekávané chybě.';
}
}
}
// ------------------------------------------------------------
// HTML VÝSTUP
// ------------------------------------------------------------
// Poznámka: žádné stylování vše je záměrně prosté,
// aby žádný prvek nebyl skrytý nebo překrytý.
// ------------------------------------------------------------
?>
<!DOCTYPE html>
<html lang="cs">
<head>
<meta charset="UTF-8">
<title>Instalace <?php echo htmlspecialchars(PROJEKT_NAZEV); ?></title>
</head>
<body>
<h1>Instalace systému přihlašování</h1>
<h2><?php echo htmlspecialchars(PROJEKT_NAZEV); ?></h2>
<?php if ($uspech): ?>
<p><strong>Instalace proběhla úspěšně!</strong></p>
<p>Tabulky byly vytvořeny a první administrátor byl založen.</p>
<p>Byl vytvořen soubor <code>auth/install.lock</code> instalaci již nelze spustit znovu.</p>
<p><a href="<?php echo htmlspecialchars(AUTH_LOGIN_URL); ?>">Přejít na přihlášení</a></p>
<?php else: ?>
<p>Tento skript vytvoří potřebné tabulky v databázi a založí prvního administrátora.</p>
<p>Spusť ho pouze jednou. Po instalaci bude zablokován souborem <code>install.lock</code>.</p>
<?php if (!empty($chyba)): ?>
<p><strong>Chyba: <?php echo htmlspecialchars($chyba); ?></strong></p>
<?php endif; ?>
<form method="POST" action="">
<p>
<label for="email">Email administrátora:</label><br>
<input
type="email"
id="email"
name="email"
value="<?php echo htmlspecialchars($_POST['email'] ?? ''); ?>"
required
>
</p>
<p>
<label for="heslo">Heslo administrátora:</label><br>
<input
type="password"
id="heslo"
name="heslo"
required
>
<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
>
</p>
<p>
<!-- Tlačítko je ve výchozím stavu zakázané.
JavaScript ho povolí, až heslo dosáhne požadované síly. -->
<button type="submit" id="tlacitko-odeslat" disabled>
Spustit instalaci
</button>
<span id="tlacitko-duvod"> (čekám na dostatečně silné heslo)</span>
</p>
</form>
<?php endif; ?>
<!-- ============================================================
zxcvbn knihovna pro hodnocení síly hesla od Dropboxu
Načítáme z CDN. Pokud chceš offline použití, stáhni soubor
a změň src na lokální cestu.
============================================================ -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/zxcvbn/4.4.2/zxcvbn.js"></script>
<script>
// Minimální požadovaná síla hesla (přebíráme z PHP konfigurace)
const MIN_SILA = <?php echo (int) HESLO_MIN_SILA; ?>;
// Popisky síly hesla (0 = nejslabší, 4 = nejsilnější)
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;
}
// Spuštění zxcvbn hodnocení
const vysledek = zxcvbn(hodnota);
const skore = vysledek.score; // 04
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>