MediaWiki REL1_37
ApiAuthManagerHelper.php
Go to the documentation of this file.
1<?php
30
38
40 private $module;
41
44
46 private $authManager;
47
52 public function __construct( ApiBase $module, AuthManager $authManager = null ) {
53 $this->module = $module;
54
55 $params = $module->extractRequestParams();
56 $this->messageFormat = $params['messageformat'] ?? 'wikitext';
57 $this->authManager = $authManager ?: MediaWikiServices::getInstance()->getAuthManager();
58 }
59
66 public static function newForModule( ApiBase $module, AuthManager $authManager = null ) {
67 return new self( $module, $authManager );
68 }
69
76 private function formatMessage( array &$res, $key, Message $message ) {
77 switch ( $this->messageFormat ) {
78 case 'none':
79 break;
80
81 case 'wikitext':
82 $res[$key] = $message->setContext( $this->module )->text();
83 break;
84
85 case 'html':
86 $res[$key] = $message->setContext( $this->module )->parseAsBlock();
87 $res[$key] = Parser::stripOuterParagraph( $res[$key] );
88 break;
89
90 case 'raw':
91 $params = $message->getParams();
92 $res[$key] = [
93 'key' => $message->getKey(),
94 'params' => $params,
95 ];
96 ApiResult::setIndexedTagName( $params, 'param' );
97 break;
98 }
99 }
100
106 public function securitySensitiveOperation( $operation ) {
107 $status = $this->authManager->securitySensitiveOperationStatus( $operation );
108 switch ( $status ) {
109 case AuthManager::SEC_OK:
110 return;
111
112 case AuthManager::SEC_REAUTH:
113 $this->module->dieWithError( 'apierror-reauthenticate' );
114 // dieWithError prevents continuation
115
116 case AuthManager::SEC_FAIL:
117 $this->module->dieWithError( 'apierror-cannotreauthenticate' );
118 // dieWithError prevents continuation
119
120 default:
121 throw new UnexpectedValueException( "Unknown status \"$status\"" );
122 }
123 }
124
131 public static function blacklistAuthenticationRequests( array $reqs, array $remove ) {
132 if ( $remove ) {
133 $remove = array_fill_keys( $remove, true );
134 $reqs = array_filter( $reqs, static function ( $req ) use ( $remove ) {
135 return !isset( $remove[get_class( $req )] );
136 } );
137 }
138 return $reqs;
139 }
140
146 public function loadAuthenticationRequests( $action ) {
147 $params = $this->module->extractRequestParams();
148
149 $reqs = $this->authManager->getAuthenticationRequests( $action, $this->module->getUser() );
150
151 // Filter requests, if requested to do so
152 $wantedRequests = null;
153 if ( isset( $params['requests'] ) ) {
154 $wantedRequests = array_fill_keys( $params['requests'], true );
155 } elseif ( isset( $params['request'] ) ) {
156 $wantedRequests = [ $params['request'] => true ];
157 }
158 if ( $wantedRequests !== null ) {
159 $reqs = array_filter(
160 $reqs,
161 static function ( AuthenticationRequest $req ) use ( $wantedRequests ) {
162 return isset( $wantedRequests[$req->getUniqueId()] );
163 }
164 );
165 }
166
167 // Collect the fields for all the requests
168 $fields = [];
169 $sensitive = [];
170 foreach ( $reqs as $req ) {
171 $info = (array)$req->getFieldInfo();
172 $fields += $info;
173 $sensitive += array_filter( $info, static function ( $opts ) {
174 return !empty( $opts['sensitive'] );
175 } );
176 }
177
178 // Extract the request data for the fields and mark those request
179 // parameters as used
180 $data = array_intersect_key( $this->module->getRequest()->getValues(), $fields );
181 $this->module->getMain()->markParamsUsed( array_keys( $data ) );
182
183 if ( $sensitive ) {
184 $this->module->getMain()->markParamsSensitive( array_keys( $sensitive ) );
185 $this->module->requirePostedParameters( array_keys( $sensitive ), 'noprefix' );
186 }
187
188 return AuthenticationRequest::loadRequestsFromSubmission( $reqs, $data );
189 }
190
197 $ret = [
198 'status' => $res->status,
199 ];
200
201 if ( $res->status === AuthenticationResponse::PASS && $res->username !== null ) {
202 $ret['username'] = $res->username;
203 }
204
205 if ( $res->status === AuthenticationResponse::REDIRECT ) {
206 $ret['redirecttarget'] = $res->redirectTarget;
207 if ( $res->redirectApiData !== null ) {
208 $ret['redirectdata'] = $res->redirectApiData;
209 }
210 }
211
212 if ( $res->status === AuthenticationResponse::REDIRECT ||
213 $res->status === AuthenticationResponse::UI ||
214 $res->status === AuthenticationResponse::RESTART
215 ) {
216 $ret += $this->formatRequests( $res->neededRequests );
217 }
218
219 if ( $res->status === AuthenticationResponse::FAIL ||
220 $res->status === AuthenticationResponse::UI ||
221 $res->status === AuthenticationResponse::RESTART
222 ) {
223 $this->formatMessage( $ret, 'message', $res->message );
224 $ret['messagecode'] = ApiMessage::create( $res->message )->getApiCode();
225 }
226
227 if ( $res->status === AuthenticationResponse::FAIL ||
228 $res->status === AuthenticationResponse::RESTART
229 ) {
230 $this->module->getRequest()->getSession()->set(
231 'ApiAuthManagerHelper::createRequest',
232 $res->createRequest
233 );
234 $ret['canpreservestate'] = $res->createRequest !== null;
235 } else {
236 $this->module->getRequest()->getSession()->remove( 'ApiAuthManagerHelper::createRequest' );
237 }
238
239 return $ret;
240 }
241
247 public function logAuthenticationResult( $event, $result ) {
248 if ( is_string( $result ) ) {
249 $status = Status::newFatal( $result );
250 } elseif ( $result->status === AuthenticationResponse::PASS ) {
251 $status = Status::newGood();
252 } elseif ( $result->status === AuthenticationResponse::FAIL ) {
253 $status = Status::newFatal( $result->message );
254 } else {
255 return;
256 }
257
258 $module = $this->module->getModuleName();
259 LoggerFactory::getInstance( 'authevents' )->info( "$module API attempt", [
260 'event' => $event,
261 'status' => strval( $status ),
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
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 ApiBase::PARAM_TYPE => 'string',
367 ApiBase::PARAM_ISMULTI => true,
368 ApiBase::PARAM_HELP_MSG => [ 'api-help-authmanagerhelper-requests', $action ],
369 ],
370 'request' => [
371 ApiBase::PARAM_TYPE => 'string',
372 ApiBase::PARAM_REQUIRED => true,
373 ApiBase::PARAM_HELP_MSG => [ 'api-help-authmanagerhelper-request', $action ],
374 ],
375 'messageformat' => [
376 ApiBase::PARAM_DFLT => 'wikitext',
377 ApiBase::PARAM_TYPE => [ 'html', 'wikitext', 'raw', 'none' ],
378 ApiBase::PARAM_HELP_MSG => 'api-help-authmanagerhelper-messageformat',
379 ],
380 'mergerequestfields' => [
381 ApiBase::PARAM_DFLT => false,
382 ApiBase::PARAM_HELP_MSG => 'api-help-authmanagerhelper-mergerequestfields',
383 ],
384 'preservestate' => [
385 ApiBase::PARAM_DFLT => false,
386 ApiBase::PARAM_HELP_MSG => 'api-help-authmanagerhelper-preservestate',
387 ],
388 'returnurl' => [
389 ApiBase::PARAM_TYPE => 'string',
390 ApiBase::PARAM_HELP_MSG => 'api-help-authmanagerhelper-returnurl',
391 ],
392 'continue' => [
393 ApiBase::PARAM_DFLT => 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}
Helper class for AuthManager-using API modules.
static newForModule(ApiBase $module, AuthManager $authManager=null)
Static version of the constructor, for chaining.
logAuthenticationResult( $event, $result)
Logs successful or failed authentication.
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)
ApiBase $module
API module, for context and parameters.
formatRequests(array $reqs)
Format an array of AuthenticationRequests for return.
formatFields(array $fields)
Clean up a field array for output.
formatMessage(array &$res, $key, Message $message)
Format a message for output.
securitySensitiveOperation( $operation)
Call $manager->securitySensitiveOperationStatus()
loadAuthenticationRequests( $action)
Fetch and load the AuthenticationRequests for an action.
string $messageFormat
Message output format.
This abstract class implements many basic API functions, and is the base of all API classes.
Definition ApiBase.php:55
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:764
getModuleName()
Get the name of the module being executed by this instance.
Definition ApiBase.php:497
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.
PSR-3 logger instance factory.
MediaWikiServices is the service locator for the application scope of MediaWiki.
The Message class deals with fetching and processing of interface message into a variety of formats.
Definition Message.php:138
getParams()
Returns the message parameters.
Definition Message.php:368
getKey()
Returns the message key.
Definition Message.php:357
setContext(IContextSource $context)
Set the language and the title from a context object.
Definition Message.php:764
static stripOuterParagraph( $html)
Strip outer.
Definition Parser.php:6336
return true
Definition router.php:92