Set new password in NewPasswordForm instead of mailing it
authorTim Düsterhus <duesterhus@woltlab.com>
Tue, 14 Jun 2016 19:58:59 +0000 (21:58 +0200)
committerTim Düsterhus <duesterhus@woltlab.com>
Fri, 29 Jul 2016 20:47:22 +0000 (22:47 +0200)
com.woltlab.wcf/templates/newPassword.tpl
wcfsetup/install/files/lib/form/LostPasswordForm.class.php
wcfsetup/install/files/lib/form/NewPasswordForm.class.php
wcfsetup/install/lang/de.xml
wcfsetup/install/lang/en.xml
wcfsetup/setup/db/install.sql

index 015c7632d58d5388d652985bd5e5ea8e84973332..9441088a877b2c29aa0a044765c76a5878d61f45 100644 (file)
@@ -2,35 +2,33 @@
 
 {include file='formError'}
 
+<p class="info">{lang}wcf.user.newPassword.info{/lang}</p>
+
 <form method="post" action="{link controller='NewPassword'}{/link}">
        <div class="section">
-               <dl{if $errorField == 'userID'} class="formError"{/if}>
-                       <dt>
-                               <label for="userID">{lang}wcf.user.userID{/lang}</label>
-                       </dt>
+               <dl{if $errorField == 'newPassword'} class="formError"{/if}>
+                       <dt><label for="newPassword">{lang}wcf.user.newPassword{/lang}</label></dt>
                        <dd>
-                               <input type="text" id="userID" name="u" value="{@$userID}" required class="medium">
-                               {if $errorField == 'userID'}
+                               <input type="password" id="newPassword" name="newPassword" value="{$newPassword}" class="medium">
+                                       
+                               {if $errorField == 'newPassword'}
                                        <small class="innerError">
-                                               {lang}wcf.user.userID.error.{$errorType}{/lang}
+                                               {if $errorType == 'empty'}{lang}wcf.global.form.error.empty{/lang}{/if}
+                                               {if $errorType == 'notSecure'}{lang}wcf.user.password.error.notSecure{/lang}{/if}
                                        </small>
                                {/if}
                        </dd>
                </dl>
                
-               <dl{if $errorField == 'lostPasswordKey'} class="formError"{/if}>
-                       <dt>
-                               <label for="lostPasswordKey">{lang}wcf.user.lostPasswordKey{/lang}</label>
-                       </dt>
+               <dl{if $errorField == 'confirmNewPassword'} class="formError"{/if}>
+                       <dt><label for="confirmNewPassword">{lang}wcf.user.confirmPassword{/lang}</label></dt>
                        <dd>
-                               <input type="text" id="lostPasswordKey" name="k" value="{$lostPasswordKey}" required class="medium">
-                               {if $errorField == 'lostPasswordKey'}
+                               <input type="password" id="confirmNewPassword" name="confirmNewPassword" value="{$confirmNewPassword}" class="medium">
+                                       
+                               {if $errorField == 'confirmNewPassword'}
                                        <small class="innerError">
-                                               {if $errorType == 'empty'}
-                                                       {lang}wcf.global.form.error.empty{/lang}
-                                               {else}
-                                                       {lang}wcf.user.lostPasswordKey.error.{$errorType}{/lang}
-                                               {/if}
+                                               {if $errorType == 'empty'}{lang}wcf.global.form.error.empty{/lang}{/if}
+                                               {if $errorType == 'notEqual'}{lang}wcf.user.confirmPassword.error.notEqual{/lang}{/if}
                                        </small>
                                {/if}
                        </dd>
index 11d1f7c558982f1e2befd98a2159c92ad437cc1b..3e67a26169badd30170f8adbeb1bbd50f4272837 100644 (file)
@@ -7,8 +7,8 @@ use wcf\system\exception\UserInputException;
 use wcf\system\mail\Mail;
 use wcf\system\request\LinkHandler;
 use wcf\system\WCF;
+use wcf\util\CryptoUtil;
 use wcf\util\HeaderUtil;
-use wcf\util\StringUtil;
 
 /**
  * Shows the lost password form.
@@ -95,7 +95,7 @@ class LostPasswordForm extends AbstractCaptchaForm {
                parent::save();
                
                // generate a new lost password key
-               $lostPasswordKey = StringUtil::getRandomID();
+               $lostPasswordKey = bin2hex(CryptoUtil::randomBytes(20));
                
                // save key and request time in database
                $this->objectAction = new UserAction([$this->user], 'update', [
index beb29fcfa3727b5b8ef9bca38daba9d4597f1dea..dfc53c4871bad5e843a8696c7bab1f18550f35f7 100644 (file)
@@ -2,14 +2,16 @@
 namespace wcf\form;
 use wcf\data\user\User;
 use wcf\data\user\UserAction;
+use wcf\data\user\UserEditor;
 use wcf\page\AbstractPage;
+use wcf\system\exception\IllegalLinkException;
 use wcf\system\exception\UserInputException;
-use wcf\system\mail\Mail;
 use wcf\system\request\LinkHandler;
 use wcf\system\WCF;
 use wcf\util\HeaderUtil;
 use wcf\util\PasswordUtil;
 use wcf\util\StringUtil;
+use wcf\util\UserRegistrationUtil;
 
 /**
  * Shows the new password form.
@@ -46,43 +48,71 @@ class NewPasswordForm extends AbstractForm {
         */
        public $newPassword = '';
        
