Files
xamxam/app/public/admin/README.md
T
Pontoporeia 554ba3ee8d fix(admin): stop logging out active long-form work; raise idle timeout to 4h
The admin idle timeout (30 min) was refreshed only by navigations and HTMX
requests. During long encoding sessions on an open form there are none, so
an actively-typing admin was logged out mid-work after ~30-45 min.

Add an activity-driven keepalive:
- /admin/session-keepalive.php: 204 when authenticated (refreshes
  admin_last_activity via AdminAuth::isAuthenticated()), 401 otherwise.
- admin-session-keepalive.js: marks activity only on real user input
  (pointer/keyboard/input/scroll/wheel/touch/focus) and pings at most once
  per 5 min while the tab is visible. A genuinely idle tab never pings, so
  the idle timeout still applies.

Raise the idle window 30 min -> 4 h: for a single-/few-admin back-office
whose main workflow is data entry, 30 min still kicked admins who stepped
away mid-form. With the keepalive in place, 4 h means "no interaction at
all", not "no navigation". Absolute timeout stays 12 h.

Also fix session ID rotation, which never fired: it used
`$absolute % IDLE_TIMEOUT_SECONDS === 0`, i.e. required a request to land
exactly on a multiple of the interval relative to login time. Replaced with
an explicit admin_last_rotation timestamp and a ROTATION_INTERVAL_SECONDS
(30 min) constant decoupled from the idle timeout, so raising the idle
window does not widen the fixation/replay window.

Refactor AdminAuth::enforceSessionTimeout() to return bool instead of
redirecting/exiting, so the keepalive endpoint can report 401 cleanly
rather than letting fetch follow a redirect to the login page.

Smoke test (just smoke-session-keepalive) covers activity refresh, 2 h idle
accepted, rotation firing, idle rejection+destruction, and unauthenticated
rejection. Docs updated.
2026-09-18 16:26:49 +02:00

93 lines
4.0 KiB
Markdown

# Admin Panel Structure
This directory and `app/templates/admin/` contain the admin panel for managing
the XAMXAM TFE database.
## Entry points (`app/public/admin/`)
| File | Purpose |
|------|---------|
| `index.php` | List all theses (main page; hosts the inline CSV import + tabs for list/trash) |
| `add.php` | Add new thesis form |
| `edit.php` | Existing thesis form |
| `recapitulatif.php` | Post-submission recap |
| `cleanup.php` | Cleanup page (Corbeille — restore/delete trashed files) |
| `system.php` | System dashboard (logs, SMTP/PeerTube status) |
| `contenus.php` | Editable content (pages, contacts) |
| `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 |
| `session-keepalive.php` | Lightweight `204`/`401` liveness ping that refreshes `admin_last_activity` while an open admin page is being actively used (see `docs/security.md`) |
| `login.php` | Login (session) |
| `import.php` | Redirects to `/admin/` (CSV import is inline in `index.php`) |
| `status.php`, `markdown-cheatsheet-fragment.php`, `*fragment.php` | HTMX fragments / helpers |
### Backend actions (`app/public/admin/actions/`)
Process forms and redirect (no HTML output):
- `formulaire.php` — thesis create submission (`ThesisCreateController::submit()`)
- `edit.php` — thesis edit submission (`ThesisEditController::save()`)
- `export-csv.php`, `export-db.php`, `export-files.php` — see `docs/export.md`
- `filepond/` — FilePond async upload endpoints
- many others: `publish`, `delete`, `corbeille` (trash), `draft`, `visibility`,
`tag`, `language`, `form-help*`, `page`, `apropos`, `smtp-test`,
`peertube-*`, `maintenance`, `settings`, `account`, `access-request`,
`acces-etudiante`, `cleanup-*`
## Templates
View templates live under `app/templates/admin/` (not in `public/`):
- `app/templates/admin/*.php` — page layouts
- `app/templates/admin/partials/` — shared fragments (toasts, dialogs, toc, …)
The public/partage and form partials live in `app/templates/partials/` and
`app/templates/partage/`.
## Auth
- **PHP session auth** (`src/AdminAuth.php`) via `AdminAuth::requireLogin()` is
the only authentication layer. The old nginx `auth_basic` layer has been
removed — see `docs/security.md` and `nginx/docs/PHP_AUTH_LAYER.md`.
- All forms include a CSRF token from `$_SESSION['csrf_token']`.
- Inputs use PDO prepared statements; uploads validated and stored outside the
webroot (`app/storage/`).
## Bootstrap / routing
Entry pages bootstrap the app and set up the environment:
```php
require_once __DIR__ . '/../../bootstrap.php'; // defines APP_ROOT, autoload, config
require_once APP_ROOT . '/src/AdminAuth.php';
AdminAuth::requireLogin();
```
`APP_ROOT` is the `app/` directory. Database access is via
`app/src/Database.php`; form logic lives in `app/src/Controllers/` and
`app/src/Form/`.
## URL structure
- `/admin/` — list theses (index.php)
- `/admin/add.php` — add thesis
- `/admin/edit.php?id=N` — edit thesis
- `/admin/cleanup.php`, `/admin/system.php`, `/admin/acces.php`, etc.
- `/admin/actions/…` — backend processors
## Development guide
**Add a page:** create `app/templates/admin/yourpage.php`, add a thin
`app/public/admin/yourpage.php` entry that bootstraps + requires the template,
and add navigation in `app/templates/admin/partials/admin-toc.php`.
**Add an action:** create `app/public/admin/actions/youraction.php` that
bootstraps, requires login, verifies the CSRF token, performs the work, and
redirects back to the referring admin page.
See `docs/development.md` for the general workflow (dev server, builds,
tests, linting).