diff --git a/.env b/.env index ec46bac1a..271957021 100644 --- a/.env +++ b/.env @@ -27,6 +27,7 @@ APP_DIALOG_BASE_URL=http://nginx REDIS_URL="redis://redis:6379" API_ADRESSE_BASE_URL=https://api-adresse.data.gouv.fr APP_IGN_GEOCODER_BASE_URL=https://data.geopf.fr +API_ORGANIZATION_FETCHER_URL=https://recherche-entreprises.api.gouv.fr/search MATOMO_ENABLED=false ###> BD TOPO ### BDTOPO_DATABASE_URL= diff --git a/config/packages/framework.yaml b/config/packages/framework.yaml index 725521fc7..665709c1b 100644 --- a/config/packages/framework.yaml +++ b/config/packages/framework.yaml @@ -31,6 +31,8 @@ framework: base_uri: '%env(APP_IGN_GEOCODER_BASE_URL)%' dialog.http.client: base_uri: '%env(APP_DIALOG_BASE_URL)%' + organization_fetcher.client: + base_uri: '%env(API_ORGANIZATION_FETCHER_URL)%' when@test: framework: diff --git a/config/services.yaml b/config/services.yaml index a66cd0fbc..6489bd110 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -148,5 +148,9 @@ when@test: arguments: ['@http_kernel'] App\Infrastructure\Adapter\DateUtils: class: App\Tests\Mock\DateUtilsMock + App\Tests\Mock\ApiOrganizationFetcherMock: + decorates: 'organization_fetcher.client' + decoration_inner_name: 'App\Tests\Mock\EudonetParis\ApiOrganizationFetcherMock::organization_fetcher.client' + Psr\Log\NullLogger: ~ logger: '@Psr\Log\NullLogger' diff --git a/config/validator/Application/User/SaveAccessRequestCommand.xml b/config/validator/Application/User/RegisterCommand.xml similarity index 77% rename from config/validator/Application/User/SaveAccessRequestCommand.xml rename to config/validator/Application/User/RegisterCommand.xml index 487dd07f6..874e48499 100644 --- a/config/validator/Application/User/SaveAccessRequestCommand.xml +++ b/config/validator/Application/User/RegisterCommand.xml @@ -2,7 +2,7 @@ - + @@ -22,13 +22,8 @@ - - - - - - + @@ -38,10 +33,5 @@ - - - - - diff --git a/config/validator/Domain/User/AccessRequest.xml b/config/validator/Domain/User/AccessRequest.xml deleted file mode 100644 index 54aa4deb4..000000000 --- a/config/validator/Domain/User/AccessRequest.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Application/ApiOrganizationFetcherInterface.php b/src/Application/ApiOrganizationFetcherInterface.php new file mode 100644 index 000000000..86f167d11 --- /dev/null +++ b/src/Application/ApiOrganizationFetcherInterface.php @@ -0,0 +1,10 @@ +accessRequestRepository->findOneByUuid($command->uuid); - if (!$accessRequest instanceof AccessRequest) { - throw new AccessRequestNotFoundException(); - } - - if (!$accessRequest->getSiret()) { - throw new SiretMissingException(); - } + $email = $this->stringUtils->normalizeEmail($command->email); - $user = $this->userRepository->findOneByEmail($accessRequest->getEmail()); + $user = $this->userRepository->findOneByEmail($email); if ($user instanceof User) { throw new UserAlreadyRegisteredException(); } - $organization = $this->organizationRepository->findOneBySiret($accessRequest->getSiret()); + $organization = $this->organizationRepository->findOneBySiret($command->organizationSiret); $organizationRole = OrganizationRolesEnum::ROLE_ORGA_CONTRIBUTOR->value; // Default organization role $now = $this->dateUtils->getNow(); if (!$organization) { + try { + ['name' => $name] = $this->organizationFetcher->findBySiret($command->organizationSiret); + } catch (OrganizationNotFoundException $e) { + throw $e; + } + $organizationRole = OrganizationRolesEnum::ROLE_ORGA_ADMIN->value; // The first user in an organization becomes an admin $organization = (new Organization($this->idFactory->make())) ->setCreatedAt($now) - ->setSiret($accessRequest->getSiret()) - ->setName($accessRequest->getOrganization()); + ->setSiret($command->organizationSiret) + ->setName($name); $this->organizationRepository->add($organization); } $user = (new User($this->idFactory->make())) - ->setFullName($accessRequest->getFullName()) - ->setEmail($accessRequest->getEmail()) + ->setFullName($command->fullName) + ->setEmail($email) ->setRoles([UserRolesEnum::ROLE_USER->value]) ->setRegistrationDate($now); $passwordUser = new PasswordUser( uuid: $this->idFactory->make(), - password: $accessRequest->getPassword(), + password: $this->passwordHasher->hash($command->password), user: $user, ); + $user->setPasswordUser($passwordUser); + $organizationUser = (new OrganizationUser($this->idFactory->make())) ->setUser($user) ->setOrganization($organization) @@ -84,6 +87,7 @@ public function __invoke(ConvertAccessRequestToUserCommand $command): void $this->userRepository->add($user); $this->passwordUserRepository->add($passwordUser); $this->organizationUserRepository->add($organizationUser); - $this->accessRequestRepository->remove($accessRequest); + + return $user; } } diff --git a/src/Application/User/Command/SaveAccessRequestCommand.php b/src/Application/User/Command/SaveAccessRequestCommand.php deleted file mode 100644 index 08cb49ce3..000000000 --- a/src/Application/User/Command/SaveAccessRequestCommand.php +++ /dev/null @@ -1,18 +0,0 @@ -stringUtils->normalizeEmail($command->email); - - if (true === $this->isAccessAlreadyRequested->isSatisfiedBy($email)) { - throw new AccessAlreadyRequestedException(); - } - - $this->accessRequestRepository->add( - new AccessRequest( - uuid: $this->idFactory->make(), - fullName: $command->fullName, - email: $email, - password: $this->passwordHasher->hash($command->password), - organization: $command->organizationName, - comment: $command->comment, - siret: $command->organizationSiret, - consentToBeContacted: $command->consentToBeContacted, - ), - ); - } -} diff --git a/src/Domain/User/AccessRequest.php b/src/Domain/User/AccessRequest.php deleted file mode 100644 index efea5fac5..000000000 --- a/src/Domain/User/AccessRequest.php +++ /dev/null @@ -1,80 +0,0 @@ -uuid; - } - - public function getFullName(): string - { - return $this->fullName; - } - - public function getSiret(): ?string - { - return $this->siret; - } - - public function getEmail(): string - { - return $this->email; - } - - public function getPassword(): string - { - return $this->password; - } - - public function getOrganization(): string - { - return $this->organization; - } - - public function getComment(): ?string - { - return $this->comment; - } - - public function isConsentToBeContacted(): bool - { - return $this->consentToBeContacted; - } - - public function setFullName(string $fullName): void - { - $this->fullName = $fullName; - } - - public function setEmail(string $email): void - { - $this->email = $email; - } - - public function setOrganization(string $organization): void - { - $this->organization = $organization; - } - - public function setSiret(string $siret): void - { - $this->siret = $siret; - } -} diff --git a/src/Domain/User/Exception/AccessAlreadyRequestedException.php b/src/Domain/User/Exception/AccessAlreadyRequestedException.php deleted file mode 100644 index 88c505448..000000000 --- a/src/Domain/User/Exception/AccessAlreadyRequestedException.php +++ /dev/null @@ -1,9 +0,0 @@ -accessRequestRepository->findOneByEmail($email) instanceof AccessRequest; - } -} diff --git a/src/Domain/User/User.php b/src/Domain/User/User.php index d47a9f284..367e04fca 100644 --- a/src/Domain/User/User.php +++ b/src/Domain/User/User.php @@ -89,6 +89,13 @@ public function getProConnectUser(): ?ProConnectUser return $this->proConnectUser; } + public function setPasswordUser(PasswordUser $passwordUser): self + { + $this->passwordUser = $passwordUser; + + return $this; + } + public function getPasswordUser(): ?PasswordUser { return $this->passwordUser; diff --git a/src/Infrastructure/Adapter/ApiOrganizationFetcher.php b/src/Infrastructure/Adapter/ApiOrganizationFetcher.php new file mode 100644 index 000000000..8fd0f5d3c --- /dev/null +++ b/src/Infrastructure/Adapter/ApiOrganizationFetcher.php @@ -0,0 +1,41 @@ +organizationFetcherClient->request( + 'GET', + 'search', + [ + 'query' => [ + 'q' => $siret, + 'est_collectivite_territoriale' => 'true', + ], + ], + ); + + $data = $response->toArray(); + + if (0 === $data['total_results']) { + throw new OrganizationNotFoundException($siret); + } + + return [ + 'name' => $data['results'][0]['nom_complet'], + ]; + } +} diff --git a/src/Infrastructure/Controller/AccessRequestController.php b/src/Infrastructure/Controller/AccessRequestController.php deleted file mode 100644 index 9389f93e8..000000000 --- a/src/Infrastructure/Controller/AccessRequestController.php +++ /dev/null @@ -1,70 +0,0 @@ -formFactory->create(AccessRequestFormType::class, $command); - $form->handleRequest($request); - - if ($form->isSubmitted() && $form->isValid()) { - /** @var FlashBagAwareSessionInterface */ - $session = $request->getSession(); - - try { - $this->commandBus->handle($command); - } catch (AccessAlreadyRequestedException) { - $session->getFlashBag()->add('error', $this->translator->trans('accessRequest.send.error')); - } - - return new RedirectResponse( - url: $this->router->generate('app_access_request', ['success' => 1]), - status: Response::HTTP_SEE_OTHER, - ); - } - - return new Response( - $this->twig->render( - name: 'accessRequest.html.twig', - context: [ - 'form' => $form->createView(), - ], - ), - status: ($form->isSubmitted() && !$form->isValid()) - ? Response::HTTP_UNPROCESSABLE_ENTITY - : Response::HTTP_OK, - ); - } -} diff --git a/src/Infrastructure/Controller/Admin/AccessRequestCrudController.php b/src/Infrastructure/Controller/Admin/AccessRequestCrudController.php deleted file mode 100644 index dc5acd758..000000000 --- a/src/Infrastructure/Controller/Admin/AccessRequestCrudController.php +++ /dev/null @@ -1,104 +0,0 @@ -setEntityLabelInSingular('Création de compte') - ->setEntityLabelInPlural('Création de comptes') - ; - } - - public function configureActions(Actions $actions): Actions - { - $convertAccessRequest = Action::new('convertAccessRequest', 'Convertir en compte utilisateur', 'fa fa-user') - ->displayAsLink() - ->displayIf(static function ($entity) { - return $entity->getSiret(); - }) - ->linkToCrudAction('convertAccessRequest') - ; - - return $actions - ->add(Crud::PAGE_DETAIL, $convertAccessRequest) - ->add(Crud::PAGE_INDEX, Action::DETAIL) - ->remove(Crud::PAGE_INDEX, Action::NEW) - ->remove(Crud::PAGE_INDEX, Action::EDIT); - } - - public function convertAccessRequest(AdminContext $context): RedirectResponse - { - $uuid = $context->getEntity()->getPrimaryKeyValue(); - - try { - $this->commandBus->handle(new ConvertAccessRequestToUserCommand($uuid)); - $this->addFlash('success', 'Le compte utilisateur a bien été créé.'); - - return $this->redirect( - $this->adminUrlGenerator - ->setController(self::class) - ->setEntityId(null) - ->setAction(Crud::PAGE_INDEX) - ->generateUrl(), - ); - } catch (AccessRequestNotFoundException) { - $this->addFlash('error', 'La demande de création de compte n\'existe pas.'); - } catch (SiretMissingException) { - $this->addFlash('error', 'Le siret est obligatoire.'); - } catch (UserAlreadyRegisteredException) { - $this->addFlash('error', 'Un utilisateur avec cette adresse email existe déjà.'); - } - - return $this->redirect($this->adminUrlGenerator - ->setController(self::class) - ->setAction(Crud::PAGE_DETAIL) - ->setEntityId($uuid) - ->generateUrl(), - ); - } - - public function configureFields(string $pageName): iterable - { - return [ - TextField::new('fullName')->setLabel('Prénom / Nom'), - EmailField::new('email'), - TextField::new('organization')->setLabel('Nom de l\'organisation'), - TextField::new('siret')->setLabel('Siret'), - TextField::new('comment')->setLabel('Message')->setDisabled(true), - BooleanField::new('consentToBeContacted', 'Je souhaite être contacté')->setDisabled(true), - ]; - } -} diff --git a/src/Infrastructure/Controller/Admin/DashboardController.php b/src/Infrastructure/Controller/Admin/DashboardController.php index 0f21f3256..f96be5013 100644 --- a/src/Infrastructure/Controller/Admin/DashboardController.php +++ b/src/Infrastructure/Controller/Admin/DashboardController.php @@ -5,7 +5,6 @@ namespace App\Infrastructure\Controller\Admin; use App\Domain\Organization\VisaModel\VisaModel; -use App\Domain\User\AccessRequest; use App\Domain\User\Feedback; use App\Domain\User\Organization; use App\Domain\User\OrganizationUser; @@ -37,7 +36,6 @@ public function configureMenuItems(): iterable yield MenuItem::section('Gestion des utilisateurs'); yield MenuItem::linkToCrud('Utilisateurs', 'fa fa-users', User::class); yield MenuItem::linkToCrud('Membres d\'organisations', 'fa fa-user-gear', OrganizationUser::class); - yield MenuItem::linkToCrud('Création de comptes', 'fa fa-code-pull-request', AccessRequest::class); yield MenuItem::section('Gestion des organisations'); yield MenuItem::linkToCrud('Organisations', 'fa fa-list', Organization::class); diff --git a/src/Infrastructure/Controller/Security/RegisterController.php b/src/Infrastructure/Controller/Security/RegisterController.php new file mode 100644 index 000000000..e53c155ef --- /dev/null +++ b/src/Infrastructure/Controller/Security/RegisterController.php @@ -0,0 +1,75 @@ +formFactory->create(RegisterFormType::class, $command); + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + try { + $user = $this->commandBus->handle($command); + $this->security->login($this->userProvider->loadUserByIdentifier($user->getEmail())); + + return new RedirectResponse( + url: $this->router->generate('app_regulations_list'), + status: Response::HTTP_SEE_OTHER, + ); + } catch (OrganizationNotFoundException) { + $form->get('organizationSiret')->addError( + new FormError($this->translator->trans('register.error.organizationSiret_not_found')), + ); + } catch (UserAlreadyRegisteredException) { + $form->get('email')->addError( + new FormError($this->translator->trans('register.error.already_exists')), + ); + } + } + + return new Response( + $this->twig->render( + name: 'register.html.twig', + context: [ + 'form' => $form->createView(), + ], + ), + status: ($form->isSubmitted() && !$form->isValid()) + ? Response::HTTP_UNPROCESSABLE_ENTITY + : Response::HTTP_OK, + ); + } +} diff --git a/src/Infrastructure/Form/User/AccessRequestFormType.php b/src/Infrastructure/Form/User/RegisterFormType.php similarity index 52% rename from src/Infrastructure/Form/User/AccessRequestFormType.php rename to src/Infrastructure/Form/User/RegisterFormType.php index e44859153..fb8daf42e 100644 --- a/src/Infrastructure/Form/User/AccessRequestFormType.php +++ b/src/Infrastructure/Form/User/RegisterFormType.php @@ -8,12 +8,12 @@ use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\EmailType; use Symfony\Component\Form\Extension\Core\Type\PasswordType; +use Symfony\Component\Form\Extension\Core\Type\RepeatedType; use Symfony\Component\Form\Extension\Core\Type\SubmitType; -use Symfony\Component\Form\Extension\Core\Type\TextareaType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; -final class AccessRequestFormType extends AbstractType +final class RegisterFormType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void { @@ -22,59 +22,46 @@ public function buildForm(FormBuilderInterface $builder, array $options): void 'fullName', TextType::class, options: [ - 'label' => 'accessRequest.fullName', + 'label' => 'register.fullName', ], ) ->add( 'email', EmailType::class, options: [ - 'label' => 'accessRequest.email', - 'help' => 'accessRequest.email.help', - ], - ) - ->add( - 'organizationName', - TextType::class, - options: [ - 'label' => 'accessRequest.organizationName', + 'label' => 'register.email', + 'help' => 'register.email.help', ], ) ->add( 'organizationSiret', TextType::class, options: [ - 'label' => 'accessRequest.organizationSiret', - 'help' => 'accessRequest.organizationSiret.help', - 'required' => false, + 'label' => 'register.organizationSiret', + 'help' => 'register.organizationSiret.help', + 'required' => true, ], ) ->add( 'password', - PasswordType::class, - options: [ - 'label' => 'accessRequest.password', - 'help' => 'accessRequest.password.help', - ], - ) - ->add( - 'comment', - TextareaType::class, + RepeatedType::class, options: [ - 'label' => 'accessRequest.comment', - 'required' => false, + 'type' => PasswordType::class, + 'first_options' => ['label' => 'register.password', 'help' => 'register.password.help'], + 'second_options' => ['label' => 'register.password.confirm', 'help' => 'register.password.help'], ], ) ->add( - 'consentToBeContacted', + 'cgu', CheckboxType::class, [ - 'label' => 'accessRequest.consentToBeContacted', - 'required' => false, + 'label' => 'register.cgu', + 'required' => true, + 'mapped' => false, ], ) ->add('save', SubmitType::class, options: [ - 'label' => 'common.send', + 'label' => 'common.submit', 'attr' => ['class' => 'fr-btn'], ], ) diff --git a/src/Infrastructure/Persistence/Doctrine/Fixtures/AccessRequestFixture.php b/src/Infrastructure/Persistence/Doctrine/Fixtures/AccessRequestFixture.php deleted file mode 100644 index 42e195d7e..000000000 --- a/src/Infrastructure/Persistence/Doctrine/Fixtures/AccessRequestFixture.php +++ /dev/null @@ -1,31 +0,0 @@ -persist($accessRequest); - $manager->flush(); - } -} diff --git a/src/Infrastructure/Persistence/Doctrine/Mapping/User.AccessRequest.orm.xml b/src/Infrastructure/Persistence/Doctrine/Mapping/User.AccessRequest.orm.xml deleted file mode 100644 index 9a7908138..000000000 --- a/src/Infrastructure/Persistence/Doctrine/Mapping/User.AccessRequest.orm.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/src/Infrastructure/Persistence/Doctrine/Migrations/Version20250116103234.php b/src/Infrastructure/Persistence/Doctrine/Migrations/Version20250116103234.php new file mode 100644 index 000000000..2a09d2e0a --- /dev/null +++ b/src/Infrastructure/Persistence/Doctrine/Migrations/Version20250116103234.php @@ -0,0 +1,32 @@ +addSql('DROP TABLE access_request'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE TABLE access_request (uuid UUID NOT NULL, full_name VARCHAR(255) NOT NULL, email VARCHAR(255) NOT NULL, organization VARCHAR(255) NOT NULL, siret VARCHAR(14) DEFAULT NULL, password VARCHAR(255) NOT NULL, consent_to_be_contacted BOOLEAN NOT NULL, comment TEXT DEFAULT NULL, PRIMARY KEY(uuid))'); + $this->addSql('CREATE UNIQUE INDEX access_request_email ON access_request (email)'); + } +} diff --git a/src/Infrastructure/Persistence/Doctrine/Repository/User/AccessRequestRepository.php b/src/Infrastructure/Persistence/Doctrine/Repository/User/AccessRequestRepository.php deleted file mode 100644 index 7d4d96441..000000000 --- a/src/Infrastructure/Persistence/Doctrine/Repository/User/AccessRequestRepository.php +++ /dev/null @@ -1,55 +0,0 @@ -getEntityManager()->persist($accessRequest); - - return $accessRequest; - } - - public function remove(AccessRequest $accessRequest): void - { - $this->getEntityManager()->remove($accessRequest); - } - - public function findOneByEmail(string $email): ?AccessRequest - { - return $this->createQueryBuilder('a') - ->where('a.email = :email') - ->setParameter('email', $this->stringUtils->normalizeEmail($email)) - ->setMaxResults(1) - ->getQuery() - ->getOneOrNullResult() - ; - } - - public function findOneByUuid(string $uuid): ?AccessRequest - { - return $this->createQueryBuilder('a') - ->where('a.uuid = :uuid') - ->setParameter('uuid', $uuid) - ->setMaxResults(1) - ->getQuery() - ->getOneOrNullResult() - ; - } -} diff --git a/src/Infrastructure/Security/Provider/UserProvider.php b/src/Infrastructure/Security/Provider/UserProvider.php index 859dec8a4..380735944 100644 --- a/src/Infrastructure/Security/Provider/UserProvider.php +++ b/src/Infrastructure/Security/Provider/UserProvider.php @@ -23,7 +23,6 @@ public function __construct( public function loadUserByIdentifier(string $identifier): UserInterface { $user = $this->userRepository->findOneByEmail($identifier); - if (!$user instanceof User || !$user->getPasswordUser()) { throw new UserNotFoundException(\sprintf('Unable to find the user %s', $identifier)); } diff --git a/templates/accessRequest.html.twig b/templates/accessRequest.html.twig deleted file mode 100644 index 8abef6e5d..000000000 --- a/templates/accessRequest.html.twig +++ /dev/null @@ -1,55 +0,0 @@ -{% extends 'layouts/layout.html.twig' %} - -{% block title %} - {{'accessRequest.meta.title'|trans}} - {{ parent() }} -{% endblock %} - -{% block body %} -
-
-
-

