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:
@@ -17,6 +17,8 @@ the XAMXAM TFE database.
|
||||
| `contenus-edit.php` | Edit a content page |
|
||||
| `acces.php` | Share-link management |
|
||||
| `file-access.php` | Restricted-file access requests |
|
||||
| `media.php` | Admin file viewer — opens files of 'Interdit' (access_type_id=3) theses; session-gated (`AdminAuth::requireLogin`), delegates to `MediaController::handle(adminBypass: true)` |
|
||||
| `media-viewer.php` | HTML wrapper that opens a thesis file with a reliable tab title (original file name); embeds the file via `media.php` in a full-viewport iframe |
|
||||
| `account.php` | Admin account / password |
|
||||
| `login.php` | Login (session) |
|
||||
| `import.php` | Redirects to `/admin/` (CSV import is inline in `index.php`) |
|
||||
|
||||
@@ -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>
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
/**
|
||||
* Dedicated admin file viewer — lets a backoffice user open files whose owning
|
||||
* thesis is marked 'Interdit' (access_type_id=3).
|
||||
*
|
||||
* Security:
|
||||
* - Auth-gated by AdminAuth::requireLogin() → only an authenticated admin
|
||||
* session (cookie scoped to /admin) can reach the handler below.
|
||||
* - Delegates to MediaController::handle(adminBypass: true), which bypasses
|
||||
* ONLY the Interdit visibility gate. The strict path-whitelist, realpath()
|
||||
* storage jail and MIME allow-list remain fully enforced, so an admin
|
||||
* cannot read arbitrary files on the server.
|
||||
*
|
||||
* Usage: /admin/media.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/Controllers/MediaController.php';
|
||||
|
||||
$controller = new MediaController();
|
||||
$controller->handle(adminBypass: true);
|
||||
Reference in New Issue
Block a user