public function testHasherSyntax() { $caught = null; try { PhabricatorPasswordHasher::getHasherForHash(new PhutilOpaqueEnvelope('xxx')); } catch (Exception $ex) { $caught = $ex; } $this->assertTrue($caught instanceof Exception, pht('Exception on unparseable hash format.')); $caught = null; try { PhabricatorPasswordHasher::getHasherForHash(new PhutilOpaqueEnvelope('__test__:yyy')); } catch (Exception $ex) { $caught = $ex; } $this->assertTrue($caught instanceof PhabricatorPasswordHasherUnavailableException, pht('Fictional hasher unavailable.')); }
private function authenticateHTTPRepositoryUser($username, PhutilOpaqueEnvelope $password) { if (!PhabricatorEnv::getEnvConfig('diffusion.allow-http-auth')) { // No HTTP auth permitted. return null; } if (!strlen($username)) { // No username. return null; } if (!strlen($password->openEnvelope())) { // No password. return null; } $user = id(new PhabricatorPeopleQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withUsernames(array($username))->executeOne(); if (!$user) { // Username doesn't match anything. return null; } if (!$user->isUserActivated()) { // User is not activated. return null; } $password_entry = id(new PhabricatorRepositoryVCSPassword())->loadOneWhere('userPHID = %s', $user->getPHID()); if (!$password_entry) { // User doesn't have a password set. return null; } if (!$password_entry->comparePassword($password, $user)) { // Password doesn't match. return null; } // If the user's password is stored using a less-than-optimal hash, upgrade // them to the strongest available hash. $hash_envelope = new PhutilOpaqueEnvelope($password_entry->getPasswordHash()); if (PhabricatorPasswordHasher::canUpgradeHash($hash_envelope)) { $password_entry->setPassword($password, $user); $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); $password_entry->save(); unset($unguarded); } return $user; }
<?php $table = new PhabricatorRepositoryVCSPassword(); $conn_w = $table->establishConnection('w'); echo "Upgrading password hashing for VCS passwords.\n"; $best_hasher = PhabricatorPasswordHasher::getBestHasher(); foreach (new LiskMigrationIterator($table) as $password) { $id = $password->getID(); echo "Migrating VCS password {$id}...\n"; $input_hash = $password->getPasswordHash(); $input_envelope = new PhutilOpaqueEnvelope($input_hash); $storage_hash = $best_hasher->getPasswordHashForStorage($input_envelope); queryfx($conn_w, 'UPDATE %T SET passwordHash = %s WHERE id = %d', $table->getTableName(), $storage_hash->openEnvelope(), $id); } echo "Done.\n";
private function hashPassword(PhutilOpaqueEnvelope $password) { $hasher = PhabricatorPasswordHasher::getBestHasher(); $input_envelope = $this->getPasswordHashInput($password); return $hasher->getPasswordHashForStorage($input_envelope); }
public function processRequest(AphrontRequest $request) { $user = $request->getUser(); $token = id(new PhabricatorAuthSessionEngine())->requireHighSecuritySession($user, $request, '/settings/'); $min_len = PhabricatorEnv::getEnvConfig('account.minimum-password-length'); $min_len = (int) $min_len; // NOTE: To change your password, you need to prove you own the account, // either by providing the old password or by carrying a token to // the workflow from a password reset email. $key = $request->getStr('key'); $token = null; if ($key) { $token = id(new PhabricatorAuthTemporaryTokenQuery())->setViewer($user)->withObjectPHIDs(array($user->getPHID()))->withTokenTypes(array(PhabricatorAuthSessionEngine::PASSWORD_TEMPORARY_TOKEN_TYPE))->withTokenCodes(array(PhabricatorHash::digest($key)))->withExpired(false)->executeOne(); } $e_old = true; $e_new = true; $e_conf = true; $errors = array(); if ($request->isFormPost()) { if (!$token) { $envelope = new PhutilOpaqueEnvelope($request->getStr('old_pw')); if (!$user->comparePassword($envelope)) { $errors[] = pht('The old password you entered is incorrect.'); $e_old = pht('Invalid'); } } $pass = $request->getStr('new_pw'); $conf = $request->getStr('conf_pw'); if (strlen($pass) < $min_len) { $errors[] = pht('Your new password is too short.'); $e_new = pht('Too Short'); } else { if ($pass !== $conf) { $errors[] = pht('New password and confirmation do not match.'); $e_conf = pht('Invalid'); } else { if (PhabricatorCommonPasswords::isCommonPassword($pass)) { $e_new = pht('Very Weak'); $e_conf = pht('Very Weak'); $errors[] = pht('Your new password is very weak: it is one of the most common ' . 'passwords in use. Choose a stronger password.'); } } } if (!$errors) { // This write is unguarded because the CSRF token has already // been checked in the call to $request->isFormPost() and // the CSRF token depends on the password hash, so when it // is changed here the CSRF token check will fail. $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); $envelope = new PhutilOpaqueEnvelope($pass); id(new PhabricatorUserEditor())->setActor($user)->changePassword($user, $envelope); unset($unguarded); if ($token) { // Destroy the token. $token->delete(); // If this is a password set/reset, kick the user to the home page // after we update their account. $next = '/'; } else { $next = $this->getPanelURI('?saved=true'); } id(new PhabricatorAuthSessionEngine())->terminateLoginSessions($user, $request->getCookie(PhabricatorCookies::COOKIE_SESSION)); return id(new AphrontRedirectResponse())->setURI($next); } } $hash_envelope = new PhutilOpaqueEnvelope($user->getPasswordHash()); if (strlen($hash_envelope->openEnvelope())) { try { $can_upgrade = PhabricatorPasswordHasher::canUpgradeHash($hash_envelope); } catch (PhabricatorPasswordHasherUnavailableException $ex) { $can_upgrade = false; // Only show this stuff if we aren't on the reset workflow. We can // do resets regardless of the old hasher's availability. if (!$token) { $errors[] = pht('Your password is currently hashed using an algorithm which is ' . 'no longer available on this install.'); $errors[] = pht('Because the algorithm implementation is missing, your password ' . 'can not be used or updated.'); $errors[] = pht('To set a new password, request a password reset link from the ' . 'login screen and then follow the instructions.'); } } if ($can_upgrade) { $errors[] = pht('The strength of your stored password hash can be upgraded. ' . 'To upgrade, either: log out and log in using your password; or ' . 'change your password.'); } } $len_caption = null; if ($min_len) { $len_caption = pht('Minimum password length: %d characters.', $min_len); } $form = new AphrontFormView(); $form->setUser($user)->addHiddenInput('key', $key); if (!$token) { $form->appendChild(id(new AphrontFormPasswordControl())->setLabel(pht('Old Password'))->setError($e_old)->setName('old_pw')); } $form->appendChild(id(new AphrontFormPasswordControl())->setDisableAutocomplete(true)->setLabel(pht('New Password'))->setError($e_new)->setName('new_pw')); $form->appendChild(id(new AphrontFormPasswordControl())->setDisableAutocomplete(true)->setLabel(pht('Confirm Password'))->setCaption($len_caption)->setError($e_conf)->setName('conf_pw')); $form->appendChild(id(new AphrontFormSubmitControl())->setValue(pht('Change Password'))); $form->appendChild(id(new AphrontFormStaticControl())->setLabel(pht('Current Algorithm'))->setValue(PhabricatorPasswordHasher::getCurrentAlgorithmName(new PhutilOpaqueEnvelope($user->getPasswordHash())))); $form->appendChild(id(new AphrontFormStaticControl())->setLabel(pht('Best Available Algorithm'))->setValue(PhabricatorPasswordHasher::getBestAlgorithmName())); $form->appendRemarkupInstructions(pht('NOTE: Changing your password will terminate any other outstanding ' . 'login sessions.')); $form_box = id(new PHUIObjectBoxView())->setHeaderText(pht('Change Password'))->setFormSaved($request->getStr('saved'))->setFormErrors($errors)->setForm($form); return array($form_box); }
public function processLoginRequest(PhabricatorAuthLoginController $controller) { $request = $controller->getRequest(); $viewer = $request->getUser(); $require_captcha = false; $captcha_valid = false; if (AphrontFormRecaptchaControl::isRecaptchaEnabled()) { $failed_attempts = PhabricatorUserLog::loadRecentEventsFromThisIP(PhabricatorUserLog::ACTION_LOGIN_FAILURE, 60 * 15); if (count($failed_attempts) > 5) { $require_captcha = true; $captcha_valid = AphrontFormRecaptchaControl::processCaptcha($request); } } $response = null; $account = null; $log_user = null; if ($request->isFormPost()) { if (!$require_captcha || $captcha_valid) { $username_or_email = $request->getStr('username'); if (strlen($username_or_email)) { $user = id(new PhabricatorUser())->loadOneWhere('username = %s', $username_or_email); if (!$user) { $user = PhabricatorUser::loadOneWithEmailAddress($username_or_email); } if ($user) { $envelope = new PhutilOpaqueEnvelope($request->getStr('password')); if ($user->comparePassword($envelope)) { $account = $this->loadOrCreateAccount($user->getPHID()); $log_user = $user; // If the user's password is stored using a less-than-optimal // hash, upgrade them to the strongest available hash. $hash_envelope = new PhutilOpaqueEnvelope($user->getPasswordHash()); if (PhabricatorPasswordHasher::canUpgradeHash($hash_envelope)) { $user->setPassword($envelope); $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); $user->save(); unset($unguarded); } } } } } } if (!$account) { if ($request->isFormPost()) { $log = PhabricatorUserLog::initializeNewLog(null, $log_user ? $log_user->getPHID() : null, PhabricatorUserLog::ACTION_LOGIN_FAILURE); $log->save(); } $request->clearCookie(PhabricatorCookies::COOKIE_USERNAME); $response = $controller->buildProviderPageResponse($this, $this->renderPasswordLoginForm($request, $require_captcha, $captcha_valid)); } return array($account, $response); }
public function processRequest(AphrontRequest $request) { $viewer = $request->getUser(); $user = $this->getUser(); $token = id(new PhabricatorAuthSessionEngine())->requireHighSecuritySession($viewer, $request, '/settings/'); $vcspassword = id(new PhabricatorRepositoryVCSPassword())->loadOneWhere('userPHID = %s', $user->getPHID()); if (!$vcspassword) { $vcspassword = id(new PhabricatorRepositoryVCSPassword()); $vcspassword->setUserPHID($user->getPHID()); } $panel_uri = $this->getPanelURI('?saved=true'); $errors = array(); $e_password = true; $e_confirm = true; if ($request->isFormPost()) { if ($request->getBool('remove')) { if ($vcspassword->getID()) { $vcspassword->delete(); return id(new AphrontRedirectResponse())->setURI($panel_uri); } } $new_password = $request->getStr('password'); $confirm = $request->getStr('confirm'); if (!strlen($new_password)) { $e_password = pht('Required'); $errors[] = pht('Password is required.'); } else { $e_password = null; } if (!strlen($confirm)) { $e_confirm = pht('Required'); $errors[] = pht('You must confirm the new password.'); } else { $e_confirm = null; } if (!$errors) { $envelope = new PhutilOpaqueEnvelope($new_password); try { // NOTE: This test is against $viewer (not $user), so that the error // message below makes sense in the case that the two are different, // and because an admin reusing their own password is bad, while // system agents generally do not have passwords anyway. $same_password = $viewer->comparePassword($envelope); } catch (PhabricatorPasswordHasherUnavailableException $ex) { // If we're missing the hasher, just let the user continue. $same_password = false; } if ($new_password !== $confirm) { $e_password = pht('Does Not Match'); $e_confirm = pht('Does Not Match'); $errors[] = pht('Password and confirmation do not match.'); } else { if ($same_password) { $e_password = pht('Not Unique'); $e_confirm = pht('Not Unique'); $errors[] = pht('This password is the same as another password associated ' . 'with your account. You must use a unique password for ' . 'VCS access.'); } else { if (PhabricatorCommonPasswords::isCommonPassword($new_password)) { $e_password = pht('Very Weak'); $e_confirm = pht('Very Weak'); $errors[] = pht('This password is extremely weak: it is one of the most common ' . 'passwords in use. Choose a stronger password.'); } } } if (!$errors) { $vcspassword->setPassword($envelope, $user); $vcspassword->save(); return id(new AphrontRedirectResponse())->setURI($panel_uri); } } } $title = pht('Set VCS Password'); $form = id(new AphrontFormView())->setUser($viewer)->appendRemarkupInstructions(pht('To access repositories hosted by Phabricator over HTTP, you must ' . 'set a version control password. This password should be unique.' . "\n\n" . "This password applies to all repositories available over " . "HTTP.")); if ($vcspassword->getID()) { $form->appendChild(id(new AphrontFormPasswordControl())->setDisableAutocomplete(true)->setLabel(pht('Current Password'))->setDisabled(true)->setValue('********************')); } else { $form->appendChild(id(new AphrontFormMarkupControl())->setLabel(pht('Current Password'))->setValue(phutil_tag('em', array(), pht('No Password Set')))); } $form->appendChild(id(new AphrontFormPasswordControl())->setDisableAutocomplete(true)->setName('password')->setLabel(pht('New VCS Password'))->setError($e_password))->appendChild(id(new AphrontFormPasswordControl())->setDisableAutocomplete(true)->setName('confirm')->setLabel(pht('Confirm VCS Password'))->setError($e_confirm))->appendChild(id(new AphrontFormSubmitControl())->setValue(pht('Change Password'))); if (!$vcspassword->getID()) { $is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business'); $suggest = Filesystem::readRandomBytes(128); $suggest = preg_replace('([^A-Za-z0-9/!().,;{}^&*%~])', '', $suggest); $suggest = substr($suggest, 0, 20); if ($is_serious) { $form->appendRemarkupInstructions(pht('Having trouble coming up with a good password? Try this randomly ' . 'generated one, made by a computer:' . "\n\n" . "`%s`", $suggest)); } else { $form->appendRemarkupInstructions(pht('Having trouble coming up with a good password? Try this ' . 'artisinal password, hand made in small batches by our expert ' . 'craftspeople: ' . "\n\n" . "`%s`", $suggest)); } } $hash_envelope = new PhutilOpaqueEnvelope($vcspassword->getPasswordHash()); $form->appendChild(id(new AphrontFormStaticControl())->setLabel(pht('Current Algorithm'))->setValue(PhabricatorPasswordHasher::getCurrentAlgorithmName($hash_envelope))); $form->appendChild(id(new AphrontFormStaticControl())->setLabel(pht('Best Available Algorithm'))->setValue(PhabricatorPasswordHasher::getBestAlgorithmName())); if (strlen($hash_envelope->openEnvelope())) { try { $can_upgrade = PhabricatorPasswordHasher::canUpgradeHash($hash_envelope); } catch (PhabricatorPasswordHasherUnavailableException $ex) { $can_upgrade = false; $errors[] = pht('Your VCS password is currently hashed using an algorithm which is ' . 'no longer available on this install.'); $errors[] = pht('Because the algorithm implementation is missing, your password ' . 'can not be used.'); $errors[] = pht('You can set a new password to replace the old password.'); } if ($can_upgrade) { $errors[] = pht('The strength of your stored VCS password hash can be upgraded. ' . 'To upgrade, either: use the password to authenticate with a ' . 'repository; or change your password.'); } } $object_box = id(new PHUIObjectBoxView())->setHeaderText($title)->setForm($form)->setFormErrors($errors); $remove_form = id(new AphrontFormView())->setUser($viewer); if ($vcspassword->getID()) { $remove_form->addHiddenInput('remove', true)->appendRemarkupInstructions(pht('You can remove your VCS password, which will prevent your ' . 'account from accessing repositories.'))->appendChild(id(new AphrontFormSubmitControl())->setValue(pht('Remove Password'))); } else { $remove_form->appendRemarkupInstructions(pht('You do not currently have a VCS password set. If you set one, you ' . 'can remove it here later.')); } $remove_box = id(new PHUIObjectBoxView())->setHeaderText(pht('Remove VCS Password'))->setForm($remove_form); $saved = null; if ($request->getBool('saved')) { $saved = id(new PHUIInfoView())->setSeverity(PHUIInfoView::SEVERITY_NOTICE)->setTitle(pht('Password Updated'))->appendChild(pht('Your VCS password has been updated.')); } return array($saved, $object_box, $remove_box); }
/** * Get the human-readable algorithm name for the best available hash. * * @return string Human-readable name for best hash. */ public static function getBestAlgorithmName() { try { $best_hasher = PhabricatorPasswordHasher::getBestHasher(); return $best_hasher->getHumanReadableName(); } catch (Exception $ex) { return pht('Unknown'); } }
public function testGetAllHashers() { PhabricatorPasswordHasher::getAllHashers(); $this->assertTrue(true); }