Füge neue Funktionen hinzu: UUID-Validierung, Datei-Download und verbessere die Fehlerbehandlung

This commit is contained in:
Troy Grunt
2026-02-01 17:46:40 +01:00
parent 1d58f09675
commit 8433c8d880
5 changed files with 288 additions and 8 deletions

View File

@@ -1,2 +1,5 @@
# businesscard # businesscard
## TODO
- flood control
- admin https://chatgpt.com/share/697f82e6-1ed0-800e-b8d1-1ba0ce969dcf

View File

@@ -1,5 +1,7 @@
RewriteEngine On RewriteEngine On
RewriteRule ^_.*$ - [R=404,L]
# Nur wenn keine echte Datei / kein echtes Verzeichnis # Nur wenn keine echte Datei / kein echtes Verzeichnis
RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-d

0
www/_files/.gitkeep Normal file
View File

View File

@@ -1,16 +1,209 @@
<?php <?php
require '_sql.php'; require '_sql.php';
$uuid = $_GET['uuid'] ?? 'keine UUID'; $uuid = $_GET['uuid'] ?? null;
if(isset($_COOKIE['PHPSESSID'])){ if (!$uuid) {
session_start(); http_response_code(400);
if($_SESSION['is_admin']??false){ exit('Ungültige Anfrage');
header('Location: /admin.php?uuid='. $uuid); }
exit(0);
/**
* Admin-Weiterleitung (nur wenn Session existiert)
*/
if (isset($_COOKIE['PHPSESSID'])) {
session_start(['read_and_close' => true]);
if ($_SESSION['is_admin'] ?? false) {
header('Location: /admin.php?uuid=' . urlencode($uuid));
exit;
} }
} }
echo "UUID: " . htmlspecialchars($uuid); /**
* UUID auflösen
*/
$token = $sql->single(
"SELECT * FROM access_tokens
WHERE uuid = ?
AND (expires_at IS NULL OR expires_at > NOW())",
"s",
[$uuid]
);
//TODO if (!$token) {
http_response_code(404);
exit('Identität nicht gefunden');
}
/**
* Sichtbare Stammdaten laden
*/
$fields = $sql->get(
"SELECT f.field_key, f.field_value
FROM identity_fields f
JOIN token_permissions p ON p.field_key = f.field_key
WHERE f.identity_id = ? AND p.token_id = ?
ORDER BY f.field_key",
"ii",
[$token['identity_id'], $token['id']]
);
/**
* Sichtbare Dateien laden
*/
$files = $sql->get(
"SELECT id, filename, mime_type
FROM files
WHERE identity_id = ?
AND (token_id IS NULL OR token_id = ?)
ORDER BY uploaded_at DESC",
"ii",
[$token['identity_id'], $token['id']]
);
/**
* Feldnamen hübsch machen
*/
function label(string $key): string {
return match ($key) {
'name' => 'Name',
'email' => 'E-Mail',
'phone' => 'Telefon',
'address' => 'Adresse',
default => ucfirst($key),
};
}
?>
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Digitale Identität</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
:root {
--bg: #0f172a;
--card: #020617;
--text: #e5e7eb;
--muted: #94a3b8;
--accent: #38bdf8;
--border: #1e293b;
}
* { box-sizing: border-box; }
body {
margin: 0;
font-family: system-ui, -apple-system, sans-serif;
background: linear-gradient(135deg, #020617, #0f172a);
color: var(--text);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 1.5rem;
}
.card {
width: 100%;
max-width: 520px;
background: var(--card);
border: 1px solid var(--border);
border-radius: 16px;
padding: 1.75rem;
box-shadow: 0 20px 40px rgba(0,0,0,.6);
}
h1 {
margin: 0 0 1rem;
font-size: 1.5rem;
text-align: center;
}
.field {
margin-bottom: 1rem;
}
.label {
font-size: .75rem;
text-transform: uppercase;
color: var(--muted);
letter-spacing: .05em;
}
.value {
font-size: 1rem;
margin-top: .25rem;
word-break: break-word;
}
a {
color: var(--accent);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
.files {
margin-top: 2rem;
padding-top: 1rem;
border-top: 1px solid var(--border);
}
.file {
display: flex;
justify-content: space-between;
align-items: center;
padding: .5rem 0;
}
.file span {
color: var(--muted);
font-size: .85rem;
}
footer {
margin-top: 1.5rem;
text-align: center;
font-size: .75rem;
color: var(--muted);
}
</style>
</head>
<body>
<div class="card">
<h1>Identität</h1>
<?php foreach ($fields as $field): ?>
<div class="field">
<div class="label"><?= htmlspecialchars(label($field['field_key'])) ?></div>
<div class="value">
<?= nl2br(htmlspecialchars($field['field_value'])) ?>
</div>
</div>
<?php endforeach; ?>
<?php if ($files): ?>
<div class="files">
<h2 style="font-size:1rem;margin-bottom:.5rem;">Dateien</h2>
<?php foreach ($files as $file): ?>
<div class="file">
<span><?= htmlspecialchars($file['filename']) ?></span>
<a href="/download.php?id=<?= (int)$file['id'] ?>&uuid=<?= urlencode($uuid) ?>">
Download
</a>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<footer>
Zugriff über sicheren Link
</footer>
</div>
</body>
</html>

82
www/download.php Normal file
View File

@@ -0,0 +1,82 @@
<?php
require '_sql.php';
/**
* Eingaben prüfen
*/
$fileId = isset($_GET['id']) ? (int)$_GET['id'] : 0;
$uuid = $_GET['uuid'] ?? null;
if ($fileId <= 0 || !$uuid) {
http_response_code(400);
exit('Ungültige Anfrage');
}
/**
* Token validieren
*/
$token = $sql->single(
"SELECT id, identity_id
FROM access_tokens
WHERE uuid = ?
AND (expires_at IS NULL OR expires_at > NOW())",
"s",
[$uuid]
);
if (!$token) {
http_response_code(403);
exit('Kein Zugriff');
}
/**
* Datei laden & Berechtigung prüfen
*/
$file = $sql->single(
"SELECT *
FROM files
WHERE id = ?
AND identity_id = ?
AND (token_id IS NULL OR token_id = ?)",
"iii",
[$fileId, $token['identity_id'], $token['id']]
);
if (!$file) {
http_response_code(404);
exit('Datei nicht gefunden');
}
/**
* Dateipfad
*/
$basePath = '/var/www/files/';
$path = realpath($basePath . $file['stored_name']);
if (!$path || !str_starts_with($path, $basePath) || !is_file($path)) {
http_response_code(404);
exit('Datei fehlt');
}
/**
* Saubere Ausgabe
*/
$filename = $file['filename'];
$mime = $file['mime_type'] ?: 'application/octet-stream';
$size = filesize($path);
header('Content-Description: File Transfer');
header('Content-Type: ' . $mime);
header('Content-Disposition: attachment; filename="' . basename($filename) . '"');
header('Content-Length: ' . $size);
header('X-Content-Type-Options: nosniff');
header('Cache-Control: private, no-store, no-cache, must-revalidate');
header('Pragma: no-cache');
header('Expires: 0');
while (ob_get_level()) {
ob_end_clean();
}
readfile($path);
exit;