feat: controller securise pour servir les PDFs de devis + stockage prive
src/Controller/DevisPdfController.php (nouveau):
- Route /devis/pdf/{id}/{type} avec type = unsigned|signed|audit
- Requiert ROLE_USER minimum
- checkAccess(): les ROLE_EMPLOYE ont toujours acces,
pour les clients un TODO est prepare pour verifier que le
client connecte est bien lie au devis (a implementer quand
la relation Customer sera ajoutee sur Devis)
- Sert le fichier via BinaryFileResponse en inline (affichage
dans le navigateur) avec nom de telechargement propre
(ex: signed-04-2026-00001.pdf)
config/packages/vich_uploader.yaml:
- Mapping devis_pdf: stockage deplace de public/uploads/devis
vers var/uploads/devis (hors du dossier public, inaccessible
directement par URL)
- uri_prefix change en /devis/pdf (pointe vers le controller)
config/packages/security.yaml:
- Suppression de la regle access_control sur /uploads/devis
(remplacee par le controller avec verification plus fine)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -48,7 +48,6 @@ security:
|
||||
|
||||
# Note: Only the *first* matching rule is applied
|
||||
access_control:
|
||||
- { path: ^/uploads/devis, roles: ROLE_USER }
|
||||
- { path: ^/2fa, role: IS_AUTHENTICATED_2FA_IN_PROGRESS }
|
||||
- { path: ^/admin, roles: ROLE_EMPLOYE }
|
||||
- { path: ^/espace-client, roles: ROLE_CUSTOMER }
|
||||
|
||||
@@ -7,6 +7,6 @@ vich_uploader:
|
||||
upload_destination: '%kernel.project_dir%/public/uploads/avatars'
|
||||
namer: Vich\UploaderBundle\Naming\SmartUniqueNamer
|
||||
devis_pdf:
|
||||
uri_prefix: /uploads/devis
|
||||
upload_destination: '%kernel.project_dir%/public/uploads/devis'
|
||||
uri_prefix: /devis/pdf
|
||||
upload_destination: '%kernel.project_dir%/var/uploads/devis'
|
||||
namer: Vich\UploaderBundle\Naming\SmartUniqueNamer
|
||||
|
||||
73
src/Controller/DevisPdfController.php
Normal file
73
src/Controller/DevisPdfController.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Entity\Devis;
|
||||
use App\Repository\DevisRepository;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
|
||||
#[IsGranted('ROLE_USER')]
|
||||
class DevisPdfController extends AbstractController
|
||||
{
|
||||
#[Route('/devis/pdf/{id}/{type}', name: 'app_devis_pdf', methods: ['GET'], requirements: ['type' => 'unsigned|signed|audit'])]
|
||||
public function __invoke(
|
||||
int $id,
|
||||
string $type,
|
||||
DevisRepository $devisRepository,
|
||||
#[Autowire('%kernel.project_dir%')] string $projectDir,
|
||||
): Response {
|
||||
$devis = $devisRepository->find($id);
|
||||
|
||||
if (null === $devis) {
|
||||
throw $this->createNotFoundException('Devis introuvable.');
|
||||
}
|
||||
|
||||
$this->checkAccess($devis);
|
||||
|
||||
$filename = match ($type) {
|
||||
'unsigned' => $devis->getUnsignedPdf(),
|
||||
'signed' => $devis->getSignedPdf(),
|
||||
'audit' => $devis->getAuditPdf(),
|
||||
default => null,
|
||||
};
|
||||
|
||||
if (null === $filename) {
|
||||
throw $this->createNotFoundException('Document non disponible.');
|
||||
}
|
||||
|
||||
$path = $projectDir.'/var/uploads/devis/'.$filename;
|
||||
|
||||
if (!file_exists($path)) {
|
||||
throw $this->createNotFoundException('Fichier introuvable.');
|
||||
}
|
||||
|
||||
$downloadName = $type.'-'.$devis->getOrderNumber()->getNumOrder().'.pdf';
|
||||
|
||||
$response = new BinaryFileResponse($path);
|
||||
$response->setContentDisposition(ResponseHeaderBag::DISPOSITION_INLINE, $downloadName);
|
||||
$response->headers->set('Content-Type', 'application/pdf');
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
private function checkAccess(Devis $devis): void
|
||||
{
|
||||
// Les admins ont toujours acces
|
||||
if ($this->isGranted('ROLE_EMPLOYE')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: verifier que le client connecte est bien lie au devis
|
||||
// Exemple futur:
|
||||
// $customer = $this->getUser()->getCustomer();
|
||||
// if ($devis->getCustomer() !== $customer) {
|
||||
// throw $this->createAccessDeniedException('Acces refuse.');
|
||||
// }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user