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 11:55:39 -08:00
|
|
|
<?php
|
|
|
|
|
|
|
|
final class PhabricatorAuthSetPasswordController
|
|
|
|
extends PhabricatorAuthController {
|
|
|
|
|
|
|
|
public function shouldAllowPartialSessions() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function shouldAllowLegallyNonCompliantUsers() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function handleRequest(AphrontRequest $request) {
|
|
|
|
$viewer = $this->getViewer();
|
|
|
|
|
|
|
|
if (!PhabricatorPasswordAuthProvider::getPasswordProvider()) {
|
|
|
|
return new Aphront404Response();
|
|
|
|
}
|
|
|
|
|
|
|
|
$token = id(new PhabricatorAuthSessionEngine())->requireHighSecuritySession(
|
|
|
|
$viewer,
|
|
|
|
$request,
|
|
|
|
'/');
|
|
|
|
|
|
|
|
$key = $request->getStr('key');
|
|
|
|
$password_type = PhabricatorAuthPasswordResetTemporaryTokenType::TOKENTYPE;
|
|
|
|
if (!$key) {
|
|
|
|
return new Aphront404Response();
|
|
|
|
}
|
|
|
|
|
|
|
|
$auth_token = id(new PhabricatorAuthTemporaryTokenQuery())
|
|
|
|
->setViewer($viewer)
|
|
|
|
->withTokenResources(array($viewer->getPHID()))
|
|
|
|
->withTokenTypes(array($password_type))
|
|
|
|
->withTokenCodes(array(PhabricatorHash::weakDigest($key)))
|
|
|
|
->withExpired(false)
|
|
|
|
->executeOne();
|
|
|
|
if (!$auth_token) {
|
|
|
|
return new Aphront404Response();
|
|
|
|
}
|
|
|
|
|
2018-01-21 15:42:24 -08:00
|
|
|
$content_source = PhabricatorContentSource::newFromRequest($request);
|
|
|
|
$account_type = PhabricatorAuthPassword::PASSWORD_TYPE_ACCOUNT;
|
|
|
|
|
|
|
|
$password_objects = id(new PhabricatorAuthPasswordQuery())
|
|
|
|
->setViewer($viewer)
|
|
|
|
->withObjectPHIDs(array($viewer->getPHID()))
|
|
|
|
->withPasswordTypes(array($account_type))
|
|
|
|
->withIsRevoked(false)
|
|
|
|
->execute();
|
|
|
|
if ($password_objects) {
|
|
|
|
$password_object = head($password_objects);
|
|
|
|
$has_password = true;
|
|
|
|
} else {
|
|
|
|
$password_object = PhabricatorAuthPassword::initializeNewPassword(
|
|
|
|
$viewer,
|
|
|
|
$account_type);
|
|
|
|
$has_password = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
$engine = id(new PhabricatorAuthPasswordEngine())
|
|
|
|
->setViewer($viewer)
|
|
|
|
->setContentSource($content_source)
|
|
|
|
->setPasswordType($account_type)
|
|
|
|
->setObject($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 11:55:39 -08:00
|
|
|
|
|
|
|
$e_password = true;
|
|
|
|
$e_confirm = true;
|
|
|
|
$errors = array();
|
|
|
|
if ($request->isFormPost()) {
|
|
|
|
$password = $request->getStr('password');
|
|
|
|
$confirm = $request->getStr('confirm');
|
|
|
|
|
2018-01-21 15:42:24 -08:00
|
|
|
$password_envelope = new PhutilOpaqueEnvelope($password);
|
|
|
|
$confirm_envelope = new PhutilOpaqueEnvelope($confirm);
|
|
|
|
|
|
|
|
try {
|
|
|
|
$engine->checkNewPassword($password_envelope, $confirm_envelope, true);
|
|
|
|
$e_password = null;
|
|
|
|
$e_confirm = null;
|
|
|
|
} catch (PhabricatorAuthPasswordException $ex) {
|
|
|
|
$errors[] = $ex->getMessage();
|
|
|
|
$e_password = $ex->getPasswordError();
|
|
|
|
$e_confirm = $ex->getConfirmError();
|
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 11:55:39 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!$errors) {
|
2018-01-21 15:42:24 -08:00
|
|
|
$password_object
|
|
|
|
->setPassword($password_envelope, $viewer)
|
|
|
|
->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 11:55:39 -08:00
|
|
|
|
|
|
|
// Destroy the token.
|
|
|
|
$auth_token->delete();
|
|
|
|
|
|
|
|
return id(new AphrontRedirectResponse())->setURI('/');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-21 15:42:24 -08:00
|
|
|
$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 11:55:39 -08:00
|
|
|
$len_caption = null;
|
|
|
|
if ($min_len) {
|
|
|
|
$len_caption = pht('Minimum password length: %d characters.', $min_len);
|
|
|
|
}
|
|
|
|
|
2018-01-21 15:42:24 -08:00
|
|
|
if ($has_password) {
|
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 11:55:39 -08:00
|
|
|
$title = pht('Reset Password');
|
|
|
|
$crumb = pht('Reset Password');
|
|
|
|
$submit = pht('Reset Password');
|
|
|
|
} else {
|
|
|
|
$title = pht('Set Password');
|
|
|
|
$crumb = pht('Set Password');
|
|
|
|
$submit = pht('Set Account Password');
|
|
|
|
}
|
|
|
|
|
|
|
|
$form = id(new AphrontFormView())
|
|
|
|
->setViewer($viewer)
|
|
|
|
->addHiddenInput('key', $key)
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormPasswordControl())
|
|
|
|
->setDisableAutocomplete(true)
|
|
|
|
->setLabel(pht('New Password'))
|
|
|
|
->setError($e_password)
|
|
|
|
->setName('password'))
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormPasswordControl())
|
|
|
|
->setDisableAutocomplete(true)
|
|
|
|
->setLabel(pht('Confirm Password'))
|
|
|
|
->setCaption($len_caption)
|
|
|
|
->setError($e_confirm)
|
|
|
|
->setName('confirm'))
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormSubmitControl())
|
|
|
|
->addCancelButton('/', pht('Skip This Step'))
|
|
|
|
->setValue($submit));
|
|
|
|
|
|
|
|
$form_box = id(new PHUIObjectBoxView())
|
|
|
|
->setHeaderText($title)
|
|
|
|
->setFormErrors($errors)
|
|
|
|
->setBackground(PHUIObjectBoxView::WHITE_CONFIG)
|
|
|
|
->setForm($form);
|
|
|
|
|
|
|
|
$main_view = id(new PHUITwoColumnView())
|
|
|
|
->setFooter($form_box);
|
|
|
|
|
|
|
|
$crumbs = $this->buildApplicationCrumbs()
|
|
|
|
->addTextCrumb($crumb)
|
|
|
|
->setBorder(true);
|
|
|
|
|
|
|
|
return $this->newPage()
|
|
|
|
->setTitle($title)
|
|
|
|
->setCrumbs($crumbs)
|
|
|
|
->appendChild($main_view);
|
|
|
|
}
|
|
|
|
}
|