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; // 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); $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)) { $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(); $next = $this->getPanelURI('?saved=true'); id(new PhabricatorAuthSessionEngine())->terminateLoginSessions( $user, new PhutilOpaqueEnvelope( $request->getCookie(PhabricatorCookies::COOKIE_SESSION))); return id(new AphrontRedirectResponse())->setURI($next); } } if ($password_object->getID()) { try { $can_upgrade = $password_object->canUpgrade(); } catch (PhabricatorPasswordHasherUnavailableException $ex) { $can_upgrade = false; $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 = id(new AphrontFormView()) ->setViewer($viewer) ->appendChild( id(new AphrontFormPasswordControl()) ->setLabel(pht('Old Password')) ->setError($e_old) ->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.')); $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, ); } }