diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2033,6 +2033,7 @@ 'PhabricatorAuthApplication' => 'applications/auth/application/PhabricatorAuthApplication.php', 'PhabricatorAuthAuthFactorPHIDType' => 'applications/auth/phid/PhabricatorAuthAuthFactorPHIDType.php', 'PhabricatorAuthAuthProviderPHIDType' => 'applications/auth/phid/PhabricatorAuthAuthProviderPHIDType.php', + 'PhabricatorAuthChangePasswordAction' => 'applications/auth/action/PhabricatorAuthChangePasswordAction.php', 'PhabricatorAuthConduitAPIMethod' => 'applications/auth/conduit/PhabricatorAuthConduitAPIMethod.php', 'PhabricatorAuthConduitTokenRevoker' => 'applications/auth/revoker/PhabricatorAuthConduitTokenRevoker.php', 'PhabricatorAuthConfirmLinkController' => 'applications/auth/controller/PhabricatorAuthConfirmLinkController.php', @@ -7321,6 +7322,7 @@ 'PhabricatorAuthApplication' => 'PhabricatorApplication', 'PhabricatorAuthAuthFactorPHIDType' => 'PhabricatorPHIDType', 'PhabricatorAuthAuthProviderPHIDType' => 'PhabricatorPHIDType', + 'PhabricatorAuthChangePasswordAction' => 'PhabricatorSystemAction', 'PhabricatorAuthConduitAPIMethod' => 'ConduitAPIMethod', 'PhabricatorAuthConduitTokenRevoker' => 'PhabricatorAuthRevoker', 'PhabricatorAuthConfirmLinkController' => 'PhabricatorAuthController', diff --git a/src/applications/auth/action/PhabricatorAuthChangePasswordAction.php b/src/applications/auth/action/PhabricatorAuthChangePasswordAction.php new file mode 100644 --- /dev/null +++ b/src/applications/auth/action/PhabricatorAuthChangePasswordAction.php @@ -0,0 +1,22 @@ +getUser(); + $viewer = $request->getUser(); + $user = $this->getUser(); + $content_source = PhabricatorContentSource::newFromRequest($request); $token = id(new PhabricatorAuthSessionEngine())->requireHighSecuritySession( - $user, + $viewer, $request, '/settings/'); @@ -44,7 +46,7 @@ $account_type = PhabricatorAuthPassword::PASSWORD_TYPE_ACCOUNT; $password_objects = id(new PhabricatorAuthPasswordQuery()) - ->setViewer($user) + ->setViewer($viewer) ->withObjectPHIDs(array($user->getPHID())) ->withPasswordTypes(array($account_type)) ->withIsRevoked(false) @@ -63,10 +65,18 @@ $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($user) + ->setViewer($viewer) ->setContentSource($content_source) ->setPasswordType($account_type) ->setObject($user); @@ -79,6 +89,12 @@ $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'); @@ -142,7 +158,7 @@ } $form = id(new AphrontFormView()) - ->setViewer($user) + ->setViewer($viewer) ->appendChild( id(new AphrontFormPasswordControl()) ->setLabel(pht('Old Password'))