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