From 523fb38678a131089f5708fdcd2bde300068cac2 Mon Sep 17 00:00:00 2001 From: skjnldsv Date: Wed, 9 Jul 2025 19:43:02 +0200 Subject: [PATCH] fix: force lowercase emails Signed-off-by: skjnldsv --- .../lib/Controller/AUserDataOCSController.php | 3 +- .../lib/Controller/UsersController.php | 8 ++- .../tests/Controller/UsersControllerTest.php | 49 ++++++++++++++++++- core/Command/User/Setting.php | 3 +- lib/private/User/User.php | 8 ++- 5 files changed, 63 insertions(+), 8 deletions(-) diff --git a/apps/provisioning_api/lib/Controller/AUserDataOCSController.php b/apps/provisioning_api/lib/Controller/AUserDataOCSController.php index 4db3528f8d3c7..7bff2fbadb05e 100644 --- a/apps/provisioning_api/lib/Controller/AUserDataOCSController.php +++ b/apps/provisioning_api/lib/Controller/AUserDataOCSController.php @@ -141,7 +141,8 @@ protected function getUserData(string $userId, bool $includeScopes = false): ?ar $additionalEmails = $additionalEmailScopes = []; $emailCollection = $userAccount->getPropertyCollection(IAccountManager::COLLECTION_EMAIL); foreach ($emailCollection->getProperties() as $property) { - $additionalEmails[] = $property->getValue(); + $email = mb_strtolower(trim($property->getValue())); + $additionalEmails[] = $email; if ($includeScopes) { $additionalEmailScopes[] = $property->getScope(); } diff --git a/apps/provisioning_api/lib/Controller/UsersController.php b/apps/provisioning_api/lib/Controller/UsersController.php index 3dfa8f1fe5a31..f5d6c0221e062 100644 --- a/apps/provisioning_api/lib/Controller/UsersController.php +++ b/apps/provisioning_api/lib/Controller/UsersController.php @@ -537,6 +537,7 @@ public function addUser( $generatePasswordResetToken = true; } + $email = mb_strtolower(trim($email)); if ($email === '' && $this->config->getAppValue('core', 'newUser.requireEmail', 'no') === 'yes') { throw new OCSException($this->l10n->t('Required email address was not provided'), 110); } @@ -583,7 +584,7 @@ public function addUser( // Send new user mail only if a mail is set if ($email !== '') { - $newUser->setEMailAddress($email); + $newUser->setSystemEMailAddress($email); if ($this->config->getAppValue('core', 'newUser.sendEmail', 'yes') === 'yes') { try { $emailTemplate = $this->newUserMailHelper->generateTemplate($newUser, $generatePasswordResetToken); @@ -857,6 +858,7 @@ public function editUserMultiValue( $mailCollection = $userAccount->getPropertyCollection(IAccountManager::COLLECTION_EMAIL); $mailCollection->removePropertyByValue($key); if ($value !== '') { + $value = mb_strtolower(trim($value)); $mailCollection->addPropertyWithDefaults($value); $property = $mailCollection->getPropertyByValue($key); if ($isAdminOrSubadmin && $property) { @@ -1142,13 +1144,15 @@ public function editUser(string $userId, string $key, string $value): DataRespon } break; case IAccountManager::PROPERTY_EMAIL: + $value = mb_strtolower(trim($value)); if (filter_var($value, FILTER_VALIDATE_EMAIL) || $value === '') { - $targetUser->setEMailAddress($value); + $targetUser->setSystemEMailAddress($value); } else { throw new OCSException('', 101); } break; case IAccountManager::COLLECTION_EMAIL: + $value = mb_strtolower(trim($value)); if (filter_var($value, FILTER_VALIDATE_EMAIL) && $value !== $targetUser->getSystemEMailAddress()) { $userAccount = $this->accountManager->getAccount($targetUser); $mailCollection = $userAccount->getPropertyCollection(IAccountManager::COLLECTION_EMAIL); diff --git a/apps/provisioning_api/tests/Controller/UsersControllerTest.php b/apps/provisioning_api/tests/Controller/UsersControllerTest.php index b2addcb460212..9bf98dbd0c3b4 100644 --- a/apps/provisioning_api/tests/Controller/UsersControllerTest.php +++ b/apps/provisioning_api/tests/Controller/UsersControllerTest.php @@ -632,7 +632,7 @@ public function testAddUserSuccessfulGeneratePassword(): void { ->willReturn(false); $newUser = $this->createMock(IUser::class); $newUser->expects($this->once()) - ->method('setEMailAddress'); + ->method('setSystemEMailAddress'); $this->userManager ->expects($this->once()) ->method('createUser') @@ -668,6 +668,51 @@ public function testAddUserSuccessfulGeneratePassword(): void { )); } + public function testAddUserSuccessfulLowercaseEmail(): void { + $this->userManager + ->expects($this->once()) + ->method('userExists') + ->with('NewUser') + ->willReturn(false); + $newUser = $this->createMock(IUser::class); + $newUser->expects($this->once()) + ->method('setSystemEMailAddress') + ->with('foo@bar.com'); + $this->userManager + ->expects($this->once()) + ->method('createUser') + ->willReturn($newUser); + $this->logger + ->expects($this->once()) + ->method('info') + ->with('Successful addUser call with userid: NewUser', ['app' => 'ocs_api']); + $loggedInUser = $this->getMockBuilder(IUser::class) + ->disableOriginalConstructor() + ->getMock(); + $loggedInUser + ->expects($this->exactly(2)) + ->method('getUID') + ->willReturn('adminUser'); + $this->userSession + ->expects($this->once()) + ->method('getUser') + ->willReturn($loggedInUser); + $this->groupManager + ->expects($this->once()) + ->method('isAdmin') + ->with('adminUser') + ->willReturn(true); + $this->eventDispatcher + ->expects($this->once()) + ->method('dispatchTyped') + ->with(new GenerateSecurePasswordEvent()); + + $this->assertTrue(key_exists( + 'id', + $this->api->addUser('NewUser', '', '', 'fOo@BaR.CoM')->getData() + )); + } + public function testAddUserFailedToGenerateUserID(): void { $this->expectException(OCSException::class); @@ -1662,7 +1707,7 @@ public function testEditUserRegularUserSelfEditChangeEmailValid(): void { ->willReturn($targetUser); $targetUser ->expects($this->once()) - ->method('setEMailAddress') + ->method('setSystemEMailAddress') ->with('demo@nextcloud.com'); $targetUser ->expects($this->any()) diff --git a/core/Command/User/Setting.php b/core/Command/User/Setting.php index 16e851d82523f..7fc5aab1dc71e 100644 --- a/core/Command/User/Setting.php +++ b/core/Command/User/Setting.php @@ -155,7 +155,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int $user = $this->userManager->get($uid); if ($user instanceof IUser) { if ($key === 'email') { - $user->setEMailAddress($input->getArgument('value')); + $email = $input->getArgument('value'); + $user->setSystemEMailAddress(mb_strtolower(trim($email))); } elseif ($key === 'display_name') { if (!$user->setDisplayName($input->getArgument('value'))) { if ($user->getDisplayName() === $input->getArgument('value')) { diff --git a/lib/private/User/User.php b/lib/private/User/User.php index f04977314e2e1..95d562d337918 100644 --- a/lib/private/User/User.php +++ b/lib/private/User/User.php @@ -155,6 +155,7 @@ public function setEMailAddress($mailAddress) { */ public function setSystemEMailAddress(string $mailAddress): void { $oldMailAddress = $this->getSystemEMailAddress(); + $mailAddress = mb_strtolower(trim($mailAddress)); if ($mailAddress === '') { $this->config->deleteUserValue($this->uid, 'settings', 'email'); @@ -177,6 +178,7 @@ public function setSystemEMailAddress(string $mailAddress): void { * @inheritDoc */ public function setPrimaryEMailAddress(string $mailAddress): void { + $mailAddress = mb_strtolower(trim($mailAddress)); if ($mailAddress === '') { $this->config->deleteUserValue($this->uid, 'settings', 'primary_email'); return; @@ -515,14 +517,16 @@ public function getEMailAddress() { * @inheritDoc */ public function getSystemEMailAddress(): ?string { - return $this->config->getUserValue($this->uid, 'settings', 'email', null); + $email = $this->config->getUserValue($this->uid, 'settings', 'email', null); + return $email ? mb_strtolower(trim($email)) : null; } /** * @inheritDoc */ public function getPrimaryEMailAddress(): ?string { - return $this->config->getUserValue($this->uid, 'settings', 'primary_email', null); + $email = $this->config->getUserValue($this->uid, 'settings', 'primary_email', null); + return $email ? mb_strtolower(trim($email)) : null; } /**