@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
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Always allow users to login via email link, even if an install does not use passwords

Summary:
Depends on D20099. Ref T13244. See PHI774. When password auth is enabled, we support a standard email-based account recovery mechanism with "Forgot password?".

When password auth is not enabled, we disable the self-serve version of this mechanism. You can still get email account login links via "Send Welcome Mail" or "bin/auth recover".

There's no real technical, product, or security reason not to let everyone do email login all the time. On the technical front, these links already work and are used in other contexts. On the product front, we just need to tweak a couple of strings.

On the security front, there's some argument that this mechanism provides more overall surface area for an attacker, but if we find that argument compelling we should probably provide a way to disable the self-serve pathway in all cases, rather than coupling it to which providers are enabled.

Also, inch toward having things iterate over configurations (saved database objects) instead of providers (abstract implementations) so we can some day live in a world where we support multiple configurations of the same provider type (T6703).

Test Plan:
- With password auth enabled, reset password.
- Without password auth enabled, did an email login recovery.

{F6184910}

Reviewers: amckinley

Reviewed By: amckinley

Maniphest Tasks: T13244

Differential Revision: https://secure.phabricator.com/D20100

+125 -60
+47
src/applications/auth/controller/PhabricatorAuthStartController.php
··· 75 75 } 76 76 } 77 77 78 + $configs = array(); 79 + foreach ($providers as $provider) { 80 + $configs[] = $provider->getProviderConfig(); 81 + } 82 + 78 83 if (!$providers) { 79 84 if ($this->isFirstTimeSetup()) { 80 85 // If this is a fresh install, let the user register their admin ··· 179 184 180 185 $custom_message = $this->newCustomStartMessage(); 181 186 187 + $email_login = $this->newEmailLoginView($configs); 188 + 182 189 $crumbs = $this->buildApplicationCrumbs(); 183 190 $crumbs->addTextCrumb(pht('Login')); 184 191 $crumbs->setBorder(true); ··· 188 195 $invite_message, 189 196 $custom_message, 190 197 $out, 198 + $email_login, 191 199 ); 192 200 193 201 return $this->newPage() ··· 310 318 ), 311 319 $remarkup_view); 312 320 } 321 + 322 + private function newEmailLoginView(array $configs) { 323 + assert_instances_of($configs, 'PhabricatorAuthProviderConfig'); 324 + 325 + // Check if password auth is enabled. If it is, the password login form 326 + // renders a "Forgot password?" link, so we don't need to provide a 327 + // supplemental link. 328 + 329 + $has_password = false; 330 + foreach ($configs as $config) { 331 + $provider = $config->getProvider(); 332 + if ($provider instanceof PhabricatorPasswordAuthProvider) { 333 + $has_password = true; 334 + } 335 + } 336 + 337 + if ($has_password) { 338 + return null; 339 + } 340 + 341 + $view = array( 342 + pht('Trouble logging in?'), 343 + ' ', 344 + phutil_tag( 345 + 'a', 346 + array( 347 + 'href' => '/login/email/', 348 + ), 349 + pht('Send a login link to your email address.')), 350 + ); 351 + 352 + return phutil_tag( 353 + 'div', 354 + array( 355 + 'class' => 'auth-custom-message', 356 + ), 357 + $view); 358 + } 359 + 313 360 314 361 }
+78 -60
src/applications/auth/controller/PhabricatorEmailLoginController.php
··· 8 8 } 9 9 10 10 public function handleRequest(AphrontRequest $request) { 11 - 12 - if (!PhabricatorPasswordAuthProvider::getPasswordProvider()) { 13 - return new Aphront400Response(); 14 - } 11 + $viewer = $this->getViewer(); 15 12 16 13 $e_email = true; 17 14 $e_captcha = true; 18 15 $errors = array(); 19 16 20 - $is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business'); 21 - 17 + $v_email = $request->getStr('email'); 22 18 if ($request->isFormPost()) { 23 19 $e_email = null; 24 20 $e_captcha = pht('Again'); ··· 29 25 $e_captcha = pht('Invalid'); 30 26 } 31 27 32 - $email = $request->getStr('email'); 33 - if (!strlen($email)) { 28 + if (!strlen($v_email)) { 34 29 $errors[] = pht('You must provide an email address.'); 35 30 $e_email = pht('Required'); 36 31 } ··· 42 37 43 38 $target_email = id(new PhabricatorUserEmail())->loadOneWhere( 44 39 'address = %s', 45 - $email); 40 + $v_email); 46 41 47 42 $target_user = null; 48 43 if ($target_email) { ··· 81 76 } 82 77 83 78 if (!$errors) { 84 - $engine = new PhabricatorAuthSessionEngine(); 85 - $uri = $engine->getOneTimeLoginURI( 86 - $target_user, 87 - null, 88 - PhabricatorAuthSessionEngine::ONETIME_RESET); 89 - 90 - if ($is_serious) { 91 - $body = pht( 92 - "You can use this link to reset your Phabricator password:". 93 - "\n\n %s\n", 94 - $uri); 95 - } else { 96 - $body = pht( 97 - "Condolences on forgetting your password. You can use this ". 98 - "link to reset it:\n\n". 99 - " %s\n\n". 100 - "After you set a new password, consider writing it down on a ". 101 - "sticky note and attaching it to your monitor so you don't ". 102 - "forget again! Choosing a very short, easy-to-remember password ". 103 - "like \"cat\" or \"1234\" might also help.\n\n". 104 - "Best Wishes,\nPhabricator\n", 105 - $uri); 106 - 107 - } 79 + $body = $this->newAccountLoginMailBody($target_user); 108 80 109 81 $mail = id(new PhabricatorMetaMTAMail()) 110 - ->setSubject(pht('[Phabricator] Password Reset')) 82 + ->setSubject(pht('[Phabricator] Account Login Link')) 111 83 ->setForceDelivery(true) 112 84 ->addRawTos(array($target_email->getAddress())) 113 85 ->setBody($body) ··· 123 95 } 124 96 } 125 97 126 - $error_view = null; 127 - if ($errors) { 128 - $error_view = new PHUIInfoView(); 129 - $error_view->setErrors($errors); 98 + $form = id(new AphrontFormView()) 99 + ->setViewer($viewer); 100 + 101 + if ($this->isPasswordAuthEnabled()) { 102 + $form->appendRemarkupInstructions( 103 + pht( 104 + 'To reset your password, provide your email address. An email '. 105 + 'with a login link will be sent to you.')); 106 + } else { 107 + $form->appendRemarkupInstructions( 108 + pht( 109 + 'To access your account, provide your email address. An email '. 110 + 'with a login link will be sent to you.')); 130 111 } 131 112 132 - $email_auth = new PHUIFormLayoutView(); 133 - $email_auth->appendChild($error_view); 134 - $email_auth 135 - ->setUser($request->getUser()) 136 - ->setFullWidth(true) 137 - ->appendChild( 113 + $form 114 + ->appendControl( 138 115 id(new AphrontFormTextControl()) 139 - ->setLabel(pht('Email')) 116 + ->setLabel(pht('Email Address')) 140 117 ->setName('email') 141 - ->setValue($request->getStr('email')) 118 + ->setValue($v_email) 142 119 ->setError($e_email)) 143 - ->appendChild( 120 + ->appendControl( 144 121 id(new AphrontFormRecaptchaControl()) 145 122 ->setLabel(pht('Captcha')) 146 123 ->setError($e_captcha)); 147 124 148 - $crumbs = $this->buildApplicationCrumbs(); 149 - $crumbs->addTextCrumb(pht('Reset Password')); 150 - $crumbs->setBorder(true); 125 + if ($this->isPasswordAuthEnabled()) { 126 + $title = pht('Password Reset'); 127 + } else { 128 + $title = pht('Email Login'); 129 + } 130 + 131 + return $this->newDialog() 132 + ->setTitle($title) 133 + ->setErrors($errors) 134 + ->setWidth(AphrontDialogView::WIDTH_FORM) 135 + ->appendForm($form) 136 + ->addCancelButton('/auth/start/') 137 + ->addSubmitButton(pht('Send Email')); 138 + } 139 + 140 + private function newAccountLoginMailBody(PhabricatorUser $user) { 141 + $engine = new PhabricatorAuthSessionEngine(); 142 + $uri = $engine->getOneTimeLoginURI( 143 + $user, 144 + null, 145 + PhabricatorAuthSessionEngine::ONETIME_RESET); 146 + 147 + $is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business'); 148 + $have_passwords = $this->isPasswordAuthEnabled(); 151 149 152 - $dialog = new AphrontDialogView(); 153 - $dialog->setUser($request->getUser()); 154 - $dialog->setTitle(pht('Forgot Password / Email Login')); 155 - $dialog->appendChild($email_auth); 156 - $dialog->addSubmitButton(pht('Send Email')); 157 - $dialog->setSubmitURI('/login/email/'); 150 + if ($have_passwords) { 151 + if ($is_serious) { 152 + $body = pht( 153 + "You can use this link to reset your Phabricator password:". 154 + "\n\n %s\n", 155 + $uri); 156 + } else { 157 + $body = pht( 158 + "Condolences on forgetting your password. You can use this ". 159 + "link to reset it:\n\n". 160 + " %s\n\n". 161 + "After you set a new password, consider writing it down on a ". 162 + "sticky note and attaching it to your monitor so you don't ". 163 + "forget again! Choosing a very short, easy-to-remember password ". 164 + "like \"cat\" or \"1234\" might also help.\n\n". 165 + "Best Wishes,\nPhabricator\n", 166 + $uri); 158 167 159 - return $this->newPage() 160 - ->setTitle(pht('Forgot Password')) 161 - ->setCrumbs($crumbs) 162 - ->appendChild($dialog); 168 + } 169 + } else { 170 + $body = pht( 171 + "You can use this login link to regain access to your Phabricator ". 172 + "account:". 173 + "\n\n". 174 + " %s\n", 175 + $uri); 176 + } 163 177 178 + return $body; 164 179 } 165 180 181 + private function isPasswordAuthEnabled() { 182 + return (bool)PhabricatorPasswordAuthProvider::getPasswordProvider(); 183 + } 166 184 }