feat: Ajoute la fonctionnalité de création d'administrateur et envoi de mot de passe temporaire
Ce commit introduit une nouvelle fonctionnalité permettant de créer un compte administrateur via une commande console et d'envoyer un mot de passe temporaire par email. Les changements incluent: - Ajout d'une commande `AccountCommand` pour créer un compte administrateur. - Création d'un service `TempPasswordGenerator` pour générer des mots de passe temporaires aléatoires. - Ajout d'un événement `CreatedAdminEvent` pour déclencher l'envoi d'email après la création d'un administrateur. - Modification du subscriber `MailerSubscriber` pour utiliser le nouveau template email et inclure le mot de passe temporaire. - Création d'un nouveau template email `mails/artemis/new_admin.twig` pour l'envoi du mot de passe temporaire. - Ajout de tests unitaires pour l'entité `Mail` et le repository `MailRepository`. - Suppression de code commenté inutile dans `MailRepository`. - Correction d'un bug dans `Mailer.php` pour passer les données au template twig. - Mise à jour de la configuration `messenger.yaml` (suppression d'une ligne inutile).
This commit is contained in:
@@ -5,4 +5,4 @@ framework:
|
||||
async: "%env(MESSENGER_TRANSPORT_DSN)%"
|
||||
|
||||
routing:
|
||||
'Symfony\Component\Mailer\Messenger\SendEmailMessage': async
|
||||
|
||||
|
||||
@@ -3,12 +3,14 @@
|
||||
namespace App\Command;
|
||||
|
||||
use App\Entity\Account;
|
||||
use App\Service\Generator\TempPasswordGenerator;
|
||||
use App\Service\Mailer\Event\CreatedAdminEvent;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Question\Question;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
||||
use Symfony\Component\Uid\Uuid;
|
||||
@@ -29,14 +31,24 @@ class AccountCommand extends Command
|
||||
|
||||
$userExit = $this->entityManager->getRepository(Account::class)->findOneBy(['email'=>'jovann@siteconseil.fr']);
|
||||
if(!$userExit instanceof Account) {
|
||||
$password = TempPasswordGenerator::generate();
|
||||
$userExit = new Account();
|
||||
$userExit->setRoles(['ROLE_ROOT']);
|
||||
$userExit->setUuid(Uuid::v4());
|
||||
$userExit->setEmail("jovann@siteconseil.fr");
|
||||
$userExit->setUsername("jovann");
|
||||
$userExit->setPassword($this->userPasswordHasher->hashPassword($userExit, 'jovann'));
|
||||
|
||||
$this->eventDispatcher->dispatch(new CreatedAdminEvent($userExit,"jovann"));
|
||||
$questionEmail = new Question("Email ?");
|
||||
$email = $io->askQuestion($questionEmail);
|
||||
|
||||
$userExit->setEmail($email);
|
||||
|
||||
$questionUsername = new Question("Username ?");
|
||||
$username = $io->askQuestion($questionUsername);
|
||||
$userExit->setUsername($username);
|
||||
$userExit->setPassword($this->userPasswordHasher->hashPassword($userExit, $password));
|
||||
|
||||
$this->entityManager->persist($userExit);
|
||||
$this->entityManager->flush();
|
||||
$this->eventDispatcher->dispatch(new CreatedAdminEvent($userExit, $password));
|
||||
}
|
||||
|
||||
return Command::SUCCESS;
|
||||
|
||||
@@ -14,9 +14,8 @@ class HomeController extends AbstractController
|
||||
{
|
||||
|
||||
#[Route(path: '/',name: 'app_login',methods: ['GET', 'POST'])]
|
||||
public function index(Mailer $mailer,AuthenticationUtils $authenticationUtils): Response
|
||||
public function index(AuthenticationUtils $authenticationUtils): Response
|
||||
{
|
||||
$mailer->sendTest();
|
||||
if ($this->getUser()) {
|
||||
return $this->redirectToRoute('artemis_dashboard');
|
||||
}
|
||||
|
||||
@@ -15,29 +15,4 @@ class MailRepository extends ServiceEntityRepository
|
||||
{
|
||||
parent::__construct($registry, Mail::class);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * @return Mail[] Returns an array of Mail objects
|
||||
// */
|
||||
// public function findByExampleField($value): array
|
||||
// {
|
||||
// return $this->createQueryBuilder('m')
|
||||
// ->andWhere('m.exampleField = :val')
|
||||
// ->setParameter('val', $value)
|
||||
// ->orderBy('m.id', 'ASC')
|
||||
// ->setMaxResults(10)
|
||||
// ->getQuery()
|
||||
// ->getResult()
|
||||
// ;
|
||||
// }
|
||||
|
||||
// public function findOneBySomeField($value): ?Mail
|
||||
// {
|
||||
// return $this->createQueryBuilder('m')
|
||||
// ->andWhere('m.exampleField = :val')
|
||||
// ->setParameter('val', $value)
|
||||
// ->getQuery()
|
||||
// ->getOneOrNullResult()
|
||||
// ;
|
||||
// }
|
||||
}
|
||||
|
||||
72
src/Service/Generator/TempPasswordGenerator.php
Normal file
72
src/Service/Generator/TempPasswordGenerator.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
namespace App\Service\Generator;
|
||||
|
||||
/**
|
||||
* Class TempPasswordGenerator
|
||||
*
|
||||
* Provides functionality to generate secure temporary passwords.
|
||||
*/
|
||||
class TempPasswordGenerator
|
||||
{
|
||||
/**
|
||||
* Generates a random temporary password.
|
||||
*
|
||||
* @param int $length The desired length of the password. Default is 12 characters.
|
||||
* @param string $characters A string of characters to use for password generation.
|
||||
* Defaults to a mix of uppercase, lowercase, numbers, and symbols.
|
||||
* @return string The generated temporary password.
|
||||
*/
|
||||
public static function generate(int $length = 12, string $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()-_=+[]{}|;:,.<>?') : string
|
||||
{
|
||||
// Ensure the length is positive
|
||||
if ($length <= 0) {
|
||||
// You might want to throw an exception or return an empty string
|
||||
// depending on how you want to handle invalid lengths.
|
||||
// For simplicity, we'll default to 12 if an invalid length is provided.
|
||||
$length = 12;
|
||||
}
|
||||
|
||||
$password = '';
|
||||
$charactersLength = strlen($characters);
|
||||
|
||||
// Generate the password character by character
|
||||
for ($i = 0; $i < $length; $i++) {
|
||||
// Use random_int for cryptographically secure random number generation
|
||||
$password .= $characters[random_int(0, $charactersLength - 1)];
|
||||
}
|
||||
|
||||
return $password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a password meets certain complexity requirements (optional).
|
||||
* This is a basic example and can be extended.
|
||||
*
|
||||
* @param string $password The password to check.
|
||||
* @return bool True if the password meets basic complexity, false otherwise.
|
||||
*/
|
||||
public static function isComplex(string $password): bool
|
||||
{
|
||||
// Minimum length
|
||||
if (strlen($password) < 8) {
|
||||
return false;
|
||||
}
|
||||
// Requires at least one uppercase letter
|
||||
if (!preg_match('/[A-Z]/', $password)) {
|
||||
return false;
|
||||
}
|
||||
// Requires at least one lowercase letter
|
||||
if (!preg_match('/[a-z]/', $password)) {
|
||||
return false;
|
||||
}
|
||||
// Requires at least one number
|
||||
if (!preg_match('/[0-9]/', $password)) {
|
||||
return false;
|
||||
}
|
||||
// Requires at least one special character
|
||||
if (!preg_match('/[!@#$%^&*()-_=+\[\]{}|;:,.<>?]/', $password)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -98,16 +98,16 @@ class Mailer
|
||||
$messageId = $mail->generateMessageId();
|
||||
$header = $mail->getHeaders();
|
||||
$header->add(new IdentificationHeader("Message-Id",$messageId));
|
||||
$datas = $this->generateTracking($mail);
|
||||
$datasSign = $this->generateTracking($mail);
|
||||
/** @var Mail $object */
|
||||
$object= $datas['object'];
|
||||
|
||||
$mjmlGenerator = $this->environment->render($template, array_merge([
|
||||
$object = $datasSign['object'];
|
||||
$mjmlGenerator = $this->environment->render($template, [
|
||||
'system' => [
|
||||
'subject' => $subject,
|
||||
'tracking_url'=>$datas['url']
|
||||
]
|
||||
], $data));
|
||||
'tracking_url'=>$datasSign['url']
|
||||
],
|
||||
'datas' => $data,
|
||||
]);
|
||||
$htmlContent = $this->convertMjmlToHtml($mjmlGenerator);
|
||||
$object->setContent($htmlContent);
|
||||
|
||||
@@ -116,6 +116,7 @@ class Mailer
|
||||
$this->mailer->send($mail);
|
||||
$object->setStatus("sent");
|
||||
} catch (TransportExceptionInterface $e) {
|
||||
dd($e);
|
||||
$object->setStatus("error");
|
||||
}
|
||||
$this->entityManager->persist($object);
|
||||
|
||||
@@ -3,11 +3,12 @@ namespace App\Service\Mailer;
|
||||
|
||||
use App\Service\Mailer\Event\CreatedAdminEvent;
|
||||
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
|
||||
#[AsEventListener(event: CreatedAdminEvent::class, method: 'onAdminEvent')]
|
||||
class MailerSubscriber
|
||||
{
|
||||
public function __construct(private readonly Mailer $mailer)
|
||||
public function __construct(private readonly UrlGeneratorInterface $urlGenerator,private readonly Mailer $mailer)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -16,10 +17,10 @@ class MailerSubscriber
|
||||
$account = $createdAdminEvent->getAccount();
|
||||
$password = $createdAdminEvent->getPassword();
|
||||
|
||||
dd($account, $password);
|
||||
$this->mailer->send($account->getEmail(), $account->getUsername(), "[MainFrame] - Création d'un compte administrateur", "mails/artemis/new_admin.twig", [
|
||||
'account' => $account,
|
||||
'username' => $account->getUsername(),
|
||||
'password' => $password,
|
||||
'url' => $this->urlGenerator->generate('app_login',[],UrlGeneratorInterface::ABSOLUTE_URL)
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
30
templates/mails/artemis/new_admin.twig
Normal file
30
templates/mails/artemis/new_admin.twig
Normal file
@@ -0,0 +1,30 @@
|
||||
{% extends 'mails/base.twig' %}
|
||||
|
||||
{% block content %}
|
||||
<mj-text font-size="16px" line-height="24px">
|
||||
Bonjour,
|
||||
<br/><br/>
|
||||
Nous avons le plaisir de vous informer que votre compte administrateur a été créé.
|
||||
<br/><br/>
|
||||
Voici vos identifiants de connexion temporaires :
|
||||
<br/>
|
||||
<strong>Nom d'utilisateur :</strong> `{{ datas.username }}`
|
||||
<br/>
|
||||
<strong>Mot de passe :</strong> `{{ datas.password }}`
|
||||
<br/><br/>
|
||||
Pour des raisons de sécurité, nous vous demandons de bien vouloir modifier votre mot de passe lors de votre première connexion.
|
||||
<br/><br/>
|
||||
Vous pouvez vous connecter à votre compte en cliquant sur le lien ci-dessous :
|
||||
</mj-text>
|
||||
<mj-button href="{{ datas.url }}" background-color="#4A90E2" color="#ffffff" font-size="16px" border-radius="5px">
|
||||
Se connecter
|
||||
</mj-button>
|
||||
<mj-text font-size="16px" line-height="24px">
|
||||
<br/><br/>
|
||||
Si vous avez des questions ou rencontrez des difficultés, n'hésitez pas à nous contacter.
|
||||
<br/><br/>
|
||||
Cordialement,
|
||||
<br/>
|
||||
L'équipe Mainframe SITECONSEIL
|
||||
</mj-text>
|
||||
{% endblock %}
|
||||
43
tests/Entity/MailTest.php
Normal file
43
tests/Entity/MailTest.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Tests\Entity;
|
||||
|
||||
use App\Entity\Mail;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class MailTest extends TestCase
|
||||
{
|
||||
public function testMailEntity(): void
|
||||
{
|
||||
$mail = new Mail();
|
||||
|
||||
// Test messageId property
|
||||
$messageId = 'test_message_id_123';
|
||||
$mail->setMessageId($messageId);
|
||||
$this->assertSame($messageId, $mail->getMessageId());
|
||||
|
||||
// Test status property
|
||||
$status = 'sent';
|
||||
$mail->setStatus($status);
|
||||
$this->assertSame($status, $mail->getStatus());
|
||||
|
||||
// Test dest property
|
||||
$dest = 'recipient@example.com';
|
||||
$mail->setDest($dest);
|
||||
$this->assertSame($dest, $mail->getDest());
|
||||
|
||||
// Test subject property
|
||||
$subject = 'Test Subject';
|
||||
$mail->setSubject($subject);
|
||||
$this->assertSame($subject, $mail->getSubject());
|
||||
|
||||
// Test content property
|
||||
$content = 'This is the test email content.';
|
||||
$mail->setContent($content);
|
||||
$this->assertSame($content, $mail->getContent());
|
||||
|
||||
// Test getId() - should be null initially as it's auto-generated by the database
|
||||
$this->assertNull($mail->getId());
|
||||
}
|
||||
}
|
||||
|
||||
36
tests/Repository/MailRepositoryTest.php
Normal file
36
tests/Repository/MailRepositoryTest.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Tests\Repository;
|
||||
|
||||
use App\Entity\Mail;
|
||||
use App\Repository\MailRepository;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
class MailRepositoryTest extends KernelTestCase
|
||||
{
|
||||
private ?EntityManagerInterface $entityManager;
|
||||
private ?MailRepository $mailRepository;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
self::bootKernel();
|
||||
$this->entityManager = self::getContainer()->get('doctrine')->getManager();
|
||||
$this->mailRepository = $this->entityManager->getRepository(Mail::class);
|
||||
}
|
||||
|
||||
public function testRepositoryExistsAndIsCorrectInstance(): void
|
||||
{
|
||||
$this->assertInstanceOf(MailRepository::class, $this->mailRepository);
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
parent::tearDown();
|
||||
|
||||
$this->entityManager->close();
|
||||
$this->entityManager = null; // Avoid memory leaks
|
||||
$this->mailRepository = null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user