From 8eb5f35bee48a81d19dec8b6d6268e096d0bd011 Mon Sep 17 00:00:00 2001 From: Kevin Papst <kpapst@gmx.net> Date: Wed, 25 Aug 2021 13:53:30 +0200 Subject: [PATCH] prevent email or username becoming non-unique --- src/Repository/UserRepository.php | 5 +++ src/User/UserService.php | 2 +- src/Validator/Constraints/User.php | 4 ++ src/Validator/Constraints/UserValidator.php | 47 +++++++++++++-------- translations/validators.de.xlf | 8 ++++ translations/validators.en.xlf | 8 ++++ 6 files changed, 56 insertions(+), 18 deletions(-) diff --git a/src/Repository/UserRepository.php b/src/Repository/UserRepository.php index 9d6670100..c34aa52bd 100644 --- a/src/Repository/UserRepository.php +++ b/src/Repository/UserRepository.php @@ -82,6 +82,11 @@ class UserRepository extends EntityRepository implements UserLoaderInterface return parent::findOneBy($criteria, $orderBy); } + public function findByUsername($username): ?User + { + return parent::findOneBy(['username' => $username]); + } + public function countUser(?bool $enabled = null): int { if (null !== $enabled) { diff --git a/src/User/UserService.php b/src/User/UserService.php index a979032a7..0237eb354 100644 --- a/src/User/UserService.php +++ b/src/User/UserService.php @@ -127,7 +127,7 @@ class UserService public function findUserByName(string $name): ?User { - return $this->repository->findOneBy(['username' => $name]); + return $this->repository->findByUsername($name); } public function findUserByConfirmationToken(string $token): ?User diff --git a/src/Validator/Constraints/User.php b/src/Validator/Constraints/User.php index 77cd5fe8c..142bb9080 100644 --- a/src/Validator/Constraints/User.php +++ b/src/Validator/Constraints/User.php @@ -20,10 +20,14 @@ class User extends Constraint { public const USER_EXISTING_EMAIL = 'kimai-user-00'; public const USER_EXISTING_NAME = 'kimai-user-01'; + public const USER_EXISTING_EMAIL_AS_NAME = 'kimai-user-02'; + public const USER_EXISTING_NAME_AS_EMAIL = 'kimai-user-03'; protected static $errorNames = [ self::USER_EXISTING_EMAIL => 'The email is already used.', self::USER_EXISTING_NAME => 'The username is already used.', + self::USER_EXISTING_EMAIL_AS_NAME => 'An equal username is already used.', + self::USER_EXISTING_NAME_AS_EMAIL => 'An equal email is already used.', ]; public $message = 'The user has invalid settings.'; diff --git a/src/Validator/Constraints/UserValidator.php b/src/Validator/Constraints/UserValidator.php index fa764b9af..f86b57ff8 100644 --- a/src/Validator/Constraints/UserValidator.php +++ b/src/Validator/Constraints/UserValidator.php @@ -44,28 +44,41 @@ class UserValidator extends ConstraintValidator protected function validateUser(UserEntity $user, ExecutionContextInterface $context) { + $matchedEmail = false; if ($user->getEmail() !== null) { - $existingByEmail = $this->userService->findUserByEmail($user->getEmail()); - - if (null !== $existingByEmail && $user->getId() !== $existingByEmail->getId()) { - $context->buildViolation(User::getErrorName(User::USER_EXISTING_EMAIL)) - ->atPath('email') - ->setTranslationDomain('validators') - ->setCode(User::USER_EXISTING_EMAIL) - ->addViolation(); - } + $this->validateEmailExists($user->getId(), $user->getEmail(), 'email', User::USER_EXISTING_EMAIL, $context); + $this->validateEmailExists($user->getId(), $user->getUsername(), 'username', User::USER_EXISTING_NAME_AS_EMAIL, $context); } if ($user->getUsername() !== null) { - $existingByName = $this->userService->findUserByName($user->getUsername()); + $this->validateUsernameExists($user->getId(), $user->getUsername(), 'username', User::USER_EXISTING_NAME, $context); + $this->validateUsernameExists($user->getId(), $user->getEmail(), 'email', User::USER_EXISTING_EMAIL_AS_NAME, $context); + } + } - if (null !== $existingByName && $user->getId() !== $existingByName->getId()) { - $context->buildViolation(User::getErrorName(User::USER_EXISTING_NAME)) - ->atPath('username') - ->setTranslationDomain('validators') - ->setCode(User::USER_EXISTING_NAME) - ->addViolation(); - } + private function validateEmailExists(?int $userId, string $email, string $path, string $code, ExecutionContextInterface $context): void + { + $existingByEmail = $this->userService->findUserByEmail($email); + + if (null !== $existingByEmail && $userId !== $existingByEmail->getId()) { + $context->buildViolation(User::getErrorName($code)) + ->atPath($path) + ->setTranslationDomain('validators') + ->setCode($code) + ->addViolation(); + } + } + + private function validateUsernameExists(?int $userId, string $username, string $path, string $code, ExecutionContextInterface $context): void + { + $existingByName = $this->userService->findUserByName($username); + + if (null !== $existingByName && $userId !== $existingByName->getId()) { + $context->buildViolation(User::getErrorName($code)) + ->atPath($path) + ->setTranslationDomain('validators') + ->setCode($code) + ->addViolation(); } } } diff --git a/translations/validators.de.xlf b/translations/validators.de.xlf index d32b86473..b13dbeaa1 100644 --- a/translations/validators.de.xlf +++ b/translations/validators.de.xlf @@ -18,6 +18,14 @@ <source>The username is already used.</source> <target>Dieser Benutzername wird bereits verwendet.</target> </trans-unit> + <trans-unit id="An equal username is already used."> + <source>An equal username is already used.</source> + <target>Eine gleichlautender Benutzername wird bereits verwendet.</target> + </trans-unit> + <trans-unit id="An equal email is already used."> + <source>An equal email is already used.</source> + <target>Eine gleichlautende E-Mail-Adresse wird bereits verwendet.</target> + </trans-unit> <trans-unit id="This value is not a valid role."> <source>This value is not a valid role.</source> <target>Dieser Wert ist keine gültige Rolle.</target> diff --git a/translations/validators.en.xlf b/translations/validators.en.xlf index bd79b2633..60b30965d 100644 --- a/translations/validators.en.xlf +++ b/translations/validators.en.xlf @@ -18,6 +18,14 @@ <source>The username is already used.</source> <target>The username is already used.</target> </trans-unit> + <trans-unit id="An equal username is already used."> + <source>An equal username is already used.</source> + <target>An equal username is already used.</target> + </trans-unit> + <trans-unit id="An equal email is already used."> + <source>An equal email is already used.</source> + <target>An equal email is already used.</target> + </trans-unit> <trans-unit id="This value is not a valid role."> <source>This value is not a valid role.</source> <target>This value is not a valid role.</target>