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