diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -4001,6 +4001,7 @@ '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 @@ 'PhabricatorPeopleDeleteController' => 'PhabricatorPeopleController', 'PhabricatorPeopleDetailsProfileMenuItem' => 'PhabricatorProfileMenuItem', 'PhabricatorPeopleDisableController' => 'PhabricatorPeopleController', + 'PhabricatorPeopleEmailLoginMailEngine' => 'PhabricatorPeopleMailEngine', 'PhabricatorPeopleEmpowerController' => 'PhabricatorPeopleController', 'PhabricatorPeopleExternalPHIDType' => 'PhabricatorPHIDType', 'PhabricatorPeopleIconSet' => 'PhabricatorIconSet', diff --git a/src/applications/auth/controller/PhabricatorEmailLoginController.php b/src/applications/auth/controller/PhabricatorEmailLoginController.php --- a/src/applications/auth/controller/PhabricatorEmailLoginController.php +++ b/src/applications/auth/controller/PhabricatorEmailLoginController.php @@ -94,29 +94,34 @@ } 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 @@ ->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(); } diff --git a/src/applications/people/mail/PhabricatorPeopleEmailLoginMailEngine.php b/src/applications/people/mail/PhabricatorPeopleEmailLoginMailEngine.php new file mode 100644 --- /dev/null +++ b/src/applications/people/mail/PhabricatorPeopleEmailLoginMailEngine.php @@ -0,0 +1,107 @@ +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; + } + +} diff --git a/src/applications/people/mail/PhabricatorPeopleMailEngine.php b/src/applications/people/mail/PhabricatorPeopleMailEngine.php --- a/src/applications/people/mail/PhabricatorPeopleMailEngine.php +++ b/src/applications/people/mail/PhabricatorPeopleMailEngine.php @@ -5,6 +5,7 @@ private $sender; private $recipient; + private $recipientAddress; final public function setSender(PhabricatorUser $sender) { $this->sender = $sender; @@ -30,6 +31,22 @@ 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 @@ $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 public function validateMail(); abstract protected function newMail(); - final protected function throwValidationException($title, $body) { throw new PhabricatorPeopleMailEngineException($title, $body); } diff --git a/src/applications/people/mail/PhabricatorPeopleUsernameMailEngine.php b/src/applications/people/mail/PhabricatorPeopleUsernameMailEngine.php --- a/src/applications/people/mail/PhabricatorPeopleUsernameMailEngine.php +++ b/src/applications/people/mail/PhabricatorPeopleUsernameMailEngine.php @@ -30,7 +30,6 @@ protected function newMail() { $sender = $this->getSender(); - $recipient = $this->getRecipient(); $sender_username = $sender->getUsername(); $sender_realname = $sender->getRealName(); @@ -52,7 +51,6 @@ $new_username)); return id(new PhabricatorMetaMTAMail()) - ->addTos(array($recipient->getPHID())) ->setSubject(pht('[Phabricator] Username Changed')) ->setBody($body); } diff --git a/src/applications/people/mail/PhabricatorPeopleWelcomeMailEngine.php b/src/applications/people/mail/PhabricatorPeopleWelcomeMailEngine.php --- a/src/applications/people/mail/PhabricatorPeopleWelcomeMailEngine.php +++ b/src/applications/people/mail/PhabricatorPeopleWelcomeMailEngine.php @@ -104,7 +104,6 @@ $message = implode("\n\n", $message); return id(new PhabricatorMetaMTAMail()) - ->addTos(array($recipient->getPHID())) ->setSubject(pht('[Phabricator] Welcome to Phabricator')) ->setBody($message); }