{{ 'accessRequest.meta.title'|trans }}

-

{{ 'accessRequest.meta.subtitle'|trans }}

-
-
-
-
-
-
-
-
-
- {% if app.request.get('success') %} -
- {{ 'accessRequest.send.success'|trans }} -
- {{ 'accessRequest.send.success.help'|trans }} - {% else %} - {{ form_start(form) }} - {{ form_row(form.fullName, {group_class: 'fr-input-group', attr: {class: 'fr-input'} }) }} - {{ form_row(form.email, {group_class: 'fr-input-group', attr: {class: 'fr-input'} }) }} - {{ form_row(form.password, {group_class: 'fr-input-group', attr: {class: 'fr-input'} }) }} - {{ form_row(form.organizationName, {group_class: 'fr-input-group', attr: {class: 'fr-input'} }) }} - {{ form_row(form.organizationSiret, {group_class: 'fr-input-group', attr: {class: 'fr-input'} }) }} - {{ form_row(form.comment, {group_class: 'fr-input-group', attr: {class: 'fr-input'} }) }} -
-
- {{ form_widget(form.consentToBeContacted) }} - {{ form_label(form.consentToBeContacted) }} -
-
- {{ form_row(form.save, {group_class: 'fr-btns-group'}) }} - {{ form_end(form) }} - {% endif %} -
-
-
- -
-
-
-{% endblock %} diff --git a/templates/common/form/dsfr_theme.html.twig b/templates/common/form/dsfr_theme.html.twig index f0a4b1516..88297f347 100644 --- a/templates/common/form/dsfr_theme.html.twig +++ b/templates/common/form/dsfr_theme.html.twig @@ -66,8 +66,8 @@ https://github.com/symfony/symfony/blob/6.3/src/Symfony/Bridge/Twig/Resources/vi {{ form_widget(form, { attr: {class: 'fr-password__input fr-input'} }) }}
- - + +
{{ form_errors(form) }} diff --git a/templates/common/header.html.twig b/templates/common/header.html.twig index 572876484..da87ce58a 100644 --- a/templates/common/header.html.twig +++ b/templates/common/header.html.twig @@ -44,8 +44,8 @@ {% else %}
  • - - {{ 'common.accessRequest'|trans }} + + {{ 'common.register'|trans }}
  • diff --git a/templates/landing_authorities.html.twig b/templates/landing_authorities.html.twig index 2113c6e1b..df4743427 100644 --- a/templates/landing_authorities.html.twig +++ b/templates/landing_authorities.html.twig @@ -9,7 +9,7 @@
    @@ -70,7 +70,7 @@ {{ 'landing.authorities.steps.register'|trans }}

    - + {{ 'landing.authorities.steps.register.cta'|trans }}

    @@ -109,7 +109,7 @@ {{ 'landing.authorities.pilots.join'|trans }}

    {{ 'landing.authorities.pilots.join.description'|trans }}

    - + {{ 'landing.authorities.pilots.join.cta'|trans }}
    diff --git a/templates/login.html.twig b/templates/login.html.twig index c73bb034d..22293577a 100644 --- a/templates/login.html.twig +++ b/templates/login.html.twig @@ -70,7 +70,7 @@

    {{ 'login.no_account.title'|trans }}

    - {{ 'login.no_account.access_request'|trans }} + {{ 'login.no_account.register'|trans }}

    diff --git a/templates/register.html.twig b/templates/register.html.twig new file mode 100644 index 000000000..d46b2cfcd --- /dev/null +++ b/templates/register.html.twig @@ -0,0 +1,53 @@ +{% extends 'layouts/layout.html.twig' %} + +{% block title %} + {{'register.meta.title'|trans}} - {{ parent() }} +{% endblock %} + +{% block body %} +
    +
    +
    +

    {{ 'register.meta.title'|trans }}

    +

    {{ 'register.meta.subtitle'|trans }}

    +
    +
    +
    +
    +
    +
    +
    +
    +
    + {{ form_start(form) }} + {{ form_row(form.fullName, {group_class: 'fr-input-group', attr: {class: 'fr-input'} }) }} + {{ form_row(form.email, {group_class: 'fr-input-group', attr: {class: 'fr-input'} }) }} + {{ form_row(form.organizationSiret, {group_class: 'fr-input-group', attr: {class: 'fr-input'} }) }} + + {{ form_row(form.password, {group_class: 'fr-input-group', attr: {class: 'fr-input'} }) }} +
    +
    + {{ form_widget(form.cgu) }} + +
    +
    + {{ form_row(form.save, {group_class: 'fr-btns-group'}) }} + {{ form_end(form) }} +
    +
    +
    + +
    +
    +
    +{% endblock %} diff --git a/tests/Integration/Infrastructure/Controller/AccessRequestControllerTest.php b/tests/Integration/Infrastructure/Controller/AccessRequestControllerTest.php deleted file mode 100644 index da3dd3ce9..000000000 --- a/tests/Integration/Infrastructure/Controller/AccessRequestControllerTest.php +++ /dev/null @@ -1,125 +0,0 @@ -request('GET', '/access-request'); - - $this->assertResponseStatusCodeSame(200); - $this->assertSecurityHeaders(); - $this->assertSame('Demande de création de compte', $crawler->filter('h2')->text()); - $this->assertMetaTitle('Demande de création de compte - DiaLog', $crawler); - - $saveButton = $crawler->selectButton('Envoyer'); - $form = $saveButton->form(); - $form['access_request_form[fullName]'] = 'Lucie Dutest'; - $form['access_request_form[organizationName]'] = 'Fairness'; - $form['access_request_form[organizationSiret]'] = '52435841300143'; - $form['access_request_form[password]'] = 'password12345'; - $form['access_request_form[email]'] = 'lucie@example.com'; - $form['access_request_form[comment]'] = 'Ceci est un test'; - $form['access_request_form[consentToBeContacted]'] = '1'; - $client->submit($form); - $this->assertResponseStatusCodeSame(303); - - $crawler = $client->followRedirect(); - $this->assertSame('Votre demande de création de compte a bien été prise en compte. Nous reviendrons vers vous dans les plus brefs délais.', $crawler->filter('div.fr-alert--success')->text()); - $this->assertResponseStatusCodeSame(200); - $this->assertRouteSame('app_access_request'); - } - - public function testAddWithoutSiret(): void - { - $client = static::createClient(); - $crawler = $client->request('GET', '/access-request'); - - $saveButton = $crawler->selectButton('Envoyer'); - $form = $saveButton->form(); - $form['access_request_form[fullName]'] = 'Hélène Maitre-Marchois'; - $form['access_request_form[organizationName]'] = 'Fairness'; - $form['access_request_form[password]'] = 'password12345'; - $form['access_request_form[email]'] = 'helene@fairness.coop'; - $form['access_request_form[comment]'] = 'Ceci est un test'; - $form['access_request_form[consentToBeContacted]'] = '1'; - $client->submit($form); - $this->assertResponseStatusCodeSame(303); - - $crawler = $client->followRedirect(); - $this->assertSame('Votre demande de création de compte a bien été prise en compte. Nous reviendrons vers vous dans les plus brefs délais.', $crawler->filter('div.fr-alert--success')->text()); - $this->assertResponseStatusCodeSame(200); - $this->assertRouteSame('app_access_request'); - } - - public function testAccessAlreadyRequested(): void - { - $client = static::createClient(); - $crawler = $client->request('GET', '/access-request'); - - $saveButton = $crawler->selectButton('Envoyer'); - $form = $saveButton->form(); - $form['access_request_form[fullName]'] = 'Mathieu Marchois'; - $form['access_request_form[organizationName]'] = 'Fairness'; - $form['access_request_form[organizationSiret]'] = '82050375300015'; - $form['access_request_form[password]'] = 'password12345'; - $form['access_request_form[email]'] = 'mathieu@fairness.coop'; - $form['access_request_form[comment]'] = 'Ceci est un test'; - $form['access_request_form[consentToBeContacted]'] = '1'; - $client->submit($form); - $this->assertResponseStatusCodeSame(303); - - $crawler = $client->followRedirect(); - $this->assertSame('Une demande de création de compte a déjà été créée avec cette adresse e-mail.', $crawler->filter('div.fr-alert--error')->text()); - $this->assertResponseStatusCodeSame(200); - $this->assertRouteSame('app_access_request'); - } - - public function testEmptyData(): void - { - $client = static::createClient(); - $crawler = $client->request('GET', '/access-request'); - - $saveButton = $crawler->selectButton('Envoyer'); - $form = $saveButton->form(); - - $crawler = $client->submit($form); - $this->assertResponseStatusCodeSame(422); - $this->assertSame('Cette valeur ne doit pas être vide.', $crawler->filter('#access_request_form_fullName_error')->text()); - $this->assertSame('Cette valeur ne doit pas être vide.', $crawler->filter('#access_request_form_email_error')->text()); - $this->assertSame('Cette valeur ne doit pas être vide.', $crawler->filter('#access_request_form_organizationName_error')->text()); - $this->assertSame('Cette valeur ne doit pas être vide.', $crawler->filter('#access_request_form_password_error')->text()); - } - - public function testBadValues(): void - { - $client = static::createClient(); - $crawler = $client->request('GET', '/access-request'); - - $saveButton = $crawler->selectButton('Envoyer'); - $form = $saveButton->form(); - $form['access_request_form[fullName]'] = str_repeat('a', 256); - $form['access_request_form[email]'] = 'helene'; - $form['access_request_form[organizationName]'] = str_repeat('a', 256); - $form['access_request_form[organizationSiret]'] = 'aaaa'; - $form['access_request_form[password]'] = 'aaaa'; - - $crawler = $client->submit($form); - - $this->assertResponseStatusCodeSame(422); - $this->assertSame('Les caractères doivent être des chiffres. Cette chaîne doit avoir exactement 14 caractères.', $crawler->filter('#access_request_form_organizationSiret_error')->text()); - $this->assertSame('Cette chaîne est trop longue. Elle doit avoir au maximum 255 caractères.', $crawler->filter('#access_request_form_fullName_error')->text()); - $this->assertSame('Cette chaîne est trop courte. Elle doit avoir au minimum 12 caractères.', $crawler->filter('#access_request_form_password_error')->text()); - $this->assertSame('Cette valeur n\'est pas une adresse email valide.', $crawler->filter('#access_request_form_email_error')->text()); - - // Email too long - $form['access_request_form[email]'] = str_repeat('a', 256) . '@gmail.com'; - $crawler = $client->submit($form); - $this->assertResponseStatusCodeSame(422); - $this->assertSame('Cette chaîne est trop longue. Elle doit avoir au maximum 255 caractères.', $crawler->filter('#access_request_form_email_error')->text()); - } -} diff --git a/tests/Integration/Infrastructure/Controller/Admin/AccessRequestCrudControllerTest.php b/tests/Integration/Infrastructure/Controller/Admin/AccessRequestCrudControllerTest.php deleted file mode 100644 index 629973700..000000000 --- a/tests/Integration/Infrastructure/Controller/Admin/AccessRequestCrudControllerTest.php +++ /dev/null @@ -1,47 +0,0 @@ -login(UserFixture::MAIN_ORG_ADMIN_EMAIL); - $crawler = $client->request('GET', '/admin?crudAction=index&crudControllerFqcn=App%5CInfrastructure%5CController%5CAdmin%5CAccessRequestCrudController'); - - $this->assertResponseStatusCodeSame(200); - $this->assertSecurityHeaders(); - $this->assertSame('Création de comptes', $crawler->filter('h1')->text()); - } - - public function testIndexWithoutAuthentication(): void - { - $client = static::createClient(); - $client->request('GET', '/admin?crudAction=index&crudControllerFqcn=App%5CInfrastructure%5CController%5CAdmin%5CAccessRequestCrudController'); - - $this->assertResponseRedirects('http://localhost/login', 302); - } - - public function testIndexWithRoleAccessRequest(): void - { - $client = $this->login(); - $client->request('GET', '/admin?crudAction=index&crudControllerFqcn=App%5CInfrastructure%5CController%5CAdmin%5CAccessRequestCrudController'); - - $this->assertResponseStatusCodeSame(403); - } - - public function testConvertWithAnUnknownOrganization(): void - { - $client = $this->login(UserFixture::MAIN_ORG_ADMIN_EMAIL); - $client->request('GET', '/admin?crudAction=convertAccessRequest&crudControllerFqcn=App%5CInfrastructure%5CController%5CAdmin%5CAccessRequestCrudController&entityId=' . AccessRequestFixture::UUID); - $crawler = $client->followRedirect(); - - $this->assertSame($crawler->filter('div.alert-success')->text(), 'Le compte utilisateur a bien été créé.'); - } -} diff --git a/tests/Integration/Infrastructure/Controller/LandingAuthoritiesControllerTest.php b/tests/Integration/Infrastructure/Controller/LandingAuthoritiesControllerTest.php index 002154c0d..b8137c63d 100644 --- a/tests/Integration/Infrastructure/Controller/LandingAuthoritiesControllerTest.php +++ b/tests/Integration/Infrastructure/Controller/LandingAuthoritiesControllerTest.php @@ -18,18 +18,18 @@ public function testPage(): void $this->assertPageStructure([ ['h1', 'Numériser votre réglementation de circulation routière avec DiaLog'], - ['a', 'Créer un compte pour ma commune', ['href' => '/access-request']], + ['a', 'Créer un compte pour ma commune', ['href' => '/register']], ['h2', 'Facilitez la vie de votre service voirie'], ['h3', 'Gagnez du temps'], ['h3', 'Simplifiez vos échanges'], ['h2', 'Comment ça marche ?'], ['h3', 'Créer un compte DiaLog'], - ['a', 'Créer un compte', ['href' => '/access-request']], + ['a', 'Créer un compte', ['href' => '/register']], ['h3', 'Renseigner mes arrêtés'], ['h3', 'Diffuser mes données'], ['h2', 'Rejoignez les communes pilotes'], ['h3', "Participez à l'expérimentation"], - ['a', 'Créer un compte', ['href' => '/access-request']], + ['a', 'Créer un compte', ['href' => '/register']], ], $crawler); } } diff --git a/tests/Integration/Infrastructure/Controller/Security/RegisterControllerTest.php b/tests/Integration/Infrastructure/Controller/Security/RegisterControllerTest.php new file mode 100644 index 000000000..fca742afd --- /dev/null +++ b/tests/Integration/Infrastructure/Controller/Security/RegisterControllerTest.php @@ -0,0 +1,117 @@ +request('GET', '/register'); + + $this->assertResponseStatusCodeSame(200); + $this->assertSecurityHeaders(); + $this->assertSame('Créer mon compte', $crawler->filter('h2')->text()); + $this->assertMetaTitle('Créer mon compte - DiaLog', $crawler); + + $saveButton = $crawler->selectButton('Valider'); + $form = $saveButton->form(); + $form['register_form[fullName]'] = 'Lucie Dutest'; + $form['register_form[organizationSiret]'] = '82050375300015'; + $form['register_form[password][first]'] = 'password12345'; + $form['register_form[password][second]'] = 'password12345'; + $form['register_form[email]'] = 'lucie@example.com'; + $form['register_form[cgu]'] = '1'; + $client->submit($form); + $this->assertResponseStatusCodeSame(303); + + $crawler = $client->followRedirect(); + $this->assertResponseStatusCodeSame(200); + $this->assertRouteSame('app_regulations_list'); + } + + public function testRegisterSiretNotExists(): void + { + $client = static::createClient(); + $crawler = $client->request('GET', '/register'); + + $saveButton = $crawler->selectButton('Valider'); + $form = $saveButton->form(); + $form['register_form[fullName]'] = 'Lucie Dutest'; + $form['register_form[organizationSiret]'] = '11111111111111'; + $form['register_form[password][first]'] = 'password12345'; + $form['register_form[password][second]'] = 'password12345'; + $form['register_form[email]'] = 'lucie@example.com'; + $form['register_form[cgu]'] = '1'; + $crawler = $client->submit($form); + $this->assertResponseStatusCodeSame(422); + $this->assertSame('Le SIRET fourni n\'existe pas. Veuillez vérifier si votre SIRET est correct.', $crawler->filter('#register_form_organizationSiret_error')->text()); + } + + public function testRegisterWithAccountAlreadyExists(): void + { + $client = static::createClient(); + $crawler = $client->request('GET', '/register'); + + $saveButton = $crawler->selectButton('Valider'); + $form = $saveButton->form(); + $form['register_form[fullName]'] = 'Mathieu Marchois'; + $form['register_form[organizationSiret]'] = '82050375300015'; + $form['register_form[password][first]'] = 'password12345'; + $form['register_form[password][second]'] = 'password12345'; + $form['register_form[email]'] = 'mathieu.marchois@beta.gouv.fr'; + $form['register_form[cgu]'] = '1'; + $crawler = $client->submit($form); + $this->assertResponseStatusCodeSame(422); + $this->assertSame('Un compte utilisant cette adresse e-mail existe déjà.', $crawler->filter('#register_form_email_error')->text()); + } + + public function testEmptyData(): void + { + $client = static::createClient(); + $crawler = $client->request('GET', '/register'); + + $saveButton = $crawler->selectButton('Valider'); + $form = $saveButton->form(); + + $crawler = $client->submit($form); + + $this->assertResponseStatusCodeSame(422); + $this->assertSame('Cette valeur ne doit pas être vide.', $crawler->filter('#register_form_fullName_error')->text()); + $this->assertSame('Cette valeur ne doit pas être vide.', $crawler->filter('#register_form_email_error')->text()); + $this->assertSame('Cette valeur ne doit pas être vide.', $crawler->filter('#register_form_organizationSiret_error')->text()); + $this->assertSame('Cette valeur ne doit pas être vide.', $crawler->filter('#register_form_password_first_error')->text()); + } + + public function testBadValues(): void + { + $client = static::createClient(); + $crawler = $client->request('GET', '/register'); + + $saveButton = $crawler->selectButton('Valider'); + $form = $saveButton->form(); + $form['register_form[fullName]'] = str_repeat('a', 256); + $form['register_form[email]'] = 'helene'; + $form['register_form[organizationSiret]'] = 'aaaa'; + $form['register_form[password][first]'] = 'aaaa'; + $form['register_form[password][second]'] = 'bbbbb'; + + $crawler = $client->submit($form); + + $this->assertResponseStatusCodeSame(422); + $this->assertSame('Les caractères doivent être des chiffres. Cette chaîne doit avoir exactement 14 caractères.', $crawler->filter('#register_form_organizationSiret_error')->text()); + $this->assertSame('Cette chaîne est trop longue. Elle doit avoir au maximum 255 caractères.', $crawler->filter('#register_form_fullName_error')->text()); + $this->assertSame('Les valeurs ne correspondent pas.', $crawler->filter('#register_form_password_first_error')->text()); + $this->assertSame('Cette valeur n\'est pas une adresse email valide.', $crawler->filter('#register_form_email_error')->text()); + + // Email too long + $form['register_form[email]'] = str_repeat('a', 256) . '@gmail.com'; + $crawler = $client->submit($form); + $this->assertResponseStatusCodeSame(422); + $this->assertSame('Cette chaîne est trop longue. Elle doit avoir au maximum 255 caractères.', $crawler->filter('#register_form_email_error')->text()); + } +} diff --git a/tests/Mock/ApiOrganizationFetcherMock.php b/tests/Mock/ApiOrganizationFetcherMock.php new file mode 100644 index 000000000..3aa18f04a --- /dev/null +++ b/tests/Mock/ApiOrganizationFetcherMock.php @@ -0,0 +1,29 @@ +baseUri); + } + + private function handleRequests(string $method, string $url, array $options): MockResponse + { + $results = match ($options['query']['q']) { + '82050375300015' => ['results' => [0 => ['nom_complet' => 'Fairness']], 'total_results' => 1], + default => ['results' => [], 'total_results' => 0], + }; + + return new MockResponse(json_encode($results), ['http_code' => 200]); + } +} diff --git a/tests/Unit/Application/User/Command/ConvertAccessRequestToUserCommandHandlerTest.php b/tests/Unit/Application/User/Command/RegisterCommandHandlerTest.php similarity index 52% rename from tests/Unit/Application/User/Command/ConvertAccessRequestToUserCommandHandlerTest.php rename to tests/Unit/Application/User/Command/RegisterCommandHandlerTest.php index 5bb1c6470..e6888c4b6 100644 --- a/tests/Unit/Application/User/Command/ConvertAccessRequestToUserCommandHandlerTest.php +++ b/tests/Unit/Application/User/Command/RegisterCommandHandlerTest.php @@ -4,20 +4,20 @@ namespace App\Tests\Unit\Application\User\Command; +use App\Application\ApiOrganizationFetcherInterface; use App\Application\DateUtilsInterface; use App\Application\IdFactoryInterface; -use App\Application\User\Command\ConvertAccessRequestToUserCommand; -use App\Application\User\Command\ConvertAccessRequestToUserCommandHandler; -use App\Domain\User\AccessRequest; +use App\Application\PasswordHasherInterface; +use App\Application\StringUtilsInterface; +use App\Application\User\Command\RegisterCommand; +use App\Application\User\Command\RegisterCommandHandler; use App\Domain\User\Enum\OrganizationRolesEnum; use App\Domain\User\Enum\UserRolesEnum; -use App\Domain\User\Exception\AccessRequestNotFoundException; -use App\Domain\User\Exception\SiretMissingException; +use App\Domain\User\Exception\OrganizationNotFoundException; use App\Domain\User\Exception\UserAlreadyRegisteredException; use App\Domain\User\Organization; use App\Domain\User\OrganizationUser; use App\Domain\User\PasswordUser; -use App\Domain\User\Repository\AccessRequestRepositoryInterface; use App\Domain\User\Repository\OrganizationRepositoryInterface; use App\Domain\User\Repository\OrganizationUserRepositoryInterface; use App\Domain\User\Repository\PasswordUserRepositoryInterface; @@ -26,49 +26,59 @@ use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -final class ConvertAccessRequestToUserCommandHandlerTest extends TestCase +final class RegisterCommandHandlerTest extends TestCase { private MockObject $idFactory; - private MockObject $accessRequestRepository; + private MockObject $passwordHasher; private MockObject $userRepository; private MockObject $passwordUserRepository; private MockObject $organizationUserRepository; private MockObject $organizationRepository; - private MockObject $accessRequest; private MockObject $dateUtils; + private MockObject $stringUtils; + private ApiOrganizationFetcherInterface $organizationFetcher; + private RegisterCommandHandler $handler; + private RegisterCommand $command; public function setUp(): void { $this->idFactory = $this->createMock(IdFactoryInterface::class); - $this->accessRequestRepository = $this->createMock(AccessRequestRepositoryInterface::class); + $this->passwordHasher = $this->createMock(PasswordHasherInterface::class); $this->organizationUserRepository = $this->createMock(OrganizationUserRepositoryInterface::class); $this->userRepository = $this->createMock(UserRepositoryInterface::class); $this->passwordUserRepository = $this->createMock(PasswordUserRepositoryInterface::class); $this->organizationRepository = $this->createMock(OrganizationRepositoryInterface::class); - $this->accessRequest = $this->createMock(AccessRequest::class); + $this->organizationFetcher = $this->createMock(ApiOrganizationFetcherInterface::class); $this->dateUtils = $this->createMock(DateUtilsInterface::class); - } + $this->stringUtils = $this->createMock(StringUtilsInterface::class); - public function testConvertWithSiretLinkedToExistingOrganization(): void - { - $organization = $this->createMock(Organization::class); + $this->handler = new RegisterCommandHandler( + $this->idFactory, + $this->userRepository, + $this->passwordUserRepository, + $this->organizationUserRepository, + $this->organizationRepository, + $this->dateUtils, + $this->stringUtils, + $this->passwordHasher, + $this->organizationFetcher, + ); + $this->command = new RegisterCommand(); + $this->command->fullName = 'Mathieu MARCHOIS'; + $this->command->email = ' mathieu@fairness.coop '; + $this->command->password = '12345'; + $this->command->organizationSiret = '82050375300015'; - $this->accessRequest + $this->stringUtils ->expects(self::once()) - ->method('getFullName') - ->willReturn('Mathieu MARCHOIS'); - $this->accessRequest - ->expects(self::exactly(2)) - ->method('getEmail') + ->method('normalizeEmail') + ->with(' mathieu@fairness.coop ') ->willReturn('mathieu@fairness.coop'); - $this->accessRequest - ->expects(self::once()) - ->method('getPassword') - ->willReturn('passwordHashed'); - $this->accessRequest - ->expects(self::exactly(2)) - ->method('getSiret') - ->willReturn('82050375300015'); + } + + public function testRegisterWithSiretLinkedToExistingOrganization(): void + { + $organization = $this->createMock(Organization::class); $this->userRepository ->expects(self::once()) @@ -86,12 +96,6 @@ public function testConvertWithSiretLinkedToExistingOrganization(): void ->expects(self::never()) ->method('add'); - $this->accessRequestRepository - ->expects(self::once()) - ->method('findOneByUuid') - ->with('e8a18fab-58d9-4aaf-bb47-b7a8edc20c23') - ->willReturn($this->accessRequest); - $this->idFactory ->expects(self::exactly(3)) ->method('make') @@ -113,10 +117,19 @@ public function testConvertWithSiretLinkedToExistingOrganization(): void ->setRegistrationDate($date) ->setRoles([UserRolesEnum::ROLE_USER->value]); + $passwordUser = new PasswordUser('3e4ea113-9f64-4933-a699-0561b8c15622', 'passwordHashed', $user); + $user->setPasswordUser($passwordUser); + + $this->passwordHasher + ->expects(self::once()) + ->method('hash') + ->with('12345') + ->willReturn('passwordHashed'); + $this->passwordUserRepository ->expects(self::once()) ->method('add') - ->with($this->equalTo(new PasswordUser('3e4ea113-9f64-4933-a699-0561b8c15622', 'passwordHashed', $user))); + ->with($this->equalTo($passwordUser)); $organizationUser = (new OrganizationUser('f40f95eb-a7dd-4232-9f03-2db10f04f37f')) ->setOrganization($organization) @@ -133,49 +146,13 @@ public function testConvertWithSiretLinkedToExistingOrganization(): void ->method('add') ->with($this->equalTo($user)); - $this->accessRequestRepository - ->expects(self::once()) - ->method('remove') - ->with($this->equalTo($this->accessRequest)); - - $handler = new ConvertAccessRequestToUserCommandHandler( - $this->idFactory, - $this->accessRequestRepository, - $this->userRepository, - $this->passwordUserRepository, - $this->organizationUserRepository, - $this->organizationRepository, - $this->dateUtils, - ); - $command = new ConvertAccessRequestToUserCommand('e8a18fab-58d9-4aaf-bb47-b7a8edc20c23'); - $handler($command); + $this->assertEquals(($this->handler)($this->command), $user); } - public function testConvertWithSiretNotLinkedToAnOrganization(): void + public function testRegisterWithSiretNotLinkedToAnOrganization(): void { $organization = $this->createMock(Organization::class); - $this->accessRequest - ->expects(self::once()) - ->method('getFullName') - ->willReturn('Mathieu MARCHOIS'); - $this->accessRequest - ->expects(self::exactly(2)) - ->method('getEmail') - ->willReturn('mathieu@fairness.coop'); - $this->accessRequest - ->expects(self::once()) - ->method('getPassword') - ->willReturn('passwordHashed'); - $this->accessRequest - ->expects(self::once()) - ->method('getOrganization') - ->willReturn('Fairness'); - $this->accessRequest - ->expects(self::exactly(3)) - ->method('getSiret') - ->willReturn('82050375300015'); - $this->userRepository ->expects(self::once()) ->method('findOneByEmail') @@ -188,6 +165,12 @@ public function testConvertWithSiretNotLinkedToAnOrganization(): void ->with('82050375300015') ->willReturn(null); + $this->passwordHasher + ->expects(self::once()) + ->method('hash') + ->with('12345') + ->willReturn('passwordHashed'); + $date = new \DateTimeImmutable('2024-05-07'); $this->dateUtils ->expects(self::once()) @@ -200,6 +183,9 @@ public function testConvertWithSiretNotLinkedToAnOrganization(): void ->setRoles([UserRolesEnum::ROLE_USER->value]) ->setRegistrationDate($date); + $passwordUser = new PasswordUser('3e4ea113-9f64-4933-a699-0561b8c15622', 'passwordHashed', $user); + $user->setPasswordUser($passwordUser); + $organization = (new Organization('d145a0e3-e397-412c-ba6a-90b150f7aec2')) ->setCreatedAt($date) ->setName('Fairness') @@ -210,6 +196,14 @@ public function testConvertWithSiretNotLinkedToAnOrganization(): void ->setUser($user) ->setRoles(OrganizationRolesEnum::ROLE_ORGA_ADMIN->value); + $this->organizationFetcher + ->expects(self::once()) + ->method('findBySiret') + ->with('82050375300015') + ->willReturn([ + 'name' => 'Fairness', + ]); + $this->organizationUserRepository ->expects(self::once()) ->method('add') @@ -223,13 +217,7 @@ public function testConvertWithSiretNotLinkedToAnOrganization(): void $this->passwordUserRepository ->expects(self::once()) ->method('add') - ->with($this->equalTo(new PasswordUser('3e4ea113-9f64-4933-a699-0561b8c15622', 'passwordHashed', $user))); - - $this->accessRequestRepository - ->expects(self::once()) - ->method('findOneByUuid') - ->with('e8a18fab-58d9-4aaf-bb47-b7a8edc20c23') - ->willReturn($this->accessRequest); + ->with($this->equalTo($passwordUser)); $this->idFactory ->expects(self::exactly(4)) @@ -246,54 +234,47 @@ public function testConvertWithSiretNotLinkedToAnOrganization(): void ->method('add') ->with($this->equalTo($user)); - $this->accessRequestRepository - ->expects(self::once()) - ->method('remove') - ->with($this->equalTo($this->accessRequest)); - - $handler = new ConvertAccessRequestToUserCommandHandler( - $this->idFactory, - $this->accessRequestRepository, - $this->userRepository, - $this->passwordUserRepository, - $this->organizationUserRepository, - $this->organizationRepository, - $this->dateUtils, - ); - $command = new ConvertAccessRequestToUserCommand('e8a18fab-58d9-4aaf-bb47-b7a8edc20c23'); - $handler($command); + $this->assertEquals(($this->handler)($this->command), $user); } - public function testAccessRequestNotFound(): void + public function testRegisterWithBadSiret(): void { - $this->expectException(AccessRequestNotFoundException::class); + $this->expectException(OrganizationNotFoundException::class); $this->userRepository - ->expects(self::never()) - ->method('findOneByEmail'); + ->expects(self::once()) + ->method('findOneByEmail') + ->with('mathieu@fairness.coop') + ->willReturn(null); $this->organizationRepository + ->expects(self::once()) + ->method('findOneBySiret') + ->with('82050375300015') + ->willReturn(null); + + $this->passwordHasher ->expects(self::never()) - ->method('findOneBySiret'); + ->method('hash'); - $this->organizationRepository + $this->organizationFetcher + ->expects(self::once()) + ->method('findBySiret') + ->with('82050375300015') + ->will($this->throwException(new OrganizationNotFoundException())); + + $this->organizationUserRepository ->expects(self::never()) ->method('add'); - $this->passwordUserRepository + $this->organizationRepository ->expects(self::never()) ->method('add'); - $this->organizationUserRepository + $this->passwordUserRepository ->expects(self::never()) ->method('add'); - $this->accessRequestRepository - ->expects(self::once()) - ->method('findOneByUuid') - ->with('e8a18fab-58d9-4aaf-bb47-b7a8edc20c23') - ->willReturn(null); - $this->idFactory ->expects(self::never()) ->method('make'); @@ -302,21 +283,7 @@ public function testAccessRequestNotFound(): void ->expects(self::never()) ->method('add'); - $this->accessRequestRepository - ->expects(self::never()) - ->method('remove'); - - $handler = new ConvertAccessRequestToUserCommandHandler( - $this->idFactory, - $this->accessRequestRepository, - $this->userRepository, - $this->passwordUserRepository, - $this->organizationUserRepository, - $this->organizationRepository, - $this->dateUtils, - ); - $command = new ConvertAccessRequestToUserCommand('e8a18fab-58d9-4aaf-bb47-b7a8edc20c23'); - $handler($command); + ($this->handler)($this->command); } public function testUserAlreadyRegistered(): void @@ -324,14 +291,6 @@ public function testUserAlreadyRegistered(): void $this->expectException(UserAlreadyRegisteredException::class); $user = $this->createMock(User::class); - $this->accessRequest - ->expects(self::once()) - ->method('getEmail') - ->willReturn('mathieu@fairness.coop'); - $this->accessRequest - ->expects(self::once()) - ->method('getSiret') - ->willReturn('aaaaaaaaaa'); $this->userRepository ->expects(self::once()) @@ -351,12 +310,6 @@ public function testUserAlreadyRegistered(): void ->expects(self::never()) ->method('add'); - $this->accessRequestRepository - ->expects(self::once()) - ->method('findOneByUuid') - ->with('e8a18fab-58d9-4aaf-bb47-b7a8edc20c23') - ->willReturn($this->accessRequest); - $this->idFactory ->expects(self::never()) ->method('make'); @@ -364,74 +317,7 @@ public function testUserAlreadyRegistered(): void $this->userRepository ->expects(self::never()) ->method('add'); - $this->accessRequestRepository - ->expects(self::never()) - ->method('remove'); - - $handler = new ConvertAccessRequestToUserCommandHandler( - $this->idFactory, - $this->accessRequestRepository, - $this->userRepository, - $this->passwordUserRepository, - $this->organizationUserRepository, - $this->organizationRepository, - $this->dateUtils, - ); - $command = new ConvertAccessRequestToUserCommand('e8a18fab-58d9-4aaf-bb47-b7a8edc20c23'); - $handler($command); - } - public function testSiretNotFound(): void - { - $this->expectException(SiretMissingException::class); - - $this->userRepository - ->expects(self::never()) - ->method('findOneByEmail'); - - $this->passwordUserRepository - ->expects(self::never()) - ->method('add'); - - $this->organizationRepository - ->expects(self::never()) - ->method('findOneBySiret'); - - $this->organizationRepository - ->expects(self::never()) - ->method('add'); - - $this->organizationUserRepository - ->expects(self::never()) - ->method('add'); - - $this->accessRequestRepository - ->expects(self::once()) - ->method('findOneByUuid') - ->with('e8a18fab-58d9-4aaf-bb47-b7a8edc20c23') - ->willReturn($this->accessRequest); - - $this->idFactory - ->expects(self::never()) - ->method('make'); - - $this->userRepository - ->expects(self::never()) - ->method('add'); - $this->accessRequestRepository - ->expects(self::never()) - ->method('remove'); - - $handler = new ConvertAccessRequestToUserCommandHandler( - $this->idFactory, - $this->accessRequestRepository, - $this->userRepository, - $this->passwordUserRepository, - $this->organizationUserRepository, - $this->organizationRepository, - $this->dateUtils, - ); - $command = new ConvertAccessRequestToUserCommand('e8a18fab-58d9-4aaf-bb47-b7a8edc20c23'); - $handler($command); + ($this->handler)($this->command); } } diff --git a/tests/Unit/Application/User/Command/SaveAccessRequestCommandHandlerTest.php b/tests/Unit/Application/User/Command/SaveAccessRequestCommandHandlerTest.php deleted file mode 100644 index eec5d680b..000000000 --- a/tests/Unit/Application/User/Command/SaveAccessRequestCommandHandlerTest.php +++ /dev/null @@ -1,140 +0,0 @@ -createMock(IdFactoryInterface::class); - $accessRequestRepository = $this->createMock(AccessRequestRepositoryInterface::class); - $isAccessAlreadyRequested = $this->createMock(IsAccessAlreadyRequested::class); - $passwordHasher = $this->createMock(PasswordHasherInterface::class); - $stringUtils = $this->createMock(StringUtilsInterface::class); - - $idFactory - ->expects(self::once()) - ->method('make') - ->willReturn('0de5692b-cab1-494c-804d-765dc14df674'); - - $isAccessAlreadyRequested - ->expects(self::once()) - ->method('isSatisfiedBy') - ->with('mathieu@fairness.coop') - ->willReturn(false); - - $passwordHasher - ->expects(self::once()) - ->method('hash') - ->with('password') - ->willReturn('passwordHashed'); - - $stringUtils - ->expects(self::once()) - ->method('normalizeEmail') - ->with(' mathiEu@fairness.cOop ') - ->willReturn('mathieu@fairness.coop'); - - $accessRequest = new AccessRequest( - uuid: '0de5692b-cab1-494c-804d-765dc14df674', - comment: 'Test comment', - consentToBeContacted: $consentContact, - fullName: 'Mathieu Marchois', - email: 'mathieu@fairness.coop', - siret: '82050375300015', - password: 'passwordHashed', - organization: 'Fairness', - ); - - $accessRequestRepository - ->expects(self::once()) - ->method('add') - ->with($this->equalTo($accessRequest)) - ; - - $handler = new SaveAccessRequestCommandHandler( - $idFactory, - $accessRequestRepository, - $isAccessAlreadyRequested, - $passwordHasher, - $stringUtils, - ); - $command = new SaveAccessRequestCommand(); - $command->comment = 'Test comment'; - $command->consentToBeContacted = $consentContact; - $command->fullName = 'Mathieu Marchois'; - $command->email = ' mathiEu@fairness.cOop '; // Will be normalized - $command->organizationName = 'Fairness'; - $command->organizationSiret = '82050375300015'; - $command->password = 'password'; - - $handler($command); - } - - public function testAlreadyRequested(): void - { - $this->expectException(AccessAlreadyRequestedException::class); - - $idFactory = $this->createMock(IdFactoryInterface::class); - $accessRequestRepository = $this->createMock(AccessRequestRepositoryInterface::class); - $isAccessAlreadyRequested = $this->createMock(IsAccessAlreadyRequested::class); - $passwordHasher = $this->createMock(PasswordHasherInterface::class); - $stringUtils = $this->createMock(StringUtilsInterface::class); - - $idFactory - ->expects(self::never()) - ->method('make'); - - $isAccessAlreadyRequested - ->expects(self::once()) - ->method('isSatisfiedBy') - ->with('mathieu@fairness.coop') - ->willReturn(true); - - $accessRequestRepository - ->expects(self::never()) - ->method('add'); - - $passwordHasher - ->expects(self::never()) - ->method('hash'); - - $stringUtils - ->expects(self::once()) - ->method('normalizeEmail') - ->with(' mathiEu@fairness.cOop ') - ->willReturn('mathieu@fairness.coop'); - - $handler = new SaveAccessRequestCommandHandler( - $idFactory, - $accessRequestRepository, - $isAccessAlreadyRequested, - $passwordHasher, - $stringUtils, - ); - $command = new SaveAccessRequestCommand(); - $command->comment = 'Test comment'; - $command->consentToBeContacted = true; - $command->fullName = 'Mathieu Marchois'; - $command->email = ' mathiEu@fairness.cOop '; - $command->organizationName = 'Fairness'; - $command->organizationSiret = '82050375300015'; - $command->password = 'password'; - - $handler($command); - } -} diff --git a/tests/Unit/Domain/User/AccessRequestTest.php b/tests/Unit/Domain/User/AccessRequestTest.php deleted file mode 100644 index 587290467..000000000 --- a/tests/Unit/Domain/User/AccessRequestTest.php +++ /dev/null @@ -1,58 +0,0 @@ -assertSame('9cebe00d-04d8-48da-89b1-059f6b7bfe44', $accessRequest->getUuid()); - $this->assertSame('Je souhaite créer un compte', $accessRequest->getComment()); - $this->assertSame('mathieu@fairness.coop', $accessRequest->getEmail()); - $this->assertSame('Mathieu Marchois', $accessRequest->getFullName()); - $this->assertSame('Fairness', $accessRequest->getOrganization()); - $this->assertSame('82050375300015', $accessRequest->getSiret()); - $this->assertSame('password', $accessRequest->getPassword()); - $this->assertTrue($accessRequest->isConsentToBeContacted()); - } - - public function testSetters(): void - { - $accessRequest = new AccessRequest( - '9cebe00d-04d8-48da-89b1-059f6b7bfe44', - 'Mathieu Marchois', - 'mathieu@fairness.coop', - 'Fairness', - 'password', - true, - '82050375300015', - 'Je souhaite créer un compte', - ); - - $accessRequest->setFullName('Marchois Mathieu'); - $accessRequest->setEmail('mathieu.marchois@fairness.coop'); - $accessRequest->setOrganization('Fairness scop'); - $accessRequest->setSiret('82050375300014'); - - $this->assertSame('mathieu.marchois@fairness.coop', $accessRequest->getEmail()); - $this->assertSame('Marchois Mathieu', $accessRequest->getFullName()); - $this->assertSame('Fairness scop', $accessRequest->getOrganization()); - $this->assertSame('82050375300014', $accessRequest->getSiret()); - } -} diff --git a/tests/Unit/Domain/User/Specification/IsAccessAlreadyRequestedTest.php b/tests/Unit/Domain/User/Specification/IsAccessAlreadyRequestedTest.php deleted file mode 100644 index e7eca119e..000000000 --- a/tests/Unit/Domain/User/Specification/IsAccessAlreadyRequestedTest.php +++ /dev/null @@ -1,40 +0,0 @@ -createMock(AccessRequest::class); - $accessRequestRepository = $this->createMock(AccessRequestRepositoryInterface::class); - $accessRequestRepository - ->expects(self::once()) - ->method('findOneByEmail') - ->with('mathieu.marchois@beta.gouv.fr') - ->willReturn($accessRequest); - - $pattern = new IsAccessAlreadyRequested($accessRequestRepository); - $this->assertTrue($pattern->isSatisfiedBy('mathieu.marchois@beta.gouv.fr')); - } - - public function testAccessNotRequested(): void - { - $accessRequestRepository = $this->createMock(AccessRequestRepositoryInterface::class); - $accessRequestRepository - ->expects(self::once()) - ->method('findOneByEmail') - ->with('mathieu.marchois@beta.gouv.fr') - ->willReturn(null); - - $pattern = new IsAccessAlreadyRequested($accessRequestRepository); - $this->assertFalse($pattern->isSatisfiedBy('mathieu.marchois@beta.gouv.fr')); - } -} diff --git a/tests/Unit/Domain/User/UserTest.php b/tests/Unit/Domain/User/UserTest.php index fee4daa7a..2bbbd7a7f 100644 --- a/tests/Unit/Domain/User/UserTest.php +++ b/tests/Unit/Domain/User/UserTest.php @@ -5,6 +5,7 @@ namespace App\Tests\Unit\Domain\User; use App\Domain\User\Enum\UserRolesEnum; +use App\Domain\User\PasswordUser; use App\Domain\User\User; use PHPUnit\Framework\TestCase; @@ -12,6 +13,7 @@ final class UserTest extends TestCase { public function testGetters(): void { + $passwordUser = $this->createMock(PasswordUser::class); $date = new \DateTime('2024-05-07'); $user = (new User('9cebe00d-04d8-48da-89b1-059f6b7bfe44')) @@ -19,6 +21,7 @@ public function testGetters(): void ->setEmail('mathieu@fairness.coop') ->setRoles([UserRolesEnum::ROLE_SUPER_ADMIN->value]); + $user->setPasswordUser($passwordUser); $user->setRegistrationDate($date); $this->assertSame('9cebe00d-04d8-48da-89b1-059f6b7bfe44', $user->getUuid()); @@ -27,7 +30,7 @@ public function testGetters(): void $this->assertSame([UserRolesEnum::ROLE_SUPER_ADMIN->value], $user->getRoles()); $this->assertSame($date, $user->getRegistrationDate()); $this->assertNull($user->getProConnectUser()); // Manage by Doctrine - $this->assertNull($user->getPasswordUser()); // Manage by Doctrine + $this->assertEquals($passwordUser, $user->getPasswordUser()); $this->assertSame('Mathieu Marchois (mathieu@fairness.coop)', (string) $user); } } diff --git a/translations/messages.fr.xlf b/translations/messages.fr.xlf index d5bbc0edf..d60696d28 100644 --- a/translations/messages.fr.xlf +++ b/translations/messages.fr.xlf @@ -10,8 +10,8 @@ common.results Résultats - - common.accessRequest + + common.register Créer un compte @@ -124,6 +124,10 @@ common.send Envoyer + + common.submit + Valider + common.cancel Annuler @@ -1632,9 +1636,9 @@ login.no_account.title Vous n'avez pas de compte ? - - login.no_account.access_request - Demander la création d'un compte + + login.no_account.register + Créer un compte login.legend @@ -2127,28 +2131,28 @@ common.form.time.minute Minutes - - accessRequest.displayPassword + + register.displayPassword Afficher - - accessRequest.fullName + + register.fullName Nom et prénom - - accessRequest.email + + register.email Adresse e-mail - - accessRequest.email.help + + register.email.help Format attendu : nom@domaine.fr - - accessRequest.organizationName + + register.organizationName Nom de l'organisation - - accessRequest.comment + + register.comment Message @@ -2187,49 +2191,53 @@ organization.siret SIRET - - accessRequest.organizationSiret + + register.organizationSiret Numéro SIRET de l'organisation - - accessRequest.organizationSiret.help + + register.organizationSiret.help Format attendu : 14 chiffres - - accessRequest.password + + register.password Mot de passe - - accessRequest.password.help + + register.password.confirm + Confirmer votre mot de passe + + + register.password.help Format attendu : 12 caractères minimum - - accessRequest.consentToBeContacted - J’accepte que l’équipe DiaLog me recontacte afin d’améliorer la plateforme - - - accessRequest.send.success - Votre demande de création de compte a bien été prise en compte. Nous - reviendrons vers vous dans les plus brefs délais. - - - accessRequest.send.success.help - La validation du compte n’est pas faite automatiquement, cela peut prendre - quelques heures. - - - accessRequest.send.error - Une demande de création de compte a déjà été créée avec cette adresse - e-mail. - - - accessRequest.meta.title - Demande de création de compte - - - accessRequest.meta.subtitle - Vous pouvez demander la création d’un compte pour votre commune ou - collectivité. + + register.cgu.end + conditions générales d'utilisation + + + register.cgu.start + J’accepte les + + + register.error.already_exists + Un compte utilisant cette adresse e-mail existe déjà. + + + register.error.organizationSiret_not_found + Le SIRET fourni n'existe pas. Veuillez vérifier si votre SIRET est correct. + + + register.find_siret + Retrouver votre SIRET sur L’Annuaire des Entreprises + + + register.meta.title + Créer mon compte + + + register.meta.subtitle + Vous pouvez créer votre compte pour votre commune ou collectivité. api.cifs.incident.description