MediaWiki  master
ApiLogin.php
Go to the documentation of this file.
1 <?php
28 
34 class ApiLogin extends ApiBase {
35 
37  private $authManager;
38 
44  public function __construct(
45  ApiMain $main,
46  $action,
48  ) {
49  parent::__construct( $main, $action, 'lg' );
50  $this->authManager = $authManager;
51  }
52 
53  protected function getExtendedDescription() {
54  if ( $this->getConfig()->get( 'EnableBotPasswords' ) ) {
55  return 'apihelp-login-extended-description';
56  } else {
57  return 'apihelp-login-extended-description-nobotpasswords';
58  }
59  }
60 
66  private function formatMessage( $message ) {
67  $message = Message::newFromSpecifier( $message );
68  $errorFormatter = $this->getErrorFormatter();
69  if ( $errorFormatter instanceof ApiErrorFormatter_BackCompat ) {
71  $message->useDatabase( false )->inLanguage( 'en' )->text()
72  );
73  } else {
74  return $errorFormatter->formatMessage( $message );
75  }
76  }
77 
87  public function execute() {
88  // If we're in a mode that breaks the same-origin policy, no tokens can
89  // be obtained
90  if ( $this->lacksSameOriginSecurity() ) {
91  $this->getResult()->addValue( null, 'login', [
92  'result' => 'Aborted',
93  'reason' => $this->formatMessage( 'api-login-fail-sameorigin' ),
94  ] );
95 
96  return;
97  }
98 
99  $this->requirePostedParameters( [ 'password', 'token' ] );
100 
101  $params = $this->extractRequestParams();
102 
103  $result = [];
104 
105  // Make sure session is persisted
107  $session->persist();
108 
109  // Make sure it's possible to log in
110  if ( !$session->canSetUser() ) {
111  $this->getResult()->addValue( null, 'login', [
112  'result' => 'Aborted',
113  'reason' => $this->formatMessage( [
114  'api-login-fail-badsessionprovider',
115  $session->getProvider()->describe( $this->getErrorFormatter()->getLanguage() ),
116  ] )
117  ] );
118 
119  return;
120  }
121 
122  $authRes = false;
123  $loginType = 'N/A';
124 
125  // Check login token
126  $token = $session->getToken( '', 'login' );
127  if ( !$params['token'] ) {
128  $authRes = 'NeedToken';
129  } elseif ( $token->wasNew() ) {
130  $authRes = 'Failed';
131  $message = ApiMessage::create( 'authpage-cannot-login-continue', 'sessionlost' );
132  } elseif ( !$token->match( $params['token'] ) ) {
133  $authRes = 'WrongToken';
134  }
135 
136  // Try bot passwords
137  if (
138  $authRes === false && $this->getConfig()->get( 'EnableBotPasswords' ) &&
139  ( $botLoginData = BotPassword::canonicalizeLoginData( $params['name'], $params['password'] ) )
140  ) {
141  $status = BotPassword::login(
142  $botLoginData[0], $botLoginData[1], $this->getRequest()
143  );
144  if ( $status->isOK() ) {
145  $session = $status->getValue();
146  $authRes = 'Success';
147  $loginType = 'BotPassword';
148  } elseif (
149  $status->hasMessage( 'login-throttled' ) ||
150  $status->hasMessage( 'botpasswords-needs-reset' ) ||
151  $status->hasMessage( 'botpasswords-locked' )
152  ) {
153  $authRes = 'Failed';
154  $message = $status->getMessage();
155  LoggerFactory::getInstance( 'authentication' )->info(
156  'BotPassword login failed: ' . $status->getWikiText( false, false, 'en' )
157  );
158  }
159  // For other errors, let's see if it's a valid non-bot login
160  }
161 
162  if ( $authRes === false ) {
163  // Simplified AuthManager login, for backwards compatibility
164  $reqs = AuthenticationRequest::loadRequestsFromSubmission(
165  $this->authManager->getAuthenticationRequests(
166  AuthManager::ACTION_LOGIN,
167  $this->getUser()
168  ),
169  [
170  'username' => $params['name'],
171  'password' => $params['password'],
172  'domain' => $params['domain'],
173  'rememberMe' => true,
174  ]
175  );
176  $res = $this->authManager->beginAuthentication( $reqs, 'null:' );
177  switch ( $res->status ) {
178  case AuthenticationResponse::PASS:
179  if ( $this->getConfig()->get( 'EnableBotPasswords' ) ) {
180  $this->addDeprecation( 'apiwarn-deprecation-login-botpw', 'main-account-login' );
181  } else {
182  $this->addDeprecation( 'apiwarn-deprecation-login-nobotpw', 'main-account-login' );
183  }
184  $authRes = 'Success';
185  $loginType = 'AuthManager';
186  break;
187 
188  case AuthenticationResponse::FAIL:
189  // Hope it's not a PreAuthenticationProvider that failed...
190  $authRes = 'Failed';
191  $message = $res->message;
193  ->info( __METHOD__ . ': Authentication failed: '
194  . $message->inLanguage( 'en' )->plain() );
195  break;
196 
197  default:
199  ->info( __METHOD__ . ': Authentication failed due to unsupported response type: '
200  . $res->status, $this->getAuthenticationResponseLogData( $res ) );
201  $authRes = 'Aborted';
202  break;
203  }
204  }
205 
206  $result['result'] = $authRes;
207  switch ( $authRes ) {
208  case 'Success':
209  $user = $session->getUser();
210 
211  // Deprecated hook
212  $injected_html = '';
213  $this->getHookRunner()->onUserLoginComplete( $user, $injected_html, true );
214 
215  $result['lguserid'] = $user->getId();
216  $result['lgusername'] = $user->getName();
217  break;
218 
219  case 'NeedToken':
220  $result['token'] = $token->toString();
221  $this->addDeprecation( 'apiwarn-deprecation-login-token', 'action=login&!lgtoken' );
222  break;
223 
224  case 'WrongToken':
225  break;
226 
227  case 'Failed':
228  $result['reason'] = $this->formatMessage( $message );
229  break;
230 
231  case 'Aborted':
232  $result['reason'] = $this->formatMessage(
233  $this->getConfig()->get( 'EnableBotPasswords' )
234  ? 'api-login-fail-aborted'
235  : 'api-login-fail-aborted-nobotpw'
236  );
237  break;
238 
239  // @codeCoverageIgnoreStart
240  // Unreachable
241  default:
242  ApiBase::dieDebug( __METHOD__, "Unhandled case value: {$authRes}" );
243  // @codeCoverageIgnoreEnd
244  }
245 
246  $this->getResult()->addValue( null, 'login', $result );
247 
248  LoggerFactory::getInstance( 'authevents' )->info( 'Login attempt', [
249  'event' => 'login',
250  'successful' => $authRes === 'Success',
251  'loginType' => $loginType,
252  'status' => $authRes,
253  ] );
254  }
255 
256  public function isDeprecated() {
257  return !$this->getConfig()->get( 'EnableBotPasswords' );
258  }
259 
260  public function mustBePosted() {
261  return true;
262  }
263 
264  public function isReadMode() {
265  return false;
266  }
267 
268  public function isWriteMode() {
269  // (T283394) Logging in triggers some database writes, so should be marked appropriately.
270  return true;
271  }
272 
273  public function getAllowedParams() {
274  return [
275  'name' => null,
276  'password' => [
277  ApiBase::PARAM_TYPE => 'password',
278  ],
279  'domain' => null,
280  'token' => [
281  ApiBase::PARAM_TYPE => 'string',
282  ApiBase::PARAM_REQUIRED => false, // for BC
283  ApiBase::PARAM_SENSITIVE => true,
284  ApiBase::PARAM_HELP_MSG => [ 'api-help-param-token', 'login' ],
285  ],
286  ];
287  }
288 
289  protected function getExamplesMessages() {
290  return [
291  'action=login&lgname=user&lgpassword=password&lgtoken=123ABC'
292  => 'apihelp-login-example-login',
293  ];
294  }
295 
296  public function getHelpUrls() {
297  return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Login';
298  }
299 
306  $ret = [
307  'status' => $response->status,
308  ];
309  if ( $response->message ) {
310  $ret['responseMessage'] = $response->message->inLanguage( 'en' )->plain();
311  }
312  $reqs = [
313  'neededRequests' => $response->neededRequests,
314  'createRequest' => $response->createRequest,
315  'linkRequest' => $response->linkRequest,
316  ];
317  foreach ( $reqs as $k => $v ) {
318  if ( $v ) {
319  $v = is_array( $v ) ? $v : [ $v ];
320  $reqClasses = array_unique( array_map( 'get_class', $v ) );
321  sort( $reqClasses );
322  $ret[$k] = implode( ', ', $reqClasses );
323  }
324  }
325  return $ret;
326  }
327 }
ApiMain
This is the main API class, used for both external and internal processing.
Definition: ApiMain.php:49
ContextSource\getConfig
getConfig()
Definition: ContextSource.php:72
ApiLogin\isReadMode
isReadMode()
Indicates whether this module requires read rights.
Definition: ApiLogin.php:264
Message\newFromSpecifier
static newFromSpecifier( $value)
Transform a MessageSpecifier or a primitive value used interchangeably with specifiers (a message key...
Definition: Message.php:398
ApiLogin\execute
execute()
Executes the log-in attempt using the parameters passed.
Definition: ApiLogin.php:87
ApiErrorFormatter_BackCompat
Format errors and warnings in the old style, for backwards compatibility.
Definition: ApiErrorFormatter_BackCompat.php:30
ApiBase\PARAM_REQUIRED
const PARAM_REQUIRED
Definition: ApiBase.php:78
BotPassword\canonicalizeLoginData
static canonicalizeLoginData( $username, $password)
There are two ways to login with a bot password: "username@appId", "password" and "username",...
Definition: BotPassword.php:378
MediaWiki\Logger\LoggerFactory\getInstance
static getInstance( $channel)
Get a named logger instance from the currently configured logger factory.
Definition: LoggerFactory.php:92
ApiBase\PARAM_HELP_MSG
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter.
Definition: ApiBase.php:105
ApiLogin\mustBePosted
mustBePosted()
Indicates whether this module must be called with a POST request.
Definition: ApiLogin.php:260
ApiLogin\isDeprecated
isDeprecated()
Indicates whether this module is deprecated.
Definition: ApiLogin.php:256
ApiBase\PARAM_TYPE
const PARAM_TYPE
Definition: ApiBase.php:72
ApiBase\getResult
getResult()
Get the result object.
Definition: ApiBase.php:571
ApiLogin\getExamplesMessages
getExamplesMessages()
Returns usage examples for this module.
Definition: ApiLogin.php:289
ContextSource\getRequest
getRequest()
Definition: ContextSource.php:81
$res
$res
Definition: testCompression.php:57
ApiBase\lacksSameOriginSecurity
lacksSameOriginSecurity()
Returns true if the current request breaks the same-origin policy.
Definition: ApiBase.php:502
ApiBase
This abstract class implements many basic API functions, and is the base of all API classes.
Definition: ApiBase.php:55
ApiBase\PARAM_SENSITIVE
const PARAM_SENSITIVE
Definition: ApiBase.php:83
ApiLogin\getHelpUrls
getHelpUrls()
Return links to more detailed help pages about the module.
Definition: ApiLogin.php:296
ApiLogin\__construct
__construct(ApiMain $main, $action, AuthManager $authManager)
Definition: ApiLogin.php:44
MediaWiki\Logger\LoggerFactory
PSR-3 logger instance factory.
Definition: LoggerFactory.php:45
ApiErrorFormatter\stripMarkup
static stripMarkup( $text)
Turn wikitext into something resembling plaintext.
Definition: ApiErrorFormatter.php:317
MediaWiki\Auth\AuthenticationResponse
This is a value object to hold authentication response data.
Definition: AuthenticationResponse.php:37
ApiBase\extractRequestParams
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition: ApiBase.php:707
ApiLogin\isWriteMode
isWriteMode()
Indicates whether this module requires write mode.
Definition: ApiLogin.php:268
ApiMessage\create
static create( $msg, $code=null, array $data=null)
Create an IApiMessage for the message.
Definition: ApiMessage.php:43
ApiLogin\getAuthenticationResponseLogData
getAuthenticationResponseLogData(AuthenticationResponse $response)
Turns an AuthenticationResponse into a hash suitable for passing to Logger.
Definition: ApiLogin.php:305
MediaWiki\Session\SessionManager\getGlobalSession
static getGlobalSession()
If PHP's session_id() has been set, returns that session.
Definition: SessionManager.php:146
ApiLogin\getAllowedParams
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
Definition: ApiLogin.php:273
ApiBase\addDeprecation
addDeprecation( $msg, $feature, $data=[])
Add a deprecation warning for this module.
Definition: ApiBase.php:1311
ApiLogin\$authManager
AuthManager $authManager
Definition: ApiLogin.php:37
ApiLogin\getExtendedDescription
getExtendedDescription()
Return the extended help text message.
Definition: ApiLogin.php:53
BotPassword\login
static login( $username, $password, WebRequest $request)
Try to log the user in.
Definition: BotPassword.php:404
MediaWiki\Auth\AuthManager
This serves as the entry point to the authentication system.
Definition: AuthManager.php:102
ApiLogin\formatMessage
formatMessage( $message)
Format a message for the response.
Definition: ApiLogin.php:66
ApiLogin
Unit to authenticate log-in attempts to the current wiki.
Definition: ApiLogin.php:34
ApiBase\getHookRunner
getHookRunner()
Get an ApiHookRunner for running core API hooks.
Definition: ApiBase.php:653
ApiBase\dieDebug
static dieDebug( $method, $message)
Internal code errors should be reported with this method.
Definition: ApiBase.php:1582
ApiBase\getErrorFormatter
getErrorFormatter()
Definition: ApiBase.php:582
ApiBase\requirePostedParameters
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:931
MediaWiki\Auth\AuthenticationRequest
This is a value object for authentication requests.
Definition: AuthenticationRequest.php:38