Prevent automatically disabled users from using activation codes
authorAlexander Ebert <ebert@woltlab.com>
Fri, 8 Mar 2019 15:50:14 +0000 (16:50 +0100)
committerAlexander Ebert <ebert@woltlab.com>
Fri, 8 Mar 2019 15:50:14 +0000 (16:50 +0100)
com.woltlab.wcf/templates/userNotice.tpl
wcfsetup/install/files/acp/templates/userList.tpl
wcfsetup/install/files/lib/data/blacklist/entry/BlacklistEntry.class.php
wcfsetup/install/files/lib/data/user/User.class.php
wcfsetup/install/files/lib/form/RegisterActivationForm.class.php
wcfsetup/install/files/lib/form/RegisterForm.class.php
wcfsetup/install/files/lib/form/RegisterNewActivationCodeForm.class.php
wcfsetup/install/lang/de.xml
wcfsetup/install/lang/en.xml
wcfsetup/setup/db/install.sql

index ecce0bb42aca458d4d5ff72d0070260ac1dcb810..5ce3635f80d72c3276c8dc8913c81d7f85d70c27 100644 (file)
@@ -10,7 +10,7 @@
                <p class="info">{lang}wcf.page.availableUpdates{/lang}</p>
        {/if}
        
-       {if $__wcf->user->activationCode && REGISTER_ACTIVATION_METHOD == 1 && $templateName != 'registerActivation' && $templateName != 'register' && $templateName != 'redirect'}
+       {if $__wcf->user->activationCode && REGISTER_ACTIVATION_METHOD == 1 && $templateName != 'registerActivation' && $templateName != 'register' && $templateName != 'redirect' && $__wcf->user->getBlacklistMatches()|empty}
                <p class="warning">{lang}wcf.user.register.needActivation{/lang}</p>
        {/if}
        
index 7bf351cdac80405e03f079c4a043b62c225b53f9..ed812f2703973bbce2246fda3ab8e13d33c73332 100644 (file)
                                                        
                                                        <span class="userStatusIcons">
                                                                {if $user->banned}<span class="icon icon16 fa-lock jsTooltip jsUserStatusBanned" title="{lang}wcf.user.status.banned{/lang}"></span>{/if}
-                                                               {if $user->activationCode != 0}<span class="icon icon16 fa-power-off jsTooltip jsUserStatusIsDisabled" title="{lang}wcf.user.status.isDisabled{/lang}"></span>{/if}
+                                                               {if $user->activationCode != 0}
+                                                                       <span class="icon icon16 fa-power-off jsTooltip jsUserStatusIsDisabled" title="{lang}wcf.user.status.isDisabled{/lang}"></span>
+                                                                       {if !$user->getBlacklistMatches()|empty}
+                                                                               <span class="icon icon16 fa-warning jsTooltip jsUserStatusBlacklistMatches" title="{lang}wcf.user.status.blacklistMatches{/lang}"></span>
+                                                                       {/if}
+                                                               {/if}
                                                        </span>
                                                        
                                                        {if MODULE_USER_RANK}
