Skip to content

Commit

Permalink
[framework] administrator email now must be unique (#3686)
Browse files Browse the repository at this point in the history
  • Loading branch information
vitek-rostislav authored Dec 23, 2024
2 parents eece100 + 6594e3b commit 659c16a
Show file tree
Hide file tree
Showing 13 changed files with 159 additions and 96 deletions.
77 changes: 31 additions & 46 deletions src/Controller/Admin/AdministratorController.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
use Shopsys\FrameworkBundle\Model\Administrator\Exception\AdministratorNotFoundException;
use Shopsys\FrameworkBundle\Model\Administrator\Exception\DeletingLastAdministratorException;
use Shopsys\FrameworkBundle\Model\Administrator\Exception\DeletingSelfException;
use Shopsys\FrameworkBundle\Model\Administrator\Exception\DuplicateUserNameException;
use Shopsys\FrameworkBundle\Model\Administrator\Security\AdministratorRolesChangedFacade;
use Shopsys\FrameworkBundle\Model\AdminNavigation\BreadcrumbOverrider;
use Shopsys\FrameworkBundle\Model\Security\Authenticator;
Expand Down Expand Up @@ -121,30 +120,23 @@ public function editAction(Request $request, int $id)
$form->handleRequest($request);

if ($form->isSubmitted() && $form->isValid()) {
try {
$this->administratorFacade->edit($id, $administratorData);

if ($loggedUser->getId() === $id) {
$this->administratorRolesChangedFacade->refreshAdministratorToken($administrator);
}

$this->addSuccessFlashTwig(
t('Administrator <strong><a href="{{ url }}">{{ name }}</a></strong> modified'),
[
'name' => $administratorData->realName,
'url' => $this->generateUrl('admin_administrator_edit', ['id' => $administrator->getId()]),
],
);

return $this->redirectToRoute('admin_administrator_list');
} catch (DuplicateUserNameException $ex) {
$this->addErrorFlashTwig(
t('Login name <strong>{{ name }}</strong> is already used'),
[
'name' => $administratorData->username,
],
);
$this->administratorFacade->edit($id, $administratorData);

if ($loggedUser->getId() === $id) {
$this->administratorRolesChangedFacade->refreshAdministratorToken($administrator);
}

$this->addSuccessFlashTwig(
t('Administrator <strong><a href="{{ url }}">{{ name }}</a></strong> modified'),
[
'name' => $administratorData->realName,
'url' => $this->generateUrl('admin_administrator_edit', ['id' => $administrator->getId()]),
],
);

$redirectRouteName = $this->isGranted(Roles::ROLE_ADMINISTRATOR_VIEW) ? 'admin_administrator_list' : 'admin_default_dashboard';

return $this->redirectToRoute($redirectRouteName);
}

if ($form->isSubmitted() && !$form->isValid()) {
Expand Down Expand Up @@ -193,27 +185,18 @@ public function newAction(Request $request)
if ($form->isSubmitted() && $form->isValid()) {
$administratorData = $form->getData();

try {
$administrator = $this->administratorFacade->create($administratorData);
$this->administratorPasswordFacade->resetPassword($administrator->getUsername());

$this->addSuccessFlashTwig(
t('Administrator <strong><a href="{{ url }}">{{ name }}</a></strong> created. A link to set a password has been sent to his email.'),
[
'name' => $administrator->getRealName(),
'url' => $this->generateUrl('admin_administrator_edit', ['id' => $administrator->getId()]),
],
);

return $this->redirectToRoute('admin_administrator_list');
} catch (DuplicateUserNameException $ex) {
$this->addErrorFlashTwig(
t('Login name <strong>{{ name }}</strong> is already used'),
[
'name' => $administratorData->username,
],
);
}
$administrator = $this->administratorFacade->create($administratorData);
$this->administratorPasswordFacade->resetPassword($administrator->getUsername());

$this->addSuccessFlashTwig(
t('Administrator <strong><a href="{{ url }}">{{ name }}</a></strong> created. A link to set a password has been sent to his email.'),
[
'name' => $administrator->getRealName(),
'url' => $this->generateUrl('admin_administrator_edit', ['id' => $administrator->getId()]),
],
);

return $this->redirectToRoute('admin_administrator_list');
}

if ($form->isSubmitted() && !$form->isValid()) {
Expand Down Expand Up @@ -551,7 +534,9 @@ public function setNewPasswordAction(Request $request): Response
$this->authenticator->loginAdministrator($administrator);
}

return $this->redirectToRoute('admin_administrator_list');
$this->addSuccessFlash(t('Password has been successfully set.'));

return $this->redirectToRoute('admin_default_dashboard');
}

if ($form->isSubmitted() && !$form->isValid()) {
Expand Down
13 changes: 13 additions & 0 deletions src/Form/Admin/Administrator/AdministratorFormType.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use Shopsys\FrameworkBundle\Component\Router\Security\RouteCsrfProtector;
use Shopsys\FrameworkBundle\Form\Constraints\Email;
use Shopsys\FrameworkBundle\Form\Constraints\UniqueEntityField;
use Shopsys\FrameworkBundle\Form\DisplayOnlyType;
use Shopsys\FrameworkBundle\Form\DisplayOnlyUrlType;
use Shopsys\FrameworkBundle\Form\GroupType;
Expand Down Expand Up @@ -68,6 +69,12 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
new Constraints\Length(
['max' => 100, 'maxMessage' => 'Username cannot be longer than {{ limit }} characters'],
),
new UniqueEntityField([
'entityInstance' => $options['administrator'],
'message' => 'Administrator with user name "{{ value }}" is already registered',
'fieldName' => 'username',
'entityName' => Administrator::class,
]),
],
'label' => t('Login name'),
])
Expand All @@ -88,6 +95,12 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
new Constraints\Length(
['max' => 255, 'maxMessage' => 'Email cannot be longer than {{ limit }} characters'],
),
new UniqueEntityField([
'entityInstance' => $options['administrator'],
'message' => 'Administrator with email "{{ value }}" is already registered',
'fieldName' => 'email',
'entityName' => Administrator::class,
]),
],
'label' => t('Email'),
]);
Expand Down
18 changes: 18 additions & 0 deletions src/Form/Constraints/UniqueEntityField.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace Shopsys\FrameworkBundle\Form\Constraints;

