From 783b8dda939c66e3555948446003867a05f849f8 Mon Sep 17 00:00:00 2001
From: joshuaruesweg
Date: Wed, 20 May 2020 19:53:19 +0200
Subject: [PATCH] Introduce new flag variable for email confirmation
---
.../email_registerNeedActivation.tpl | 2 +-
.../files/lib/data/user/User.class.php | 27 ++++++++++++++--
.../files/lib/data/user/UserAction.class.php | 32 +++++++++++++++++--
.../files/lib/data/user/UserProfile.class.php | 2 +-
.../lib/data/user/UserProfileAction.class.php | 2 +-
.../lib/form/RegisterActivationForm.class.php | 7 ++--
.../files/lib/form/RegisterForm.class.php | 2 ++
.../page/PaidSubscriptionListPage.class.php | 2 +-
.../action/UserClipboardAction.class.php | 5 +--
.../condition/UserStateCondition.class.php | 4 +--
.../DailyMailNotificationCronjob.class.php | 2 +-
.../system/importer/UserImporter.class.php | 2 +-
.../UserNotificationHandler.class.php | 2 +-
wcfsetup/install/lang/de.xml | 6 ++--
wcfsetup/install/lang/en.xml | 4 +--
wcfsetup/setup/db/install.sql | 1 +
16 files changed, 77 insertions(+), 25 deletions(-)
diff --git a/com.woltlab.wcf/templates/email_registerNeedActivation.tpl b/com.woltlab.wcf/templates/email_registerNeedActivation.tpl
index c81f8313d1..31185e0ccd 100644
--- a/com.woltlab.wcf/templates/email_registerNeedActivation.tpl
+++ b/com.woltlab.wcf/templates/email_registerNeedActivation.tpl
@@ -7,7 +7,7 @@
{lang}wcf.user.register.needActivation.mail.html.intro{/lang}
{capture assign=button}
-
+
{lang}wcf.user.register.needActivation.mail.html.activate{/lang}
{/capture}
diff --git a/wcfsetup/install/files/lib/data/user/User.class.php b/wcfsetup/install/files/lib/data/user/User.class.php
index 761ea0b50c..ff91f24c7f 100644
--- a/wcfsetup/install/files/lib/data/user/User.class.php
+++ b/wcfsetup/install/files/lib/data/user/User.class.php
@@ -34,7 +34,8 @@ use wcf\util\UserUtil;
* @property-read integer $banned is `1` if the user is banned, otherwise `0`
* @property-read string $banReason reason why the user is banned
* @property-read integer $banExpires timestamp at which the banned user is automatically unbanned
- * @property-read integer $activationCode code sent to the user's email address used for account activation
+ * @property-read integer $activationCode flag which determines, whether the user is activated (for legacy reasons an random integer, if the user is *not* activated)
+ * @property-read string $emailConfirmed code sent to the user's email address used for account activation
* @property-read integer $lastLostPasswordRequestTime timestamp at which the user has reported that they lost their password or 0 if password has not been reported as lost
* @property-read string $lostPasswordKey code used for authenticating setting new password after password loss or empty if password has not been reported as lost
* @property-read integer $lastUsernameChange timestamp at which the user changed their name the last time or 0 if username has not been changed
@@ -404,6 +405,26 @@ final class User extends DatabaseObject implements IRouteController, IUserConten
return 0;
}
+ /**
+ * Returns true if this user is activated.
+ *
+ * @return boolean
+ * @since 5.3
+ */
+ public function isActivated() {
+ return $this->activationCode == 0;
+ }
+
+ /**
+ * Returns true if the email is confirmed.
+ *
+ * @return boolean
+ * @since 5.3
+ */
+ public function isEmailConfirmed() {
+ return empty($this->emailConfirmed);
+ }
+
/**
* Returns the time zone of this user.
*
@@ -593,7 +614,7 @@ final class User extends DatabaseObject implements IRouteController, IUserConten
* @return boolean
*/
public function canPurchasePaidSubscriptions() {
- return WCF::getUser()->userID && WCF::getUser()->activationCode == 0;
+ return WCF::getUser()->userID && $this->isActivated();
}
/**
@@ -604,7 +625,7 @@ final class User extends DatabaseObject implements IRouteController, IUserConten
* @since 5.2
*/
public function getBlacklistMatches() {
- if ($this->activationCode && $this->blacklistMatches) {
+ if ($this->isActivated() && $this->blacklistMatches) {
$matches = JSON::decode($this->blacklistMatches);
if (is_array($matches)) {
return $matches;
diff --git a/wcfsetup/install/files/lib/data/user/UserAction.class.php b/wcfsetup/install/files/lib/data/user/UserAction.class.php
index 6443bfddad..e280e51f87 100644
--- a/wcfsetup/install/files/lib/data/user/UserAction.class.php
+++ b/wcfsetup/install/files/lib/data/user/UserAction.class.php
@@ -22,6 +22,7 @@ use wcf\system\language\LanguageFactory;
use wcf\system\request\RequestHandler;
use wcf\system\user\group\assignment\UserGroupAssignmentHandler;
use wcf\system\WCF;
+use wcf\util\CryptoUtil;
use wcf\util\UserRegistrationUtil;
/**
@@ -618,6 +619,28 @@ class UserAction extends AbstractDatabaseObjectAction implements IClipboardActio
$this->validateEnable();
}
+ /**
+ * Marks the email address as confirmed and enables the user, iff the register method is user activation only.
+ * @since 5.3
+ */
+ public function confirmEmail() {
+ if (empty($this->objects)) $this->readObjects();
+
+ if (REGISTER_ACTIVATION_METHOD & UserProfile::REGISTER_ACTIVATION_ADMIN) {
+ $action = new UserAction($this->objects, 'update', [
+ 'data' => [
+ 'emailConfirmed' => null
+ ]
+ ]);
+ $action->executeAction();
+ }
+ else {
+ $this->enable();
+ }
+
+ $this->unmarkItems();
+ }
+
/**
* Enables users.
*/
@@ -628,6 +651,7 @@ class UserAction extends AbstractDatabaseObjectAction implements IClipboardActio
'data' => [
'activationCode' => 0,
'blacklistMatches' => '',
+ 'emailConfirmed' => null
],
'removeGroups' => UserGroup::getGroupIDsByType([UserGroup::GUESTS])
]);
@@ -670,7 +694,8 @@ class UserAction extends AbstractDatabaseObjectAction implements IClipboardActio
$action = new UserAction($this->objects, 'update', [
'data' => [
- 'activationCode' => UserRegistrationUtil::getActivationCode()
+ 'activationCode' => UserRegistrationUtil::getActivationCode(),
+ 'emailConfirmed' => bin2hex(\random_bytes(20))
],
'removeGroups' => UserGroup::getGroupIDsByType([UserGroup::USERS])
]);
@@ -1009,7 +1034,8 @@ class UserAction extends AbstractDatabaseObjectAction implements IClipboardActio
}
foreach ($this->objects as $object) {
- if (!$object->activationCode) {
+ /** @var UserEditor $object */
+ if ($object->isActivated()) {
throw new UserInputException('objectIDs');
}
}
@@ -1024,7 +1050,7 @@ class UserAction extends AbstractDatabaseObjectAction implements IClipboardActio
foreach ($this->objects as $object) {
$action = new UserAction([$object], 'update', [
'data' => [
- 'activationCode' => UserRegistrationUtil::getActivationCode()
+ 'emailConfirmed' => bin2hex(\random_bytes(20))
]
]);
$action->executeAction();
diff --git a/wcfsetup/install/files/lib/data/user/UserProfile.class.php b/wcfsetup/install/files/lib/data/user/UserProfile.class.php
index 8932e527c7..d4c0a9c774 100644
--- a/wcfsetup/install/files/lib/data/user/UserProfile.class.php
+++ b/wcfsetup/install/files/lib/data/user/UserProfile.class.php
@@ -886,7 +886,7 @@ class UserProfile extends DatabaseObjectDecorator implements ITitledLinkObject {
* @return boolean
*/
public function canEditOwnProfile() {
- if ($this->activationCode || !$this->getPermission('user.profile.canEditUserProfile')) {
+ if (!$this->isActivated() || !$this->getPermission('user.profile.canEditUserProfile')) {
return false;
}
diff --git a/wcfsetup/install/files/lib/data/user/UserProfileAction.class.php b/wcfsetup/install/files/lib/data/user/UserProfileAction.class.php
index 94fed670b1..4cc50a943f 100644
--- a/wcfsetup/install/files/lib/data/user/UserProfileAction.class.php
+++ b/wcfsetup/install/files/lib/data/user/UserProfileAction.class.php
@@ -407,7 +407,7 @@ class UserProfileAction extends UserAction {
$fixUserGroupIDs[$user->userID] = [UserGroup::EVERYONE];
$groupIDs[] = UserGroup::EVERYONE;
}
- if ($user->activationCode) {
+ if (!$user->isActivated()) {
if (!in_array(UserGroup::GUESTS, $groupIDs)) {
if (!isset($fixUserGroupIDs[$user->userID])) $fixUserGroupIDs[$user->userID] = [];
$fixUserGroupIDs[$user->userID][] = UserGroup::GUESTS;
diff --git a/wcfsetup/install/files/lib/form/RegisterActivationForm.class.php b/wcfsetup/install/files/lib/form/RegisterActivationForm.class.php
index 675a9cc576..492d914b34 100644
--- a/wcfsetup/install/files/lib/form/RegisterActivationForm.class.php
+++ b/wcfsetup/install/files/lib/form/RegisterActivationForm.class.php
@@ -10,6 +10,7 @@ use wcf\system\exception\PermissionDeniedException;
use wcf\system\exception\UserInputException;
use wcf\system\request\LinkHandler;
use wcf\system\WCF;
+use wcf\util\CryptoUtil;
use wcf\util\HeaderUtil;
use wcf\util\StringUtil;
@@ -79,12 +80,12 @@ class RegisterActivationForm extends AbstractForm {
}
// user is already enabled
- if ($this->user->activationCode == 0) {
+ if ($this->user->isEmailConfirmed()) {
throw new NamedUserException(WCF::getLanguage()->get('wcf.user.registerActivation.error.userAlreadyEnabled'));
}
// check given activation code
- if ($this->user->activationCode != $this->activationCode) {
+ if (\hash_equals($this->activationCode, $this->user->emailConfirmed)) {
throw new UserInputException('activationCode', 'invalid');
}
@@ -100,7 +101,7 @@ class RegisterActivationForm extends AbstractForm {
parent::save();
// enable user
- $this->objectAction = new UserAction([$this->user], 'enable', ['skipNotification' => true]);
+ $this->objectAction = new UserAction([$this->user], 'confirmEmail', ['skipNotification' => true]);
$this->objectAction->executeAction();
$this->saved();
diff --git a/wcfsetup/install/files/lib/form/RegisterForm.class.php b/wcfsetup/install/files/lib/form/RegisterForm.class.php
index bd1f98176c..19901a2279 100644
--- a/wcfsetup/install/files/lib/form/RegisterForm.class.php
+++ b/wcfsetup/install/files/lib/form/RegisterForm.class.php
@@ -431,7 +431,9 @@ class RegisterForm extends UserAddForm {
$addDefaultGroups = true;
if (!empty($this->blacklistMatches) || (REGISTER_ACTIVATION_METHOD & UserProfile::REGISTER_ACTIVATION_USER && !$registerVia3rdParty) || REGISTER_ACTIVATION_METHOD & UserProfile::REGISTER_ACTIVATION_ADMIN) {
$activationCode = UserRegistrationUtil::getActivationCode();
+ $emailConfirmCode = bin2hex(\random_bytes(20));
$this->additionalFields['activationCode'] = $activationCode;
+ $this->additionalFields['emailConfirmed'] = $emailConfirmCode;
$addDefaultGroups = false;
$this->groupIDs = UserGroup::getGroupIDsByType([UserGroup::EVERYONE, UserGroup::GUESTS]);
}
diff --git a/wcfsetup/install/files/lib/page/PaidSubscriptionListPage.class.php b/wcfsetup/install/files/lib/page/PaidSubscriptionListPage.class.php
index f8fde99e07..d5c7cf00ed 100644
--- a/wcfsetup/install/files/lib/page/PaidSubscriptionListPage.class.php
+++ b/wcfsetup/install/files/lib/page/PaidSubscriptionListPage.class.php
@@ -43,7 +43,7 @@ class PaidSubscriptionListPage extends AbstractPage {
public function checkPermissions() {
parent::checkPermissions();
- if (WCF::getUser()->activationCode != 0) {
+ if (!WCF::getUser()->isActivated()) {
throw new PermissionDeniedException();
}
}
diff --git a/wcfsetup/install/files/lib/system/clipboard/action/UserClipboardAction.class.php b/wcfsetup/install/files/lib/system/clipboard/action/UserClipboardAction.class.php
index e832a3df61..ceba0e4266 100644
--- a/wcfsetup/install/files/lib/system/clipboard/action/UserClipboardAction.class.php
+++ b/wcfsetup/install/files/lib/system/clipboard/action/UserClipboardAction.class.php
@@ -196,7 +196,8 @@ class UserClipboardAction extends AbstractClipboardAction {
$userIDs = [];
foreach ($this->objects as $user) {
- if ($user->activationCode) $userIDs[] = $user->userID;
+ /** @var User $user */
+ if ($user->isActivated()) $userIDs[] = $user->userID;
}
return $userIDs;
@@ -234,7 +235,7 @@ class UserClipboardAction extends AbstractClipboardAction {
$userIDs = [];
foreach ($this->objects as $user) {
- if ($user->activationCode) $userIDs[] = $user->userID;
+ if ($user->is) $userIDs[] = $user->userID;
}
return $userIDs;
diff --git a/wcfsetup/install/files/lib/system/condition/UserStateCondition.class.php b/wcfsetup/install/files/lib/system/condition/UserStateCondition.class.php
index 47093fdb18..013409ff56 100644
--- a/wcfsetup/install/files/lib/system/condition/UserStateCondition.class.php
+++ b/wcfsetup/install/files/lib/system/condition/UserStateCondition.class.php
@@ -82,10 +82,10 @@ class UserStateCondition extends AbstractSingleFieldCondition implements IConten
/** @noinspection PhpUndefinedFieldInspection */
$userIsEnabled = $condition->userIsEnabled;
if ($userIsEnabled !== null) {
- if ($userIsEnabled && $user->activationCode) {
+ if ($userIsEnabled && !$user->isActivated()) {
return false;
}
- else if (!$userIsEnabled && !$user->activationCode) {
+ else if (!$userIsEnabled && $user->isActivated()) {
return false;
}
}
diff --git a/wcfsetup/install/files/lib/system/cronjob/DailyMailNotificationCronjob.class.php b/wcfsetup/install/files/lib/system/cronjob/DailyMailNotificationCronjob.class.php
index 0013290037..d39709ecc9 100644
--- a/wcfsetup/install/files/lib/system/cronjob/DailyMailNotificationCronjob.class.php
+++ b/wcfsetup/install/files/lib/system/cronjob/DailyMailNotificationCronjob.class.php
@@ -145,7 +145,7 @@ class DailyMailNotificationCronjob extends AbstractCronjob {
$user = $users[$userID];
// no notifications for disabled or banned users
- if ($user->activationCode) continue;
+ if (!$user->isEmailConfirmed()) continue;
if ($user->banned) continue;
$notifications = array_map(function ($notificationID) use ($notificationObjects, $eventObjects, $user, $objectTypes, $authors, $authorToNotification, $unknownAuthor) {
diff --git a/wcfsetup/install/files/lib/system/importer/UserImporter.class.php b/wcfsetup/install/files/lib/system/importer/UserImporter.class.php
index 1ecd17568f..e0116833f8 100644
--- a/wcfsetup/install/files/lib/system/importer/UserImporter.class.php
+++ b/wcfsetup/install/files/lib/system/importer/UserImporter.class.php
@@ -176,7 +176,7 @@ class UserImporter extends AbstractImporter {
}
}
- if (!$user->activationCode) $defaultGroupIDs = UserGroup::getGroupIDsByType([UserGroup::EVERYONE, UserGroup::USERS]);
+ if ($user->isActivated()) $defaultGroupIDs = UserGroup::getGroupIDsByType([UserGroup::EVERYONE, UserGroup::USERS]);
else $defaultGroupIDs = UserGroup::getGroupIDsByType([UserGroup::EVERYONE, UserGroup::GUESTS]);
$groupIDs = array_merge($groupIDs, $defaultGroupIDs);
diff --git a/wcfsetup/install/files/lib/system/user/notification/UserNotificationHandler.class.php b/wcfsetup/install/files/lib/system/user/notification/UserNotificationHandler.class.php
index 4672efdfa0..e57b4d181f 100644
--- a/wcfsetup/install/files/lib/system/user/notification/UserNotificationHandler.class.php
+++ b/wcfsetup/install/files/lib/system/user/notification/UserNotificationHandler.class.php
@@ -672,7 +672,7 @@ class UserNotificationHandler extends SingletonFactory {
*/
public function sendInstantMailNotification(UserNotification $notification, User $user, IUserNotificationEvent $event) {
// no notifications for disabled or banned users
- if ($user->activationCode) return;
+ if (!$user->isEmailConfirmed()) return;
if ($user->banned) return;
// recipient's language
diff --git a/wcfsetup/install/lang/de.xml b/wcfsetup/install/lang/de.xml
index f12d6ac09c..2b03fa48df 100644
--- a/wcfsetup/install/lang/de.xml
+++ b/wcfsetup/install/lang/de.xml
@@ -4713,7 +4713,7 @@ sich{/if} nicht bei uns registriert {if LANGUAGE_USE_INFORMAL_VARIANT}hast{else}
Benutzerkonto vollständig verwenden {if LANGUAGE_USE_INFORMAL_VARIANT}kannst{else}können{/if}, ist es notwendig, dass {if LANGUAGE_USE_INFORMAL_VARIANT}du{else}Sie{/if} einmalig die Gültigkeit {if LANGUAGE_USE_INFORMAL_VARIANT}deiner{else}Ihrer{/if} E-Mail-Adresse {if LANGUAGE_USE_INFORMAL_VARIANT}bestätigst{else}bestätigen{/if}:
]]>
- {if LANGUAGE_USE_INFORMAL_VARIANT}Dein{else}Ihr{/if} Aktivierungscode lautet: {$mailbox->getUser()->activationCode}.
+
{if LANGUAGE_USE_INFORMAL_VARIANT}Dein{else}Ihr{/if} Aktivierungscode lautet: {$mailbox->getUser()->emailConfirmed}.
Wenn {if LANGUAGE_USE_INFORMAL_VARIANT}du{else}Sie{/if} Probleme mit der Aktivierung {if LANGUAGE_USE_INFORMAL_VARIANT}deines{else}Ihres{/if} Benutzerkontos {if LANGUAGE_USE_INFORMAL_VARIANT}hast{else}haben{/if}, dann {if LANGUAGE_USE_INFORMAL_VARIANT}wende dich{else}wenden Sie sich{/if} bitte an den Administrator
unter: {MAIL_ADMIN_ADDRESS}. Wenn {if LANGUAGE_USE_INFORMAL_VARIANT}du dich{else}Sie sich{/if} nicht bei uns registriert {if LANGUAGE_USE_INFORMAL_VARIANT}hast{else}haben{/if},
dann {if LANGUAGE_USE_INFORMAL_VARIANT}kannst du{else}können Sie{/if} diese E-Mail ignorieren.
]]>
@@ -4724,9 +4724,9 @@ Bevor {if LANGUAGE_USE_INFORMAL_VARIANT}du dein{else}Sie Ihr{/if} Benutzerkonto
dass {if LANGUAGE_USE_INFORMAL_VARIANT}du{else}Sie{/if} einmalig durch Klicken des folgenden Links die Gültigkeit {if LANGUAGE_USE_INFORMAL_VARIANT}deiner{else}Ihrer{/if}
E-Mail-Adresse {if LANGUAGE_USE_INFORMAL_VARIANT}bestätigst{else}bestätigen{/if}:
- {link controller='RegisterActivation' isEmail=true}u={@$mailbox->getUser()->userID}&a={@$mailbox->getUser()->activationCode}{/link} {* this line ends with a space *}
+ {link controller='RegisterActivation' isEmail=true}u={@$mailbox->getUser()->userID}&a={@$mailbox->getUser()->emailConfirmed}{/link} {* this line ends with a space *}
-{if LANGUAGE_USE_INFORMAL_VARIANT}Dein{else}Ihr{/if} Aktivierungscode lautet: {@$mailbox->getUser()->activationCode} {* this line ends with a space *}
+{if LANGUAGE_USE_INFORMAL_VARIANT}Dein{else}Ihr{/if} Aktivierungscode lautet: {@$mailbox->getUser()->emailConfirmed} {* this line ends with a space *}
Wenn {if LANGUAGE_USE_INFORMAL_VARIANT}du{else}Sie{/if} Probleme mit der Aktivierung Ihres Benutzerkontos {if LANGUAGE_USE_INFORMAL_VARIANT}hast{else}haben{/if}, dann {if LANGUAGE_USE_INFORMAL_VARIANT}wende
dich{else}wenden
diff --git a/wcfsetup/install/lang/en.xml b/wcfsetup/install/lang/en.xml
index 79bb275a55..0707122a51 100644
--- a/wcfsetup/install/lang/en.xml
+++ b/wcfsetup/install/lang/en.xml
@@ -4709,7 +4709,7 @@ not register with us.]]>
user account to itâs full extent it is required that you confirm validity of your email address once:]]>
- Your activation code is: {$mailbox->getUser()->activationCode}.
+
Your activation code is: {$mailbox->getUser()->emailConfirmed}.
If you have trouble confirming your email address, please contact the administrator at:
{MAIL_ADMIN_ADDRESS}. Please ignore this email if
you did not register an account with us.
]]>
@@ -4719,7 +4719,7 @@ Thank you for registering at: {@PAGE_TITLE|language} [URL:{link isEmail=true}{/l
able to use your user account to it's full extent it is required that you
confirm the validity of your email address once:
- {link controller='RegisterActivation' isEmail=true}u={@$mailbox->getUser()->userID}&a={@$mailbox->getUser()->activationCode}{/link} {* this line ends with a space *}
+ {link controller='RegisterActivation' isEmail=true}u={@$mailbox->getUser()->userID}&a={@$mailbox->getUser()->emailConfirmed}{/link} {* this line ends with a space *}
Your activation code is: {@$mailbox->getUser()->activationCode} {* this line ends with a space *}
diff --git a/wcfsetup/setup/db/install.sql b/wcfsetup/setup/db/install.sql
index 50def4fe5e..64ae131b62 100644
--- a/wcfsetup/setup/db/install.sql
+++ b/wcfsetup/setup/db/install.sql
@@ -1452,6 +1452,7 @@ CREATE TABLE wcf1_user (
banReason MEDIUMTEXT NULL,
banExpires INT(10) NOT NULL DEFAULT 0,
activationCode INT(10) NOT NULL DEFAULT 0,
+ emailConfirmed CHAR(40) DEFAULT NULL,
lastLostPasswordRequestTime INT(10) NOT NULL DEFAULT 0,
lostPasswordKey CHAR(40) DEFAULT NULL,
lastUsernameChange INT(10) NOT NULL DEFAULT 0,
--
2.20.1