diff --git a/adminer/README.md b/adminer/README.md index 7343427..03f325e 100644 --- a/adminer/README.md +++ b/adminer/README.md @@ -2,17 +2,32 @@ Dieses Projekt enthält eine kleine, selbst implementierte DB-Verwaltung unter `/adminer`. +## Schutz / Login +Statt Browser-Basic-Auth gibt es eine eigene Login-Seite. + +- User werden in der Datenbanktabelle `adminer_users` gespeichert. +- Passwörter werden gehasht (`password_hash`). + +### Initialen User anlegen (Seed) +Setze in deiner lokalen `.env` (Projekt-Root) einmalig: + +- `ADMINER_APP_SEED_USER=...` +- `ADMINER_APP_SEED_PASS=...` + +Beim ersten Aufruf von `/adminer` wird (falls der User noch nicht existiert) automatisch ein Nutzer angelegt. + ## Setup 1. Erstelle eine lokale `.env` im Projekt-Root (siehe `.env.example`). -2. Trage dort deine DB-Zugangsdaten ein. +2. Trage dort deine DB-Zugangsdaten ein (`DB_*`). > Wichtig: `.env` wird durch `.gitignore` ignoriert. ## Nutzung - Öffne im Browser: `/adminer` -- Login erfolgt über das Formular. +- Schritt 1: App-Login +- Schritt 2: DB-Verbindung testen und Datenbanken laden +- Schritt 3: In gewünschte Datenbank einloggen ## Hinweise - Das Tool ist bewusst minimal (Tabellenliste + Browse + einfache SQL-Query). -- Für produktive Nutzung bitte zusätzlich absichern (z.B. Basic Auth / IP-Allowlist). - +- Für produktive Nutzung bitte zusätzlich absichern (z.B. IP-Allowlist, VPN). diff --git a/adminer/index.php b/adminer/index.php index 0997f07..91336c8 100644 --- a/adminer/index.php +++ b/adminer/index.php @@ -1,11 +1,66 @@

DB-Verwaltung

' . h($e->getMessage()) . '
'); + exit; +} + +$appAction = (string)($_GET['auth'] ?? ''); +if ($appAction === 'logout') { + adminer_app_logout(); + header('Location: /adminer', true, 302); + exit; +} + +// Handle app login +$appError = null; +if ($_SERVER['REQUEST_METHOD'] === 'POST' && (string)($_POST['action'] ?? '') === 'app_login') { + $u = (string)($_POST['username'] ?? ''); + $p = (string)($_POST['password'] ?? ''); + $res = adminer_app_try_login($u, $p); + if (!empty($res['ok'])) { + header('Location: /adminer', true, 302); + exit; + } + $appError = (string)($res['error'] ?? 'Login fehlgeschlagen.'); +} + +if (!adminer_app_is_logged_in()) { + $body = '

DB-Verwaltung

Login
'; + $body .= '
'; + $body .= '

Bitte melde dich an, um die DB-Verwaltung zu öffnen.

'; + + if ($appError) { + $body .= '
' . h($appError) . '

'; + } + + $body .= '
' + . '' + . '

' + . '

' + . '' + . '
'; + + $body .= '
'; + $body .= '
Admin-Hinweis: Du kannst initial einen User per .env seeden: ' + . 'ADMINER_APP_SEED_USER + ADMINER_APP_SEED_PASS (einmalig; wird nicht überschrieben).
'; + + $body .= '
'; + + admin_layout('DB-Verwaltung', $body); + exit; +} require_once __DIR__ . '/auth.php'; -require_once __DIR__ . '/views.php'; admin_session_start(); @@ -109,7 +164,7 @@ if (!admin_is_logged_in()) { $body .= "
"; $body .= "

Hinweise

"; $body .= ""; $body .= "
"; @@ -195,7 +250,10 @@ try { $body = '
' . '

DB-Verwaltung

eingeloggt
' - . '
Logout
' + . '
' + . 'App-Logout' + . 'DB-Logout' + . '
' . '
'; $body .= '
'; diff --git a/adminer/user_auth.php b/adminer/user_auth.php new file mode 100644 index 0000000..300d3bb --- /dev/null +++ b/adminer/user_auth.php @@ -0,0 +1,116 @@ + PDO::ERRMODE_EXCEPTION, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, + ]); +} + +function adminer_app_bootstrap() +{ + $pdo = adminer_app_pdo(); + + // Minimal users table + $pdo->exec( + 'CREATE TABLE IF NOT EXISTS adminer_users (' + . 'id INT AUTO_INCREMENT PRIMARY KEY,' + . 'username VARCHAR(190) NOT NULL UNIQUE,' + . 'password_hash VARCHAR(255) NOT NULL,' + . 'created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP' + . ') ENGINE=InnoDB DEFAULT CHARSET=utf8mb4' + ); + + // Optional: auto-seed from .env ADMINER_APP_SEED_USER/PASS + $vars = env_load(dirname(__DIR__) . '/.env'); + $seedUser = env_get($vars, 'ADMINER_APP_SEED_USER', ''); + $seedPass = env_get($vars, 'ADMINER_APP_SEED_PASS', ''); + + if ($seedUser !== '' && $seedPass !== '') { + $stmt = $pdo->prepare('SELECT id FROM adminer_users WHERE username = ?'); + $stmt->execute([$seedUser]); + $exists = (bool)$stmt->fetchColumn(); + if (!$exists) { + $hash = password_hash($seedPass, PASSWORD_DEFAULT); + $ins = $pdo->prepare('INSERT INTO adminer_users (username, password_hash) VALUES (?, ?)'); + $ins->execute([$seedUser, $hash]); + } + } +} + +function adminer_app_is_logged_in() +{ + adminer_app_session_start(); + return !empty($_SESSION['adminer_app']['ok']); +} + +function adminer_app_logout() +{ + adminer_app_session_start(); + unset($_SESSION['adminer_app']); +} + +function adminer_app_try_login($username, $password) +{ + $username = trim((string)$username); + $password = (string)$password; + + if ($username === '' || $password === '') { + return ['ok' => false, 'error' => 'Bitte Benutzername und Passwort eingeben.']; + } + + $pdo = adminer_app_pdo(); + $stmt = $pdo->prepare('SELECT id, password_hash FROM adminer_users WHERE username = ?'); + $stmt->execute([$username]); + $row = $stmt->fetch(); + + if (!$row || empty($row['password_hash']) || !password_verify($password, (string)$row['password_hash'])) { + return ['ok' => false, 'error' => 'Login fehlgeschlagen.']; + } + + adminer_app_session_start(); + $_SESSION['adminer_app'] = [ + 'ok' => true, + 'username' => $username, + 'uid' => (int)$row['id'], + ]; + + return ['ok' => true, 'error' => null]; +} +