use Symfony\Component\Validator\Constraint;

class UniqueEntityField extends Constraint
{
public string $message = 'The "{{ value }}" value of "{{ fieldName }}" field must be unique';

public string $fieldName;

public string $entityName;

public ?object $entityInstance = null;
}
45 changes: 45 additions & 0 deletions src/Form/Constraints/UniqueEntityFieldValidator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

declare(strict_types=1);

namespace Shopsys\FrameworkBundle\Form\Constraints;

use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;

class UniqueEntityFieldValidator extends ConstraintValidator
{
/**
* @param \Doctrine\ORM\EntityManagerInterface $em
*/
public function __construct(
protected readonly EntityManagerInterface $em,
) {
}

/**
* @param mixed $value
* @param \Symfony\Component\Validator\Constraint $constraint
*/
public function validate(mixed $value, Constraint $constraint): void
{
if (!$constraint instanceof UniqueEntityField) {
throw new UnexpectedTypeException($constraint, UniqueEntityField::class);
}

if ($value === null || $value === '') {
return;
}

$entity = $this->em->getRepository($constraint->entityName)->findOneBy([$constraint->fieldName => $value]);

if ($entity !== null && $entity !== $constraint->entityInstance) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $value)
->setParameter('{{ fieldName }}', $constraint->fieldName)
->addViolation();
}
}
}
2 changes: 1 addition & 1 deletion src/Migrations/Version20180702111015.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public function up(Schema $schema): void

$this->sql(
'INSERT INTO administrators (id, username, real_name, password, login_token, email, superadmin) VALUES '
. '(1, \'superadmin\', \'superadmin\', \'$2y$12$ppwYj/By0pDkiLlE.ssf6uuwCvtfdDfsJJNr84fU59HmxSfj0luSC\', \'\', \'[email protected]\', true),'
. '(1, \'superadmin\', \'superadmin\', \'$2y$12$ppwYj/By0pDkiLlE.ssf6uuwCvtfdDfsJJNr84fU59HmxSfj0luSC\', \'\', \'no-reply.1@shopsys.com\', true),'
. '(2, \'admin\', \'admin\', \'$2y$12$tRU86hi0UxWEMQzP08nl..hKiClF.Pj3D1oIcKDL.aA7ph2Vomwh2\', \'\', \'[email protected]\', false)',
);

Expand Down
26 changes: 26 additions & 0 deletions src/Migrations/Version20241220094923.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace Shopsys\FrameworkBundle\Migrations;

use Doctrine\DBAL\Schema\Schema;
use Shopsys\MigrationBundle\Component\Doctrine\Migrations\AbstractMigration;

class Version20241220094923 extends AbstractMigration
{
/**
* @param \Doctrine\DBAL\Schema\Schema $schema
*/
public function up(Schema $schema): void
{
$this->sql('CREATE UNIQUE INDEX UNIQ_73A716FE7927C74 ON administrators (email)');
}

/**
* @param \Doctrine\DBAL\Schema\Schema $schema
*/
public function down(Schema $schema): void
{
}
}
2 changes: 1 addition & 1 deletion src/Model/Administrator/Administrator.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ class Administrator implements UserInterface, UniqueLoginInterface, TimelimitLog

/**
* @var string
* @ORM\Column(type="string", length=255)
* @ORM\Column(type="string", length=255, unique=true)
*/
protected $email;

