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

at upstream/main 369 lines 10 kB view raw
1<?php 2 3final class PhabricatorPasswordAuthProvider extends PhabricatorAuthProvider { 4 5 private $adapter; 6 7 public function getProviderName() { 8 return pht('Username/Password'); 9 } 10 11 public function getConfigurationHelp() { 12 return pht( 13 "(WARNING) Examine the table below for information on how password ". 14 "hashes will be stored in the database.\n\n". 15 "(NOTE) You can select a minimum password length by setting ". 16 "`%s` in configuration.", 17 'account.minimum-password-length'); 18 } 19 20 public function renderConfigurationFooter() { 21 $hashers = PhabricatorPasswordHasher::getAllHashers(); 22 $hashers = msort($hashers, 'getStrength'); 23 $hashers = array_reverse($hashers); 24 25 $yes = phutil_tag( 26 'strong', 27 array( 28 'style' => 'color: #009900', 29 ), 30 pht('Yes')); 31 32 $no = phutil_tag( 33 'strong', 34 array( 35 'style' => 'color: #990000', 36 ), 37 pht('Not Installed')); 38 39 $best_hasher_name = null; 40 try { 41 $best_hasher = PhabricatorPasswordHasher::getBestHasher(); 42 $best_hasher_name = $best_hasher->getHashName(); 43 } catch (PhabricatorPasswordHasherUnavailableException $ex) { 44 // There are no suitable hashers. The user might be able to enable some, 45 // so we don't want to fatal here. We'll fatal when users try to actually 46 // use this stuff if it isn't fixed before then. Until then, we just 47 // don't highlight a row. In practice, at least one hasher should always 48 // be available. 49 } 50 51 $rows = array(); 52 $rowc = array(); 53 foreach ($hashers as $hasher) { 54 $is_installed = $hasher->canHashPasswords(); 55 56 $rows[] = array( 57 $hasher->getHumanReadableName(), 58 $hasher->getHashName(), 59 $hasher->getHumanReadableStrength(), 60 ($is_installed ? $yes : $no), 61 ($is_installed ? null : $hasher->getInstallInstructions()), 62 ); 63 $rowc[] = ($best_hasher_name == $hasher->getHashName()) 64 ? 'highlighted' 65 : null; 66 } 67 68 $table = new AphrontTableView($rows); 69 $table->setRowClasses($rowc); 70 $table->setHeaders( 71 array( 72 pht('Algorithm'), 73 pht('Name'), 74 pht('Strength'), 75 pht('Installed'), 76 pht('Install Instructions'), 77 )); 78 79 $table->setColumnClasses( 80 array( 81 '', 82 '', 83 '', 84 '', 85 'wide', 86 )); 87 88 $header = id(new PHUIHeaderView()) 89 ->setHeader(pht('Password Hash Algorithms')) 90 ->setSubheader( 91 pht( 92 'Stronger algorithms are listed first. The highlighted algorithm '. 93 'will be used when storing new hashes. Older hashes will be '. 94 'upgraded to the best algorithm over time.')); 95 96 return id(new PHUIObjectBoxView()) 97 ->setHeader($header) 98 ->setTable($table); 99 } 100 101 public function getDescriptionForCreate() { 102 return pht( 103 'Allow users to log in or register using a username and password.'); 104 } 105 106 public function getAdapter() { 107 if (!$this->adapter) { 108 $adapter = new PhutilEmptyAuthAdapter(); 109 $adapter->setAdapterType('password'); 110 $adapter->setAdapterDomain('self'); 111 $this->adapter = $adapter; 112 } 113 return $this->adapter; 114 } 115 116 public function getLoginOrder() { 117 // Make sure username/password appears first if it is enabled. 118 return '100-'.$this->getProviderName(); 119 } 120 121 public function shouldAllowAccountLink() { 122 return false; 123 } 124 125 public function shouldAllowAccountUnlink() { 126 return false; 127 } 128 129 public function isDefaultRegistrationProvider() { 130 return true; 131 } 132 133 public function buildLoginForm( 134 PhabricatorAuthStartController $controller) { 135 $request = $controller->getRequest(); 136 return $this->renderPasswordLoginForm($request); 137 } 138 139 public function buildInviteForm( 140 PhabricatorAuthStartController $controller) { 141 $request = $controller->getRequest(); 142 $viewer = $request->getViewer(); 143 144 $form = id(new AphrontFormView()) 145 ->setUser($viewer) 146 ->addHiddenInput('invite', true) 147 ->appendChild( 148 id(new AphrontFormTextControl()) 149 ->setLabel(pht('Username')) 150 ->setName('username')); 151 152 $dialog = id(new AphrontDialogView()) 153 ->setUser($viewer) 154 ->setTitle(pht('Register an Account')) 155 ->appendForm($form) 156 ->setSubmitURI('/auth/register/') 157 ->addSubmitButton(pht('Continue')); 158 159 return $dialog; 160 } 161 162 public function buildLinkForm($controller) { 163 throw new Exception(pht("Password providers can't be linked.")); 164 } 165 166 private function renderPasswordLoginForm( 167 AphrontRequest $request, 168 $require_captcha = false, 169 $captcha_valid = false) { 170 171 $viewer = $request->getUser(); 172 173 $dialog = id(new AphrontDialogView()) 174 ->setSubmitURI($this->getLoginURI()) 175 ->setUser($viewer) 176 ->setTitle(pht('Log In')) 177 ->addSubmitButton(pht('Log In')); 178 179 if ($this->shouldAllowRegistration()) { 180 $dialog->addCancelButton( 181 '/auth/register/', 182 pht('Register New Account')); 183 } 184 185 $dialog->addFooter( 186 phutil_tag( 187 'a', 188 array( 189 'href' => '/login/email/', 190 ), 191 pht('Forgot your password?'))); 192 193 $v_user = nonempty( 194 $request->getStr('username'), 195 $request->getCookie(PhabricatorCookies::COOKIE_USERNAME)); 196 197 $e_user = null; 198 $e_pass = null; 199 $e_captcha = null; 200 201 $errors = array(); 202 if ($require_captcha && !$captcha_valid) { 203 if (AphrontFormRecaptchaControl::hasCaptchaResponse($request)) { 204 $e_captcha = pht('Invalid'); 205 $errors[] = pht('CAPTCHA was not entered correctly.'); 206 } else { 207 $e_captcha = pht('Required'); 208 $errors[] = pht( 209 'Too many login failures recently. You must '. 210 'submit a CAPTCHA with your login request.'); 211 } 212 } else if ($request->isHTTPPost()) { 213 // NOTE: This is intentionally vague so as not to disclose whether a 214 // given username or email is registered. 215 $e_user = pht('Invalid'); 216 $e_pass = pht('Invalid'); 217 $errors[] = pht('Username or password are incorrect.'); 218 } 219 220 if ($errors) { 221 $errors = id(new PHUIInfoView())->setErrors($errors); 222 } 223 224 $form = id(new PHUIFormLayoutView()) 225 ->setFullWidth(true) 226 ->appendChild($errors) 227 ->appendChild( 228 id(new AphrontFormTextControl()) 229 ->setLabel(pht('Username or Email')) 230 ->setName('username') 231 ->setAutofocus(true) 232 ->setValue($v_user) 233 ->setError($e_user)) 234 ->appendChild( 235 id(new AphrontFormPasswordControl()) 236 ->setLabel(pht('Password')) 237 ->setName('password') 238 ->setError($e_pass)); 239 240 if ($require_captcha) { 241 $form->appendChild( 242 id(new AphrontFormRecaptchaControl()) 243 ->setError($e_captcha)); 244 } 245 246 $dialog->appendChild($form); 247 248 return $dialog; 249 } 250 251 public function processLoginRequest( 252 PhabricatorAuthLoginController $controller) { 253 254 $request = $controller->getRequest(); 255 $viewer = $request->getUser(); 256 $content_source = PhabricatorContentSource::newFromRequest($request); 257 258 $rate_actor = PhabricatorSystemActionEngine::newActorFromRequest($request); 259 260 PhabricatorSystemActionEngine::willTakeAction( 261 array($rate_actor), 262 new PhabricatorAuthTryPasswordAction(), 263 1); 264 265 // If the same remote address has submitted several failed login attempts 266 // recently, require they provide a CAPTCHA response for new attempts. 267 $require_captcha = false; 268 $captcha_valid = false; 269 if (AphrontFormRecaptchaControl::isRecaptchaEnabled()) { 270 try { 271 PhabricatorSystemActionEngine::willTakeAction( 272 array($rate_actor), 273 new PhabricatorAuthTryPasswordWithoutCAPTCHAAction(), 274 1); 275 } catch (PhabricatorSystemActionRateLimitException $ex) { 276 $require_captcha = true; 277 $captcha_valid = AphrontFormRecaptchaControl::processCaptcha($request); 278 } 279 } 280 281 $response = null; 282 $account = null; 283 $log_user = null; 284 285 if ($request->isFormPost()) { 286 if (!$require_captcha || $captcha_valid) { 287 $username_or_email = $request->getStr('username'); 288 if (strlen($username_or_email)) { 289 $user = id(new PhabricatorUser())->loadOneWhere( 290 'username = %s', 291 $username_or_email); 292 293 if (!$user) { 294 $user = PhabricatorUser::loadOneWithEmailAddress( 295 $username_or_email); 296 } 297 298 if ($user) { 299 $envelope = new PhutilOpaqueEnvelope($request->getStr('password')); 300 301 $engine = id(new PhabricatorAuthPasswordEngine()) 302 ->setViewer($user) 303 ->setContentSource($content_source) 304 ->setPasswordType(PhabricatorAuthPassword::PASSWORD_TYPE_ACCOUNT) 305 ->setObject($user); 306 307 if ($engine->isValidPassword($envelope)) { 308 $account = $this->newExternalAccountForUser($user); 309 $log_user = $user; 310 } 311 } 312 } 313 } 314 } 315 316 if (!$account) { 317 if ($request->isFormPost()) { 318 $log = PhabricatorUserLog::initializeNewLog( 319 null, 320 $log_user ? $log_user->getPHID() : null, 321 PhabricatorLoginFailureUserLogType::LOGTYPE); 322 $log->save(); 323 } 324 325 $request->clearCookie(PhabricatorCookies::COOKIE_USERNAME); 326 327 $response = $controller->buildProviderPageResponse( 328 $this, 329 $this->renderPasswordLoginForm( 330 $request, 331 $require_captcha, 332 $captcha_valid)); 333 } 334 335 return array($account, $response); 336 } 337 338 public function shouldRequireRegistrationPassword() { 339 return true; 340 } 341 342 public static function getPasswordProvider() { 343 $providers = self::getAllEnabledProviders(); 344 345 foreach ($providers as $provider) { 346 if ($provider instanceof PhabricatorPasswordAuthProvider) { 347 return $provider; 348 } 349 } 350 351 return null; 352 } 353 354 public function willRenderLinkedAccount( 355 PhabricatorUser $viewer, 356 PHUIObjectItemView $item, 357 PhabricatorExternalAccount $account) { 358 return; 359 } 360 361 public function shouldAllowAccountRefresh() { 362 return false; 363 } 364 365 public function shouldAllowEmailTrustConfiguration() { 366 return false; 367 } 368 369}