Compare commits
2 Commits
8433c8d880
...
43ab962ca5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
43ab962ca5 | ||
|
|
b2a74c2a17 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
||||
www/secret.php
|
||||
www/_user.php
|
||||
|
||||
@@ -4,7 +4,7 @@ FROM php:8.2-apache
|
||||
RUN a2enmod rewrite
|
||||
|
||||
# PHP-Extensions für MariaDB
|
||||
RUN docker-php-ext-install pdo pdo_mysql
|
||||
RUN docker-php-ext-install mysqli pdo pdo_mysql
|
||||
|
||||
# VHost kopieren
|
||||
COPY vhost.conf /etc/apache2/sites-available/000-default.conf
|
||||
|
||||
@@ -22,6 +22,7 @@ CREATE TABLE access_tokens (
|
||||
uuid CHAR(36) NOT NULL UNIQUE,
|
||||
expires_at DATETIME NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
notes TEXT NULL,
|
||||
FOREIGN KEY (identity_id)
|
||||
REFERENCES identities(id)
|
||||
ON DELETE CASCADE
|
||||
@@ -53,3 +54,13 @@ CREATE TABLE files (
|
||||
REFERENCES access_tokens(id)
|
||||
ON DELETE SET NULL
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE admin_login_attempts (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
ip_address VARCHAR(45) NOT NULL,
|
||||
attempts INT NOT NULL DEFAULT 1,
|
||||
locked_until DATETIME NULL,
|
||||
last_attempt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY (ip_address)
|
||||
);
|
||||
|
||||
|
||||
70
www/_func.php
Normal file
70
www/_func.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
function registerFailedLogin(string $ip, $sql): void {
|
||||
global $sql;
|
||||
$entry = $sql->single(
|
||||
"SELECT id, attempts
|
||||
FROM admin_login_attempts
|
||||
WHERE ip_address = ?",
|
||||
"s",
|
||||
[$ip]
|
||||
);
|
||||
|
||||
if (!$entry) {
|
||||
// Erster Fehlversuch
|
||||
$sql->set(
|
||||
"INSERT INTO admin_login_attempts (ip_address, attempts, last_attempt)
|
||||
VALUES (?, 1, NOW())",
|
||||
"s",
|
||||
[$ip]
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
$attempts = (int)$entry['attempts'] + 1;
|
||||
|
||||
if ($attempts >= 3) {
|
||||
$sql->set(
|
||||
"UPDATE admin_login_attempts
|
||||
SET attempts = ?,
|
||||
locked_until = DATE_ADD(NOW(), INTERVAL 1 HOUR),
|
||||
last_attempt = NOW()
|
||||
WHERE id = ?",
|
||||
"ii",
|
||||
[$attempts, $entry['id']]
|
||||
);
|
||||
} else {
|
||||
$sql->set(
|
||||
"UPDATE admin_login_attempts
|
||||
SET attempts = ?,
|
||||
last_attempt = NOW()
|
||||
WHERE id = ?",
|
||||
"ii",
|
||||
[$attempts, $entry['id']]
|
||||
);
|
||||
}
|
||||
}
|
||||
function isIpLocked(string $ip, $sql): bool {
|
||||
global $sql;
|
||||
$entry = $sql->single(
|
||||
"SELECT locked_until
|
||||
FROM admin_login_attempts
|
||||
WHERE ip_address = ?
|
||||
AND locked_until IS NOT NULL
|
||||
AND locked_until > NOW()",
|
||||
"s",
|
||||
[$ip]
|
||||
);
|
||||
|
||||
return (bool)$entry;
|
||||
}
|
||||
function clearLoginAttempts(string $ip, $sql): void {
|
||||
global $sql;
|
||||
$sql->set(
|
||||
"DELETE FROM admin_login_attempts WHERE ip_address = ?",
|
||||
"s",
|
||||
[$ip]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
@@ -171,4 +171,6 @@ class SQL {
|
||||
// echo 'DESTROY';
|
||||
}
|
||||
}
|
||||
|
||||
$sql = new SQL();
|
||||
?>
|
||||
3
www/_user.php.example
Normal file
3
www/_user.php.example
Normal file
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
$admin_user = 'admin';
|
||||
$admin_password = 'password';
|
||||
219
www/admin.php
Normal file
219
www/admin.php
Normal file
@@ -0,0 +1,219 @@
|
||||
<?php
|
||||
require '_sql.php';
|
||||
require '_func.php';
|
||||
require '_user.php';
|
||||
|
||||
session_start();
|
||||
|
||||
$ip = $_SERVER['REMOTE_ADDR'];
|
||||
|
||||
if (isIpLocked($ip, $sql)) {
|
||||
http_response_code(403);
|
||||
exit('Zu viele Fehlversuche. IP für 1 Stunde gesperrt.');
|
||||
}
|
||||
|
||||
if (!($_SESSION['is_admin'] ?? false)) {
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$user = $_POST['username'] ?? '';
|
||||
$pass = $_POST['password'] ?? '';
|
||||
|
||||
if (
|
||||
$user !== $admin_user ||
|
||||
$pass !== $admin_password
|
||||
) {
|
||||
registerFailedLogin($ip, $sql);
|
||||
$error = 'Ungültige Zugangsdaten';
|
||||
} else {
|
||||
clearLoginAttempts($ip, $sql);
|
||||
$_SESSION['is_admin'] = true;
|
||||
header('Location: admin.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
if (!$admin || !password_verify($pass, $admin['password_hash'])) {
|
||||
registerFailedLogin($ip, $sql);
|
||||
$error = 'Ungültige Zugangsdaten';
|
||||
} else {
|
||||
clearLoginAttempts($ip, $sql);
|
||||
$_SESSION['is_admin'] = true;
|
||||
$_SESSION['admin_id'] = $admin['id'];
|
||||
header('Location: admin.php');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
// 🔑 Login-Formular
|
||||
?>
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Admin Login</title>
|
||||
<style>
|
||||
body { font-family: sans-serif; background:#0f172a; color:#e5e7eb; display:flex; height:100vh; align-items:center; justify-content:center; }
|
||||
form { background:#020617; padding:2rem; border-radius:12px; width:300px; }
|
||||
input, button { width:100%; padding:.6rem; margin-top:.75rem; }
|
||||
button { background:#38bdf8; border:0; cursor:pointer; }
|
||||
.err { color:#f87171; margin-top:.5rem; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<form method="post">
|
||||
<h2>Admin Login</h2>
|
||||
<input name="username" placeholder="Benutzername" required>
|
||||
<input name="password" type="password" placeholder="Passwort" required>
|
||||
<button>Login</button>
|
||||
<?php if (!empty($error)): ?>
|
||||
<div class="err"><?= htmlspecialchars($error) ?></div>
|
||||
<?php endif; ?>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
<?php
|
||||
exit;
|
||||
}
|
||||
|
||||
$uuid = $_GET['uuid'] ?? null;
|
||||
|
||||
if ($uuid) {
|
||||
$token = $sql->single(
|
||||
"SELECT * FROM access_tokens WHERE uuid = ?",
|
||||
"s",
|
||||
[$uuid]
|
||||
);
|
||||
|
||||
if (!$token) {
|
||||
$sql->set(
|
||||
"INSERT INTO access_tokens (identity_id, uuid)
|
||||
VALUES (1, ?)",
|
||||
"s",
|
||||
[$uuid]
|
||||
);
|
||||
|
||||
$token = $sql->single(
|
||||
"SELECT * FROM access_tokens WHERE uuid = ?",
|
||||
"s",
|
||||
[$uuid]
|
||||
);
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$issuedTo = $_POST['issued_to'] ?? '';
|
||||
$fields = $_POST['fields'] ?? [];
|
||||
|
||||
// Notiz speichern
|
||||
$sql->set(
|
||||
"UPDATE access_tokens SET notes = ? WHERE id = ?",
|
||||
"si",
|
||||
[$issuedTo, $token['id']]
|
||||
);
|
||||
|
||||
// Rechte neu setzen
|
||||
$sql->set(
|
||||
"DELETE FROM token_permissions WHERE token_id = ?",
|
||||
"i",
|
||||
[$token['id']]
|
||||
);
|
||||
|
||||
foreach ($fields as $key) {
|
||||
$sql->set(
|
||||
"INSERT INTO token_permissions (token_id, field_key)
|
||||
VALUES (?, ?)",
|
||||
"is",
|
||||
[$token['id'], $key]
|
||||
);
|
||||
}
|
||||
|
||||
$saved = true;
|
||||
}
|
||||
|
||||
// Alle Felder der Identität
|
||||
$allFields = $sql->get(
|
||||
"SELECT DISTINCT field_key FROM identity_fields WHERE identity_id = ?",
|
||||
"i",
|
||||
[$token['identity_id']]
|
||||
);
|
||||
|
||||
// Aktive Rechte
|
||||
$allowed = $sql->get(
|
||||
"SELECT field_key FROM token_permissions WHERE token_id = ?",
|
||||
"i",
|
||||
[$token['id']]
|
||||
);
|
||||
|
||||
$allowedKeys = array_column($allowed, 'field_key');
|
||||
?>
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>UUID bearbeiten</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>UUID verwalten</h1>
|
||||
<p><strong><?= htmlspecialchars($uuid) ?></strong></p>
|
||||
|
||||
<?php if (!empty($saved)) echo '<p>Gespeichert ✔</p>'; ?>
|
||||
|
||||
<form method="post">
|
||||
<h3>Sichtbare Informationen</h3>
|
||||
|
||||
<?php foreach ($allFields as $f): ?>
|
||||
<label>
|
||||
<input type="checkbox" name="fields[]" value="<?= htmlspecialchars($f['field_key']) ?>"
|
||||
<?= in_array($f['field_key'], $allowedKeys) ? 'checked' : '' ?>>
|
||||
<?= htmlspecialchars($f['field_key']) ?>
|
||||
</label><br>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<h3>Ausgegeben an</h3>
|
||||
<textarea name="issued_to" rows="4" cols="40"><?= htmlspecialchars($token['notes']) ?></textarea>
|
||||
|
||||
<br><br>
|
||||
<button>Speichern</button>
|
||||
</form>
|
||||
|
||||
<p><a href="admin.php">← Zurück</a></p>
|
||||
</body>
|
||||
</html>
|
||||
<?php
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* 📊 ADMIN DASHBOARD
|
||||
*/
|
||||
$identities = $sql->get("SELECT * FROM identities ORDER BY id DESC");
|
||||
$tokens = $sql->get("SELECT * FROM access_tokens ORDER BY created_at DESC");
|
||||
|
||||
?>
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Admin Dashboard</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Admin Dashboard</h1>
|
||||
|
||||
<h2>Identitäten</h2>
|
||||
<ul>
|
||||
<?php foreach ($identities as $i): ?>
|
||||
<li>ID <?= $i['id'] ?></li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
|
||||
<h2>UUIDs</h2>
|
||||
<ul>
|
||||
<?php foreach ($tokens as $t): ?>
|
||||
<li>
|
||||
<a href="admin.php?uuid=<?= htmlspecialchars($t['uuid']) ?>">
|
||||
<?= htmlspecialchars($t['uuid']) ?>
|
||||
</a>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
|
||||
<p><a href="logout.php">Logout</a></p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,5 +1,6 @@
|
||||
<?php
|
||||
require '_sql.php';
|
||||
require '_func.php';
|
||||
|
||||
$uuid = $_GET['uuid'] ?? null;
|
||||
if (!$uuid) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?php
|
||||
require '_sql.php';
|
||||
require '_func.php';
|
||||
|
||||
/**
|
||||
* Eingaben prüfen
|
||||
|
||||
Reference in New Issue
Block a user