Expand Down
23 changes: 0 additions & 23 deletions src/Model/Administrator/AdministratorFacade.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
use Shopsys\FrameworkBundle\Model\Administrator\Exception\DeletingLastAdministratorException;
use Shopsys\FrameworkBundle\Model\Administrator\Exception\DeletingSelfException;
use Shopsys\FrameworkBundle\Model\Administrator\Exception\DeletingSuperadminException;
use Shopsys\FrameworkBundle\Model\Administrator\Exception\DuplicateUserNameException;
use Shopsys\FrameworkBundle\Model\Administrator\Role\AdministratorRoleFacade;
use Shopsys\FrameworkBundle\Model\Administrator\Security\Exception\AdministratorIsNotLoggedException;
use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactoryInterface;
Expand Down Expand Up @@ -42,11 +41,6 @@ public function __construct(
*/
public function create(AdministratorData $administratorData): Administrator
{
$administratorByUserName = $this->administratorRepository->findByUserName($administratorData->username);

if ($administratorByUserName !== null) {
throw new DuplicateUserNameException($administratorByUserName->getUsername());
}
$administrator = $this->administratorFactory->create($administratorData);

$this->em->persist($administrator);
Expand All @@ -65,7 +59,6 @@ public function create(AdministratorData $administratorData): Administrator
public function edit($administratorId, AdministratorData $administratorData): Administrator
{
$administrator = $this->administratorRepository->getById($administratorId);
$this->checkUsername($administrator, $administratorData->username);
$administrator->edit($administratorData);

$this->em->flush();
Expand All @@ -75,22 +68,6 @@ public function edit($administratorId, AdministratorData $administratorData): Ad
return $administrator;
}

/**
* @param \Shopsys\FrameworkBundle\Model\Administrator\Administrator $administrator
* @param string $username
*/
protected function checkUsername(Administrator $administrator, string $username): void
{
$administratorByUserName = $this->administratorRepository->findByUserName($username);

if ($administratorByUserName !== null
&& $administratorByUserName !== $administrator
&& $administratorByUserName->getUsername() === $username
) {
throw new DuplicateUserNameException($administrator->getUsername());
}
}

/**
* @param int $administratorId
*/
Expand Down
19 changes: 0 additions & 19 deletions src/Model/Administrator/Exception/DuplicateUserNameException.php

This file was deleted.

6 changes: 3 additions & 3 deletions src/Resources/translations/messages.cs.po
Original file line number Diff line number Diff line change
Expand Up @@ -2356,9 +2356,6 @@ msgstr "Přihlášení se nepodařilo."
msgid "Login name"
msgstr "Přihlašovací jméno"

msgid "Login name <strong>{{ name }}</strong> is already used"
msgstr "Přihlašovací jméno <strong>{{ name }}</strong> je již použito"

msgid "Login time"
msgstr "Čas přihlášení"

Expand Down Expand Up @@ -3013,6 +3010,9 @@ msgstr "Heslo"
msgid "Password again"
msgstr "Heslo znovu"

msgid "Password has been successfully set."
msgstr "Heslo bylo úspěšně nastaveno."

msgid "Password has to include uppercase letters, lowercase letters, numbers and must be longer than 10 characters."
msgstr "Heslo musí obsahovat velká písmena, malá písmena, číslice a musí být delší než 10 znaků."

Expand Down
6 changes: 3 additions & 3 deletions src/Resources/translations/messages.en.po
Original file line number Diff line number Diff line change
Expand Up @@ -2356,9 +2356,6 @@ msgstr ""
msgid "Login name"
msgstr ""

msgid "Login name <strong>{{ name }}</strong> is already used"
msgstr ""

msgid "Login time"
msgstr ""

Expand Down Expand Up @@ -3013,6 +3010,9 @@ msgstr ""
msgid "Password again"
msgstr ""

msgid "Password has been successfully set."
msgstr ""

msgid "Password has to include uppercase letters, lowercase letters, numbers and must be longer than 10 characters."
msgstr ""

Expand Down
9 changes: 9 additions & 0 deletions src/Resources/translations/validators.cs.po
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ msgstr ""
msgid "Address {{ url }} already exists."
msgstr "Adresa {{ url }} již existuje."

msgid "Administrator with email \"{{ value }}\" is already registered"
msgstr "Aministrátor s e-mailem \"{{ value }}\" je již registrován"

msgid "Administrator with user name \"{{ value }}\" is already registered"
msgstr "Aministrátor s uživatelským jménem \"{{ value }}\" je již registrován"

msgid "Amount of money should be greater than or equal to zero."
msgstr "Tato částka musí být větší nebo rovna nule."

Expand Down Expand Up @@ -547,6 +553,9 @@ msgstr "DIČ nesmí být delší než {{ limit }} znaků"
msgid "Telephone number cannot be longer than {{ limit }} characters"
msgstr "Telefon nesmí být delší než {{ limit }} znaků"

msgid "The \"{{ value }}\" value of \"{{ fieldName }}\" field must be unique"
msgstr "Hodnota \"{{ value }}\" pole \"{{ fieldName }}\" musí být unikátní"

msgid "The amount of money should be {{ limit }} or less."
msgstr "Tato částka musí být {{ limit }} nebo méně."

Expand Down
Loading

0 comments on commit 659c16a

Please sign in to comment.