MediaWiki master
ApiAuthManagerHelper.php
Go to the documentation of this file.
1<?php
33
41
43 private $module;
44
46 private $messageFormat;
47
48 private AuthManager $authManager;
49
54 public function __construct( ApiBase $module, AuthManager $authManager = null ) {
55 $this->module = $module;
56
57 $params = $module->extractRequestParams();
58 $this->messageFormat = $params['messageformat'] ?? 'wikitext';
59 $this->authManager = $authManager ?: MediaWikiServices::getInstance()->getAuthManager();
60 }
61
68 public static function newForModule( ApiBase $module, AuthManager $authManager = null ) {
69 return new self( $module, $authManager );
70 }
71
78 private function formatMessage( array &$res, $key, Message $message ) {
79 switch ( $this->messageFormat ) {
80 case 'none':
81 break;
82
83 case 'wikitext':
84 $res[$key] = $message->setContext( $this->module )->text();
85 break;
86
87 case 'html':
88 $res[$key] = $message->setContext( $this->module )->parseAsBlock();
89 $res[$key] = Parser::stripOuterParagraph( $res[$key] );
90 break;
91
92 case 'raw':
93 $params = $message->getParams();
94 $res[$key] = [
95 'key' => $message->getKey(),
96 'params' => $params,
97 ];
98 ApiResult::setIndexedTagName( $params, 'param' );
99 break;
100 }
101 }
102
108 public function securitySensitiveOperation( $operation ) {
109 $status = $this->authManager->securitySensitiveOperationStatus( $operation );
110 switch ( $status ) {
111 case AuthManager::SEC_OK:
112 return;
113
114 case AuthManager::SEC_REAUTH:
115 $this->module->dieWithError( 'apierror-reauthenticate' );
116 // dieWithError prevents continuation
117
118 case AuthManager::SEC_FAIL:
119 $this->module->dieWithError( 'apierror-cannotreauthenticate' );
120 // dieWithError prevents continuation
121
122 default:
123 throw new UnexpectedValueException( "Unknown status \"$status\"" );
124 }
125 }
126
133 public static function blacklistAuthenticationRequests( array $reqs, array $remove ) {
134 if ( $remove ) {
135 $remove = array_fill_keys( $remove, true );
136 $reqs = array_filter( $reqs, static function ( $req ) use ( $remove ) {
137 return !isset( $remove[get_class( $req )] );
138 } );
139 }
140 return $reqs;
141 }
142
148 public function loadAuthenticationRequests( $action ) {
149 $params = $this->module->extractRequestParams();
150
151 $reqs = $this->authManager->getAuthenticationRequests( $action, $this->module->getUser() );
152
153 // Filter requests, if requested to do so
154 $wantedRequests = null;
155 if ( isset( $params['requests'] ) ) {
156 $wantedRequests = array_fill_keys( $params['requests'], true );
157 } elseif ( isset( $params['request'] ) ) {
158 $wantedRequests = [ $params['request'] => true ];
159 }
160 if ( $wantedRequests !== null ) {
161 $reqs = array_filter(
162 $reqs,
163 static function ( AuthenticationRequest $req ) use ( $wantedRequests ) {
164 return isset( $wantedRequests[$req->getUniqueId()] );
165 }
166 );
167 }
168
169 // Collect the fields for all the requests
170 $fields = [];
171 $sensitive = [];
172 foreach ( $reqs as $req ) {
173 $info = (array)$req->getFieldInfo();
174 $fields += $info;
175 $sensitive += array_filter( $info, static function ( $opts ) {
176 return !empty( $opts['sensitive'] );
177 } );
178 }
179
180 // Extract the request data for the fields and mark those request
181 // parameters as used
182 $data = array_intersect_key( $this->module->getRequest()->getValues(), $fields );
183 $this->module->getMain()->markParamsUsed( array_keys( $data ) );
184
185 if ( $sensitive ) {
186 $this->module->getMain()->markParamsSensitive( array_keys( $sensitive ) );
187 $this->module->requirePostedParameters( array_keys( $sensitive ), 'noprefix' );
188 }
189
190 return AuthenticationRequest::loadRequestsFromSubmission( $reqs, $data );
191 }
192
199 $ret = [
200 'status' => $res->status,
201 ];
202
203 if ( $res->status === AuthenticationResponse::PASS && $res->username !== null ) {
204 $ret['username'] = $res->username;
205 }
206
207 if ( $res->status === AuthenticationResponse::REDIRECT ) {
208 $ret['redirecttarget'] = $res->redirectTarget;
209 if ( $res->redirectApiData !== null ) {
210 $ret['redirectdata'] = $res->redirectApiData;
211 }
212 }
213
214 if ( $res->status === AuthenticationResponse::REDIRECT ||
215 $res->status === AuthenticationResponse::UI ||
216 $res->status === AuthenticationResponse::RESTART
217 ) {
218 $ret += $this->formatRequests( $res->neededRequests );
219 }
220
221 if ( $res->status === AuthenticationResponse::FAIL ||
222 $res->status === AuthenticationResponse::UI ||
223 $res->status === AuthenticationResponse::RESTART
224 ) {
225 $this->formatMessage( $ret, 'message', $res->message );
226 $ret['messagecode'] = ApiMessage::create( $res->message )->getApiCode();
227 }
228
229 if ( $res->status === AuthenticationResponse::FAIL ||
230 $res->status === AuthenticationResponse::RESTART
231 ) {
232 $this->module->getRequest()->getSession()->set(
233 'ApiAuthManagerHelper::createRequest',
234 $res->createRequest
235 );
236 $ret['canpreservestate'] = $res->createRequest !== null;
237 } else {
238 $this->module->getRequest()->getSession()->remove( 'ApiAuthManagerHelper::createRequest' );
239 }
240
241 return $ret;
242 }
243
249 public function logAuthenticationResult( $event, AuthenticationResponse $result ) {
250 if ( !in_array( $result->status, [ AuthenticationResponse::PASS, AuthenticationResponse::FAIL ] ) ) {
251 return;
252 }
253
254 $module = $this->module->getModuleName();
255 LoggerFactory::getInstance( 'authevents' )->info( "$module API attempt", [
256 'event' => $event,
257 'successful' => $result->status === AuthenticationResponse::PASS,
258 'status' => $result->message ? $result->message->getKey() : '-',
259 'module' => $module,
260 ] );
261 }
262
267 public function getPreservedRequest() {
268 $ret = $this->module->getRequest()->getSession()->get( 'ApiAuthManagerHelper::createRequest' );
269 return $ret instanceof CreateFromLoginAuthenticationRequest ? $ret : null;
270 }
271
278 public function formatRequests( array $reqs ) {
279 $params = $this->module->extractRequestParams();
280 $mergeFields = !empty( $params['mergerequestfields'] );
281
282 $ret = [ 'requests' => [] ];
283 foreach ( $reqs as $req ) {
284 $describe = $req->describeCredentials();
285 $reqInfo = [
286 'id' => $req->getUniqueId(),
287 'metadata' => $req->getMetadata() + [ ApiResult::META_TYPE => 'assoc' ],
288 ];
289 switch ( $req->required ) {
290 case AuthenticationRequest::OPTIONAL:
291 $reqInfo['required'] = 'optional';
292 break;
293 case AuthenticationRequest::REQUIRED:
294 $reqInfo['required'] = 'required';
295 break;
296 case AuthenticationRequest::PRIMARY_REQUIRED:
297 $reqInfo['required'] = 'primary-required';
298 break;
299 }
300 $this->formatMessage( $reqInfo, 'provider', $describe['provider'] );
301 $this->formatMessage( $reqInfo, 'account', $describe['account'] );
302 if ( !$mergeFields ) {
303 $reqInfo['fields'] = $this->formatFields( (array)$req->getFieldInfo() );
304 }
305 $ret['requests'][] = $reqInfo;
306 }
307
308 if ( $mergeFields ) {
309 $fields = AuthenticationRequest::mergeFieldInfo( $reqs );
310 $ret['fields'] = $this->formatFields( $fields );
311 }
312
313 return $ret;
314 }
315
323 private function formatFields( array $fields ) {
324 static $copy = [
325 'type' => true,
326 'value' => true,
327 ];
328
329 $module = $this->module;
330 $retFields = [];
331
332 foreach ( $fields as $name => $field ) {
333 $ret = array_intersect_key( $field, $copy );
334
335 if ( isset( $field['options'] ) ) {
336 $ret['options'] = array_map( static function ( $msg ) use ( $module ) {
337 return $msg->setContext( $module )->plain();
338 }, $field['options'] );
339 ApiResult::setArrayType( $ret['options'], 'assoc' );
340 }
341 $this->formatMessage( $ret, 'label', $field['label'] );
342 $this->formatMessage( $ret, 'help', $field['help'] );
343 $ret['optional'] = !empty( $field['optional'] );
344 $ret['sensitive'] = !empty( $field['sensitive'] );
345
346 $retFields[$name] = $ret;
347 }
348
349 ApiResult::setArrayType( $retFields, 'assoc' );
350
351 return $retFields;
352 }
353
360 public static function getStandardParams( $action, ...$wantedParams ) {
361 $params = [
362 'requests' => [
363 ParamValidator::PARAM_TYPE => 'string',
364 ParamValidator::PARAM_ISMULTI => true,
365 ApiBase::PARAM_HELP_MSG => [ 'api-help-authmanagerhelper-requests', $action ],
366 ],
367 'request' => [
368 ParamValidator::PARAM_TYPE => 'string',
369 ParamValidator::PARAM_REQUIRED => true,
370 ApiBase::PARAM_HELP_MSG => [ 'api-help-authmanagerhelper-request', $action ],
371 ],
372 'messageformat' => [
373 ParamValidator::PARAM_DEFAULT => 'wikitext',
374 ParamValidator::PARAM_TYPE => [ 'html', 'wikitext', 'raw', 'none' ],
375 ApiBase::PARAM_HELP_MSG => 'api-help-authmanagerhelper-messageformat',
376 ],
377 'mergerequestfields' => [
378 ParamValidator::PARAM_DEFAULT => false,
379 ApiBase::PARAM_HELP_MSG => 'api-help-authmanagerhelper-mergerequestfields',
380 ],
381 'preservestate' => [
382 ParamValidator::PARAM_DEFAULT => false,
383 ApiBase::PARAM_HELP_MSG => 'api-help-authmanagerhelper-preservestate',
384 ],
385 'returnurl' => [
386 ParamValidator::PARAM_TYPE => 'string',
387 ApiBase::PARAM_HELP_MSG => 'api-help-authmanagerhelper-returnurl',
388 ],
389 'continue' => [
390 ParamValidator::PARAM_DEFAULT => false,
391 ApiBase::PARAM_HELP_MSG => 'api-help-authmanagerhelper-continue',
392 ],
393 ];
394
395 $ret = [];
396 foreach ( $wantedParams as $name ) {
397 if ( isset( $params[$name] ) ) {
398 $ret[$name] = $params[$name];
399 }
400 }
401 return $ret;
402 }
403}
array $params
The job parameters.
Helper class for AuthManager-using API modules.
static newForModule(ApiBase $module, AuthManager $authManager=null)
Static version of the constructor, for chaining.
getPreservedRequest()
Fetch the preserved CreateFromLoginAuthenticationRequest, if any.
static getStandardParams( $action,... $wantedParams)
Fetch the standard parameters this helper recognizes.
static blacklistAuthenticationRequests(array $reqs, array $remove)
Filter out authentication requests by class name.
formatAuthenticationResponse(AuthenticationResponse $res)
Format an AuthenticationResponse for return.
__construct(ApiBase $module, AuthManager $authManager=null)
formatRequests(array $reqs)
Format an array of AuthenticationRequests for return.
securitySensitiveOperation( $operation)
Call $manager->securitySensitiveOperationStatus()
loadAuthenticationRequests( $action)
Fetch and load the AuthenticationRequests for an action.
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:65
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:821
getModuleName()
Get the name of the module being executed by this instance.
Definition ApiBase.php:542
static setArrayType(array &$arr, $type, $kvpKeyName=null)
Set the array data type.
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.
The Message class deals with fetching and processing of interface message into a variety of formats.
Definition Message.php:158
getParams()
Returns the message parameters.
Definition Message.php:403
setContext(IContextSource $context)
Set the language and the title from a context object.
Definition Message.php:857
getKey()
Returns the message key.
Definition Message.php:392
PHP Parser - Processes wiki markup (which uses a more user-friendly syntax, such as "[[link]]" for ma...
Definition Parser.php:155
Service for formatting and validating API parameters.