use wcf\data\object\type\ObjectTypeCache;
use wcf\data\user\UserEditor;
use wcf\form\AbstractFormBuilderForm;
+use wcf\system\background\BackgroundQueueHandler;
+use wcf\system\email\SimpleEmail;
use wcf\system\exception\IllegalLinkException;
use wcf\system\exception\PermissionDeniedException;
use wcf\system\form\builder\FormDocument;
$this->setup = $setup;
+ $sendEmail = false;
if (!$this->hasBackupCodes()) {
$this->generateBackupCodes();
+ $sendEmail = true;
}
$this->enableMultifactorAuth();
WCF::getDB()->commitTransaction();
+ // Send the email outside of the transaction.
+ //
+ // Sending the email is not an absolute requirement and queueing some external
+ // process that might need to be rolled back is a bit wonky from an UX perspective.
+ if ($sendEmail) {
+ $this->sendEmail();
+ }
+
$this->saved();
}
}
/**
- * Enables multifactor authentication for the user.
+ * Enables multi-factor authentication for the user.
*/
protected function enableMultifactorAuth(): void {
// This method intentionally does not use UserAction to prevent
]);
}
+ /**
+ * Sends an email letting the user know that multi-factor authentication
+ * is enabled now.
+ */
+ protected function sendEmail(): void {
+ $email = new SimpleEmail();
+ $email->setRecipient(WCF::getUser());
+
+ $email->setSubject(
+ WCF::getLanguage()->getDynamicVariable('wcf.user.security.multifactor.setupEmail.subject', [
+ 'user' => WCF::getUser(),
+ 'method' => $this->method,
+ 'backupMethod' => $this->getBackupCodesObjectType(),
+ ])
+ );
+ $email->setHtmlMessage(
+ WCF::getLanguage()->getDynamicVariable('wcf.user.security.multifactor.setupEmail.body.html', [
+ 'user' => WCF::getUser(),
+ 'method' => $this->method,
+ 'backupMethod' => $this->getBackupCodesObjectType(),
+ ])
+ );
+ $email->setMessage(
+ WCF::getLanguage()->getDynamicVariable('wcf.user.security.multifactor.setupEmail.body.plain', [
+ 'user' => WCF::getUser(),
+ 'method' => $this->method,
+ 'backupMethod' => $this->getBackupCodesObjectType(),
+ ])
+ );
+
+ $jobs = $email->getEmail()->getJobs();
+ foreach ($jobs as $job) {
+ // Wait 15 minutes to give the user a bit of time to complete the process of
+ // storing their backup codes somewhere without distracting them by causing
+ // a notification to arrive.
+ // It also allows us to remind the user of the importance of the backup codes
+ // in case they simply close the tab without reading, similar to remarketing
+ // emails.
+ BackgroundQueueHandler::getInstance()->enqueueIn(
+ $job,
+ 15 * 60
+ );
+ }
+ }
+
/**
* @inheritDoc
*/
{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 *}
{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>
+ <item name="wcf.user.security.multifactor.setupEmail.subject"><![CDATA[Mehrfaktor-Authentifizierung auf {@PAGE_TITLE|language} aktiviert]]></item>
+ <item name="wcf.user.security.multifactor.setupEmail.body.html"><![CDATA[<h2>Hallo {$user->username},</h2>
+
+<p>{if LANGUAGE_USE_INFORMAL_VARIANT}Du hast{else}Sie haben{/if} die Mehrfaktor-Authentifizierung mit dem Verfahren <b>{lang}wcf.user.security.multifactor.{$method->objectType}{/lang}</b> erfolgreich aktiviert. {if LANGUAGE_USE_INFORMAL_VARIANT}Du wirst{else}Sie werden{/if} von nun an bei jedem Login den zusätzlichen Faktor benötigen.</p>
+
+<p>{if LANGUAGE_USE_INFORMAL_VARIANT}Du hast{else}Sie haben{/if} auch eine Liste von Notfallcodes erhalten, falls der zusätzliche Faktor unbrauchbar werden sollte. <b>{if LANGUAGE_USE_INFORMAL_VARIANT}Hast du dir{else}Haben Sie sich{/if} die Notfallcodes notiert?</b> Falls {if LANGUAGE_USE_INFORMAL_VARIANT}du es vergessen hast, kannst du{else}Sie es vergessen haben, können Sie{/if} dies nachholen, indem {if LANGUAGE_USE_INFORMAL_VARIANT}du{else}Sie{/if} in der <a href="{link controller='MultifactorManage' object=$backupMethod isHtmlEmail=true}{/link}">Verwaltung der Notfallcodes</a> neue Notfallcodes {if LANGUAGE_USE_INFORMAL_VARIANT}generierst{else}generieren{/if}.</p>]]></item>
+ <item name="wcf.user.security.multifactor.setupEmail.body.plain"><![CDATA[Hallo {$user->username},\r
+\r
+{if LANGUAGE_USE_INFORMAL_VARIANT}Du hast{else}Sie haben{/if} die Mehrfaktor-Authentifizierung mit dem Verfahren „{lang}wcf.user.security.multifactor.{$method->objectType}{/lang}“ erfolgreich aktiviert. {if LANGUAGE_USE_INFORMAL_VARIANT}Du wirst{else}Sie werden{/if} von nun an bei jedem Login den zusätzlichen Faktor benötigen.\r
+\r
+{if LANGUAGE_USE_INFORMAL_VARIANT}Du hast{else}Sie haben{/if} auch eine Liste von Notfallcodes erhalten, falls der zusätzliche Faktor unbrauchbar werden sollte. **{if LANGUAGE_USE_INFORMAL_VARIANT}Hast du dir{else}Haben Sie sich{/if} die Notfallcodes notiert?** Falls {if LANGUAGE_USE_INFORMAL_VARIANT}du es vergessen hast, kannst du{else}Sie es vergessen haben, können Sie{/if} dies nachholen, indem {if LANGUAGE_USE_INFORMAL_VARIANT}du{else}Sie{/if} in der Verwaltung der Notfallcodes [URL:{link controller='MultifactorManage' object=$backupMethod isEmail=true}{/link}] neue Notfallcodes {if LANGUAGE_USE_INFORMAL_VARIANT}generierst{else}generieren{/if}.]]></item>
</category>
<category name="wcf.user.trophy">
<item name="wcf.user.trophy.trophyPoints"><![CDATA[Trophäen]]></item>
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 *}
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>
+ <item name="wcf.user.security.multifactor.setupEmail.subject"><![CDATA[Multi-factor Authentication Enabled on {@PAGE_TITLE|language}]]></item>
+ <item name="wcf.user.security.multifactor.setupEmail.body.html"><![CDATA[<h2>Dear {$user->username},</h2>
+
+<p>You successfully enabled multi-factor authentication using the <b>{lang}wcf.user.security.multifactor.{$method->objectType}{/lang}</b> method. Going forward you will need to have your second factor handy for every login.</p>
+
+<p>You also received a list of emergency codes to use when your second factor becomes unavailable. <b>Did you store these emergency codes securely?</b> If you forgot you can do now by generating new codes in the <a href="{link controller='MultifactorManage' object=$backupMethod isHtmlEmail=true}{/link}">Emergency Code Management</a>.</p>]]></item>
+ <item name="wcf.user.security.multifactor.setupEmail.body.plain"><![CDATA[Dear {$user->username},\r
+\r
+You successfully enabled multi-factor authentication using the „{lang}wcf.user.security.multifactor.{$method->objectType}{/lang}“ method. Going forward you will need to have your second factor handy for every login.\r
+\r
+You also received a list of emergency codes to use when your second factor becomes unavailable. **Did you store these emergency codes securely?** If you forgot you can do now by generating new codes in the Emergency Code Management [URL:{link controller='MultifactorManage' object=$backupMethod isEmail=true}{/link}].]]></item>
</category>
<category name="wcf.user.trophy">
<item name="wcf.user.trophy.trophyPoints"><![CDATA[Trophies]]></item>