예제 #1
0
 /**
  * Executes the log-in attempt using the parameters passed. If
  * the log-in succeeds, it attaches a cookie to the session
  * and outputs the user id, username, and session token. If a
  * log-in fails, as the result of a bad password, a nonexistent
  * user, or any other reason, the host is cached with an expiry
  * and no log-in attempts will be accepted until that expiry
  * is reached. The expiry is $this->mLoginThrottle.
  */
 public function execute()
 {
     // If we're in a mode that breaks the same-origin policy, no tokens can
     // be obtained
     if ($this->lacksSameOriginSecurity()) {
         $this->getResult()->addValue(null, 'login', ['result' => 'Aborted', 'reason' => 'Cannot log in when the same-origin policy is not applied']);
         return;
     }
     try {
         $this->requirePostedParameters(['password', 'token']);
     } catch (UsageException $ex) {
         // Make this a warning for now, upgrade to an error in 1.29.
         $this->setWarning($ex->getMessage());
         $this->logFeatureUsage('login-params-in-query-string');
     }
     $params = $this->extractRequestParams();
     $result = [];
     // Make sure session is persisted
     $session = MediaWiki\Session\SessionManager::getGlobalSession();
     $session->persist();
     // Make sure it's possible to log in
     if (!$session->canSetUser()) {
         $this->getResult()->addValue(null, 'login', ['result' => 'Aborted', 'reason' => 'Cannot log in when using ' . $session->getProvider()->describe(Language::factory('en'))]);
         return;
     }
     $authRes = false;
     $context = new DerivativeContext($this->getContext());
     $loginType = 'N/A';
     // Check login token
     $token = $session->getToken('', 'login');
     if ($token->wasNew() || !$params['token']) {
         $authRes = 'NeedToken';
     } elseif (!$token->match($params['token'])) {
         $authRes = 'WrongToken';
     }
     // Try bot passwords
     if ($authRes === false && $this->getConfig()->get('EnableBotPasswords') && ($botLoginData = BotPassword::canonicalizeLoginData($params['name'], $params['password']))) {
         $status = BotPassword::login($botLoginData[0], $botLoginData[1], $this->getRequest());
         if ($status->isOK()) {
             $session = $status->getValue();
             $authRes = 'Success';
             $loginType = 'BotPassword';
         } elseif (!$botLoginData[2]) {
             $authRes = 'Failed';
             $message = $status->getMessage();
             LoggerFactory::getInstance('authentication')->info('BotPassword login failed: ' . $status->getWikiText(false, false, 'en'));
         }
     }
     if ($authRes === false) {
         // Simplified AuthManager login, for backwards compatibility
         $manager = AuthManager::singleton();
         $reqs = AuthenticationRequest::loadRequestsFromSubmission($manager->getAuthenticationRequests(AuthManager::ACTION_LOGIN, $this->getUser()), ['username' => $params['name'], 'password' => $params['password'], 'domain' => $params['domain'], 'rememberMe' => true]);
         $res = AuthManager::singleton()->beginAuthentication($reqs, 'null:');
         switch ($res->status) {
             case AuthenticationResponse::PASS:
                 if ($this->getConfig()->get('EnableBotPasswords')) {
                     $warn = 'Main-account login via action=login is deprecated and may stop working ' . 'without warning.';
                     $warn .= ' To continue login with action=login, see [[Special:BotPasswords]].';
                     $warn .= ' To safely continue using main-account login, see action=clientlogin.';
                 } else {
                     $warn = 'Login via action=login is deprecated and may stop working without warning.';
                     $warn .= ' To safely log in, see action=clientlogin.';
                 }
                 $this->setWarning($warn);
                 $authRes = 'Success';
                 $loginType = 'AuthManager';
                 break;
             case AuthenticationResponse::FAIL:
                 // Hope it's not a PreAuthenticationProvider that failed...
                 $authRes = 'Failed';
                 $message = $res->message;
                 \MediaWiki\Logger\LoggerFactory::getInstance('authentication')->info(__METHOD__ . ': Authentication failed: ' . $message->inLanguage('en')->plain());
                 break;
             default:
                 \MediaWiki\Logger\LoggerFactory::getInstance('authentication')->info(__METHOD__ . ': Authentication failed due to unsupported response type: ' . $res->status, $this->getAuthenticationResponseLogData($res));
                 $authRes = 'Aborted';
                 break;
         }
     }
     $result['result'] = $authRes;
     switch ($authRes) {
         case 'Success':
             $user = $session->getUser();
             ApiQueryInfo::resetTokenCache();
             // Deprecated hook
             $injected_html = '';
             Hooks::run('UserLoginComplete', [&$user, &$injected_html, true]);
             $result['lguserid'] = intval($user->getId());
             $result['lgusername'] = $user->getName();
             break;
         case 'NeedToken':
             $result['token'] = $token->toString();
             $this->setWarning('Fetching a token via action=login is deprecated. ' . 'Use action=query&meta=tokens&type=login instead.');
             $this->logFeatureUsage('action=login&!lgtoken');
             break;
         case 'WrongToken':
             break;
         case 'Failed':
             $result['reason'] = $message->useDatabase('false')->inLanguage('en')->text();
             break;
         case 'Aborted':
             $result['reason'] = 'Authentication requires user interaction, ' . 'which is not supported by action=login.';
             if ($this->getConfig()->get('EnableBotPasswords')) {
                 $result['reason'] .= ' To be able to login with action=login, see [[Special:BotPasswords]].';
                 $result['reason'] .= ' To continue using main-account login, see action=clientlogin.';
             } else {
                 $result['reason'] .= ' To log in, see action=clientlogin.';
             }
             break;
         default:
             ApiBase::dieDebug(__METHOD__, "Unhandled case value: {$authRes}");
     }
     $this->getResult()->addValue(null, 'login', $result);
     if ($loginType === 'LoginForm' && isset(LoginForm::$statusCodes[$authRes])) {
         $authRes = LoginForm::$statusCodes[$authRes];
     }
     LoggerFactory::getInstance('authevents')->info('Login attempt', ['event' => 'login', 'successful' => $authRes === 'Success', 'loginType' => $loginType, 'status' => $authRes]);
 }
예제 #2
0
 /**
  * @dataProvider provideCanonicalizeLoginData
  */
 public function testCanonicalizeLoginData($username, $password, $expectedResult)
 {
     $result = BotPassword::canonicalizeLoginData($username, $password);
     if (is_array($expectedResult)) {
         $this->assertArrayEquals($expectedResult, $result, true, true);
     } else {
         $this->assertSame($expectedResult, $result);
     }
 }