+       /**
+        * confirmed new password
+        * @var string
+        */
+       public $confirmNewPassword = '';
+       
        /**
         * @inheritDoc
         */
        public function readParameters() {
                parent::readParameters();
                
-               if (isset($_REQUEST['u'])) $this->userID = intval($_REQUEST['u']);
-               if (isset($_REQUEST['k'])) $this->lostPasswordKey = StringUtil::trim($_REQUEST['k']);
+               if (isset($_GET['id'])) $this->userID = intval($_GET['id']);
+               else if (WCF::getSession()->getVar('lostPasswordRequest')) $this->userID = intval(WCF::getSession()->getVar('lostPasswordRequest'));
+               if (isset($_GET['k'])) $this->lostPasswordKey = StringUtil::trim($_GET['k']);
+               
+               $this->user = new User($this->userID);
+               if (!$this->user->userID) throw new IllegalLinkException();
                
-               // disable check for security token for GET requests
-               if ($this->userID || $this->lostPasswordKey) {
-                       $_POST['t'] = WCF::getSession()->getSecurityToken();
+               if ($this->lostPasswordKey) {
+                       if (!$this->user->lostPasswordKey) throw new IllegalLinkException();
+                       if (!PasswordUtil::secureCompare($this->user->lostPasswordKey, $this->lostPasswordKey)) {
+                               throw new IllegalLinkException();
+                       }
+                       // expire lost password requests after a day
+                       if ($this->user->lastLostPasswordRequestTime < TIME_NOW - 86400) throw new IllegalLinkException();
+                       
+                       (new UserEditor($this->user))->update([
+                               'lastLostPasswordRequestTime' => 0,
+                               'lostPasswordKey' => NULL
+                       ]);
+                       WCF::getSession()->register('lostPasswordRequest', $this->user->userID);
                }
        }
        
+       /**
+        * @inheritDoc
+        */
+       public function readFormParameters() {
+               parent::readFormParameters();
+               
+               if (isset($_POST['newPassword'])) $this->newPassword = $_POST['newPassword'];
+               if (isset($_POST['confirmNewPassword'])) $this->confirmNewPassword = $_POST['confirmNewPassword'];
+       }
+       
        /**
         * @inheritDoc
         */
        public function validate() {
                parent::validate();
                
-               // get user
-               $this->user = new User($this->userID);
-               
-               if (!$this->user->userID) {
-                       throw new UserInputException('userID', 'notValid');
+               if (empty($this->newPassword)) {
+                       throw new UserInputException('newPassword');
                }
-               if (!$this->lostPasswordKey) {
-                       throw new UserInputException('lostPasswordKey');
+               
+               if (empty($this->confirmNewPassword)) {
+                       throw new UserInputException('confirmNewPassword');
                }
                
-               if (!$this->user->lostPasswordKey) {
-                       throw new UserInputException('lostPasswordKey', 'notValid');
+               if (!UserRegistrationUtil::isSecurePassword($this->newPassword)) {
+                       throw new UserInputException('newPassword', 'notSecure');
                }
                
-               if (!PasswordUtil::secureCompare($this->user->lostPasswordKey, $this->lostPasswordKey)) {
-                       throw new UserInputException('lostPasswordKey', 'notValid');
+               if ($this->newPassword != $this->confirmNewPassword) {
+                       throw new UserInputException('confirmNewPassword', 'notEqual');
                }
        }
        
@@ -92,8 +122,7 @@ class NewPasswordForm extends AbstractForm {
        public function save() {
                parent::save();
                
-               // generate new password
-               $this->newPassword = PasswordUtil::getRandomPassword((REGISTER_PASSWORD_MIN_LENGTH > 12 ? REGISTER_PASSWORD_MIN_LENGTH : 12));
+               WCF::getSession()->unregister('lostPasswordRequest');
                
                // update user
                $this->objectAction = new UserAction([$this->user], 'update', [
@@ -105,17 +134,8 @@ class NewPasswordForm extends AbstractForm {
                ]);
                $this->objectAction->executeAction();
                
-               // send mail
-               $mail = new Mail([$this->user->username => $this->user->email], WCF::getLanguage()->getDynamicVariable('wcf.user.newPassword.mail.subject'), WCF::getLanguage()->getDynamicVariable('wcf.user.newPassword.mail', [
-                       'username' => $this->user->username,
-                       'userID' => $this->user->userID,
-                       'newPassword' => $this->newPassword
-               ]));
-               $mail->send();
-               $this->saved();
-               
                // forward to index page
-               HeaderUtil::delayedRedirect(LinkHandler::getInstance()->getLink(), WCF::getLanguage()->get('wcf.user.newPassword.success'));
+               HeaderUtil::delayedRedirect(LinkHandler::getInstance()->getLink(), WCF::getLanguage()->getDynamicVariable('wcf.user.newPassword.success', ['user' => $this->user]));
                exit;
        }
        
@@ -126,19 +146,9 @@ class NewPasswordForm extends AbstractForm {
                parent::assignVariables();
                
                WCF::getTPL()->assign([
-                       'userID' => $this->userID,
-                       'lostPasswordKey' => $this->lostPasswordKey
+                       'user' => $this->user,
+                       'newPassword' => $this->newPassword,
+                       'confirmNewPassword' => $this->confirmNewPassword
                ]);
        }
-       
-       /**
-        * @inheritDoc
-        */
-       public function readData() {
-               AbstractPage::readData();
-               
-               if (!empty($_POST) || (!empty($this->userID) && !empty($this->lostPasswordKey))) {
-                       $this->submit();
-               }
-       }
 }
index 50d4cc43bd12d16e093017fbc62c30f431d58b4f..dc1489a080aea15e3d552c3e08d29125514d0809 100644 (file)
@@ -2847,20 +2847,13 @@ Fehler sind beispielsweise:
                <item name="wcf.user.lostPassword.mail.sent"><![CDATA[{if LANGUAGE_USE_INFORMAL_VARIANT}Du erhälst{else}Sie erhalten{/if} in Kürze eine E-Mail mit weiteren Informationen.]]></item>
                <item name="wcf.user.lostPasswordKey"><![CDATA[Sicherheitsschlüssel]]></item>
                <item name="wcf.user.lostPasswordKey.error.notValid"><![CDATA[{if LANGUAGE_USE_INFORMAL_VARIANT}Du hast{else}Sie haben{/if} einen ungültigen Sicherheitsschlüssel angegeben.]]></item>
+               <item name="wcf.user.newPassword"><![CDATA[Neues Kennwort]]></item>
+               <item name="wcf.user.newPassword.info"><![CDATA[{if LANGUAGE_USE_INFORMAL_VARIANT}Du bist{else}Sie sind{/if} im Begriff das Kennwort des Benutzers „{$user->username}“ zu ändern.]]></item>
+               <item name="wcf.user.newPassword.success"><![CDATA[Das Kennwort des Benutzers „{$user->username}“ wurde erfolgreich geändert. {if LANGUAGE_USE_INFORMAL_VARIANT}Du kannst dich{else}Sie können sich{/if} nun mit dem neuen Kennwort einloggen.]]></item>
                <item name="wcf.user.userID.error.notValid"><![CDATA[{if LANGUAGE_USE_INFORMAL_VARIANT}Du hast{else}Sie haben{/if} eine ungültige Benutzer-ID angegeben.]]></item>
-               <item name="wcf.user.newPassword.mail"><![CDATA[Hallo {@$username},
-
-{if LANGUAGE_USE_INFORMAL_VARIANT}dein{else}Ihr{/if} neues Kennwort für die Website "{@PAGE_TITLE|language}" lautet:
-{@$newPassword}
-
-
-{if LANGUAGE_USE_INFORMAL_VARIANT}Du kannst dein{else}Sie können Ihr{/if} Kennwort unter folgender Adresse jederzeit ändern:
-{link controller='AccountManagement' isEmail=true}{/link} ]]></item>
-               <item name="wcf.user.newPassword.mail.subject"><![CDATA[Neues Kennwort auf der Website: {@PAGE_TITLE|language}]]></item>
-               <item name="wcf.user.newPassword.success"><![CDATA[{if LANGUAGE_USE_INFORMAL_VARIANT}Du erhälst{else}Sie erhalten{/if} in Kürze eine E-Mail mit {if LANGUAGE_USE_INFORMAL_VARIANT}deinem{else}Ihrem{/if} neuen Kennwort.]]></item>
+               <item name="wcf.user.accountManagement"><![CDATA[Benutzerkonto-Verwaltung]]></item>
                <item name="wcf.user.accountManagement.warning"><![CDATA[{if LANGUAGE_USE_INFORMAL_VARIANT}Du bearbeitest dein{else}Sie bearbeiten Ihr{/if} eigenes Benutzerkonto. Unbedachte Änderungen können dazu führen, dass {if LANGUAGE_USE_INFORMAL_VARIANT}du dich nicht mehr anmelden kannst. Bitte sei entsprechend vorsichtig!{else}Sie sich nicht mehr anmelden können. Bitte seien Sie entsprechend vorsichtig!{/if}]]></item>
                <item name="wcf.user.accountManagement.password.description"><![CDATA[Bitte {if LANGUAGE_USE_INFORMAL_VARIANT}gib zur Bestätigung dein{else}geben Sie zur Bestätigung Ihr{/if} <u>bisheriges</u> Kennwort ein!]]></item>
-               <item name="wcf.user.newPassword"><![CDATA[Neues Kennwort]]></item>
                <item name="wcf.user.changeUsername"><![CDATA[Benutzernamen ändern]]></item>
                <item name="wcf.user.changeUsername.description"><![CDATA[{if LANGUAGE_USE_INFORMAL_VARIANT}Du kannst deinen{else}Sie können Ihren{/if} Benutzernamen nur einmal alle {$renamePeriod} Tage ändern. Änderungen von Groß- auf Kleinschreibung und umgekehrt sind jederzeit möglich.
                {if $__wcf->getUser()->lastUsernameChange}Die letzte Änderung erfolgte am {@$__wcf->getUser()->lastUsernameChange|date}.{/if}]]></item>
index 739179225fea2c41ae818e93b9e834c89743b658..e4e94b849d2a92623599c4ba56bfbc3c4c4599d0 100644 (file)
@@ -2888,20 +2888,13 @@ If you have not lost your password, you can safely ignore this email.]]></item>
                <item name="wcf.user.lostPassword.mail.sent"><![CDATA[You should receive an email shortly.]]></item>
                <item name="wcf.user.lostPasswordKey"><![CDATA[Security Key]]></item>
                <item name="wcf.user.lostPasswordKey.error.notValid"><![CDATA[Security Key is invalid.]]></item>
+               <item name="wcf.user.newPassword"><![CDATA[New Password]]></item>
+               <item name="wcf.user.newPassword.info"><![CDATA[You are setting a new password for the user “{$user->username}”.]]></item>
+               <item name="wcf.user.newPassword.success"><![CDATA[The password of the user “{$user->username}” has been changed successfully. You may now login with your new password.]]></item>
                <item name="wcf.user.userID.error.notValid"><![CDATA[User ID is invalid.]]></item>
-               <item name="wcf.user.newPassword.mail"><![CDATA[Dear {@$username},
-
-your new password for "{@PAGE_TITLE|language}" is:
-{@$newPassword}
-
-
-You can change your password any time at:
-{link controller='AccountManagement' isEmail=true}{/link} ]]></item>
-               <item name="wcf.user.newPassword.mail.subject"><![CDATA[New Password for Website: {@PAGE_TITLE|language}]]></item>
-               <item name="wcf.user.newPassword.success"><![CDATA[You should receive an email with your password shortly.]]></item>
+               <item name="wcf.user.accountManagement"><![CDATA[Account Management]]></item>
                <item name="wcf.user.accountManagement.warning"><![CDATA[Heads up! You’re editing your own user account, careless changes might lock you out!]]></item>
                <item name="wcf.user.accountManagement.password.description"><![CDATA[Please confirm changes with your <u>current</u> password!]]></item>
-               <item name="wcf.user.newPassword"><![CDATA[New Password]]></item>
                <item name="wcf.user.changeUsername"><![CDATA[Change Username]]></item>
                <item name="wcf.user.changeUsername.description"><![CDATA[You may change your username every {$renamePeriod} days. Changes between uppercase or lowercase are always allowed.
                {if $__wcf->getUser()->lastUsernameChange}Last change was {@$__wcf->getUser()->lastUsernameChange|date}.{/if}]]></item>
index 494a428c399f5d4bb9587444fdee3242de6fe519..133029dc3e7a7bad71d9833918efa84298d51e3b 100644 (file)
@@ -1273,7 +1273,7 @@ CREATE TABLE wcf1_user (
        banExpires INT(10) NOT NULL DEFAULT 0,
        activationCode INT(10) NOT NULL DEFAULT 0,
        lastLostPasswordRequestTime INT(10) NOT NULL DEFAULT 0,
-       lostPasswordKey VARCHAR(40) NOT NULL DEFAULT '',
+       lostPasswordKey CHAR(40) DEFAULT NULL,
        lastUsernameChange INT(10) NOT NULL DEFAULT 0,
        newEmail VARCHAR(255) NOT NULL DEFAULT '',
        oldUsername VARCHAR(255) NOT NULL DEFAULT '',