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