Move the REGISTER_USERNAME_FORCE_ASCII check into an event listener
authorTim Düsterhus <duesterhus@woltlab.com>
Thu, 20 Apr 2023 15:33:16 +0000 (17:33 +0200)
committerTim Düsterhus <duesterhus@woltlab.com>
Thu, 20 Apr 2023 15:33:16 +0000 (17:33 +0200)
wcfsetup/install/files/lib/bootstrap/com.woltlab.wcf.php
wcfsetup/install/files/lib/system/event/listener/UsernameValidatingCheckCharactersListener.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/util/UserRegistrationUtil.class.php

index 7d60857e8c12680460ec36fd3e85e1f72c1dbd22..59732e3992c1ceb37d3a286ee93ebf027d3f6f4b 100644 (file)
@@ -6,6 +6,7 @@ use wcf\system\event\listener\PhraseChangedPreloadListener;
 use wcf\system\event\listener\PipSyncedPhrasePreloadListener;
 use wcf\system\event\listener\PreloadPhrasesCollectingListener;
 use wcf\system\event\listener\UserLoginCancelLostPasswordListener;
+use wcf\system\event\listener\UsernameValidatingCheckCharactersListener;
 use wcf\system\language\event\LanguageImported;
 use wcf\system\language\event\PhraseChanged;
 use wcf\system\language\LanguageFactory;
@@ -15,6 +16,7 @@ use wcf\system\language\preload\PhrasePreloader;
 use wcf\system\package\event\PackageInstallationPluginSynced;
 use wcf\system\package\event\PackageListChanged;
 use wcf\system\user\authentication\event\UserLoggedIn;
+use wcf\system\user\event\UsernameValidating;
 use wcf\system\WCF;
 use wcf\system\worker\event\RebuildWorkerCollecting;
 
@@ -28,6 +30,8 @@ return static function (): void {
 
     $eventHandler->register(UserLoggedIn::class, UserLoginCancelLostPasswordListener::class);
 
+    $eventHandler->register(UsernameValidating::class, UsernameValidatingCheckCharactersListener::class);
+
     $eventHandler->register(PackageListChanged::class, static function () {
         foreach (LanguageFactory::getInstance()->getLanguages() as $language) {
             $command = new ResetPreloadCache($language);
diff --git a/wcfsetup/install/files/lib/system/event/listener/UsernameValidatingCheckCharactersListener.class.php b/wcfsetup/install/files/lib/system/event/listener/UsernameValidatingCheckCharactersListener.class.php
new file mode 100644 (file)
index 0000000..cd35b96
--- /dev/null
@@ -0,0 +1,73 @@
+<?php
+
+namespace wcf\system\event\listener;
+
+use Spoofchecker;
+use wcf\system\user\event\UsernameValidating;
+
+/**
+ * Checks the username against the REGISTER_USERNAME_FORCE_ASCII option.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2023 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @since 6.0
+ */
+final class UsernameValidatingCheckCharactersListener
+{
+    public function __invoke(UsernameValidating $event): void
+    {
+        if (!$this->isValid($event->username)) {
+            $event->preventDefault();
+        }
+    }
+
+    private function isValid(string $name): bool
+    {
+        switch (REGISTER_USERNAME_FORCE_ASCII) {
+            case 0:
+                break;
+            case 1:
+                if (!\preg_match('/^[\x20-\x7E]+$/', $name)) {
+                    return false;
+                }
+                break;
+            case 2:
+                $spoofchecker = new Spoofchecker();
+                $checks = Spoofchecker::INVISIBLE;
+                if (\defined(Spoofchecker::class . '::HIDDEN_OVERLAY')) {
+                    // The constant will exist with PHP 8.3.
+                    $checks |= Spoofchecker::HIDDEN_OVERLAY;
+                } else {
+                    // HIDDEN_OVERLAY == 256
+                    $checks |= 256;
+                }
+
+                // ->setRestrictionLevel() requires ICU 58.
+                if (\method_exists($spoofchecker, 'setRestrictionLevel')) {
+                    // This method needs to be called first. ->setRestrictionLevel() will
+                    // implicitly enable the check for the restriction level for which no
+                    // constant exists. When calling ->setChecks() after ->setRestrictionLevel()
+                    // the check will be implicitly disabled again.
+                    $spoofchecker->setChecks($checks);
+
+                    // https://unicode.org/reports/tr39/#Restriction_Level_Detection
+                    $spoofchecker->setRestrictionLevel(Spoofchecker::HIGHLY_RESTRICTIVE);
+                } else {
+                    $spoofchecker->setChecks($checks | Spoofchecker::SINGLE_SCRIPT);
+                }
+
+                \assert(
+                    $spoofchecker->isSuspicious("GREEK CAPITAL LETTER SIGMA: \u{03A3}"),
+                    "The restriction level check was not correctly enabled."
+                );
+
+                if ($spoofchecker->isSuspicious($name)) {
+                    return false;
+                }
+                break;
+        }
+
+        return true;
+    }
+}
index 42b734a915a1a716bc70ebef0edce76c86877f6e..d31e9d3217c9a52caea2c27dc230d2629297826b 100644 (file)
@@ -47,50 +47,6 @@ final class UserRegistrationUtil
             return false;
         }
 
-        switch (REGISTER_USERNAME_FORCE_ASCII) {
-            case 0:
-                break;
-            case 1:
-                if (!\preg_match('/^[\x20-\x7E]+$/', $name)) {
-                    return false;
-                }
-                break;
-            case 2:
-                $spoofchecker = new \Spoofchecker();
-                $checks = Spoofchecker::INVISIBLE;
-                if (\defined(Spoofchecker::class . '::HIDDEN_OVERLAY')) {
-                    // The constant will exist with PHP 8.3.
-                    $checks |= Spoofchecker::HIDDEN_OVERLAY;
-                } else {
-                    // HIDDEN_OVERLAY == 256
-                    $checks |= 256;
-                }
-
-                // ->setRestrictionLevel() requires ICU 58.
-                if (\method_exists($spoofchecker, 'setRestrictionLevel')) {
-                    // This method needs to be called first. ->setRestrictionLevel() will
-                    // implicitly enable the check for the restriction level for which no
-                    // constant exists. When calling ->setChecks() after ->setRestrictionLevel()
-                    // the check will be implicitly disabled again.
-                    $spoofchecker->setChecks($checks);
-
-                    // https://unicode.org/reports/tr39/#Restriction_Level_Detection
-                    $spoofchecker->setRestrictionLevel(Spoofchecker::HIGHLY_RESTRICTIVE);
-                } else {
-                    $spoofchecker->setChecks($checks | Spoofchecker::SINGLE_SCRIPT);
-                }
-
-                \assert(
-                    $spoofchecker->isSuspicious("GREEK CAPITAL LETTER SIGMA: \u{03A3}"),
-                    "The restriction level check was not correctly enabled."
-                );
-
-                if ($spoofchecker->isSuspicious($name)) {
-                    return false;
-                }
-                break;
-        }
-
         return true;
     }