3 final class PhabricatorPasswordSettingsPanel
extends PhabricatorSettingsPanel
{
5 public function getPanelKey() {
9 public function getPanelName() {
10 return pht('Password');
13 public function getPanelMenuIcon() {
17 public function getPanelGroupKey() {
18 return PhabricatorSettingsAuthenticationPanelGroup
::PANELGROUPKEY
;
21 public function isEnabled() {
22 // There's no sense in showing a change password panel if this install
23 // doesn't support password authentication.
24 if (!PhabricatorPasswordAuthProvider
::getPasswordProvider()) {
31 public function processRequest(AphrontRequest
$request) {
32 $viewer = $request->getUser();
33 $user = $this->getUser();
35 $content_source = PhabricatorContentSource
::newFromRequest($request);
37 $min_len = PhabricatorEnv
::getEnvConfig('account.minimum-password-length');
38 $min_len = (int)$min_len;
40 // NOTE: Users can also change passwords through the separate "set/reset"
41 // interface which is reached by logging in with a one-time token after
42 // registration or password reset. If this flow changes, that flow may
43 // also need to change.
45 $account_type = PhabricatorAuthPassword
::PASSWORD_TYPE_ACCOUNT
;
47 $password_objects = id(new PhabricatorAuthPasswordQuery())
49 ->withObjectPHIDs(array($user->getPHID()))
50 ->withPasswordTypes(array($account_type))
51 ->withIsRevoked(false)
53 if (!$password_objects) {
54 return $this->newSetPasswordView($request);
56 $password_object = head($password_objects);
63 if ($request->isFormOrHisecPost()) {
64 $workflow_key = sprintf(
65 'password.change(%s)',
68 $hisec_token = id(new PhabricatorAuthSessionEngine())
69 ->setWorkflowKey($workflow_key)
70 ->requireHighSecurityToken($viewer, $request, '/settings/');
72 // Rate limit guesses about the old password. This page requires MFA and
73 // session compromise already, so this is mostly just to stop researchers
74 // from reporting this as a vulnerability.
75 PhabricatorSystemActionEngine
::willTakeAction(
76 array($viewer->getPHID()),
77 new PhabricatorAuthChangePasswordAction(),
80 $envelope = new PhutilOpaqueEnvelope($request->getStr('old_pw'));
82 $engine = id(new PhabricatorAuthPasswordEngine())
84 ->setContentSource($content_source)
85 ->setPasswordType($account_type)
88 if (!strlen($envelope->openEnvelope())) {
89 $errors[] = pht('You must enter your current password.');
90 $e_old = pht('Required');
91 } else if (!$engine->isValidPassword($envelope)) {
92 $errors[] = pht('The old password you entered is incorrect.');
93 $e_old = pht('Invalid');
97 // Refund the user an action credit for getting the password right.
98 PhabricatorSystemActionEngine
::willTakeAction(
99 array($viewer->getPHID()),
100 new PhabricatorAuthChangePasswordAction(),
104 $pass = $request->getStr('new_pw');
105 $conf = $request->getStr('conf_pw');
106 $password_envelope = new PhutilOpaqueEnvelope($pass);
107 $confirm_envelope = new PhutilOpaqueEnvelope($conf);
110 $engine->checkNewPassword($password_envelope, $confirm_envelope);
113 } catch (PhabricatorAuthPasswordException
$ex) {
114 $errors[] = $ex->getMessage();
115 $e_new = $ex->getPasswordError();
116 $e_conf = $ex->getConfirmError();
121 ->setPassword($password_envelope, $user)
124 $next = $this->getPanelURI('?saved=true');
126 id(new PhabricatorAuthSessionEngine())->terminateLoginSessions(
128 new PhutilOpaqueEnvelope(
129 $request->getCookie(PhabricatorCookies
::COOKIE_SESSION
)));
131 return id(new AphrontRedirectResponse())->setURI($next);
135 if ($password_object->getID()) {
137 $can_upgrade = $password_object->canUpgrade();
138 } catch (PhabricatorPasswordHasherUnavailableException
$ex) {
139 $can_upgrade = false;
142 'Your password is currently hashed using an algorithm which is '.
143 'no longer available on this install.');
145 'Because the algorithm implementation is missing, your password '.
146 'can not be used or updated.');
148 'To set a new password, request a password reset link from the '.
149 'login screen and then follow the instructions.');
154 'The strength of your stored password hash can be upgraded. '.
155 'To upgrade, either: log out and log in using your password; or '.
156 'change your password.');
162 $len_caption = pht('Minimum password length: %d characters.', $min_len);
165 $form = id(new AphrontFormView())
168 id(new AphrontFormPasswordControl())
169 ->setLabel(pht('Old Password'))
173 id(new AphrontFormPasswordControl())
174 ->setDisableAutocomplete(true)
175 ->setLabel(pht('New Password'))
179 id(new AphrontFormPasswordControl())
180 ->setDisableAutocomplete(true)
181 ->setLabel(pht('Confirm Password'))
182 ->setCaption($len_caption)
184 ->setName('conf_pw'))
186 id(new AphrontFormSubmitControl())
187 ->setValue(pht('Change Password')));
189 $properties = id(new PHUIPropertyListView());
191 $properties->addProperty(
192 pht('Current Algorithm'),
193 PhabricatorPasswordHasher
::getCurrentAlgorithmName(
194 $password_object->newPasswordEnvelope()));
196 $properties->addProperty(
197 pht('Best Available Algorithm'),
198 PhabricatorPasswordHasher
::getBestAlgorithmName());
200 $info_view = id(new PHUIInfoView())
201 ->setSeverity(PHUIInfoView
::SEVERITY_NOTICE
)
203 pht('Changing your password will terminate any other outstanding '.
206 $algo_box = $this->newBox(pht('Password Algorithms'), $properties);
207 $form_box = id(new PHUIObjectBoxView())
208 ->setHeaderText(pht('Change Password'))
209 ->setFormErrors($errors)
210 ->setBackground(PHUIObjectBoxView
::WHITE_CONFIG
)
220 private function newSetPasswordView(AphrontRequest
$request) {
221 $viewer = $request->getUser();
222 $user = $this->getUser();
224 $form = id(new AphrontFormView())
226 ->appendRemarkupInstructions(
228 'Your account does not currently have a password set. You can '.
229 'choose a password by performing a password reset.'))
231 id(new AphrontFormSubmitControl())
232 ->addCancelButton('/login/email/', pht('Reset Password')));
234 $form_box = id(new PHUIObjectBoxView())
235 ->setHeaderText(pht('Set Password'))
236 ->setBackground(PHUIObjectBoxView
::WHITE_CONFIG
)