1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-05 13:16:14 +01:00
phorge-phorge/src/applications/settings/panel/PhabricatorPasswordSettingsPanel.php

223 lines
6.9 KiB
PHP
Raw Normal View History

<?php
final class PhabricatorPasswordSettingsPanel extends PhabricatorSettingsPanel {
public function getPanelKey() {
return 'password';
}
public function getPanelName() {
return pht('Password');
}
public function getPanelMenuIcon() {
return 'fa-key';
}
public function getPanelGroupKey() {
return PhabricatorSettingsAuthenticationPanelGroup::PANELGROUPKEY;
}
public function isEnabled() {
// There's no sense in showing a change password panel if this install
// doesn't support password authentication.
if (!PhabricatorPasswordAuthProvider::getPasswordProvider()) {
return false;
}
return true;
}
public function processRequest(AphrontRequest $request) {
$viewer = $request->getUser();
$user = $this->getUser();
$content_source = PhabricatorContentSource::newFromRequest($request);
$token = id(new PhabricatorAuthSessionEngine())->requireHighSecuritySession(
$viewer,
$request,
'/settings/');
$min_len = PhabricatorEnv::getEnvConfig('account.minimum-password-length');
$min_len = (int)$min_len;
Separate "Set/Reset Password" from "Change Password" Summary: See PHI223. Ref T13024. There's a remaining registration/login order issue after the other changes in T13024: we lose track of the current URI when we go through the MFA flow, so we can lose "Set Password" at the end of the flow. Specifically, the flow goes like this today: - User clicks the welcome link in email. - They get redirected to the "set password" settings panel. - This gets pre-empted by Legalpad (although we'll potentially survive this with the URI intact). - This also gets pre-empted by the "Set MFA" workflow. If the user completes this flow, they get redirected to a `/auth/multifactor/?id=123` sort of URI to highlight the factor they added. This causes us to lose the `/settings/panel/password/blah/blah?key=xyz` URI. The ordering on this is also not ideal; it's preferable to start with a password, then do the other steps, so the user can return to the flow more easily if they are interrupted. Resolve this by separating the "change your password" and "set/reset your password" flows onto two different pages. This copy/pastes a bit of code, but both flows end up simpler so it feels reasonable to me overall. We don't require a full session for "set/reset password" (so you can do it if you don't have MFA/legalpad yet) and do it first. This works better and is broadly simpler for users. Test Plan: - Required MFA + legalpad, invited a user via email, registered. - Before: password set flow got lost when setting MFA. - After: prompted to set password, then sign documents, then set up MFA. - Reset password (with MFA confgiured, was required to MFA first). - Tried to reset password without a valid reset key, wasn't successful. - Changed password using existing flow. - Hit various (all?) error cases (short password, common password, mismatch, missing password, etc). Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13024 Differential Revision: https://secure.phabricator.com/D18840
2017-12-22 20:55:39 +01:00
// NOTE: Users can also change passwords through the separate "set/reset"
// interface which is reached by logging in with a one-time token after
// registration or password reset. If this flow changes, that flow may
// also need to change.
$account_type = PhabricatorAuthPassword::PASSWORD_TYPE_ACCOUNT;
$password_objects = id(new PhabricatorAuthPasswordQuery())
->setViewer($viewer)
->withObjectPHIDs(array($user->getPHID()))
->withPasswordTypes(array($account_type))
->withIsRevoked(false)
->execute();
if ($password_objects) {
$password_object = head($password_objects);
} else {
$password_object = PhabricatorAuthPassword::initializeNewPassword(
$user,
$account_type);
}
$e_old = true;
$e_new = true;
$e_conf = true;
$errors = array();
if ($request->isFormPost()) {
// Rate limit guesses about the old password. This page requires MFA and
// session compromise already, so this is mostly just to stop researchers
// from reporting this as a vulnerability.
PhabricatorSystemActionEngine::willTakeAction(
array($viewer->getPHID()),
new PhabricatorAuthChangePasswordAction(),
1);
Separate "Set/Reset Password" from "Change Password" Summary: See PHI223. Ref T13024. There's a remaining registration/login order issue after the other changes in T13024: we lose track of the current URI when we go through the MFA flow, so we can lose "Set Password" at the end of the flow. Specifically, the flow goes like this today: - User clicks the welcome link in email. - They get redirected to the "set password" settings panel. - This gets pre-empted by Legalpad (although we'll potentially survive this with the URI intact). - This also gets pre-empted by the "Set MFA" workflow. If the user completes this flow, they get redirected to a `/auth/multifactor/?id=123` sort of URI to highlight the factor they added. This causes us to lose the `/settings/panel/password/blah/blah?key=xyz` URI. The ordering on this is also not ideal; it's preferable to start with a password, then do the other steps, so the user can return to the flow more easily if they are interrupted. Resolve this by separating the "change your password" and "set/reset your password" flows onto two different pages. This copy/pastes a bit of code, but both flows end up simpler so it feels reasonable to me overall. We don't require a full session for "set/reset password" (so you can do it if you don't have MFA/legalpad yet) and do it first. This works better and is broadly simpler for users. Test Plan: - Required MFA + legalpad, invited a user via email, registered. - Before: password set flow got lost when setting MFA. - After: prompted to set password, then sign documents, then set up MFA. - Reset password (with MFA confgiured, was required to MFA first). - Tried to reset password without a valid reset key, wasn't successful. - Changed password using existing flow. - Hit various (all?) error cases (short password, common password, mismatch, missing password, etc). Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13024 Differential Revision: https://secure.phabricator.com/D18840
2017-12-22 20:55:39 +01:00
$envelope = new PhutilOpaqueEnvelope($request->getStr('old_pw'));
$engine = id(new PhabricatorAuthPasswordEngine())
->setViewer($viewer)
->setContentSource($content_source)
->setPasswordType($account_type)
->setObject($user);
if (!strlen($envelope->openEnvelope())) {
$errors[] = pht('You must enter your current password.');
$e_old = pht('Required');
} else if (!$engine->isValidPassword($envelope)) {
Separate "Set/Reset Password" from "Change Password" Summary: See PHI223. Ref T13024. There's a remaining registration/login order issue after the other changes in T13024: we lose track of the current URI when we go through the MFA flow, so we can lose "Set Password" at the end of the flow. Specifically, the flow goes like this today: - User clicks the welcome link in email. - They get redirected to the "set password" settings panel. - This gets pre-empted by Legalpad (although we'll potentially survive this with the URI intact). - This also gets pre-empted by the "Set MFA" workflow. If the user completes this flow, they get redirected to a `/auth/multifactor/?id=123` sort of URI to highlight the factor they added. This causes us to lose the `/settings/panel/password/blah/blah?key=xyz` URI. The ordering on this is also not ideal; it's preferable to start with a password, then do the other steps, so the user can return to the flow more easily if they are interrupted. Resolve this by separating the "change your password" and "set/reset your password" flows onto two different pages. This copy/pastes a bit of code, but both flows end up simpler so it feels reasonable to me overall. We don't require a full session for "set/reset password" (so you can do it if you don't have MFA/legalpad yet) and do it first. This works better and is broadly simpler for users. Test Plan: - Required MFA + legalpad, invited a user via email, registered. - Before: password set flow got lost when setting MFA. - After: prompted to set password, then sign documents, then set up MFA. - Reset password (with MFA confgiured, was required to MFA first). - Tried to reset password without a valid reset key, wasn't successful. - Changed password using existing flow. - Hit various (all?) error cases (short password, common password, mismatch, missing password, etc). Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13024 Differential Revision: https://secure.phabricator.com/D18840
2017-12-22 20:55:39 +01:00
$errors[] = pht('The old password you entered is incorrect.');
$e_old = pht('Invalid');
} else {
$e_old = null;
// Refund the user an action credit for getting the password right.
PhabricatorSystemActionEngine::willTakeAction(
array($viewer->getPHID()),
new PhabricatorAuthChangePasswordAction(),
-1);
}
$pass = $request->getStr('new_pw');
$conf = $request->getStr('conf_pw');
$password_envelope = new PhutilOpaqueEnvelope($pass);
$confirm_envelope = new PhutilOpaqueEnvelope($conf);
try {
$engine->checkNewPassword($password_envelope, $confirm_envelope);
$e_new = null;
$e_conf = null;
} catch (PhabricatorAuthPasswordException $ex) {
$errors[] = $ex->getMessage();
$e_new = $ex->getPasswordError();
$e_conf = $ex->getConfirmError();
}
if (!$errors) {
$password_object
->setPassword($password_envelope, $user)
->save();
Separate "Set/Reset Password" from "Change Password" Summary: See PHI223. Ref T13024. There's a remaining registration/login order issue after the other changes in T13024: we lose track of the current URI when we go through the MFA flow, so we can lose "Set Password" at the end of the flow. Specifically, the flow goes like this today: - User clicks the welcome link in email. - They get redirected to the "set password" settings panel. - This gets pre-empted by Legalpad (although we'll potentially survive this with the URI intact). - This also gets pre-empted by the "Set MFA" workflow. If the user completes this flow, they get redirected to a `/auth/multifactor/?id=123` sort of URI to highlight the factor they added. This causes us to lose the `/settings/panel/password/blah/blah?key=xyz` URI. The ordering on this is also not ideal; it's preferable to start with a password, then do the other steps, so the user can return to the flow more easily if they are interrupted. Resolve this by separating the "change your password" and "set/reset your password" flows onto two different pages. This copy/pastes a bit of code, but both flows end up simpler so it feels reasonable to me overall. We don't require a full session for "set/reset password" (so you can do it if you don't have MFA/legalpad yet) and do it first. This works better and is broadly simpler for users. Test Plan: - Required MFA + legalpad, invited a user via email, registered. - Before: password set flow got lost when setting MFA. - After: prompted to set password, then sign documents, then set up MFA. - Reset password (with MFA confgiured, was required to MFA first). - Tried to reset password without a valid reset key, wasn't successful. - Changed password using existing flow. - Hit various (all?) error cases (short password, common password, mismatch, missing password, etc). Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13024 Differential Revision: https://secure.phabricator.com/D18840
2017-12-22 20:55:39 +01:00
$next = $this->getPanelURI('?saved=true');
2014-08-04 21:04:35 +02:00
id(new PhabricatorAuthSessionEngine())->terminateLoginSessions(
$user,
Upgrade sessions digests to HMAC256, retaining compatibility with old digests Summary: Ref T13222. Ref T13225. We store a digest of the session key in the session table (not the session key itself) so that users with access to this table can't easily steal sessions by just setting their cookies to values from the table. Users with access to the database can //probably// do plenty of other bad stuff (e.g., T13134 mentions digesting Conduit tokens) but there's very little cost to storing digests instead of live tokens. We currently digest session keys with HMAC-SHA1. This is fine, but HMAC-SHA256 is better. Upgrade: - Always write new digests. - We still match sessions with either digest. - When we read a session with an old digest, upgrade it to a new digest. In a few months we can throw away the old code. When we do, installs that skip upgrades for a long time may suffer a one-time logout, but I'll note this in the changelog. We could avoid this by storing `hmac256(hmac1(key))` instead and re-hashing in a migration, but I think the cost of a one-time logout for some tiny subset of users is very low, and worth keeping things simpler in the long run. Test Plan: - Hit a page with an old session, got a session upgrade. - Reviewed sessions in Settings. - Reviewed user logs. - Logged out. - Logged in. - Terminated other sessions individually. - Terminated all other sessions. - Spot checked session table for general sanity. Reviewers: amckinley Reviewed By: amckinley Subscribers: PHID-OPKG-gm6ozazyms6q6i22gyam Maniphest Tasks: T13225, T13222 Differential Revision: https://secure.phabricator.com/D19883
2018-12-13 19:52:54 +01:00
new PhutilOpaqueEnvelope(
$request->getCookie(PhabricatorCookies::COOKIE_SESSION)));
2014-08-04 21:04:35 +02:00
return id(new AphrontRedirectResponse())->setURI($next);
}
}
if ($password_object->getID()) {
try {
$can_upgrade = $password_object->canUpgrade();
} catch (PhabricatorPasswordHasherUnavailableException $ex) {
$can_upgrade = false;
Separate "Set/Reset Password" from "Change Password" Summary: See PHI223. Ref T13024. There's a remaining registration/login order issue after the other changes in T13024: we lose track of the current URI when we go through the MFA flow, so we can lose "Set Password" at the end of the flow. Specifically, the flow goes like this today: - User clicks the welcome link in email. - They get redirected to the "set password" settings panel. - This gets pre-empted by Legalpad (although we'll potentially survive this with the URI intact). - This also gets pre-empted by the "Set MFA" workflow. If the user completes this flow, they get redirected to a `/auth/multifactor/?id=123` sort of URI to highlight the factor they added. This causes us to lose the `/settings/panel/password/blah/blah?key=xyz` URI. The ordering on this is also not ideal; it's preferable to start with a password, then do the other steps, so the user can return to the flow more easily if they are interrupted. Resolve this by separating the "change your password" and "set/reset your password" flows onto two different pages. This copy/pastes a bit of code, but both flows end up simpler so it feels reasonable to me overall. We don't require a full session for "set/reset password" (so you can do it if you don't have MFA/legalpad yet) and do it first. This works better and is broadly simpler for users. Test Plan: - Required MFA + legalpad, invited a user via email, registered. - Before: password set flow got lost when setting MFA. - After: prompted to set password, then sign documents, then set up MFA. - Reset password (with MFA confgiured, was required to MFA first). - Tried to reset password without a valid reset key, wasn't successful. - Changed password using existing flow. - Hit various (all?) error cases (short password, common password, mismatch, missing password, etc). Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13024 Differential Revision: https://secure.phabricator.com/D18840
2017-12-22 20:55:39 +01:00
$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);
}
Separate "Set/Reset Password" from "Change Password" Summary: See PHI223. Ref T13024. There's a remaining registration/login order issue after the other changes in T13024: we lose track of the current URI when we go through the MFA flow, so we can lose "Set Password" at the end of the flow. Specifically, the flow goes like this today: - User clicks the welcome link in email. - They get redirected to the "set password" settings panel. - This gets pre-empted by Legalpad (although we'll potentially survive this with the URI intact). - This also gets pre-empted by the "Set MFA" workflow. If the user completes this flow, they get redirected to a `/auth/multifactor/?id=123` sort of URI to highlight the factor they added. This causes us to lose the `/settings/panel/password/blah/blah?key=xyz` URI. The ordering on this is also not ideal; it's preferable to start with a password, then do the other steps, so the user can return to the flow more easily if they are interrupted. Resolve this by separating the "change your password" and "set/reset your password" flows onto two different pages. This copy/pastes a bit of code, but both flows end up simpler so it feels reasonable to me overall. We don't require a full session for "set/reset password" (so you can do it if you don't have MFA/legalpad yet) and do it first. This works better and is broadly simpler for users. Test Plan: - Required MFA + legalpad, invited a user via email, registered. - Before: password set flow got lost when setting MFA. - After: prompted to set password, then sign documents, then set up MFA. - Reset password (with MFA confgiured, was required to MFA first). - Tried to reset password without a valid reset key, wasn't successful. - Changed password using existing flow. - Hit various (all?) error cases (short password, common password, mismatch, missing password, etc). Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13024 Differential Revision: https://secure.phabricator.com/D18840
2017-12-22 20:55:39 +01:00
$form = id(new AphrontFormView())
->setViewer($viewer)
Separate "Set/Reset Password" from "Change Password" Summary: See PHI223. Ref T13024. There's a remaining registration/login order issue after the other changes in T13024: we lose track of the current URI when we go through the MFA flow, so we can lose "Set Password" at the end of the flow. Specifically, the flow goes like this today: - User clicks the welcome link in email. - They get redirected to the "set password" settings panel. - This gets pre-empted by Legalpad (although we'll potentially survive this with the URI intact). - This also gets pre-empted by the "Set MFA" workflow. If the user completes this flow, they get redirected to a `/auth/multifactor/?id=123` sort of URI to highlight the factor they added. This causes us to lose the `/settings/panel/password/blah/blah?key=xyz` URI. The ordering on this is also not ideal; it's preferable to start with a password, then do the other steps, so the user can return to the flow more easily if they are interrupted. Resolve this by separating the "change your password" and "set/reset your password" flows onto two different pages. This copy/pastes a bit of code, but both flows end up simpler so it feels reasonable to me overall. We don't require a full session for "set/reset password" (so you can do it if you don't have MFA/legalpad yet) and do it first. This works better and is broadly simpler for users. Test Plan: - Required MFA + legalpad, invited a user via email, registered. - Before: password set flow got lost when setting MFA. - After: prompted to set password, then sign documents, then set up MFA. - Reset password (with MFA confgiured, was required to MFA first). - Tried to reset password without a valid reset key, wasn't successful. - Changed password using existing flow. - Hit various (all?) error cases (short password, common password, mismatch, missing password, etc). Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13024 Differential Revision: https://secure.phabricator.com/D18840
2017-12-22 20:55:39 +01:00
->appendChild(
id(new AphrontFormPasswordControl())
->setLabel(pht('Old Password'))
->setError($e_old)
Separate "Set/Reset Password" from "Change Password" Summary: See PHI223. Ref T13024. There's a remaining registration/login order issue after the other changes in T13024: we lose track of the current URI when we go through the MFA flow, so we can lose "Set Password" at the end of the flow. Specifically, the flow goes like this today: - User clicks the welcome link in email. - They get redirected to the "set password" settings panel. - This gets pre-empted by Legalpad (although we'll potentially survive this with the URI intact). - This also gets pre-empted by the "Set MFA" workflow. If the user completes this flow, they get redirected to a `/auth/multifactor/?id=123` sort of URI to highlight the factor they added. This causes us to lose the `/settings/panel/password/blah/blah?key=xyz` URI. The ordering on this is also not ideal; it's preferable to start with a password, then do the other steps, so the user can return to the flow more easily if they are interrupted. Resolve this by separating the "change your password" and "set/reset your password" flows onto two different pages. This copy/pastes a bit of code, but both flows end up simpler so it feels reasonable to me overall. We don't require a full session for "set/reset password" (so you can do it if you don't have MFA/legalpad yet) and do it first. This works better and is broadly simpler for users. Test Plan: - Required MFA + legalpad, invited a user via email, registered. - Before: password set flow got lost when setting MFA. - After: prompted to set password, then sign documents, then set up MFA. - Reset password (with MFA confgiured, was required to MFA first). - Tried to reset password without a valid reset key, wasn't successful. - Changed password using existing flow. - Hit various (all?) error cases (short password, common password, mismatch, missing password, etc). Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13024 Differential Revision: https://secure.phabricator.com/D18840
2017-12-22 20:55:39 +01:00
->setName('old_pw'))
->appendChild(
id(new AphrontFormPasswordControl())
->setDisableAutocomplete(true)
->setLabel(pht('New Password'))
->setError($e_new)
->setName('new_pw'))
->appendChild(
id(new AphrontFormPasswordControl())
->setDisableAutocomplete(true)
->setLabel(pht('Confirm Password'))
->setCaption($len_caption)
->setError($e_conf)
->setName('conf_pw'))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Change Password')));
$properties = id(new PHUIPropertyListView());
$properties->addProperty(
pht('Current Algorithm'),
PhabricatorPasswordHasher::getCurrentAlgorithmName(
$password_object->newPasswordEnvelope()));
$properties->addProperty(
pht('Best Available Algorithm'),
PhabricatorPasswordHasher::getBestAlgorithmName());
$info_view = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
->appendChild(
pht('Changing your password will terminate any other outstanding '.
'login sessions.'));
2014-08-04 21:04:35 +02:00
$algo_box = $this->newBox(pht('Password Algorithms'), $properties);
$form_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Change Password'))
->setFormSaved($request->getStr('saved'))
->setFormErrors($errors)
->setBackground(PHUIObjectBoxView::WHITE_CONFIG)
->setForm($form);
return array(
$form_box,
$algo_box,
$info_view,
);
}
2014-08-04 21:04:35 +02:00
}