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