1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-23 14:00:56 +01:00

Move "Password Reset" email to "PeopleMailEngine"

Summary:
Ref T13343. This makes "Password Reset" email a little more consistent with other modern types of email. My expectation is that this patch has no functional changes, just organizes code a little more consistently.

The new `setRecipientAddress()` mechanism deals with the case where the user types a secondary (but still verified) address.

Test Plan:
  - Sent a normal "login with email" email.
  - Sent a "login with email to set password" email by trying to set a password on an account with no password yet.
  - Tried to email reset a bot account (no dice: they can't do web logins so this operation isn't valid).
  - Tested existing "PeopleMailEngine" subclasses:
    - Created a new user and sent a "welcome" email.
    - Renamed a user and sent a "username changed" email.
  - Reviewed all generated mail with `bin/mail list-outbound`.

Reviewers: amckinley

Reviewed By: amckinley

Maniphest Tasks: T13343

Differential Revision: https://secure.phabricator.com/D20662
This commit is contained in:
epriestley 2019-07-19 07:03:55 -07:00
parent f55aac49f4
commit 5dd4895001
6 changed files with 151 additions and 65 deletions

View file

@ -4001,6 +4001,7 @@ phutil_register_library_map(array(
'PhabricatorPeopleDeleteController' => 'applications/people/controller/PhabricatorPeopleDeleteController.php',
'PhabricatorPeopleDetailsProfileMenuItem' => 'applications/people/menuitem/PhabricatorPeopleDetailsProfileMenuItem.php',
'PhabricatorPeopleDisableController' => 'applications/people/controller/PhabricatorPeopleDisableController.php',
'PhabricatorPeopleEmailLoginMailEngine' => 'applications/people/mail/PhabricatorPeopleEmailLoginMailEngine.php',
'PhabricatorPeopleEmpowerController' => 'applications/people/controller/PhabricatorPeopleEmpowerController.php',
'PhabricatorPeopleExternalPHIDType' => 'applications/people/phid/PhabricatorPeopleExternalPHIDType.php',
'PhabricatorPeopleIconSet' => 'applications/people/icon/PhabricatorPeopleIconSet.php',
@ -10231,6 +10232,7 @@ phutil_register_library_map(array(
'PhabricatorPeopleDeleteController' => 'PhabricatorPeopleController',
'PhabricatorPeopleDetailsProfileMenuItem' => 'PhabricatorProfileMenuItem',
'PhabricatorPeopleDisableController' => 'PhabricatorPeopleController',
'PhabricatorPeopleEmailLoginMailEngine' => 'PhabricatorPeopleMailEngine',
'PhabricatorPeopleEmpowerController' => 'PhabricatorPeopleController',
'PhabricatorPeopleExternalPHIDType' => 'PhabricatorPHIDType',
'PhabricatorPeopleIconSet' => 'PhabricatorIconSet',

View file

@ -94,29 +94,34 @@ final class PhabricatorEmailLoginController
}
if (!$errors) {
$body = $this->newAccountLoginMailBody(
$target_user,
$is_logged_in);
$target_address = new PhutilEmailAddress($target_email->getAddress());
$mail_engine = id(new PhabricatorPeopleEmailLoginMailEngine())
->setSender($viewer)
->setRecipient($target_user)
->setRecipientAddress($target_address);
try {
$mail_engine->validateMail();
} catch (PhabricatorPeopleMailEngineException $ex) {
return $this->newDialog()
->setTitle($ex->getTitle())
->appendParagraph($ex->getBody())
->addCancelButton('/auth/start/', pht('Done'));
}
$mail_engine->sendMail();
if ($is_logged_in) {
$subject = pht('[Phabricator] Account Password Link');
$instructions = pht(
'An email has been sent containing a link you can use to set '.
'a password for your account.');
} else {
$subject = pht('[Phabricator] Account Login Link');
$instructions = pht(
'An email has been sent containing a link you can use to log '.
'in to your account.');
}
$mail = id(new PhabricatorMetaMTAMail())
->setSubject($subject)
->setForceDelivery(true)
->addRawTos(array($target_email->getAddress()))
->setBody($body)
->saveAndSend();
return $this->newDialog()
->setTitle(pht('Check Your Email'))
->setShortTitle(pht('Email Sent'))
@ -182,55 +187,6 @@ final class PhabricatorEmailLoginController
->addSubmitButton(pht('Send Email'));
}
private function newAccountLoginMailBody(
PhabricatorUser $user,
$is_logged_in) {
$engine = new PhabricatorAuthSessionEngine();
$uri = $engine->getOneTimeLoginURI(
$user,
null,
PhabricatorAuthSessionEngine::ONETIME_RESET);
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
$have_passwords = $this->isPasswordAuthEnabled();
if ($have_passwords) {
if ($is_logged_in) {
$body = pht(
'You can use this link to set a password on your account:'.
"\n\n %s\n",
$uri);
} else if ($is_serious) {
$body = pht(
"You can use this link to reset your Phabricator password:".
"\n\n %s\n",
$uri);
} else {
$body = pht(
"Condolences on forgetting your password. You can use this ".
"link to reset it:\n\n".
" %s\n\n".
"After you set a new password, consider writing it down on a ".
"sticky note and attaching it to your monitor so you don't ".
"forget again! Choosing a very short, easy-to-remember password ".
"like \"cat\" or \"1234\" might also help.\n\n".
"Best Wishes,\nPhabricator\n",
$uri);
}
} else {
$body = pht(
"You can use this login link to regain access to your Phabricator ".
"account:".
"\n\n".
" %s\n",
$uri);
}
return $body;
}
private function isPasswordAuthEnabled() {
return (bool)PhabricatorPasswordAuthProvider::getPasswordProvider();
}

View file

@ -0,0 +1,107 @@
<?php
final class PhabricatorPeopleEmailLoginMailEngine
extends PhabricatorPeopleMailEngine {
public function validateMail() {
$recipient = $this->getRecipient();
if ($recipient->getIsDisabled()) {
$this->throwValidationException(
pht('User is Disabled'),
pht(
'You can not send an email login link to this email address '.
'because the associated user account is disabled.'));
}
if (!$recipient->canEstablishWebSessions()) {
$this->throwValidationException(
pht('Not a Normal User'),
pht(
'You can not send an email login link to this email address '.
'because the associated user account is not a normal user account '.
'and can not log in to the web interface.'));
}
}
protected function newMail() {
$is_set_password = $this->isSetPasswordWorkflow();
if ($is_set_password) {
$subject = pht('[Phabricator] Account Password Link');
} else {
$subject = pht('[Phabricator] Account Login Link');
}
$recipient = $this->getRecipient();
$engine = new PhabricatorAuthSessionEngine();
$login_uri = $engine->getOneTimeLoginURI(
$recipient,
null,
PhabricatorAuthSessionEngine::ONETIME_RESET);
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
$have_passwords = $this->isPasswordAuthEnabled();
if ($have_passwords) {
if ($is_set_password) {
$body = pht(
'You can use this link to set a password on your account:'.
"\n\n %s\n",
$login_uri);
} else if ($is_serious) {
$body = pht(
"You can use this link to reset your Phabricator password:".
"\n\n %s\n",
$login_uri);
} else {
$body = pht(
"Condolences on forgetting your password. You can use this ".
"link to reset it:\n\n".
" %s\n\n".
"After you set a new password, consider writing it down on a ".
"sticky note and attaching it to your monitor so you don't ".
"forget again! Choosing a very short, easy-to-remember password ".
"like \"cat\" or \"1234\" might also help.\n\n".
"Best Wishes,\nPhabricator\n",
$login_uri);
}
} else {
$body = pht(
"You can use this login link to regain access to your Phabricator ".
"account:".
"\n\n".
" %s\n",
$login_uri);
}
return id(new PhabricatorMetaMTAMail())
->setSubject($subject)
->setBody($body);
}
private function isPasswordAuthEnabled() {
return (bool)PhabricatorPasswordAuthProvider::getPasswordProvider();
}
private function isSetPasswordWorkflow() {
$sender = $this->getSender();
$recipient = $this->getRecipient();
// Users can hit the "login with an email link" workflow while trying to
// set a password on an account which does not yet have a password. We
// require they verify that they own the email address and send them
// through the email login flow. In this case, the messaging is slightly
// different.
if ($sender->getPHID()) {
if ($sender->getPHID() === $recipient->getPHID()) {
return true;
}
}
return false;
}
}

View file

@ -5,6 +5,7 @@ abstract class PhabricatorPeopleMailEngine
private $sender;
private $recipient;
private $recipientAddress;
final public function setSender(PhabricatorUser $sender) {
$this->sender = $sender;
@ -30,6 +31,22 @@ abstract class PhabricatorPeopleMailEngine
return $this->recipient;
}
final public function setRecipientAddress(PhutilEmailAddress $address) {
$this->recipientAddress = $address;
return $this;
}
final public function getRecipientAddress() {
if (!$this->recipientAddress) {
throw new PhutilInvalidStateException('recipientAddress');
}
return $this->recipientAddress;
}
final public function hasRecipientAddress() {
return ($this->recipientAddress !== null);
}
final public function canSendMail() {
try {
$this->validateMail();
@ -43,6 +60,14 @@ abstract class PhabricatorPeopleMailEngine
$this->validateMail();
$mail = $this->newMail();
if ($this->hasRecipientAddress()) {
$recipient_address = $this->getRecipientAddress();
$mail->addRawTos(array($recipient_address->getAddress()));
} else {
$recipient = $this->getRecipient();
$mail->addTos(array($recipient->getPHID()));
}
$mail
->setForceDelivery(true)
->save();
@ -53,7 +78,6 @@ abstract class PhabricatorPeopleMailEngine
abstract public function validateMail();
abstract protected function newMail();
final protected function throwValidationException($title, $body) {
throw new PhabricatorPeopleMailEngineException($title, $body);
}

View file

@ -30,7 +30,6 @@ final class PhabricatorPeopleUsernameMailEngine
protected function newMail() {
$sender = $this->getSender();
$recipient = $this->getRecipient();
$sender_username = $sender->getUsername();
$sender_realname = $sender->getRealName();
@ -52,7 +51,6 @@ final class PhabricatorPeopleUsernameMailEngine
$new_username));
return id(new PhabricatorMetaMTAMail())
->addTos(array($recipient->getPHID()))
->setSubject(pht('[Phabricator] Username Changed'))
->setBody($body);
}

View file

@ -104,7 +104,6 @@ final class PhabricatorPeopleWelcomeMailEngine
$message = implode("\n\n", $message);
return id(new PhabricatorMetaMTAMail())
->addTos(array($recipient->getPHID()))
->setSubject(pht('[Phabricator] Welcome to Phabricator'))
->setBody($message);
}