fix(security) : harden auth, session, document access and health endpoint

- Remove orphaned PUBLIC_ACCESS rule for deleted /api/test route
- Remove JWT login firewall (app is session-based only)
- Set APP_SECRET placeholder (real value must be in .env.local)
- Remove JWT env vars from .env
- Add session regeneration on login (prevent session fixation)
- Remove Document.path from API serialization groups (prevent path leak)
- Restrict health check details to ROLE_ADMIN (anonymes get status only)
- Add path traversal guard in DocumentStorageService
- Convert CreateProfileCommand password to interactive hidden prompt
- Restrict Profile Get endpoint to ROLE_ADMIN
- Change api firewall to stateless: false (matches session-based auth)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-08 13:42:09 +01:00
parent 0709d01240
commit b342d0e50a
9 changed files with 148 additions and 102 deletions

View File

@@ -25,8 +25,9 @@ use Symfony\Component\Validator\Constraints as Assert;
#[ORM\UniqueConstraint(name: 'UNIQ_email', columns: ['email'])]
#[ORM\HasLifecycleCallbacks]
#[ApiResource(
description: 'Profils utilisateurs. Chaque profil possède un rôle (Admin, Gestionnaire, Viewer, User), un email unique et un mot de passe. Gère l\'authentification par session.',
operations: [
new Get(security: "is_granted('ROLE_VIEWER')"),
new Get(security: "is_granted('ROLE_ADMIN')"),
new GetCollection(security: "is_granted('ROLE_ADMIN')"),
new Post(
security: "is_granted('ROLE_ADMIN')",
@@ -103,7 +104,7 @@ class Profile implements UserInterface, PasswordAuthenticatedUserInterface
public function __construct()
{
$this->id = 'cl'.substr(strtolower(base_convert(random_bytes(12), 2, 36)), 0, 24);
$this->id = 'cl'.bin2hex(random_bytes(12));
$this->createdAt = new DateTimeImmutable();
$this->updatedAt = new DateTimeImmutable();
}