```
✨ feat(security): [FR] Améliore la gestion des erreurs et la sécurité de l'intranet.
```
This commit is contained in:
40
src/Controller/LegalController.php
Normal file
40
src/Controller/LegalController.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Entity\Account;
|
||||
use App\Entity\AccountResetPasswordRequest;
|
||||
use App\Form\RequestPasswordConfirmType;
|
||||
use App\Form\RequestPasswordRequestType;
|
||||
use App\Logger\AppLogger;
|
||||
use App\Service\ResetPassword\Event\ResetPasswordConfirmEvent;
|
||||
use App\Service\ResetPassword\Event\ResetPasswordEvent;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
|
||||
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||
|
||||
|
||||
class LegalController extends AbstractController
|
||||
{
|
||||
|
||||
#[Route('/mentions-legal', name: 'mentions-legal')]
|
||||
public function mentionsLegal()
|
||||
{
|
||||
return $this->render('legal/mentions.html.twig');
|
||||
|
||||
}
|
||||
#[Route('/conditions-general-de-vente', name: 'cgv')]
|
||||
public function cgv()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,28 +2,88 @@
|
||||
|
||||
namespace App\Security;
|
||||
|
||||
use App\Service\Mailer\Mailer;
|
||||
use App\Service\Signature\Client;
|
||||
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Event\RequestEvent;
|
||||
use Twig\Environment;
|
||||
|
||||
#[AsEventListener(RequestEvent::class,method: 'onLocked')]
|
||||
#[AsEventListener(RequestEvent::class,method: 'onLocked',priority: 10)]
|
||||
#[AsEventListener(RequestEvent::class,method: 'onControl',priority: 5)]
|
||||
class IntranetLocked
|
||||
{
|
||||
public function __construct(private readonly Environment $environment)
|
||||
{
|
||||
public function __construct(private readonly Environment $environment,
|
||||
private readonly Client $signatureClient,
|
||||
private readonly \App\Service\Search\Client $searchClient,
|
||||
private readonly \App\Service\Stripe\Client $stripeClient,
|
||||
private readonly Mailer $mailer
|
||||
|
||||
){
|
||||
|
||||
}
|
||||
|
||||
public function onLocked(RequestEvent $requestEvent)
|
||||
{
|
||||
if($_ENV['INTRANET_LOCK'] == "true" &&
|
||||
!str_contains( $requestEvent->getRequest()->getPathInfo(),"_wdt") &&
|
||||
!str_contains( $requestEvent->getRequest()->getPathInfo(),"_profiler")
|
||||
) {
|
||||
if($_ENV['INTRANET_LOCK'] == "true" && ! $this->isDebugRoute($requestEvent)) {
|
||||
$response = new Response($this->environment->render('security/locked.twig'));
|
||||
$response->setStatusCode(Response::HTTP_FORBIDDEN);
|
||||
$requestEvent->setResponse($response);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private function isDebugRoute(RequestEvent $event): bool
|
||||
{
|
||||
$path = $event->getRequest()->getPathInfo();
|
||||
return str_contains($path, "_wdt") || str_contains($path, "_profiler");
|
||||
}
|
||||
public function onControl(RequestEvent $requestEvent)
|
||||
{
|
||||
if ($this->isDebugRoute($requestEvent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
$isValid = true;
|
||||
$message = [];
|
||||
if(!$this->signatureClient->status()) {
|
||||
$isValid = false;
|
||||
$message = [
|
||||
'service' => 'Signature',
|
||||
'status' => 'Hors Service'
|
||||
];
|
||||
$this->advertTech($message);
|
||||
}
|
||||
if(!$this->searchClient->status()) {
|
||||
$isValid = false;
|
||||
$message = [
|
||||
'service' => 'Recherche',
|
||||
'status' => 'Hors Service'
|
||||
];
|
||||
$this->advertTech($message);
|
||||
}
|
||||
if(!$this->stripeClient->status()) {
|
||||
$isValid = false;
|
||||
$message = [
|
||||
'service' => 'Stripe',
|
||||
'status' => 'Hors Service'
|
||||
];
|
||||
$this->advertTech($message);
|
||||
}
|
||||
if(!$isValid) {
|
||||
$response = new Response($this->environment->render('security/error.twig',[
|
||||
'message' => $message,
|
||||
]));
|
||||
$response->setStatusCode(Response::HTTP_FORBIDDEN);
|
||||
$requestEvent->setResponse($response);
|
||||
}
|
||||
}
|
||||
public function advertTech(array $message) {
|
||||
$this->mailer->send('notification@siteconseil.fr',"Notification siteconseil",
|
||||
"[Intranet Ludikevent] - Accées impossible suite à une erreur de service",
|
||||
"mails/tech/noaccess.twig",[
|
||||
'message' => $message
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,15 +40,19 @@ class DevisPdfService extends Fpdf
|
||||
}
|
||||
|
||||
$this->SetFont('Arial', 'B', 14);
|
||||
$this->Cell(0, 7, $this->clean('LUDIKEVENT'), 0, 1, 'L');
|
||||
$this->Cell(0, 7, $this->clean('Lilian SEGARD - LUDIKEVENT'), 0, 1, 'L');
|
||||
|
||||
$this->SetX(25);
|
||||
$this->SetFont('Arial', '', 8);
|
||||
$this->SetTextColor(80, 80, 80);
|
||||
$this->Cell(0, 4, $this->clean('SIRET : 930 488 408 00012 | RCS : 930 488 408'), 0, 1, 'L');
|
||||
$this->SetX(25);
|
||||
$this->Cell(0, 4, $this->clean('6 Rue du Château – 02800 Danizy – France'), 0, 1, 'L');
|
||||
|
||||
$this->SetX(25);
|
||||
$this->Cell(0, 4, $this->clean('Tél. : 06 14 17 24 47'), 0, 1, 'L');
|
||||
$this->SetX(25);
|
||||
$this->Cell(0, 4, $this->clean('Assurance RC Pro : '), 0, 1, 'L');
|
||||
|
||||
$this->SetX(25);
|
||||
$this->SetTextColor(37, 99, 235);
|
||||
@@ -134,12 +138,6 @@ class DevisPdfService extends Fpdf
|
||||
$this->Cell(30, 8, $this->clean('TOTAL HT'), 0, 0, 'L');
|
||||
$this->Cell(40, 8, number_format($totalHT, 2, ',', ' ') . $this->clean(' €'), 0, 1, 'R');
|
||||
|
||||
$this->Cell(120);
|
||||
$this->SetTextColor(37, 99, 235);
|
||||
$this->SetFont('Arial', 'B', 12);
|
||||
$this->Cell(30, 10, $this->clean('TOTAL TTC'), 0, 0, 'L');
|
||||
$this->Cell(40, 10, number_format($totalHT * 1.20, 2, ',', ' ') . $this->clean(' €'), 0, 1, 'R');
|
||||
|
||||
return $this->Output('S');
|
||||
}
|
||||
|
||||
|
||||
@@ -130,4 +130,14 @@ class Client
|
||||
$fullName = $this->env . "_" . $key;
|
||||
$this->client->deleteIndex($fullName);
|
||||
}
|
||||
|
||||
public function status() : bool
|
||||
{
|
||||
try {
|
||||
$result = $this->client->health();
|
||||
return $result['status'] == "available";
|
||||
}catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ class Client
|
||||
) {
|
||||
// Configuration via les variables d'environnement
|
||||
$key = $_ENV['ESYSIGN_APIEY'] ?? '';
|
||||
$this->baseUrl = $_ENV['ESYSIGN_URL'] ?? '';
|
||||
$this->baseUrl = "https://signature.esy-web.dev";
|
||||
|
||||
// L'URL API est le point d'entrée pour le SDK Docuseal
|
||||
$apiUrl = rtrim($this->baseUrl, '/') . '/api';
|
||||
@@ -101,4 +101,14 @@ class Client
|
||||
{
|
||||
return $this->docuseal->getSubmitter($submitterId);
|
||||
}
|
||||
|
||||
public function status(): bool
|
||||
{
|
||||
try {
|
||||
$this->docuseal->listTemplates();
|
||||
return true;
|
||||
} catch (\Throwable $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -365,4 +365,10 @@ class Client
|
||||
return ['state' => false, 'message' => $e->getMessage()];
|
||||
}
|
||||
}
|
||||
|
||||
public function status()
|
||||
{
|
||||
$result = $this->check();
|
||||
return $result['state'];
|
||||
}
|
||||
}
|
||||
|
||||
4
templates/legal/mentions.html.twig
Normal file
4
templates/legal/mentions.html.twig
Normal file
@@ -0,0 +1,4 @@
|
||||
{% extends 'base.twig' %}
|
||||
{% block title %}Mentions légal{% endblock %}
|
||||
{% block body %}
|
||||
{% endblock %}
|
||||
48
templates/mails/tech/noaccess.twig
Normal file
48
templates/mails/tech/noaccess.twig
Normal file
@@ -0,0 +1,48 @@
|
||||
{% extends 'mails/base.twig' %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<mj-section padding-bottom="0px" background-color="transparent">
|
||||
<mj-column>
|
||||
<mj-text align="center" font-size="12px" font-weight="900" color="#9ca3af" letter-spacing="2px">
|
||||
SITECONSEIL MONITORING
|
||||
</mj-text>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
|
||||
<mj-section border-radius="20px 20px 0 0" padding-top="40px">
|
||||
<mj-column>
|
||||
<mj-image width="64px" src="https://img.icons8.com/fluency/96/error.png" alt="Alert Icon" />
|
||||
<mj-text align="center" font-weight="800" font-size="24px" color="#111827" padding-top="20px">
|
||||
Interruption de Service
|
||||
</mj-text>
|
||||
<mj-text align="center" color="#dc2626" font-size="12px" font-weight="bold" text-transform="uppercase" letter-spacing="1px">
|
||||
Accès Intranet Verrouillé
|
||||
</mj-text>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
|
||||
<mj-section padding="0 40px">
|
||||
<mj-column background-color="#f9fafb" border="1px solid #e5e7eb" border-radius="12px" padding="20px">
|
||||
<mj-text font-size="13px" font-weight="bold" color="#6b7280" text-transform="uppercase" padding-bottom="10px">
|
||||
Détails de l'incident :
|
||||
</mj-text>
|
||||
<mj-table color="#374151" font-size="15px">
|
||||
<tr style="height:30px;">
|
||||
<td style="font-weight:bold;">Service :</td>
|
||||
<td style="text-align:right; color:#111827;">{{ datas.message.service|default('Inconnu') }}</td>
|
||||
</tr>
|
||||
<tr style="height:30px;">
|
||||
<td style="font-weight:bold;">Statut :</td>
|
||||
<td style="text-align:right; color:#dc2626; font-weight:bold;">{{ datas.message.status|default('DOWN') }}</td>
|
||||
</tr>
|
||||
<tr style="height:30px;">
|
||||
<td style="font-weight:bold;">Date :</td>
|
||||
<td style="text-align:right;">{{ "now"|date("d/m/Y H:i") }}</td>
|
||||
</tr>
|
||||
</mj-table>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
70
templates/security/error.twig
Normal file
70
templates/security/error.twig
Normal file
@@ -0,0 +1,70 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Erreur Service - Siteconseil</title>
|
||||
{{ vite_asset('error.js')}}
|
||||
</head>
|
||||
<body class="bg-[#0f172a] text-slate-200 font-sans min-h-screen flex items-center justify-center overflow-hidden">
|
||||
|
||||
<div class="absolute inset-0 overflow-hidden pointer-events-none">
|
||||
<div class="absolute -top-[10%] -left-[10%] w-[50%] h-[50%] bg-red-600/5 rounded-full blur-[120px]"></div>
|
||||
<div class="absolute -bottom-[10%] -right-[10%] w-[50%] h-[50%] bg-orange-600/5 rounded-full blur-[120px]"></div>
|
||||
</div>
|
||||
|
||||
<div class="relative z-10 w-full max-w-lg p-6 mx-auto">
|
||||
<div class="backdrop-blur-3xl bg-slate-900/60 border border-white/10 rounded-[3rem] p-10 shadow-2xl text-center">
|
||||
|
||||
<div class="flex justify-center mb-8">
|
||||
<div class="relative">
|
||||
<div class="absolute inset-0 bg-red-500 blur-2xl opacity-20 animate-pulse"></div>
|
||||
<div class="relative p-6 bg-red-500/10 rounded-3xl border border-red-500/20">
|
||||
<svg class="w-12 h-12 text-red-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1 class="text-2xl font-black text-white mb-2 uppercase tracking-tight">
|
||||
Service <span class="text-red-500">Indisponible</span>
|
||||
</h1>
|
||||
|
||||
<div class="inline-flex items-center px-3 py-1 rounded-full bg-red-500/10 border border-red-500/20 mb-6">
|
||||
<span class="w-2 h-2 rounded-full bg-red-500 animate-ping mr-2"></span>
|
||||
<span class="text-[10px] font-bold text-red-400 uppercase tracking-widest">
|
||||
{{ message.service }} : {{ message.status|upper }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<p class="text-slate-400 text-sm leading-relaxed mb-10">
|
||||
Une interruption technique affecte actuellement le module de <strong>{{ message.service }}</strong>.
|
||||
L'accès à l'intranet est suspendu par mesure de sécurité jusqu'au rétablissement du service.
|
||||
</p>
|
||||
|
||||
<div class="grid grid-cols-1 gap-4 text-left">
|
||||
<div class="p-4 rounded-2xl bg-white/5 border border-white/5">
|
||||
<p class="text-[9px] text-slate-500 font-black uppercase tracking-widest mb-3">Support Technique Siteconseil</p>
|
||||
|
||||
<div class="flex flex-col space-y-3">
|
||||
<a href="mailto:s.com@siteconseil.fr" class="text-sm font-semibold text-white hover:text-blue-400 transition-colors flex items-center">
|
||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/></svg>
|
||||
s.com@siteconseil.fr
|
||||
</a>
|
||||
<a href="tel:0323056243" class="text-sm font-semibold text-white hover:text-green-400 transition-colors flex items-center">
|
||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z"/></svg>
|
||||
03 23 05 62 43
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-8">
|
||||
<p class="text-[9px] text-slate-600 uppercase tracking-[0.4em] font-black italic">Siteconseil Intranet</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -6,7 +6,7 @@
|
||||
<title>Accès Restreint - Siteconseil</title>
|
||||
{{ vite_asset('error.js') }}
|
||||
</head>
|
||||
<body class="bg-[#0f172a] text-slate-200 font-sans min-h-screen flex items-center justify-center overflow-hidden">
|
||||
<body class="bg-[#0f172a] text-slate-200 font-sans min-h-screen flex items-center justify-center">
|
||||
|
||||
<div class="absolute inset-0 overflow-hidden pointer-events-none">
|
||||
<div class="absolute -top-[10%] -left-[10%] w-[50%] h-[50%] bg-blue-600/10 rounded-full blur-[120px]"></div>
|
||||
@@ -68,6 +68,37 @@
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{# ... après le div du footer ... #}
|
||||
<div class="mt-10 p-6 backdrop-blur-md bg-black/20 border border-white/5 rounded-3xl overflow-hidden">
|
||||
<h3 class="text-[10px] font-black text-blue-500 uppercase tracking-[0.2em] mb-4 flex items-center">
|
||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
|
||||
</svg>
|
||||
Request Headers (Debug)
|
||||
</h3>
|
||||
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-left border-collapse">
|
||||
<thead>
|
||||
<tr class="border-b border-white/10">
|
||||
<th class="py-2 text-[9px] font-bold text-slate-500 uppercase tracking-wider">Clé</th>
|
||||
<th class="py-2 text-[9px] font-bold text-slate-500 uppercase tracking-wider pl-4">Valeur</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-white/5">
|
||||
{% for key, values in app.request.headers.all %}
|
||||
<tr class="group hover:bg-white/5 transition-colors">
|
||||
<td class="py-3 text-[11px] font-mono text-blue-400 align-top break-all">{{ key }}</td>
|
||||
<td class="py-3 pl-4 text-[11px] text-slate-300 align-top break-all">
|
||||
{# Les headers peuvent être des tableaux, on les joint proprement #}
|
||||
{{ values|join(', ') }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user