diff --git a/src/applications/auth/controller/PhabricatorAuthController.php b/src/applications/auth/controller/PhabricatorAuthController.php --- a/src/applications/auth/controller/PhabricatorAuthController.php +++ b/src/applications/auth/controller/PhabricatorAuthController.php @@ -45,9 +45,12 @@ * event and do something else if they prefer. * * @param PhabricatorUser User to log the viewer in as. + * @param bool True to issue a full session immediately, bypassing MFA. * @return AphrontResponse Response which continues the login process. */ - protected function loginUser(PhabricatorUser $user) { + protected function loginUser( + PhabricatorUser $user, + $force_full_session = false) { $response = $this->buildLoginValidateResponse($user); $session_type = PhabricatorAuthSession::TYPE_WEB; @@ -66,8 +69,14 @@ $should_login = $event->getValue('shouldLogin'); if ($should_login) { + if ($force_full_session) { + $partial_session = false; + } else { + $partial_session = true; + } + $session_key = id(new PhabricatorAuthSessionEngine()) - ->establishSession($session_type, $user->getPHID(), $partial = true); + ->establishSession($session_type, $user->getPHID(), $partial_session); // NOTE: We allow disabled users to login and roadblock them later, so // there's no check for users being disabled here. diff --git a/src/applications/auth/controller/PhabricatorAuthOneTimeLoginController.php b/src/applications/auth/controller/PhabricatorAuthOneTimeLoginController.php --- a/src/applications/auth/controller/PhabricatorAuthOneTimeLoginController.php +++ b/src/applications/auth/controller/PhabricatorAuthOneTimeLoginController.php @@ -152,7 +152,12 @@ PhabricatorCookies::setNextURICookie($request, $next, $force = true); - return $this->loginUser($target_user); + $force_full_session = false; + if ($link_type === PhabricatorAuthSessionEngine::ONETIME_RECOVER) { + $force_full_session = $token->getShouldForceFullSession(); + } + + return $this->loginUser($target_user, $force_full_session); } // NOTE: We need to CSRF here so attackers can't generate an email link, diff --git a/src/applications/auth/engine/PhabricatorAuthSessionEngine.php b/src/applications/auth/engine/PhabricatorAuthSessionEngine.php --- a/src/applications/auth/engine/PhabricatorAuthSessionEngine.php +++ b/src/applications/auth/engine/PhabricatorAuthSessionEngine.php @@ -893,24 +893,28 @@ * link is used. * @param string Optional context string for the URI. This is purely cosmetic * and used only to customize workflow and error messages. + * @param bool True to generate a URI which forces an immediate upgrade to + * a full session, bypassing MFA and other login checks. * @return string Login URI. * @task onetime */ public function getOneTimeLoginURI( PhabricatorUser $user, PhabricatorUserEmail $email = null, - $type = self::ONETIME_RESET) { + $type = self::ONETIME_RESET, + $force_full_session = false) { $key = Filesystem::readRandomCharacters(32); $key_hash = $this->getOneTimeLoginKeyHash($user, $email, $key); $onetime_type = PhabricatorAuthOneTimeLoginTemporaryTokenType::TOKENTYPE; $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); - id(new PhabricatorAuthTemporaryToken()) + $token = id(new PhabricatorAuthTemporaryToken()) ->setTokenResource($user->getPHID()) ->setTokenType($onetime_type) ->setTokenExpires(time() + phutil_units('1 day in seconds')) ->setTokenCode($key_hash) + ->setShouldForceFullSession($force_full_session) ->save(); unset($unguarded); diff --git a/src/applications/auth/management/PhabricatorAuthManagementRecoverWorkflow.php b/src/applications/auth/management/PhabricatorAuthManagementRecoverWorkflow.php --- a/src/applications/auth/management/PhabricatorAuthManagementRecoverWorkflow.php +++ b/src/applications/auth/management/PhabricatorAuthManagementRecoverWorkflow.php @@ -13,7 +13,13 @@ 'of Phabricator.')) ->setArguments( array( - 'username' => array( + array( + 'name' => 'force-full-session', + 'help' => pht( + 'Recover directly into a full session without requiring MFA '. + 'or other login checks.'), + ), + array( 'name' => 'username', 'wildcard' => true, ), @@ -54,11 +60,14 @@ $username)); } + $force_full_session = $args->getArg('force-full-session'); + $engine = new PhabricatorAuthSessionEngine(); $onetime_uri = $engine->getOneTimeLoginURI( $user, null, - PhabricatorAuthSessionEngine::ONETIME_RECOVER); + PhabricatorAuthSessionEngine::ONETIME_RECOVER, + $force_full_session); $console = PhutilConsole::getConsole(); $console->writeOut( diff --git a/src/applications/auth/storage/PhabricatorAuthTemporaryToken.php b/src/applications/auth/storage/PhabricatorAuthTemporaryToken.php --- a/src/applications/auth/storage/PhabricatorAuthTemporaryToken.php +++ b/src/applications/auth/storage/PhabricatorAuthTemporaryToken.php @@ -106,6 +106,16 @@ return $this; } + public function setShouldForceFullSession($force_full) { + return $this->setTemporaryTokenProperty('force-full-session', $force_full); + } + + public function getShouldForceFullSession() { + return $this->getTemporaryTokenProperty('force-full-session', false); + } + + + /* -( PhabricatorPolicyInterface )----------------------------------------- */