@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.

Bring new password validation into AuthPasswordEngine

Summary:
Ref T13043. We have ~4 copies of this logic (registration, lost password recovery, set password, set VCS password).

Currently it varies a bit from case to case, but since it's all going to be basically identical once account passwords swap to the new infrastructure, bring it into the Engine so it can live in one place.

This also fixes VCS passwords not being affected by `account.minimum-password-length`.

Test Plan: Hit all errors in "VCS Password" panel. Successfully changed password.

Reviewers: amckinley

Reviewed By: amckinley

Maniphest Tasks: T13043

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

+130 -59
+2
src/__phutil_library_map__.php
··· 2092 2092 'PhabricatorAuthPassword' => 'applications/auth/storage/PhabricatorAuthPassword.php', 2093 2093 'PhabricatorAuthPasswordEditor' => 'applications/auth/editor/PhabricatorAuthPasswordEditor.php', 2094 2094 'PhabricatorAuthPasswordEngine' => 'applications/auth/engine/PhabricatorAuthPasswordEngine.php', 2095 + 'PhabricatorAuthPasswordException' => 'applications/auth/password/PhabricatorAuthPasswordException.php', 2095 2096 'PhabricatorAuthPasswordPHIDType' => 'applications/auth/phid/PhabricatorAuthPasswordPHIDType.php', 2096 2097 'PhabricatorAuthPasswordQuery' => 'applications/auth/query/PhabricatorAuthPasswordQuery.php', 2097 2098 'PhabricatorAuthPasswordResetTemporaryTokenType' => 'applications/auth/tokentype/PhabricatorAuthPasswordResetTemporaryTokenType.php', ··· 7387 7388 ), 7388 7389 'PhabricatorAuthPasswordEditor' => 'PhabricatorApplicationTransactionEditor', 7389 7390 'PhabricatorAuthPasswordEngine' => 'Phobject', 7391 + 'PhabricatorAuthPasswordException' => 'Exception', 7390 7392 'PhabricatorAuthPasswordPHIDType' => 'PhabricatorPHIDType', 7391 7393 'PhabricatorAuthPasswordQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 7392 7394 'PhabricatorAuthPasswordResetTemporaryTokenType' => 'PhabricatorAuthTemporaryTokenType',
+73
src/applications/auth/engine/PhabricatorAuthPasswordEngine.php
··· 54 54 return $this->upgradeHashers; 55 55 } 56 56 57 + public function checkNewPassword( 58 + PhutilOpaqueEnvelope $password, 59 + PhutilOpaqueEnvelope $confirm, 60 + $can_skip = false) { 61 + 62 + $raw_password = $password->openEnvelope(); 63 + 64 + if (!strlen($raw_password)) { 65 + if ($can_skip) { 66 + throw new PhabricatorAuthPasswordException( 67 + pht('You must choose a password or skip this step.'), 68 + pht('Required')); 69 + } else { 70 + throw new PhabricatorAuthPasswordException( 71 + pht('You must choose a password.'), 72 + pht('Required')); 73 + } 74 + } 75 + 76 + $min_len = PhabricatorEnv::getEnvConfig('account.minimum-password-length'); 77 + $min_len = (int)$min_len; 78 + if ($min_len) { 79 + if (strlen($raw_password) < $min_len) { 80 + throw new PhabricatorAuthPasswordException( 81 + pht( 82 + 'The selected password is too short. Passwords must be a minimum '. 83 + 'of %s characters long.', 84 + new PhutilNumber($min_len)), 85 + pht('Too Short')); 86 + } 87 + } 88 + 89 + $raw_confirm = $confirm->openEnvelope(); 90 + 91 + if (!strlen($raw_confirm)) { 92 + throw new PhabricatorAuthPasswordException( 93 + pht('You must confirm the selected password.'), 94 + null, 95 + pht('Required')); 96 + } 97 + 98 + if ($raw_password !== $raw_confirm) { 99 + throw new PhabricatorAuthPasswordException( 100 + pht('The password and confirmation do not match.'), 101 + pht('Invalid'), 102 + pht('Invalid')); 103 + } 104 + 105 + if (PhabricatorCommonPasswords::isCommonPassword($raw_password)) { 106 + throw new PhabricatorAuthPasswordException( 107 + pht( 108 + 'The selected password is very weak: it is one of the most common '. 109 + 'passwords in use. Choose a stronger password.'), 110 + pht('Very Weak')); 111 + } 112 + 113 + if ($this->isRevokedPassword($password)) { 114 + throw new PhabricatorAuthPasswordException( 115 + pht( 116 + 'The password you entered has been revoked. You can not reuse '. 117 + 'a password which has been revoked. Choose a new password.'), 118 + pht('Revoked')); 119 + } 120 + 121 + if (!$this->isUniquePassword($password)) { 122 + throw new PhabricatorAuthPasswordException( 123 + pht( 124 + 'The password you entered is the same as another password '. 125 + 'associated with your account. Each password must be unique.'), 126 + pht('Not Unique')); 127 + } 128 + } 129 + 57 130 public function isValidPassword(PhutilOpaqueEnvelope $envelope) { 58 131 $this->requireSetup(); 59 132
+28
src/applications/auth/password/PhabricatorAuthPasswordException.php
··· 1 + <?php 2 + 3 + final class PhabricatorAuthPasswordException 4 + extends Exception { 5 + 6 + private $passwordError; 7 + private $confirmErorr; 8 + 9 + public function __construct( 10 + $message, 11 + $password_error, 12 + $confirm_error = null) { 13 + 14 + $this->passwordError = $password_error; 15 + $this->confirmError = $confirm_error; 16 + 17 + parent::__construct($message); 18 + } 19 + 20 + public function getPasswordError() { 21 + return $this->passwordError; 22 + } 23 + 24 + public function getConfirmError() { 25 + return $this->confirmError; 26 + } 27 + 28 + }
+27 -59
src/applications/diffusion/panel/DiffusionSetPasswordSettingsPanel.php
··· 58 58 $e_password = true; 59 59 $e_confirm = true; 60 60 61 + $content_source = PhabricatorContentSource::newFromRequest($request); 62 + 63 + // NOTE: This test is against $viewer (not $user), so that the error 64 + // message below makes sense in the case that the two are different, 65 + // and because an admin reusing their own password is bad, while 66 + // system agents generally do not have passwords anyway. 67 + 68 + $engine = id(new PhabricatorAuthPasswordEngine()) 69 + ->setViewer($viewer) 70 + ->setContentSource($content_source) 71 + ->setObject($viewer) 72 + ->setPasswordType($vcs_type); 73 + 61 74 if ($request->isFormPost()) { 62 75 if ($request->getBool('remove')) { 63 76 if ($vcspassword->getID()) { ··· 68 81 69 82 $new_password = $request->getStr('password'); 70 83 $confirm = $request->getStr('confirm'); 71 - if (!strlen($new_password)) { 72 - $e_password = pht('Required'); 73 - $errors[] = pht('Password is required.'); 74 - } else { 84 + 85 + $envelope = new PhutilOpaqueEnvelope($new_password); 86 + $confirm_envelope = new PhutilOpaqueEnvelope($confirm); 87 + 88 + try { 89 + $engine->checkNewPassword($envelope, $confirm_envelope); 75 90 $e_password = null; 76 - } 77 - 78 - if (!strlen($confirm)) { 79 - $e_confirm = pht('Required'); 80 - $errors[] = pht('You must confirm the new password.'); 81 - } else { 82 91 $e_confirm = null; 92 + } catch (PhabricatorAuthPasswordException $ex) { 93 + $errors[] = $ex->getMessage(); 94 + $e_password = $ex->getPasswordError(); 95 + $e_confirm = $ex->getConfirmError(); 83 96 } 84 97 85 98 if (!$errors) { 86 - $envelope = new PhutilOpaqueEnvelope($new_password); 87 - $content_source = PhabricatorContentSource::newFromRequest($request); 88 - 89 - // NOTE: This test is against $viewer (not $user), so that the error 90 - // message below makes sense in the case that the two are different, 91 - // and because an admin reusing their own password is bad, while 92 - // system agents generally do not have passwords anyway. 93 - 94 - $engine = id(new PhabricatorAuthPasswordEngine()) 95 - ->setViewer($viewer) 96 - ->setContentSource($content_source) 97 - ->setObject($viewer) 98 - ->setPasswordType($vcs_type); 99 + $vcspassword 100 + ->setPassword($envelope, $user) 101 + ->save(); 99 102 100 - $same_password = !$engine->isUniquePassword($envelope); 101 - $revoked_password = $engine->isRevokedPassword($envelope); 102 - 103 - if ($new_password !== $confirm) { 104 - $e_password = pht('Does Not Match'); 105 - $e_confirm = pht('Does Not Match'); 106 - $errors[] = pht('Password and confirmation do not match.'); 107 - } else if ($revoked_password) { 108 - $e_password = pht('Revoked'); 109 - $e_confirm = pht('Revoked'); 110 - $errors[] = pht( 111 - 'This password has been revoked. You must choose a new, unique '. 112 - 'password.'); 113 - } else if ($same_password) { 114 - $e_password = pht('Not Unique'); 115 - $e_confirm = pht('Not Unique'); 116 - $errors[] = pht( 117 - 'This password is the same as another password associated '. 118 - 'with your account. You must use a unique password for '. 119 - 'VCS access.'); 120 - } else if ( 121 - PhabricatorCommonPasswords::isCommonPassword($new_password)) { 122 - $e_password = pht('Very Weak'); 123 - $e_confirm = pht('Very Weak'); 124 - $errors[] = pht( 125 - 'This password is extremely weak: it is one of the most common '. 126 - 'passwords in use. Choose a stronger password.'); 127 - } 128 - 129 - 130 - if (!$errors) { 131 - $vcspassword->setPassword($envelope, $user); 132 - $vcspassword->save(); 133 - 134 - return id(new AphrontRedirectResponse())->setURI($panel_uri); 135 - } 103 + return id(new AphrontRedirectResponse())->setURI($panel_uri); 136 104 } 137 105 } 138 106