@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
at recaptime-dev/main 207 lines 6.8 kB view raw
1<?php 2 3final class PhabricatorEmailLoginController 4 extends PhabricatorAuthController { 5 6 public function shouldRequireLogin() { 7 return false; 8 } 9 10 public function handleRequest(AphrontRequest $request) { 11 $viewer = $this->getViewer(); 12 $is_logged_in = $viewer->isLoggedIn(); 13 14 $e_email = true; 15 $e_captcha = true; 16 $errors = array(); 17 18 if ($is_logged_in) { 19 if (!$this->isPasswordAuthEnabled()) { 20 return $this->newDialog() 21 ->setTitle(pht('No Password Auth')) 22 ->appendParagraph( 23 pht( 24 'Password authentication is not enabled and you are already '. 25 'logged in. There is nothing for you here.')) 26 ->addCancelButton('/', pht('Continue')); 27 } 28 29 $v_email = $viewer->loadPrimaryEmailAddress(); 30 } else { 31 $v_email = $request->getStr('email'); 32 } 33 34 if ($request->isFormPost()) { 35 $e_email = null; 36 $e_captcha = pht('Again'); 37 38 if (!$is_logged_in) { 39 $captcha_ok = AphrontFormRecaptchaControl::processCaptcha($request); 40 if (!$captcha_ok) { 41 $errors[] = pht('Captcha response is incorrect, try again.'); 42 $e_captcha = pht('Invalid'); 43 } 44 } 45 46 if (!strlen($v_email)) { 47 $errors[] = pht('You must provide an email address.'); 48 $e_email = pht('Required'); 49 } 50 51 if (!$errors) { 52 // NOTE: Don't validate the email unless the captcha is good; this makes 53 // it expensive to fish for valid email addresses while giving the user 54 // a better error if they goof their email. 55 56 $action_actor = PhabricatorSystemActionEngine::newActorFromRequest( 57 $request); 58 59 PhabricatorSystemActionEngine::willTakeAction( 60 array($action_actor), 61 new PhabricatorAuthTryEmailLoginAction(), 62 1); 63 64 $target_email = id(new PhabricatorUserEmail())->loadOneWhere( 65 'address = %s', 66 $v_email); 67 68 $target_user = null; 69 if ($target_email) { 70 $target_user = id(new PhabricatorUser())->loadOneWhere( 71 'phid = %s', 72 $target_email->getUserPHID()); 73 } 74 75 if (!$target_user) { 76 $errors[] = 77 pht('There is no account associated with that email address.'); 78 $e_email = pht('Invalid'); 79 } 80 81 // If this address is unverified, only send a reset link to it if 82 // the account has no verified addresses. This prevents an opportunistic 83 // attacker from compromising an account if a user adds an email 84 // address but mistypes it and doesn't notice. 85 86 // (For a newly created account, all the addresses may be unverified, 87 // which is why we'll send to an unverified address in that case.) 88 89 if ($target_email && !$target_email->getIsVerified()) { 90 $verified_addresses = id(new PhabricatorUserEmail())->loadAllWhere( 91 'userPHID = %s AND isVerified = 1', 92 $target_email->getUserPHID()); 93 if ($verified_addresses) { 94 $errors[] = pht( 95 'That email address is not verified, but the account it is '. 96 'connected to has at least one other verified address. When an '. 97 'account has at least one verified address, you can only send '. 98 'password reset links to one of the verified addresses. Try '. 99 'a verified address instead.'); 100 $e_email = pht('Unverified'); 101 } 102 } 103 104 if (!$errors) { 105 $target_address = new PhutilEmailAddress($target_email->getAddress()); 106 107 $user_log = PhabricatorUserLog::initializeNewLog( 108 $viewer, 109 $target_user->getPHID(), 110 PhabricatorEmailLoginUserLogType::LOGTYPE); 111 112 $mail_engine = id(new PhabricatorPeopleEmailLoginMailEngine()) 113 ->setSender($viewer) 114 ->setRecipient($target_user) 115 ->setRecipientAddress($target_address) 116 ->setActivityLog($user_log); 117 118 try { 119 $mail_engine->validateMail(); 120 } catch (PhabricatorPeopleMailEngineException $ex) { 121 return $this->newDialog() 122 ->setTitle($ex->getTitle()) 123 ->appendParagraph($ex->getBody()) 124 ->addCancelButton('/auth/start/', pht('Done')); 125 } 126 127 $mail_engine->sendMail(); 128 129 if ($is_logged_in) { 130 $instructions = pht( 131 'An email has been sent containing a link you can use to set '. 132 'a password for your account.'); 133 } else { 134 $instructions = pht( 135 'An email has been sent containing a link you can use to log '. 136 'in to your account.'); 137 } 138 139 return $this->newDialog() 140 ->setTitle(pht('Check Your Email')) 141 ->setShortTitle(pht('Email Sent')) 142 ->appendParagraph($instructions) 143 ->addCancelButton('/', pht('Done')); 144 } 145 } 146 } 147 148 $form = id(new AphrontFormView()) 149 ->setViewer($viewer); 150 151 if ($this->isPasswordAuthEnabled()) { 152 if ($is_logged_in) { 153 $title = pht('Set Password'); 154 $form->appendRemarkupInstructions( 155 pht( 156 'A password reset link will be sent to your primary email '. 157 'address. Follow the link to set an account password.')); 158 } else { 159 $title = pht('Password Reset'); 160 $form->appendRemarkupInstructions( 161 pht( 162 'To reset your password, provide your email address. An email '. 163 'with a login link will be sent to you.')); 164 } 165 } else { 166 $title = pht('Email Login'); 167 $form->appendRemarkupInstructions( 168 pht( 169 'To access your account, provide your email address. An email '. 170 'with a login link will be sent to you.')); 171 } 172 173 if ($is_logged_in) { 174 $address_control = new AphrontFormStaticControl(); 175 } else { 176 $address_control = id(new AphrontFormTextControl()) 177 ->setName('email') 178 ->setError($e_email); 179 } 180 181 $address_control 182 ->setLabel(pht('Email Address')) 183 ->setValue($v_email); 184 185 $form 186 ->appendControl($address_control); 187 188 if (!$is_logged_in) { 189 $form->appendControl( 190 id(new AphrontFormRecaptchaControl()) 191 ->setLabel(pht('Captcha')) 192 ->setError($e_captcha)); 193 } 194 195 return $this->newDialog() 196 ->setTitle($title) 197 ->setErrors($errors) 198 ->setWidth(AphrontDialogView::WIDTH_FORM) 199 ->appendForm($form) 200 ->addCancelButton('/auth/start/') 201 ->addSubmitButton(pht('Send Email')); 202 } 203 204 private function isPasswordAuthEnabled() { 205 return (bool)PhabricatorPasswordAuthProvider::getPasswordProvider(); 206 } 207}