Working state but no uploads

This commit is contained in:
julien
2026-03-16 11:48:26 +01:00
parent e24ee5d622
commit 8e59daa4cd
21 changed files with 353 additions and 119 deletions

View File

@@ -924,13 +924,8 @@ EditorMiddleware.php — redirige si rôle != editor ni admin
`AuthController::login()` orchestre trois responsabilités dans l'ordre : vérifier le rate limit, authentifier, ouvrir la session.
```php
// 0. Résolution de l'IP réelle derrière un reverse proxy (Caddy/Nginx)
// En production Docker, REMOTE_ADDR retourne l'IP interne du proxy.
// X-Forwarded-For contient l'IP d'origine du client — on lit le premier
// segment, qui est injecté par Nginx/Caddy et ne peut pas être forgé.
$forwarded = trim((string) ($serverParams['HTTP_X_FORWARDED_FOR'] ?? ''));
$ip = $forwarded !== '' ? trim(explode(',', $forwarded)[0])
: ($serverParams['REMOTE_ADDR'] ?? '0.0.0.0');
// 0. Résolution de l'IP réelle derrière un reverse proxy approuvé
$ip = $this->clientIpResolver->resolve($req);
// 1. Vérification du rate limit (avant toute authentification)
$remainingMinutes = $this->authService->checkRateLimit($ip);
@@ -952,7 +947,7 @@ $this->authService->login($user); // écrit userId/username/role en session
> 💡 `AuthService::authenticate()` ne gère pas le rate limiting — c'est `AuthController` qui en est responsable. Cette séparation permet de tester chaque comportement indépendamment.
>
> ⚠️ L'IP lue depuis `REMOTE_ADDR` derrière un proxy retourne l'IP interne du proxy — le rate-limit se verrouillerait alors pour tous les utilisateurs simultanément. La logique ci-dessus lit `HTTP_X_FORWARDED_FOR` en priorité, ce qui est sûr dans un contexte Docker où seul Nginx/Caddy contrôle cet en-tête.
> ⚠️ L'IP lue depuis `REMOTE_ADDR` derrière un proxy retourne l'IP interne du proxy — le rate-limit se verrouillerait alors pour tous les utilisateurs simultanément. Le projet centralise désormais cette logique dans `ClientIpResolver` / `RequestContext` et ne fait confiance aux en-têtes `X-Forwarded-*` que pour les proxies explicitement approuvés via `TRUSTED_PROXIES`.
#### Réinitialisation de mot de passe
@@ -1138,7 +1133,7 @@ Le provisionnement des données initiales (compte admin) est géré séparément
### 7.1 Développement local (sans Docker)
Prérequis : PHP 8.1+ avec les extensions `pdo_sqlite`, `mbstring`, `fileinfo`, `gd` (WebP), `dom`. Composer. Node.js 18+.
Prérequis : PHP 8.4.1+ avec les extensions `pdo_sqlite`, `mbstring`, `fileinfo`, `gd` (WebP), `dom`. Composer. Node.js 18+.
```bash
git clone https://git.netig.net/netig/slim-blog
@@ -1206,7 +1201,7 @@ L'application est accessible sur `http://localhost:8888` une fois le service `ap
Le conteneur nginx écoute sur `127.0.0.1:8888` — uniquement sur l'interface loopback de la machine hôte. Il n'est **pas** accessible depuis Internet sans un reverse proxy configuré sur le serveur. Ce choix est délibéré : c'est le reverse proxy qui prend en charge le TLS et redirige le trafic HTTPS vers le conteneur.
La config Nginx interne transmet déjà les en-têtes nécessaires à PHP (`X-Forwarded-For`, `X-Forwarded-Proto`) pour que l'application connaisse l'IP réelle du client et sache si la connexion est HTTPS.
La config Nginx interne transmet déjà les en-têtes nécessaires à PHP (`X-Forwarded-For`, `X-Forwarded-Proto`) pour que l'application connaisse l'IP réelle du client et sache si la connexion est HTTPS. Le conteneur `app` accepte ces en-têtes uniquement depuis les proxies listés dans `TRUSTED_PROXIES` (par défaut `*` dans `docker-compose.yml`, car PHP-FPM n'est joignable qu'à travers le Nginx interne).
Exemple minimal avec **Caddy** (`/etc/caddy/Caddyfile`) :
@@ -1226,6 +1221,7 @@ Caddy gère automatiquement l'obtention et le renouvellement du certificat TLS.
| APP_NAME | Nom du blog (flux RSS, e-mails) | `Slim Blog` |
| APP_URL | URL publique (liens e-mails, flux RSS) | `https://blog.exemple.com` |
| TIMEZONE | Fuseau horaire PHP | `Europe/Paris` |
| TRUSTED_PROXIES | Proxies autorisés à fournir `X-Forwarded-For` / `X-Forwarded-Proto` | `127.0.0.1,::1` ou `*` |
| ADMIN_USERNAME | Nom d'utilisateur du compte admin | `admin` |
| ADMIN_EMAIL | E-mail du compte admin | `admin@example.com` |
| ADMIN_PASSWORD | Mot de passe admin (obligatoire en production) | *(à changer)* |