mirror of
https://we.phorge.it/source/phorge.git
synced 2025-02-27 05:59:08 +01:00
Summary: Ref T4443. - Add a `password_hash()`-based bcrypt hasher if `password_hash()` is available. - When a user logs in using a password, upgrade their password to the strongest available hash format. - On the password settings page: - Warn the user if their password uses any algorithm other than the strongest one. - Show the algorithm the password uses. - Show the best available algorithm. Test Plan: As an md5 user, viewed password settings page and saw a warning. Logged out. Logged in, got upgraded, no more warning. Changed password, verified database rehash. Logged out, logged in. Reviewers: btrahan Reviewed By: btrahan CC: aran Maniphest Tasks: T4443 Differential Revision: https://secure.phabricator.com/D8270
199 lines
5.8 KiB
PHP
199 lines
5.8 KiB
PHP
<?php
|
|
|
|
final class PhabricatorSettingsPanelPassword
|
|
extends PhabricatorSettingsPanel {
|
|
|
|
public function getPanelKey() {
|
|
return 'password';
|
|
}
|
|
|
|
public function getPanelName() {
|
|
return pht('Password');
|
|
}
|
|
|
|
public function getPanelGroup() {
|
|
return pht('Authentication');
|
|
}
|
|
|
|
public function isEnabled() {
|
|
// There's no sense in showing a change password panel if the user
|
|
// can't change their password...
|
|
|
|
if (!PhabricatorEnv::getEnvConfig('account.editable')) {
|
|
return false;
|
|
}
|
|
|
|
// ...or this install doesn't support password authentication at all.
|
|
|
|
if (!PhabricatorAuthProviderPassword::getPasswordProvider()) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public function processRequest(AphrontRequest $request) {
|
|
$user = $request->getUser();
|
|
|
|
$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.
|
|
|
|
$token = $request->getStr('token');
|
|
|
|
$valid_token = false;
|
|
if ($token) {
|
|
$email_address = $request->getStr('email');
|
|
$email = id(new PhabricatorUserEmail())->loadOneWhere(
|
|
'address = %s',
|
|
$email_address);
|
|
if ($email) {
|
|
$valid_token = $user->validateEmailToken($email, $token);
|
|
}
|
|
}
|
|
|
|
$e_old = true;
|
|
$e_new = true;
|
|
$e_conf = true;
|
|
|
|
$errors = array();
|
|
if ($request->isFormPost()) {
|
|
if (!$valid_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 ($valid_token) {
|
|
// 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');
|
|
}
|
|
|
|
return id(new AphrontRedirectResponse())->setURI($next);
|
|
}
|
|
}
|
|
|
|
$hash_envelope = new PhutilOpaqueEnvelope($user->getPasswordHash());
|
|
if (PhabricatorPasswordHasher::canUpgradeHash($hash_envelope)) {
|
|
$best_hash = PhabricatorPasswordHasher::getBestHasher();
|
|
$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('token', $token);
|
|
|
|
if (!$valid_token) {
|
|
$form->appendChild(
|
|
id(new AphrontFormPasswordControl())
|
|
->setLabel(pht('Old Password'))
|
|
->setError($e_old)
|
|
->setName('old_pw'));
|
|
}
|
|
|
|
$form
|
|
->appendChild(
|
|
id(new AphrontFormPasswordControl())
|
|
->setLabel(pht('New Password'))
|
|
->setError($e_new)
|
|
->setName('new_pw'));
|
|
$form
|
|
->appendChild(
|
|
id(new AphrontFormPasswordControl())
|
|
->setLabel(pht('Confirm Password'))
|
|
->setCaption($len_caption)
|
|
->setError($e_conf)
|
|
->setName('conf_pw'));
|
|
$form
|
|
->appendChild(
|
|
id(new AphrontFormSubmitControl())
|
|
->setValue(pht('Change Password')));
|
|
|
|
if (!strlen($user->getPasswordHash())) {
|
|
$current_name = pht('None');
|
|
} else {
|
|
try {
|
|
$current_hasher = PhabricatorPasswordHasher::getHasherForHash(
|
|
new PhutilOpaqueEnvelope($user->getPasswordHash()));
|
|
$current_name = $current_hasher->getHumanReadableName();
|
|
} catch (Exception $ex) {
|
|
$current_name = pht('Unknown');
|
|
}
|
|
}
|
|
|
|
$form->appendChild(
|
|
id(new AphrontFormStaticControl())
|
|
->setLabel(pht('Current Algorithm'))
|
|
->setValue($current_name));
|
|
|
|
try {
|
|
$best_hasher = PhabricatorPasswordHasher::getBestHasher();
|
|
$best_name = $best_hasher->getHumanReadableName();
|
|
} catch (Exception $ex) {
|
|
$best_name = pht('Unknown');
|
|
}
|
|
|
|
$form->appendChild(
|
|
id(new AphrontFormStaticControl())
|
|
->setLabel(pht('Best Available Algorithm'))
|
|
->setValue($best_name));
|
|
|
|
$form_box = id(new PHUIObjectBoxView())
|
|
->setHeaderText(pht('Change Password'))
|
|
->setFormSaved($request->getStr('saved'))
|
|
->setFormErrors($errors)
|
|
->setForm($form);
|
|
|
|
return array(
|
|
$form_box,
|
|
);
|
|
}
|
|
}
|