Files
xamxam/app/public/admin/media-viewer.php
T
Pontoporeia 6e1fc6a781 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)
2026-09-18 16:26:36 +02:00

75 lines
2.7 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
/**
* 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>