Add email notification when using a backup code
authorTim Düsterhus <duesterhus@woltlab.com>
Tue, 24 Nov 2020 13:21:54 +0000 (14:21 +0100)
committerTim Düsterhus <duesterhus@woltlab.com>
Tue, 24 Nov 2020 13:21:54 +0000 (14:21 +0100)
wcfsetup/install/files/lib/system/user/multifactor/BackupMultifactorMethod.class.php
wcfsetup/install/lang/de.xml
wcfsetup/install/lang/en.xml

index 05d8fc4b5d3dcf00b8e2baa4374c96c8e0675e01..dbd7d44bc2080cd85c7af4e8e886fbc74f185c40 100644 (file)
@@ -1,5 +1,6 @@
 <?php
 namespace wcf\system\user\multifactor;
+use wcf\system\email\SimpleEmail;
 use wcf\system\flood\FloodControl;
 use wcf\system\form\builder\container\FormContainer;
 use wcf\system\form\builder\field\ButtonFormField;
@@ -275,5 +276,47 @@ class BackupMultifactorMethod implements IMultifactorMethod {
                if ($statement->getAffectedRows() !== 1) {
                        throw new \RuntimeException('Unable to invalidate the code.');
                }
+               
+               $this->sendAuthenticationEmail($setup, $usedCode);
+       }
+       
+       /**
+        * Notifies the user that an emergency code has been used.
+        */
+       private function sendAuthenticationEmail(Setup $setup, array $usedCode): void {
+               $sql = "SELECT  COUNT(*) - COUNT(useTime) AS count
+                       FROM    wcf".WCF_N."_user_multifactor_backup
+                       WHERE   setupID = ?";
+               $statement = WCF::getDB()->prepareStatement($sql);
+               $statement->execute([$setup->getId()]);
+               
+               $remaining = $statement->fetchSingleColumn();
+       
+               $email = new SimpleEmail();
+               $email->setRecipient($setup->getUser());
+               
+               $email->setSubject(
+                       WCF::getLanguage()->getDynamicVariable('wcf.user.security.multifactor.backup.authenticationEmail.subject', [
+                               'remaining' => $remaining,
+                               'usedCode' => $usedCode,
+                               'setup' => $setup,
+                       ])
+               );
+               $email->setHtmlMessage(
+                       WCF::getLanguage()->getDynamicVariable('wcf.user.security.multifactor.backup.authenticationEmail.body.html', [
+                               'remaining' => $remaining,
+                               'usedCode' => $usedCode,
+                               'setup' => $setup,
+                       ])
+               );
+               $email->setMessage(
+                       WCF::getLanguage()->getDynamicVariable('wcf.user.security.multifactor.backup.authenticationEmail.body.plain', [
+                               'remaining' => $remaining,
+                               'usedCode' => $usedCode,
+                               'setup' => $setup,
+                       ])
+               );
+               
+               $email->send();
        }
 }
index 994acdc89dd528ecbd83931dda2bbf5063d5d62b..eaf9674fdb7fbed8da5d1e26f9a6696f452951bc 100644 (file)
@@ -4920,6 +4920,17 @@ Die E-Mail-Adresse des neuen Benutzers lautet: {@$user->email}
                <item name="wcf.user.security.multifactor.disable.confirm.required"><![CDATA[{if LANGUAGE_USE_INFORMAL_VARIANT}Bitte bestätige, dass du die Hinweise gelesen hast und mit der Deaktivierung fortfahren möchtest.{else}Bitte bestätigen Sie, dass Sie die Hinweise gelesen haben und mit der Deaktivierung fortfahren möchten.{/if}]]></item>
                <item name="wcf.user.security.multifactor.disable.success"><![CDATA[Das Verfahren <strong>{lang}wcf.user.security.multifactor.{$setup->getObjectType()->objectType}{/lang}</strong> wurde erfolgreich deaktiviert.]]></item>
                <item name="wcf.user.security.multifactor.disable.success.full"><![CDATA[Die Mehrfaktor-Authentifizierung wurde erfolgreich deaktiviert.]]></item>