index d005b20b6cbbfb1cbe0e8950d1b235c475524a34..8adcff96346da942e6b50dbede3aa517909ac5d8 100644 (file)
@@ -24,9 +24,9 @@ class BlacklistEntry extends DatabaseObject {
         * @param string $username
         * @param string $email
         * @param string $ipAddress
-        * @return boolean
+        * @return string[]
         */
-       public static function shouldReject($username, $email, $ipAddress) {
+       public static function getMatches($username, $email, $ipAddress) {
                if (BLACKLIST_SFS_USERNAME === 'skip' && BLACKLIST_SFS_EMAIL_ADDRESS === 'skip' && BLACKLIST_SFS_IP_ADDRESS === 'skip') {
                        return false;
                }
@@ -62,13 +62,14 @@ class BlacklistEntry extends DatabaseObject {
                        ".$conditions;
                $statement = WCF::getDB()->prepareStatement($sql);
                $statement->execute($conditions->getParameters());
+               $matches = [];
                while ($row = $statement->fetchArray()) {
                        if (self::isMatch($row['type'], $row['occurrences'])) {
-                               return true;
+                               $matches[] = ($row['type'] === 'ipv4' || $row['type'] === 'ipv6') ? 'ip' : $row['type'];
                        }
                }
                
-               return false;
+               return $matches;
        }
        
        protected static function getHash($string) {
index 001d24352e230fdd9cbff1f3a932369ad22d999b..b34fa26781371a494c4834d7b3b9de5b0a62784a 100644 (file)
@@ -11,6 +11,7 @@ use wcf\system\request\IRouteController;
 use wcf\system\request\LinkHandler;
 use wcf\system\user\storage\UserStorageHandler;
 use wcf\system\WCF;
+use wcf\util\JSON;
 use wcf\util\PasswordUtil;
 use wcf\util\UserUtil;
 
@@ -70,6 +71,7 @@ use wcf\util\UserUtil;
  * @property-read      string          $disableCoverPhotoReason        reason why the user's cover photo is disabled
  * @property-read      integer         $disableCoverPhotoExpires       timestamp at which the user's cover photo will automatically be enabled again
  * @property-read      integer         $articles                       number of articles written by the user
+ * @property-read       string          $blacklistMatches               JSON string of an array with all matches in the blacklist, otherwise an empty string 
  */
 final class User extends DatabaseObject implements IRouteController, IUserContent {
        /**
@@ -557,4 +559,22 @@ final class User extends DatabaseObject implements IRouteController, IUserConten
        public function canPurchasePaidSubscriptions() {
                return WCF::getUser()->userID && WCF::getUser()->activationCode == 0;
        }
+       
+       /**
+        * Returns the list of fields that had matches in the blacklist. An empty list is
+        * returned if the user has been approved, regardless of any known matches.
+        * 
+        * @return string[]
+        * @since 5.2
+        */
+       public function getBlacklistMatches() {
+               if ($this->activationCode && $this->blacklistMatches) {
+                       $matches = JSON::decode($this->blacklistMatches);
+                       if (is_array($matches)) {
+                               return $matches;
+                       }
+               }
+               
+               return [];
+       }
 }
index d795f48831018699afe9b952a6e8b2d543f6fdce..ea72f12487e7415f2795bdafb10bec615f7dfb67 100644 (file)
@@ -5,6 +5,7 @@ use wcf\data\user\UserAction;
 use wcf\system\event\EventHandler;
 use wcf\system\exception\IllegalLinkException;
 use wcf\system\exception\NamedUserException;
+use wcf\system\exception\PermissionDeniedException;
 use wcf\system\exception\UserInputException;
 use wcf\system\request\LinkHandler;
 use wcf\system\WCF;
@@ -85,6 +86,10 @@ class RegisterActivationForm extends AbstractForm {
                if ($this->user->activationCode != $this->activationCode) {
                        throw new UserInputException('activationCode', 'invalid');
                }
+               
+               if (!empty($this->user->getBlacklistMatches())) {
+                       throw new PermissionDeniedException();
+               }
        }
        
        /**
@@ -127,6 +132,10 @@ class RegisterActivationForm extends AbstractForm {
                        $this->submit();
                }
                
+               if ($this->user === null && !empty(WCF::getUser()->getBlacklistMatches())) {
+                       throw new PermissionDeniedException();
+               }
+               
                parent::show();
        }
 }
index e259a49a2bc1c551051000557ed201d91b020366..8a9cdc5afa97b2a7f324f13de61c330bd1a1a6c1 100644 (file)
@@ -28,6 +28,7 @@ use wcf\system\user\notification\object\UserRegistrationUserNotificationObject;
 use wcf\system\user\notification\UserNotificationHandler;
 use wcf\system\WCF;
 use wcf\util\HeaderUtil;
+use wcf\util\JSON;
 use wcf\util\StringUtil;
 use wcf\util\UserRegistrationUtil;
 use wcf\util\UserUtil;
@@ -84,11 +85,11 @@ class RegisterForm extends UserAddForm {
        public $randomFieldNames = [];
        
        /**
-        * the user will be disabled and requires manual approval
-        * @var bool
+        * list of fields that have matches in the blacklist
+        * @var string[]
         * @since 5.2
         */
-       public $forceDisableUser = false;
+       public $blacklistMatches = [];
        
        /**
         * min number of seconds between form request and submit
@@ -175,13 +176,9 @@ class RegisterForm extends UserAddForm {
                }
                
                if (BLACKLIST_SFS_ENABLE) {
-                       if (BlacklistEntry::shouldReject($this->username, $this->email, UserUtil::getIpAddress())) {
-                               if (BLACKLIST_SFS_ACTION === 'disable') {
-                                       $this->forceDisableUser = true;
-                               }
-                               else {
-                                       throw new PermissionDeniedException();
-                               }
+                       $this->blacklistMatches = BlacklistEntry::getMatches($this->username, $this->email, UserUtil::getIpAddress());
+                       if (BLACKLIST_SFS_ACTION === 'block') {
+                               throw new PermissionDeniedException();
                        }
                }
        }
@@ -431,7 +428,7 @@ class RegisterForm extends UserAddForm {
                
                // generate activation code
                $addDefaultGroups = true;
-               if ($this->forceDisableUser || (REGISTER_ACTIVATION_METHOD == 1 && !$registerVia3rdParty) || REGISTER_ACTIVATION_METHOD == 2) {
+               if (!empty($this->blacklistMatches) || (REGISTER_ACTIVATION_METHOD == 1 && !$registerVia3rdParty) || REGISTER_ACTIVATION_METHOD == 2) {
                        $activationCode = UserRegistrationUtil::getActivationCode();
                        $this->additionalFields['activationCode'] = $activationCode;
                        $addDefaultGroups = false;
@@ -448,7 +445,8 @@ class RegisterForm extends UserAddForm {
                        'data' => array_merge($this->additionalFields, [
                                'username' => $this->username,
                                'email' => $this->email,
-                               'password' => $this->password
+                               'password' => $this->password,
+                               'blacklistMatches' => (!empty($this->blacklistMatches)) ? JSON::encode($this->blacklistMatches) : '',
                        ]),
                        'groups' => $this->groupIDs,
                        'languageIDs' => $this->visibleLanguages,
@@ -473,10 +471,10 @@ class RegisterForm extends UserAddForm {
                }
                
                // activation management
-               if (REGISTER_ACTIVATION_METHOD == 0 && !$this->forceDisableUser) {
+               if (REGISTER_ACTIVATION_METHOD == 0 && empty($this->blacklistMatches)) {
                        $this->message = 'wcf.user.register.success';
                }
-               else if (REGISTER_ACTIVATION_METHOD == 1 && !$this->forceDisableUser) {
+               else if (REGISTER_ACTIVATION_METHOD == 1 && empty($this->blacklistMatches)) {
                        // registering via 3rdParty leads to instant activation
                        if ($registerVia3rdParty) {
                                $this->message = 'wcf.user.register.success';
@@ -493,7 +491,7 @@ class RegisterForm extends UserAddForm {
                                $this->message = 'wcf.user.register.success.needActivation';
                        }
                }
-               else if (REGISTER_ACTIVATION_METHOD == 2 || $this->forceDisableUser) {
+               else if (REGISTER_ACTIVATION_METHOD == 2 || !empty($this->blacklistMatches)) {
                        $this->message = 'wcf.user.register.success.awaitActivation';
                }
                
index 27212a7f2d1b28cfd4761ba7feddee95ca0a8a2f..1be7e8b42eecf153d6bc5bfbb077ea611b9f9909 100644 (file)
@@ -7,6 +7,7 @@ use wcf\system\email\mime\RecipientAwareTextMimePart;
 use wcf\system\email\Email;
 use wcf\system\email\UserMailbox;
 use wcf\system\exception\IllegalLinkException;
+use wcf\system\exception\PermissionDeniedException;
 use wcf\system\exception\UserInputException;
 use wcf\system\request\LinkHandler;
 use wcf\system\WCF;
@@ -91,6 +92,10 @@ class RegisterNewActivationCodeForm extends AbstractForm {
                if ($this->user->activationCode == 0) {
                        throw new UserInputException('username', 'alreadyEnabled');
                }
+               
+               if (!empty($this->user->getBlacklistMatches())) {
+                       throw new PermissionDeniedException();
+               }
        }
        
        /**
@@ -197,6 +202,10 @@ class RegisterNewActivationCodeForm extends AbstractForm {
                        throw new IllegalLinkException();
                }
                
+               if ($this->user === null && !empty(WCF::getUser()->getBlacklistMatches())) {
+                       throw new PermissionDeniedException();
+               }
+               
                parent::show();
        }
 }
index 554d7659b4c03843ded39d53e3c0c3852acf2abf..71a0dd3440dbe19cb62de591c960d46550f8c719 100644 (file)
@@ -4534,6 +4534,7 @@ sich{/if} nicht bei uns registriert {if LANGUAGE_USE_INFORMAL_VARIANT}hast{else}
                <item name="wcf.user.articles"><![CDATA[Artikel]]></item>
                <item name="wcf.user.status.banned"><![CDATA[Der Benutzer ist gesperrt.]]></item>
                <item name="wcf.user.status.isDisabled"><![CDATA[Der Benutzer ist nicht freigeschaltet.]]></item>
+               <item name="wcf.user.status.blacklistMatches"><![CDATA[Der Benutzer wurde auf Grund eines Treffers in der Datenbank von „Stop Forum Spam“ automatisch deaktiviert.]]></item>
        </category>
        <category name="wcf.user.menu">
                <item name="wcf.user.menu.community"><![CDATA[Community]]></item>
index 29661797b09a11a9a3cd04af802768f70f177c59..38878ba0f5c969b4aabc94e9541e97b17cfa68aa 100644 (file)
@@ -4531,6 +4531,7 @@ not register with us.]]></item>
                <item name="wcf.user.articles"><![CDATA[Articles]]></item>
                <item name="wcf.user.status.banned"><![CDATA[The user has been banned.]]></item>
                <item name="wcf.user.status.isDisabled"><![CDATA[The user has not been approved yet.]]></item>
+               <item name="wcf.user.status.blacklistMatches"><![CDATA[The user has been automatically disabled because of matches in the “Stop Forum Spam” database.]]></item>
        </category>
        <category name="wcf.user.menu">
                <item name="wcf.user.menu.community"><![CDATA[Community]]></item>
index b33f66e96c480ec5bd84b0795b0b06b1e80ee716..9ed46d7ccbc1307cea051f620403bbed829a81bf 100644 (file)
@@ -1491,6 +1491,7 @@ CREATE TABLE wcf1_user (
        positiveReactionsReceived INT(10) NOT NULL DEFAULT 0,
        negativeReactionsReceived INT(10) NOT NULL DEFAULT 0,
        neutralReactionsReceived INT(10) NOT NULL DEFAULT 0,
+       blacklistMatches VARCHAR(255) NOT NULL DEFAULT '',
        
        KEY username (username),
        KEY email (email),