MediaWiki  master
ApiLogin.php
Go to the documentation of this file.
1 <?php
28 
34 class ApiLogin extends ApiBase {
35 
36  public function __construct( ApiMain $main, $action ) {
37  parent::__construct( $main, $action, 'lg' );
38  }
39 
40  protected function getExtendedDescription() {
41  if ( $this->getConfig()->get( 'EnableBotPasswords' ) ) {
42  return 'apihelp-login-extended-description';
43  } else {
44  return 'apihelp-login-extended-description-nobotpasswords';
45  }
46  }
47 
53  private function formatMessage( $message ) {
54  $message = Message::newFromSpecifier( $message );
55  $errorFormatter = $this->getErrorFormatter();
56  if ( $errorFormatter instanceof ApiErrorFormatter_BackCompat ) {
58  $message->useDatabase( false )->inLanguage( 'en' )->text()
59  );
60  } else {
61  return $errorFormatter->formatMessage( $message );
62  }
63  }
64 
74  public function execute() {
75  // If we're in a mode that breaks the same-origin policy, no tokens can
76  // be obtained
77  if ( $this->lacksSameOriginSecurity() ) {
78  $this->getResult()->addValue( null, 'login', [
79  'result' => 'Aborted',
80  'reason' => $this->formatMessage( 'api-login-fail-sameorigin' ),
81  ] );
82 
83  return;
84  }
85 
86  $this->requirePostedParameters( [ 'password', 'token' ] );
87 
88  $params = $this->extractRequestParams();
89 
90  $result = [];
91 
92  // Make sure session is persisted
94  $session->persist();
95 
96  // Make sure it's possible to log in
97  if ( !$session->canSetUser() ) {
98  $this->getResult()->addValue( null, 'login', [
99  'result' => 'Aborted',
100  'reason' => $this->formatMessage( [
101  'api-login-fail-badsessionprovider',
102  $session->getProvider()->describe( $this->getErrorFormatter()->getLanguage() ),
103  ] )
104  ] );
105 
106  return;
107  }
108 
109  $authRes = false;
110  $loginType = 'N/A';
111 
112  // Check login token
113  $token = $session->getToken( '', 'login' );
114  if ( $token->wasNew() || !$params['token'] ) {
115  $authRes = 'NeedToken';
116  } elseif ( !$token->match( $params['token'] ) ) {
117  $authRes = 'WrongToken';
118  }
119 
120  // Try bot passwords
121  if (
122  $authRes === false && $this->getConfig()->get( 'EnableBotPasswords' ) &&
123  ( $botLoginData = BotPassword::canonicalizeLoginData( $params['name'], $params['password'] ) )
124  ) {
125  $status = BotPassword::login(
126  $botLoginData[0], $botLoginData[1], $this->getRequest()
127  );
128  if ( $status->isOK() ) {
129  $session = $status->getValue();
130  $authRes = 'Success';
131  $loginType = 'BotPassword';
132  } elseif (
133  $status->hasMessage( 'login-throttled' ) ||
134  $status->hasMessage( 'botpasswords-needs-reset' ) ||
135  $status->hasMessage( 'botpasswords-locked' )
136  ) {
137  $authRes = 'Failed';
138  $message = $status->getMessage();
139  LoggerFactory::getInstance( 'authentication' )->info(
140  'BotPassword login failed: ' . $status->getWikiText( false, false, 'en' )
141  );
142  }
143  // For other errors, let's see if it's a valid non-bot login
144  }
145 
146  if ( $authRes === false ) {
147  // Simplified AuthManager login, for backwards compatibility
148  $manager = AuthManager::singleton();
149  $reqs = AuthenticationRequest::loadRequestsFromSubmission(
150  $manager->getAuthenticationRequests( AuthManager::ACTION_LOGIN, $this->getUser() ),
151  [
152  'username' => $params['name'],
153  'password' => $params['password'],
154  'domain' => $params['domain'],
155  'rememberMe' => true,
156  ]
157  );
158  $res = AuthManager::singleton()->beginAuthentication( $reqs, 'null:' );
159  switch ( $res->status ) {
160  case AuthenticationResponse::PASS:
161  if ( $this->getConfig()->get( 'EnableBotPasswords' ) ) {
162  $this->addDeprecation( 'apiwarn-deprecation-login-botpw', 'main-account-login' );
163  } else {
164  $this->addDeprecation( 'apiwarn-deprecation-login-nobotpw', 'main-account-login' );
165  }
166  $authRes = 'Success';
167  $loginType = 'AuthManager';
168  break;
169 
170  case AuthenticationResponse::FAIL:
171  // Hope it's not a PreAuthenticationProvider that failed...
172  $authRes = 'Failed';
173  $message = $res->message;
175  ->info( __METHOD__ . ': Authentication failed: '
176  . $message->inLanguage( 'en' )->plain() );
177  break;
178 
179  default:
181  ->info( __METHOD__ . ': Authentication failed due to unsupported response type: '
182  . $res->status, $this->getAuthenticationResponseLogData( $res ) );
183  $authRes = 'Aborted';
184  break;
185  }
186  }
187 
188  $result['result'] = $authRes;
189  switch ( $authRes ) {
190  case 'Success':
191  $user = $session->getUser();
192 
194 
195  // Deprecated hook
196  $injected_html = '';
197  Hooks::run( 'UserLoginComplete', [ &$user, &$injected_html, true ] );
198 
199  $result['lguserid'] = (int)$user->getId();
200  $result['lgusername'] = $user->getName();
201  break;
202 
203  case 'NeedToken':
204  $result['token'] = $token->toString();
205  $this->addDeprecation( 'apiwarn-deprecation-login-token', 'action=login&!lgtoken' );
206  break;
207 
208  case 'WrongToken':
209  break;
210 
211  case 'Failed':
212  $result['reason'] = $this->formatMessage( $message );
213  break;
214 
215  case 'Aborted':
216  $result['reason'] = $this->formatMessage(
217  $this->getConfig()->get( 'EnableBotPasswords' )
218  ? 'api-login-fail-aborted'
219  : 'api-login-fail-aborted-nobotpw'
220  );
221  break;
222 
223  // @codeCoverageIgnoreStart
224  // Unreachable
225  default:
226  ApiBase::dieDebug( __METHOD__, "Unhandled case value: {$authRes}" );
227  // @codeCoverageIgnoreEnd
228  }
229 
230  $this->getResult()->addValue( null, 'login', $result );
231 
232  LoggerFactory::getInstance( 'authevents' )->info( 'Login attempt', [
233  'event' => 'login',
234  'successful' => $authRes === 'Success',
235  'loginType' => $loginType,
236  'status' => $authRes,
237  ] );
238  }
239 
240  public function isDeprecated() {
241  return !$this->getConfig()->get( 'EnableBotPasswords' );
242  }
243 
244  public function mustBePosted() {
245  return true;
246  }
247 
248  public function isReadMode() {
249  return false;
250  }
251 
252  public function getAllowedParams() {
253  return [
254  'name' => null,
255  'password' => [
256  ApiBase::PARAM_TYPE => 'password',
257  ],
258  'domain' => null,
259  'token' => [
260  ApiBase::PARAM_TYPE => 'string',
261  ApiBase::PARAM_REQUIRED => false, // for BC
262  ApiBase::PARAM_SENSITIVE => true,
263  ApiBase::PARAM_HELP_MSG => [ 'api-help-param-token', 'login' ],
264  ],
265  ];
266  }
267 
268  protected function getExamplesMessages() {
269  return [
270  'action=login&lgname=user&lgpassword=password&lgtoken=123ABC'
271  => 'apihelp-login-example-login',
272  ];
273  }
274 
275  public function getHelpUrls() {
276  return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Login';
277  }
278 
285  $ret = [
286  'status' => $response->status,
287  ];
288  if ( $response->message ) {
289  $ret['message'] = $response->message->inLanguage( 'en' )->plain();
290  }
291  $reqs = [
292  'neededRequests' => $response->neededRequests,
293  'createRequest' => $response->createRequest,
294  'linkRequest' => $response->linkRequest,
295  ];
296  foreach ( $reqs as $k => $v ) {
297  if ( $v ) {
298  $v = is_array( $v ) ? $v : [ $v ];
299  $reqClasses = array_unique( array_map( 'get_class', $v ) );
300  sort( $reqClasses );
301  $ret[$k] = implode( ', ', $reqClasses );
302  }
303  }
304  return $ret;
305  }
306 }
const PARAM_TYPE
(string|string[]) Either an array of allowed value strings, or a string type as described below...
Definition: ApiBase.php:94
$response
getErrorFormatter()
Get the error formatter.
Definition: ApiBase.php:654
formatMessage( $message)
Format a message for the response.
Definition: ApiLogin.php:53
getResult()
Get the result object.
Definition: ApiBase.php:640
static stripMarkup( $text)
Turn wikitext into something resembling plaintext.
getAuthenticationResponseLogData(AuthenticationResponse $response)
Turns an AuthenticationResponse into a hash suitable for passing to Logger.
Definition: ApiLogin.php:284
getAllowedParams()
Definition: ApiLogin.php:252
addDeprecation( $msg, $feature, $data=[])
Add a deprecation warning for this module.
Definition: ApiBase.php:1939
static canonicalizeLoginData( $username, $password)
There are two ways to login with a bot password: "username@appId", "password" and "username"...
const PARAM_REQUIRED
(boolean) Is the parameter required?
Definition: ApiBase.php:118
lacksSameOriginSecurity()
Returns true if the current request breaks the same-origin policy.
Definition: ApiBase.php:568
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user...
Definition: ApiBase.php:761
static getInstance( $channel)
Get a named logger instance from the currently configured logger factory.
This is a value object to hold authentication response data.
isDeprecated()
Definition: ApiLogin.php:240
getExtendedDescription()
Definition: ApiLogin.php:40
Unit to authenticate log-in attempts to the current wiki.
Definition: ApiLogin.php:34
__construct(ApiMain $main, $action)
Definition: ApiLogin.php:36
This is the main API class, used for both external and internal processing.
Definition: ApiMain.php:42
Format errors and warnings in the old style, for backwards compatibility.
mustBePosted()
Definition: ApiLogin.php:244
static login( $username, $password, WebRequest $request)
Try to log the user in.
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter...
Definition: ApiBase.php:131
const PARAM_SENSITIVE
(boolean) Is the parameter sensitive? Note &#39;password&#39;-type fields are always sensitive regardless of ...
Definition: ApiBase.php:200
static getGlobalSession()
Get the "global" session.
static newFromSpecifier( $value)
Transform a MessageSpecifier or a primitive value used interchangeably with specifiers (a message key...
Definition: Message.php:430
getExamplesMessages()
Definition: ApiLogin.php:268
static dieDebug( $method, $message)
Internal code errors should be reported with this method.
Definition: ApiBase.php:2212
This abstract class implements many basic API functions, and is the base of all API classes...
Definition: ApiBase.php:42
requirePostedParameters( $params, $prefix='prefix')
Die if any of the specified parameters were found in the query part of the URL rather than the post b...
Definition: ApiBase.php:980
getHelpUrls()
Definition: ApiLogin.php:275
static resetTokenCache()
isReadMode()
Definition: ApiLogin.php:248
execute()
Executes the log-in attempt using the parameters passed.
Definition: ApiLogin.php:74
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:200