+               <item name="wcf.user.security.multifactor.backup.authenticationEmail.subject"><![CDATA[Authentifizierung mittels Notfall-Code auf {@PAGE_TITLE|language}]]></item>
+               <item name="wcf.user.security.multifactor.backup.authenticationEmail.body.html"><![CDATA[<h2>Hallo {$setup->getUser()->username},</h2>
+
+<p>{if LANGUAGE_USE_INFORMAL_VARIANT}Du hast{else}Sie haben{/if} den Notfall-Code <code>{$usedCode[identifier]}</code> zur Mehrfaktor-Authentifizierung genutzt. Dieser Code ist nun nicht mehr gültig. {plural value=$remaining 0='<b>Es gibt keine weiteren gültigen Codes.</b>' 1='Es verbleibt ein gültiger Code.' other='Es verbleiben # gültige Codes.'}</p>
+
+<p>{if LANGUAGE_USE_INFORMAL_VARIANT}Du kannst{else}Sie können{/if} die Mehrfaktor-Authentifizierung in der <a href="{link controller='AccountSecurity' isHtmlEmail=true}{/link}">Account-Sicherheit</a> verwalten und dort neue Notfall-Codes generieren oder die Mehrfaktor-Authentifizierung deaktivieren.</p>]]></item>
+               <item name="wcf.user.security.multifactor.backup.authenticationEmail.body.plain"><![CDATA[Hallo {$setup->getUser()->username},\r
+\r
+{if LANGUAGE_USE_INFORMAL_VARIANT}Du hast{else}Sie haben{/if} den Notfall-Code „{$usedCode[identifier]}“ zur Mehrfaktor-Authentifizierung genutzt. Dieser Code ist nun nicht mehr gültig. {plural value=$remaining 0='**Es gibt keine weiteren gültigen Codes.**' 1='Es verbleibt ein gültiger Code.' other='Es verbleiben # gültige Codes.'} {* this line ends with a space *}\r
+\r
+{if LANGUAGE_USE_INFORMAL_VARIANT}Du kannst{else}Sie können{/if} die Mehrfaktor-Authentifizierung in der Account-Sicherheit [URL:{link controller='AccountSecurity' isEmail=true}{/link}] verwalten und dort neue Notfall-Codes generieren oder die Mehrfaktor-Authentifizierung deaktivieren.]]></item>
        </category>
        <category name="wcf.user.trophy">
                <item name="wcf.user.trophy.trophyPoints"><![CDATA[Trophäen]]></item>
index f440faba8fc89ef20774d27182aaeadec160ceba..d0045d3e5b95593647432b25302e23dcbd1193f9 100644 (file)
@@ -4917,6 +4917,17 @@ Open the link below to access the user profile:
                <item name="wcf.user.security.multifactor.disable.confirm.required"><![CDATA[Please confirm that you read the explanation and that you would like to proceed.]]></item>
                <item name="wcf.user.security.multifactor.disable.success"><![CDATA[The <strong>{lang}wcf.user.security.multifactor.{$setup->getObjectType()->objectType}{/lang}</strong> method has successfully been disabled.]]></item>
                <item name="wcf.user.security.multifactor.disable.success.full"><![CDATA[The multi-factor authentication has successfully been disabled.]]></item>
+               <item name="wcf.user.security.multifactor.backup.authenticationEmail.subject"><![CDATA[Authentication using emergency code on {@PAGE_TITLE|language}]]></item>
+               <item name="wcf.user.security.multifactor.backup.authenticationEmail.body.html"><![CDATA[<h2>Dear {$setup->getUser()->username},</h2>
+
+<p>You used the emergency code <code>{$usedCode[identifier]}</code> for multi-factor authentication. This code no longer is valid. {plural value=$remaining 0='<b>You don't have any remaining codes.</b>' 1='You have one remaining code.' other='You have # remaining codes.'}</p>
+
+<p>You can manage multi-factor authentication within the <a href="{link controller='AccountSecurity' isHtmlEmail=true}{/link}">Account Security</a> page. Within account security you can generate new emergency codes or disable multi-factor authentication.</p>]]></item>
+               <item name="wcf.user.security.multifactor.backup.authenticationEmail.body.plain"><![CDATA[Dear {$setup->getUser()->username},\r
+\r
+You used the emergency code “{$usedCode[identifier]}“ for multi-factor authentication. This code no longer is valid. {plural value=$remaining 0='**You don't have any remaining codes.**' 1='You have one remaining code.' other='You have # remaining codes.'} {* this line ends with a space *}\r
+\r
+You can manage multi-factor authentication within the Account Security page [URL:{link controller='AccountSecurity' isEmail=true}{/link}]. Within account security you can generate new emergency codes or disable multi-factor authentication.]]></item>
        </category>
        <category name="wcf.user.trophy">
                <item name="wcf.user.trophy.trophyPoints"><![CDATA[Trophies]]></item>