security: add download action for attachments - refactor controllers (params)

This commit is contained in:
Simon Vieille 2020-11-11 19:48:16 +01:00
parent b3eadc5139
commit 1a01841326
Signed by: deblan
GPG Key ID: 03383D15A1D31745
8 changed files with 82 additions and 39 deletions

2
.gitignore vendored
View File

@ -5,7 +5,7 @@
/.env.*.local /.env.*.local
/config/secrets/prod/prod.decrypt.private.php /config/secrets/prod/prod.decrypt.private.php
/public/bundles/ /public/bundles/
/public/attachments/ /private/attachments/
/migrations/ /migrations/
/data/ /data/
/var/ /var/

View File

@ -91,7 +91,7 @@ class MailImportCommand extends Command
$this->em->flush(); $this->em->flush();
if (!empty($attachments)) { if (!empty($attachments)) {
$attachmentsDirectory = $this->kernel->getProjectDir().'/public/attachments/'.$mailing->getId().'/'.$entity->getId(); $attachmentsDirectory = $this->kernel->getProjectDir().'/private/attachments/'.$mailing->getId().'/'.$entity->getId();
$filesystem = new Filesystem(); $filesystem = new Filesystem();
$filesystem->mkdir($attachmentsDirectory); $filesystem->mkdir($attachmentsDirectory);

View File

@ -39,7 +39,7 @@ class MailingListCommand extends Command
{ {
$io = new SymfonyStyle($input, $output); $io = new SymfonyStyle($input, $output);
$headers = ['Label', 'Feed', 'Created at', 'Updated at']; $headers = ['Label', 'ID', 'Feed', 'Created at', 'Updated at'];
$rows = []; $rows = [];
$entities = $this->repo->findAll([], ['createdAt' => 'DEC']); $entities = $this->repo->findAll([], ['createdAt' => 'DEC']);
@ -47,9 +47,10 @@ class MailingListCommand extends Command
foreach ($entities as $entity) { foreach ($entities as $entity) {
$rows[] = [ $rows[] = [
$entity->getLabel(), $entity->getLabel(),
$entity->getId(),
$this->router->generate( $this->router->generate(
'mailing_rss', 'mailing_rss',
['id' => $entity->getId()], ['mailing' => $entity->getId()],
UrlGeneratorInterface::ABSOLUTE_URL UrlGeneratorInterface::ABSOLUTE_URL
), ),
$entity->getCreatedAt()->format('Y-m-d H:i:s'), $entity->getCreatedAt()->format('Y-m-d H:i:s'),

View File

@ -0,0 +1,38 @@
<?php
namespace App\Controller;
use App\Entity\Mail;
use App\Entity\Mailing;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpKernel\KernelInterface;
use App\Entity\MailAttachment;
class MailAttachmentController extends AbstractController
{
/**
* @Route("/attachment/{mail}/{attachment}/download", name="attachment_download")
* @ParamConverter("attachment", options={"mapping": {"id": "attachment", "mail": "mail"}})
*/
public function download(MailAttachment $attachment, KernelInterface $kernel): Response
{
$mail = $attachment->getMail();
$mailing = $mail->getMailing();
$filename = $attachment->getFilename();
$path = $kernel->getProjectDir().'/private/attachments/'.$mailing->getId().'/'.$mail->getId().'/'.$filename;
return new BinaryFileResponse(
$path,
200,
[
'Content-Type' => $attachment->getContentType(),
'Content-Disposition' => sprintf('inline; filename="%s"', $filename),
]
);
}
}

View File

@ -7,46 +7,38 @@ use App\Entity\Mailing;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Annotation\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
class MailController extends AbstractController class MailController extends AbstractController
{ {
/** /**
* @Route("/mail/{mailing}/{id}/show", name="mail_show") * @Route("/mail/{mailing}/{mail}/show", name="mail_show")
* @ParamConverter("mail", options={"mapping": {"mail": "id", "mailing": "mailing"}})
*/ */
public function show(string $mailing, Mail $mail): Response public function show(Mail $mail): Response
{ {
if ($mail->getMailing()->getId() !== $mailing) {
throw $this->createNotFoundException();
}
return $this->render('mail/show.html.twig', [ return $this->render('mail/show.html.twig', [
'mail' => $mail, 'mail' => $mail,
]); ]);
} }
/** /**
* @Route("/mail/{mailing}/{id}/html", name="mail_html") * @Route("/mail/{mailing}/{mail}/html", name="mail_html")
* @ParamConverter("mail", options={"mapping": {"mail": "id", "mailing": "mailing"}})
*/ */
public function html(string $mailing, Mail $mail): Response public function html(Mail $mail): Response
{ {
if ($mail->getMailing()->getId() !== $mailing) {
throw $this->createNotFoundException();
}
return $this->render('mail/html.html.twig', [ return $this->render('mail/html.html.twig', [
'mail' => $mail, 'mail' => $mail,
]); ]);
} }
/** /**
* @Route("/mail/{mailing}/{id}/text", name="mail_text") * @Route("/mail/{mailing}/{mail}/text", name="mail_text")
* @ParamConverter("mail", options={"mapping": {"mail": "id", "mailing": "mailing"}})
*/ */
public function text(string $mailing, Mail $mail): Response public function text(Mail $mail): Response
{ {
if ($mail->getMailing()->getId() !== $mailing) {
throw $this->createNotFoundException();
}
$response = $this->render('mail/text.html.twig', [ $response = $this->render('mail/text.html.twig', [
'mail' => $mail, 'mail' => $mail,
]); ]);

View File

@ -8,11 +8,13 @@ use Symfony\Component\Routing\Annotation\Route;
use App\Repository\MailRepository; use App\Repository\MailRepository;
use App\Entity\Mailing; use App\Entity\Mailing;
use App\Entity\Mail; use App\Entity\Mail;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
class MailingController extends AbstractController class MailingController extends AbstractController
{ {
/** /**
* @Route("/mailing/{id}/rss", name="mailing_rss") * @Route("/mailing/{mailing}/rss", name="mailing_rss")
* @ParamConverter("mailing", options={"mapping": {"mailing": "id"}})
*/ */
public function rss(Mailing $mailing, MailRepository $mailRepository): Response public function rss(Mailing $mailing, MailRepository $mailRepository): Response
{ {

View File

@ -45,31 +45,41 @@
Pièces jointes Pièces jointes
</a> </a>
</li> </li>
<li class="nav-item">
<a class="nav-link" href="{{ path('mailing_rss', {mailing: mail.mailing.id}) }}">
Afficher le flux RSS
</a>
</li>
</ul> </ul>
</div> </div>
<div class="col-12"> <div class="col-12">
<div class="tab-content"> <div class="tab-content">
<div class="tab-pane fade show active" id="html" role="tabpanel" aria-labelledby="html-tab"> <div class="tab-pane fade show active" id="html" role="tabpanel" aria-labelledby="html-tab">
<iframe src="{{ path('mail_html', {mailing: mail.mailing.id, id: mail.id}) }}"></iframe> <iframe src="{{ path('mail_html', {mailing: mail.mailing.id, mail: mail.id}) }}"></iframe>
</div> </div>
<div class="tab-pane fade" id="text" role="tabpanel" aria-labelledby="text-tab"> <div class="tab-pane fade" id="text" role="tabpanel" aria-labelledby="text-tab">
<iframe src="{{ path('mail_text', {mailing: mail.mailing.id, id: mail.id}) }}"></iframe> <iframe src="{{ path('mail_text', {mailing: mail.mailing.id, mail: mail.id}) }}"></iframe>
</div> </div>
<div class="tab-pane fade" id="attachments" role="tabpanel" aria-labelledby="attachments-tab"> <div class="tab-pane fade" id="attachments" role="tabpanel" aria-labelledby="attachments-tab">
{% if mail.mailAttachments|length %} {% if mail.mailAttachments|length %}
<ul> <div class="row">
{% for item in mail.mailAttachments %} <div class="col-4 m-4">
<li> <ul class="list-group">
<a target="_blank" href="{{ asset('attachments/' ~ mail.mailing.id ~ '/' ~ mail.id ~ '/' ~ item.filename) }}"> {% for item in mail.mailAttachments %}
{{ item.filename }} <li class="list-group-item d-flex justify-content-between align-items-center">
</a> <a target="_blank" href="{{ path('attachment_download', {mail: mail.id, attachment: item.id}) }}">
{{ item.filename }}
</a>
<span class="badge badge-secondary"> <span class="badge badge-secondary">
{{ item.contentType }} {{ item.contentType }}
</span> </span>
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>
</div>
</div>
{% else %} {% else %}
<div class="alert alert-info"> <div class="alert alert-info">
Aucune pièce jointe Aucune pièce jointe

View File

@ -6,7 +6,7 @@
{% for item in mails %} {% for item in mails %}
<item> <item>
<title><![CDATA[{{ item.subject|raw }}]]></title> <title><![CDATA[{{ item.subject|raw }}]]></title>
<link>{{ absolute_url(path('mail_show', {mailing: mailing.id, id: item.id})) }}</link> <link>{{ absolute_url(path('mail_show', {mailing: mailing.id, mail: item.id})) }}</link>
<description> <description>
<![CDATA[ <![CDATA[
{% if item.htmlContent %} {% if item.htmlContent %}