mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-09-25 01:53:03 +02:00
Add admin-only route to open Interdit thesis files from backoffice:
- reliable tab title /favicon wrapper, - Content-Disposition filename, - admin media route hardened to thesis-file prefixes only (defense-in-depth)
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
/**
|
||||
* Admin media viewer — a minimal HTML wrapper that reliably sets a useful
|
||||
* browser tab title when opening thesis files (incl. 'Interdit') from the
|
||||
* backoffice.
|
||||
*
|
||||
* Why a wrapper?
|
||||
* - PDFs served directly show the URL ("media.php") as the tab title because
|
||||
* most don't embed a /Title metadata and Chrome/Firefox's PDF viewer ignores
|
||||
* Content-Disposition: filename for the tab. A wrapper <title> always wins.
|
||||
* - The actual bytes stream through /admin/media.php (the server validated
|
||||
* inlined below), preserving the existing security checks (path whitelist,
|
||||
* realpath() jail, MIME allow-list, Interdit bypass gated by admin session).
|
||||
*
|
||||
* Security:
|
||||
* - Auth-gated by AdminAuth::requireLogin().
|
||||
* - Path is validated against thesis-file prefixes before it is passed on.
|
||||
* - The embedded iframe is same-origin (/admin/media.php); admin CSP permits
|
||||
* frame-src 'self'. noindex/nofollow and no-referrer keep it private.
|
||||
*
|
||||
* Usage: /admin/media-viewer.php?path=tfe/2025/2025-001/rapport.pdf
|
||||
*/
|
||||
require_once __DIR__ . '/../../bootstrap.php';
|
||||
require_once __DIR__ . '/../../src/AdminAuth.php';
|
||||
|
||||
AdminAuth::requireLogin();
|
||||
|
||||
require_once APP_ROOT . '/src/Database.php';
|
||||
require_once APP_ROOT . '/src/ErrorHandler.php';
|
||||
|
||||
$path = trim((string)($_GET['path'] ?? ''));
|
||||
|
||||
// Only thesis-file paths are ever served; reject anything else early.
|
||||
if (!preg_match('#^(theses|documents|tfe|these|frart)/[^?&]{1,255}$#', $path)) {
|
||||
http_response_code(400);
|
||||
echo 'Chemin invalide.';
|
||||
exit;
|
||||
}
|
||||
|
||||
$displayName = 'Document';
|
||||
try {
|
||||
$mediaDb = Database::getInstance();
|
||||
$displayName = $mediaDb->getFileDisplayName($path) ?? basename($path);
|
||||
} catch (\Throwable $e) {
|
||||
ErrorHandler::log('media_viewer_display', $e, ['path' => $path]);
|
||||
}
|
||||
if ($displayName === '') {
|
||||
$displayName = 'Document';
|
||||
}
|
||||
|
||||
$src = '/admin/media.php?path=' . urlencode($path);
|
||||
$title = htmlspecialchars($displayName);
|
||||
$srcAttr = htmlspecialchars($src);
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="robots" content="noindex, nofollow">
|
||||
<meta name="referrer" content="no-referrer">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/assets/favicon/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/assets/favicon/favicon-16x16.png">
|
||||
<link rel="shortcut icon" href="/assets/favicon/favicon.ico">
|
||||
<title><?= $title ?> – XAMXAM</title>
|
||||
<style>
|
||||
html, body { margin: 0; height: 100%; background: #525659; }
|
||||
iframe { display: block; width: 100%; height: 100%; border: 0; background: #525659; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<iframe src="<?= $srcAttr ?>" title="<?= $title ?>" allow="autoplay; fullscreen"></iframe>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user