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:
Pontoporeia
2026-09-18 16:26:36 +02:00
parent e518163c5b
commit 6e1fc6a781
9 changed files with 404 additions and 7 deletions
+37
View File
@@ -175,6 +175,43 @@ Files are never served directly from disk. All access goes through `MediaControl
- Visibility gate: `access_type_id = 3` (Interdit) → HTTP 403
- MIME allow-list check before serving
### Opening “Interdit” files from the backoffice
Publicly, an Interdit file's `/media?path=…` returns 403. The admin can still open
one via a **dedicated admin-only route**: `/admin/media.php?path=…`.
- It is gated by `AdminAuth::requireLogin()` — served under `/admin` so the
administrator's session cookie (scoped to `/admin`) is sent.
- It calls `MediaController::handle(adminBypass: true)`, which lifts **only** the
Interdit visibility gate. Path whitelist, `realpath()` jail and the MIME
allow-list are still enforced — an admin cannot read arbitrary server files.
- **Defence-in-depth:** on the admin route the requested `path` is additionally
restricted to the five thesis-file prefixes (`tfe/`, `these/`, `frart/`,
`documents/`, `theses/`) via `MediaController::isThesisFilePath()`. Any other
storage path (`schema.sql`, `xamxam.db`, `tmp/`, `backups/`, `cache/`, …) is
rejected with 403 before it even reaches the MIME check.
- The backoffice recap page (`recapitulatif.php`) already emits the admin URL
for files whose owning thesis is `access_type_id = 3`.
### Reliable tab title for opened files
The recap opens an Interdit file via `/admin/media-viewer.php?path=…`, a small
HTML wrapper that sets a proper `<title>` (the original uploaded file name) and
embeds the file through `/admin/media.php?path=…` in a full-viewport iframe.
This gives a useful, consistent tab title across every file type. (Serving the
raw PDF directly shows the URL, `media.php`, instead of the file name, because
most PDFs carry no embedded `/Title` metadata that Chrome/Firefox's viewer
would otherwise use.) The wrapper also re-declares the site's favicon
(`/assets/favicon/…`) so the tab keeps the site icon even though it's a
standalone page.
Every served file (admin route and public `/media`) sets `Content-Disposition`
to `inline` together with the original uploaded filename (`thesis_files.file_name`),
so the browser shows a meaningful tab title (e.g. `rapport_2024.pdf`) instead of
`media.php`. The name is emitted safely as an ASCII `filename` fallback plus a
UTF-8 `filename*=UTF-8''…` form for accented / international names (RFC 6266),
with control chars / quotes / backslashes stripped to prevent header injection.
---
## Security notes