MediaWiki 1.43.1
ApiAuthManagerHelper.php
Go to the documentation of this file.
1<?php
24namespace MediaWiki\Api;
25
34use UnexpectedValueException;
36
44
46 private $module;
47
49 private $messageFormat;
50
51 private AuthManager $authManager;
52
57 public function __construct( ApiBase $module, ?AuthManager $authManager = null ) {
58 $this->module = $module;
59
60 $params = $module->extractRequestParams();
61 $this->messageFormat = $params['messageformat'] ?? 'wikitext';
62 $this->authManager = $authManager ?: MediaWikiServices::getInstance()->getAuthManager();
63 }
64
71 public static function newForModule( ApiBase $module, ?AuthManager $authManager = null ) {
72 return new self( $module, $authManager );
73 }
74
81 private function formatMessage( array &$res, $key, Message $message ) {
82 switch ( $this->messageFormat ) {
83 case 'none':
84 break;
85
86 case 'wikitext':
87 $res[$key] = $message->setContext( $this->module )->text();
88 break;
89
90 case 'html':
91 $res[$key] = $message->setContext( $this->module )->parseAsBlock();
92 $res[$key] = Parser::stripOuterParagraph( $res[$key] );
93 break;
94
95 case 'raw':
96 $params = $message->getParams();
97 $res[$key] = [
98 'key' => $message->getKey(),
99 'params' => $params,
100 ];
102 break;
103 }
104 }
105
111 public function securitySensitiveOperation( $operation ) {
112 $status = $this->authManager->securitySensitiveOperationStatus( $operation );
113 switch ( $status ) {
114 case AuthManager::SEC_OK:
115 return;
116
117 case AuthManager::SEC_REAUTH:
118 $this->module->dieWithError( 'apierror-reauthenticate' );
119 // dieWithError prevents continuation
120
121 case AuthManager::SEC_FAIL:
122 $this->module->dieWithError( 'apierror-cannotreauthenticate' );
123 // dieWithError prevents continuation
124
125 default:
126 throw new UnexpectedValueException( "Unknown status \"$status\"" );
127 }
128 }
129
136 public static function blacklistAuthenticationRequests( array $reqs, array $remove ) {
137 if ( $remove ) {
138 $remove = array_fill_keys( $remove, true );
139 $reqs = array_filter( $reqs, static function ( $req ) use ( $remove ) {
140 return !isset( $remove[get_class( $req )] );
141 } );
142 }
143 return $reqs;
144 }
145
151 public function loadAuthenticationRequests( $action ) {
152 $params = $this->module->extractRequestParams();
153
154 $reqs = $this->authManager->getAuthenticationRequests( $action, $this->module->getUser() );
155
156 // Filter requests, if requested to do so
157 $wantedRequests = null;
158 if ( isset( $params['requests'] ) ) {
159 $wantedRequests = array_fill_keys( $params['requests'], true );
160 } elseif ( isset( $params['request'] ) ) {
161 $wantedRequests = [ $params['request'] => true ];
162 }
163 if ( $wantedRequests !== null ) {
164 $reqs = array_filter(
165 $reqs,
166 static function ( AuthenticationRequest $req ) use ( $wantedRequests ) {
167 return isset( $wantedRequests[$req->getUniqueId()] );
168 }
169 );
170 }
171
172 // Collect the fields for all the requests
173 $fields = [];
174 $sensitive = [];
175 foreach ( $reqs as $req ) {
176 $info = (array)$req->getFieldInfo();
177 $fields += $info;
178 $sensitive += array_filter( $info, static function ( $opts ) {
179 return !empty( $opts['sensitive'] );
180 } );
181 }
182
183 // Extract the request data for the fields and mark those request
184 // parameters as used
185 $data = array_intersect_key( $this->module->getRequest()->getValues(), $fields );
186 $this->module->getMain()->markParamsUsed( array_keys( $data ) );
187
188 if ( $sensitive ) {
189 $this->module->getMain()->markParamsSensitive( array_keys( $sensitive ) );
190 $this->module->requirePostedParameters( array_keys( $sensitive ), 'noprefix' );
191 }
192
193 return AuthenticationRequest::loadRequestsFromSubmission( $reqs, $data );
194 }
195
202 $ret = [
203 'status' => $res->status,
204 ];
205
206 if ( $res->status === AuthenticationResponse::PASS && $res->username !== null ) {
207 $ret['username'] = $res->username;
208 }
209
210 if ( $res->status === AuthenticationResponse::REDIRECT ) {
211 $ret['redirecttarget'] = $res->redirectTarget;
212 if ( $res->redirectApiData !== null ) {
213 $ret['redirectdata'] = $res->redirectApiData;
214 }
215 }
216
217 if ( $res->status === AuthenticationResponse::REDIRECT ||
218 $res->status === AuthenticationResponse::UI ||
219 $res->status === AuthenticationResponse::RESTART
220 ) {
221 $ret += $this->formatRequests( $res->neededRequests );
222 }
223
224 if ( $res->status === AuthenticationResponse::FAIL ||
225 $res->status === AuthenticationResponse::UI ||
226 $res->status === AuthenticationResponse::RESTART
227 ) {
228 $this->formatMessage( $ret, 'message', $res->message );
229 $ret['messagecode'] = ApiMessage::create( $res->message )->getApiCode();
230 }
231
232 if ( $res->status === AuthenticationResponse::FAIL ||
233 $res->status === AuthenticationResponse::RESTART
234 ) {
235 $this->module->getRequest()->getSession()->set(
236 'ApiAuthManagerHelper::createRequest',
237 $res->createRequest
238 );
239 $ret['canpreservestate'] = $res->createRequest !== null;
240 } else {
241 $this->module->getRequest()->getSession()->remove( 'ApiAuthManagerHelper::createRequest' );
242 }
243
244 return $ret;
245 }
246
252 public function logAuthenticationResult( $event, AuthenticationResponse $result ) {
253 if ( !in_array( $result->status, [ AuthenticationResponse::PASS, AuthenticationResponse::FAIL ] ) ) {
254 return;
255 }
256
257 $module = $this->module->getModuleName();
258 LoggerFactory::getInstance( 'authevents' )->info( "$module API attempt", [
259 'event' => $event,
260 'successful' => $result->status === AuthenticationResponse::PASS,
261 'status' => $result->message ? $result->message->getKey() : '-',
262 'module' => $module,
263 ] );
264 }
265
270 public function getPreservedRequest() {
271 $ret = $this->module->getRequest()->getSession()->get( 'ApiAuthManagerHelper::createRequest' );
272 return $ret instanceof CreateFromLoginAuthenticationRequest ? $ret : null;
273 }
274
281 public function formatRequests( array $reqs ) {
282 $params = $this->module->extractRequestParams();
283 $mergeFields = !empty( $params['mergerequestfields'] );
284
285 $ret = [ 'requests' => [] ];
286 foreach ( $reqs as $req ) {
287 $describe = $req->describeCredentials();
288 $reqInfo = [
289 'id' => $req->getUniqueId(),
290 'metadata' => $req->getMetadata() + [ ApiResult::META_TYPE => 'assoc' ],
291 ];
292 switch ( $req->required ) {
293 case AuthenticationRequest::OPTIONAL:
294 $reqInfo['required'] = 'optional';
295 break;
296 case AuthenticationRequest::REQUIRED:
297 $reqInfo['required'] = 'required';
298 break;
299 case AuthenticationRequest::PRIMARY_REQUIRED:
300 $reqInfo['required'] = 'primary-required';
301 break;
302 }
303 $this->formatMessage( $reqInfo, 'provider', $describe['provider'] );
304 $this->formatMessage( $reqInfo, 'account', $describe['account'] );
305 if ( !$mergeFields ) {
306 $reqInfo['fields'] = $this->formatFields( (array)$req->getFieldInfo() );
307 }
308 $ret['requests'][] = $reqInfo;
309 }
310
311 if ( $mergeFields ) {
312 $fields = AuthenticationRequest::mergeFieldInfo( $reqs );
313 $ret['fields'] = $this->formatFields( $fields );
314 }
315
316 return $ret;
317 }
318
326 private function formatFields( array $fields ) {
327 static $copy = [
328 'type' => true,
329 'value' => true,
330 ];
331
332 $module = $this->module;
333 $retFields = [];
334
335 foreach ( $fields as $name => $field ) {
336 $ret = array_intersect_key( $field, $copy );
337
338 if ( isset( $field['options'] ) ) {
339 $ret['options'] = array_map( static function ( $msg ) use ( $module ) {
340 return $msg->setContext( $module )->plain();
341 }, $field['options'] );
342 ApiResult::setArrayType( $ret['options'], 'assoc' );
343 }
344 $this->formatMessage( $ret, 'label', $field['label'] );
345 $this->formatMessage( $ret, 'help', $field['help'] );
346 $ret['optional'] = !empty( $field['optional'] );
347 $ret['sensitive'] = !empty( $field['sensitive'] );
348
349 $retFields[$name] = $ret;
350 }
351
352 ApiResult::setArrayType( $retFields, 'assoc' );
353
354 return $retFields;
355 }
356
363 public static function getStandardParams( $action, ...$wantedParams ) {
364 $params = [
365 'requests' => [
366 ParamValidator::PARAM_TYPE => 'string',
367 ParamValidator::PARAM_ISMULTI => true,
368 ApiBase::PARAM_HELP_MSG => [ 'api-help-authmanagerhelper-requests', $action ],
369 ],
370 'request' => [
371 ParamValidator::PARAM_TYPE => 'string',
372 ParamValidator::PARAM_REQUIRED => true,
373 ApiBase::PARAM_HELP_MSG => [ 'api-help-authmanagerhelper-request', $action ],
374 ],
375 'messageformat' => [
376 ParamValidator::PARAM_DEFAULT => 'wikitext',
377 ParamValidator::PARAM_TYPE => [ 'html', 'wikitext', 'raw', 'none' ],
378 ApiBase::PARAM_HELP_MSG => 'api-help-authmanagerhelper-messageformat',
379 ],
380 'mergerequestfields' => [
381 ParamValidator::PARAM_DEFAULT => false,
382 ApiBase::PARAM_HELP_MSG => 'api-help-authmanagerhelper-mergerequestfields',
383 ],
384 'preservestate' => [
385 ParamValidator::PARAM_DEFAULT => false,
386 ApiBase::PARAM_HELP_MSG => 'api-help-authmanagerhelper-preservestate',
387 ],
388 'returnurl' => [
389 ParamValidator::PARAM_TYPE => 'string',
390 ApiBase::PARAM_HELP_MSG => 'api-help-authmanagerhelper-returnurl',
391 ],
392 'continue' => [
393 ParamValidator::PARAM_DEFAULT => false,
394 ApiBase::PARAM_HELP_MSG => 'api-help-authmanagerhelper-continue',
395 ],
396 ];
397
398 $ret = [];
399 foreach ( $wantedParams as $name ) {
400 if ( isset( $params[$name] ) ) {
401 $ret[$name] = $params[$name];
402 }
403 }
404 return $ret;
405 }
406}
407
409class_alias( ApiAuthManagerHelper::class, 'ApiAuthManagerHelper' );
array $params
The job parameters.
Helper class for AuthManager-using API modules.
static getStandardParams( $action,... $wantedParams)
Fetch the standard parameters this helper recognizes.
securitySensitiveOperation( $operation)
Call $manager->securitySensitiveOperationStatus()
formatAuthenticationResponse(AuthenticationResponse $res)
Format an AuthenticationResponse for return.
__construct(ApiBase $module, ?AuthManager $authManager=null)
getPreservedRequest()
Fetch the preserved CreateFromLoginAuthenticationRequest, if any.
static newForModule(ApiBase $module, ?AuthManager $authManager=null)
Static version of the constructor, for chaining.
loadAuthenticationRequests( $action)
Fetch and load the AuthenticationRequests for an action.
formatRequests(array $reqs)
Format an array of AuthenticationRequests for return.
static blacklistAuthenticationRequests(array $reqs, array $remove)
Filter out authentication requests by class name.
logAuthenticationResult( $event, AuthenticationResponse $result)
Logs successful or failed authentication.
This abstract class implements many basic API functions, and is the base of all API classes.
Definition ApiBase.php:76
getModuleName()
Get the name of the module being executed by this instance.
Definition ApiBase.php:571
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter.
Definition ApiBase.php:184
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:851
static create( $msg, $code=null, ?array $data=null)
Create an IApiMessage for the message.
static setIndexedTagName(array &$arr, $tag)
Set the tag name for numeric-keyed values in XML format.
static setArrayType(array &$arr, $type, $kvpKeyName=null)
Set the array data type.
const META_TYPE
Key for the 'type' metadata item.
This serves as the entry point to the authentication system.
This is a value object for authentication requests.
getUniqueId()
Supply a unique key for deduplication.
This is a value object to hold authentication response data.
This transfers state between the login and account creation flows.
Create PSR-3 logger objects.
Service locator for MediaWiki core services.
static getInstance()
Returns the global default instance of the top level service locator.
The Message class deals with fetching and processing of interface message into a variety of formats.
Definition Message.php:156
getParams()
Returns the message parameters.
Definition Message.php:415
setContext(IContextSource $context)
Set the language and the title from a context object.
Definition Message.php:872
getKey()
Returns the message key.
Definition Message.php:404
PHP Parser - Processes wiki markup (which uses a more user-friendly syntax, such as "[[link]]" for ma...
Definition Parser.php:143
Service for formatting and validating API parameters.