From a402a84544533fbc2169a92e31937eb2cbe9dc3b Mon Sep 17 00:00:00 2001 From: Richard Steinmetz Date: Fri, 20 Jun 2025 10:13:56 +0200 Subject: [PATCH] fix: revive always storing lowercased email addresses Signed-off-by: Richard Steinmetz --- .../features/provisioning-v1.feature | 12 +++++++++++ .../Version32000Date20250620081925.php | 16 ++++++++++++++ lib/composer/composer/autoload_classmap.php | 1 + lib/composer/composer/autoload_static.php | 1 + lib/private/Config/UserConfig.php | 5 +++++ tests/lib/AllConfigTest.php | 21 +++++++++++++++++++ tests/lib/Config/UserConfigTest.php | 13 ++++++++++++ version.php | 2 +- 8 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 core/Migrations/Version32000Date20250620081925.php diff --git a/build/integration/features/provisioning-v1.feature b/build/integration/features/provisioning-v1.feature index 31d69fc61b2df..bad633bde4e42 100644 --- a/build/integration/features/provisioning-v1.feature +++ b/build/integration/features/provisioning-v1.feature @@ -187,6 +187,18 @@ Feature: provisioning | timezoneOffset | 0 | | pronouns | NULL | + Scenario: Edit a user with mixed case emails + Given As an "admin" + And user "brand-new-user" exists + And sending "PUT" to "/cloud/users/brand-new-user" with + | key | email | + | value | mixed-CASE@Nextcloud.com | + And the OCS status code should be "100" + And the HTTP status code should be "200" + Then user "brand-new-user" has + | id | brand-new-user | + | email | mixed-case@nextcloud.com | + Scenario: Edit a user account properties scopes Given user "brand-new-user" exists And As an "brand-new-user" diff --git a/core/Migrations/Version32000Date20250620081925.php b/core/Migrations/Version32000Date20250620081925.php new file mode 100644 index 0000000000000..13e1ac0f87dcb --- /dev/null +++ b/core/Migrations/Version32000Date20250620081925.php @@ -0,0 +1,16 @@ + $baseDir . '/core/Migrations/Version31000Date20240101084401.php', 'OC\\Core\\Migrations\\Version31000Date20240814184402' => $baseDir . '/core/Migrations/Version31000Date20240814184402.php', 'OC\\Core\\Migrations\\Version31000Date20250213102442' => $baseDir . '/core/Migrations/Version31000Date20250213102442.php', + 'OC\\Core\\Migrations\\Version32000Date20250620081925' => $baseDir . '/core/Migrations/Version32000Date20250620081925.php', 'OC\\Core\\Notification\\CoreNotifier' => $baseDir . '/core/Notification/CoreNotifier.php', 'OC\\Core\\ResponseDefinitions' => $baseDir . '/core/ResponseDefinitions.php', 'OC\\Core\\Service\\LoginFlowV2Service' => $baseDir . '/core/Service/LoginFlowV2Service.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 0b6349d446bc5..80af2509303e9 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -1508,6 +1508,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\Core\\Migrations\\Version31000Date20240101084401' => __DIR__ . '/../../..' . '/core/Migrations/Version31000Date20240101084401.php', 'OC\\Core\\Migrations\\Version31000Date20240814184402' => __DIR__ . '/../../..' . '/core/Migrations/Version31000Date20240814184402.php', 'OC\\Core\\Migrations\\Version31000Date20250213102442' => __DIR__ . '/../../..' . '/core/Migrations/Version31000Date20250213102442.php', + 'OC\\Core\\Migrations\\Version32000Date20250620081925' => __DIR__ . '/../../..' . '/core/Migrations/Version32000Date20250620081925.php', 'OC\\Core\\Notification\\CoreNotifier' => __DIR__ . '/../../..' . '/core/Notification/CoreNotifier.php', 'OC\\Core\\ResponseDefinitions' => __DIR__ . '/../../..' . '/core/ResponseDefinitions.php', 'OC\\Core\\Service\\LoginFlowV2Service' => __DIR__ . '/../../..' . '/core/Service/LoginFlowV2Service.php', diff --git a/lib/private/Config/UserConfig.php b/lib/private/Config/UserConfig.php index 07118d919b361..0ea4e0ac43c9c 100644 --- a/lib/private/Config/UserConfig.php +++ b/lib/private/Config/UserConfig.php @@ -1058,6 +1058,11 @@ private function setTypedValue( int $flags, ValueType $type, ): bool { + // Primary email addresses are always(!) expected to be lowercase + if ($app === 'settings' && $key === 'email') { + $value = strtolower($value); + } + $this->assertParams($userId, $app, $key); if (!$this->matchAndApplyLexiconDefinition($userId, $app, $key, $lazy, $type, $flags)) { // returns false as database is not updated diff --git a/tests/lib/AllConfigTest.php b/tests/lib/AllConfigTest.php index e892e441ecf07..3314ba46afcd3 100644 --- a/tests/lib/AllConfigTest.php +++ b/tests/lib/AllConfigTest.php @@ -91,6 +91,27 @@ public function testSetUserValue(): void { $config->deleteUserValue('userSet', 'appSet', 'keySet'); } + /** + * This test needs to stay! Emails are expected to be lowercase due to performance reasons. + * This way we can skip the expensive casing change on the database. + */ + public function testSetUserValueSettingsEmail(): void { + $selectAllSQL = 'SELECT `userid`, `appid`, `configkey`, `configvalue` FROM `*PREFIX*preferences` WHERE `userid` = ?'; + $config = $this->getConfig(); + + $config->setUserValue('userSet', 'settings', 'email', 'mixed.CASE@domain.COM'); + + $result = $this->connection->executeQuery($selectAllSQL, ['userSet'])->fetchAll(); + + $this->assertEquals(1, count($result)); + $this->assertEquals([ + 'userid' => 'userSet', + 'appid' => 'settings', + 'configkey' => 'email', + 'configvalue' => 'mixed.case@domain.com' + ], $result[0]); + } + public function testSetUserValueWithPreCondition(): void { $config = $this->getConfig(); diff --git a/tests/lib/Config/UserConfigTest.php b/tests/lib/Config/UserConfigTest.php index f9a9b5c52720f..93483a9b71a4e 100644 --- a/tests/lib/Config/UserConfigTest.php +++ b/tests/lib/Config/UserConfigTest.php @@ -1249,6 +1249,19 @@ public function testSetValueMixed( } } + /** + * This test needs to stay! Emails are expected to be lowercase due to performance reasons. + * This way we can skip the expensive casing change on the database. + */ + public function testSetValueMixedWithSettingsEmail(): void { + $userConfig = $this->generateUserConfig(); + + $edited = $userConfig->setValueMixed('user1', 'settings', 'email', 'mixed.CASE@Nextcloud.com'); + $this->assertTrue($edited); + + $actual = $userConfig->getValueMixed('user1', 'settings', 'email'); + $this->assertEquals('mixed.case@nextcloud.com', $actual); + } public function providerSetValueString(): array { return [ diff --git a/version.php b/version.php index 0cce4c8340e43..761b39b549793 100644 --- a/version.php +++ b/version.php @@ -9,7 +9,7 @@ // between betas, final and RCs. This is _not_ the public version number. Reset minor/patch level // when updating major/minor version number. -$OC_Version = [31, 0, 6, 2]; +$OC_Version = [31, 0, 6, 3]; // The human-readable string $OC_VersionString = '31.0.6';