MediaWiki REL1_34
ApiAuthManagerHelper.php
Go to the documentation of this file.
1<?php
29
37
39 private $module;
40
43
47 public function __construct( ApiBase $module ) {
48 $this->module = $module;
49
50 $params = $module->extractRequestParams();
51 $this->messageFormat = $params['messageformat'] ?? 'wikitext';
52 }
53
59 public static function newForModule( ApiBase $module ) {
60 return new self( $module );
61 }
62
69 private function formatMessage( array &$res, $key, Message $message ) {
70 switch ( $this->messageFormat ) {
71 case 'none':
72 break;
73
74 case 'wikitext':
75 $res[$key] = $message->setContext( $this->module )->text();
76 break;
77
78 case 'html':
79 $res[$key] = $message->setContext( $this->module )->parseAsBlock();
80 $res[$key] = Parser::stripOuterParagraph( $res[$key] );
81 break;
82
83 case 'raw':
84 $res[$key] = [
85 'key' => $message->getKey(),
86 'params' => $message->getParams(),
87 ];
88 ApiResult::setIndexedTagName( $res[$key]['params'], 'param' );
89 break;
90 }
91 }
92
98 public function securitySensitiveOperation( $operation ) {
99 $status = AuthManager::singleton()->securitySensitiveOperationStatus( $operation );
100 switch ( $status ) {
101 case AuthManager::SEC_OK:
102 return;
103
104 case AuthManager::SEC_REAUTH:
105 $this->module->dieWithError( 'apierror-reauthenticate' );
106
107 case AuthManager::SEC_FAIL:
108 $this->module->dieWithError( 'apierror-cannotreauthenticate' );
109
110 default:
111 throw new UnexpectedValueException( "Unknown status \"$status\"" );
112 }
113 }
114
121 public static function blacklistAuthenticationRequests( array $reqs, array $blacklist ) {
122 if ( $blacklist ) {
123 $blacklist = array_flip( $blacklist );
124 $reqs = array_filter( $reqs, function ( $req ) use ( $blacklist ) {
125 return !isset( $blacklist[get_class( $req )] );
126 } );
127 }
128 return $reqs;
129 }
130
136 public function loadAuthenticationRequests( $action ) {
137 $params = $this->module->extractRequestParams();
138
139 $manager = AuthManager::singleton();
140 $reqs = $manager->getAuthenticationRequests( $action, $this->module->getUser() );
141
142 // Filter requests, if requested to do so
143 $wantedRequests = null;
144 if ( isset( $params['requests'] ) ) {
145 $wantedRequests = array_flip( $params['requests'] );
146 } elseif ( isset( $params['request'] ) ) {
147 $wantedRequests = [ $params['request'] => true ];
148 }
149 if ( $wantedRequests !== null ) {
150 $reqs = array_filter( $reqs, function ( $req ) use ( $wantedRequests ) {
151 return isset( $wantedRequests[$req->getUniqueId()] );
152 } );
153 }
154
155 // Collect the fields for all the requests
156 $fields = [];
157 $sensitive = [];
158 foreach ( $reqs as $req ) {
159 $info = (array)$req->getFieldInfo();
160 $fields += $info;
161 $sensitive += array_filter( $info, function ( $opts ) {
162 return !empty( $opts['sensitive'] );
163 } );
164 }
165
166 // Extract the request data for the fields and mark those request
167 // parameters as used
168 $data = array_intersect_key( $this->module->getRequest()->getValues(), $fields );
169 $this->module->getMain()->markParamsUsed( array_keys( $data ) );
170
171 if ( $sensitive ) {
172 $this->module->getMain()->markParamsSensitive( array_keys( $sensitive ) );
173 $this->module->requirePostedParameters( array_keys( $sensitive ), 'noprefix' );
174 }
175
176 return AuthenticationRequest::loadRequestsFromSubmission( $reqs, $data );
177 }
178
185 $ret = [
186 'status' => $res->status,
187 ];
188
189 if ( $res->status === AuthenticationResponse::PASS && $res->username !== null ) {
190 $ret['username'] = $res->username;
191 }
192
193 if ( $res->status === AuthenticationResponse::REDIRECT ) {
194 $ret['redirecttarget'] = $res->redirectTarget;
195 if ( $res->redirectApiData !== null ) {
196 $ret['redirectdata'] = $res->redirectApiData;
197 }
198 }
199
200 if ( $res->status === AuthenticationResponse::REDIRECT ||
201 $res->status === AuthenticationResponse::UI ||
202 $res->status === AuthenticationResponse::RESTART
203 ) {
204 $ret += $this->formatRequests( $res->neededRequests );
205 }
206
207 if ( $res->status === AuthenticationResponse::FAIL ||
208 $res->status === AuthenticationResponse::UI ||
209 $res->status === AuthenticationResponse::RESTART
210 ) {
211 $this->formatMessage( $ret, 'message', $res->message );
212 $ret['messagecode'] = ApiMessage::create( $res->message )->getApiCode();
213 }
214
215 if ( $res->status === AuthenticationResponse::FAIL ||
216 $res->status === AuthenticationResponse::RESTART
217 ) {
218 $this->module->getRequest()->getSession()->set(
219 'ApiAuthManagerHelper::createRequest',
220 $res->createRequest
221 );
222 $ret['canpreservestate'] = $res->createRequest !== null;
223 } else {
224 $this->module->getRequest()->getSession()->remove( 'ApiAuthManagerHelper::createRequest' );
225 }
226
227 return $ret;
228 }
229
235 public function logAuthenticationResult( $event, $result ) {
236 if ( is_string( $result ) ) {
237 $status = Status::newFatal( $result );
238 } elseif ( $result->status === AuthenticationResponse::PASS ) {
239 $status = Status::newGood();
240 } elseif ( $result->status === AuthenticationResponse::FAIL ) {
241 $status = Status::newFatal( $result->message );
242 } else {
243 return;
244 }
245
246 $module = $this->module->getModuleName();
247 LoggerFactory::getInstance( 'authevents' )->info( "$module API attempt", [
248 'event' => $event,
249 'status' => $status,
250 'module' => $module,
251 ] );
252 }
253
258 public function getPreservedRequest() {
259 $ret = $this->module->getRequest()->getSession()->get( 'ApiAuthManagerHelper::createRequest' );
260 return $ret instanceof CreateFromLoginAuthenticationRequest ? $ret : null;
261 }
262
269 public function formatRequests( array $reqs ) {
270 $params = $this->module->extractRequestParams();
271 $mergeFields = !empty( $params['mergerequestfields'] );
272
273 $ret = [ 'requests' => [] ];
274 foreach ( $reqs as $req ) {
275 $describe = $req->describeCredentials();
276 $reqInfo = [
277 'id' => $req->getUniqueId(),
278 'metadata' => $req->getMetadata() + [ ApiResult::META_TYPE => 'assoc' ],
279 ];
280 switch ( $req->required ) {
281 case AuthenticationRequest::OPTIONAL:
282 $reqInfo['required'] = 'optional';
283 break;
284 case AuthenticationRequest::REQUIRED:
285 $reqInfo['required'] = 'required';
286 break;
287 case AuthenticationRequest::PRIMARY_REQUIRED:
288 $reqInfo['required'] = 'primary-required';
289 break;
290 }
291 $this->formatMessage( $reqInfo, 'provider', $describe['provider'] );
292 $this->formatMessage( $reqInfo, 'account', $describe['account'] );
293 if ( !$mergeFields ) {
294 $reqInfo['fields'] = $this->formatFields( (array)$req->getFieldInfo() );
295 }
296 $ret['requests'][] = $reqInfo;
297 }
298
299 if ( $mergeFields ) {
300 $fields = AuthenticationRequest::mergeFieldInfo( $reqs );
301 $ret['fields'] = $this->formatFields( $fields );
302 }
303
304 return $ret;
305 }
306
315 private function formatFields( array $fields ) {
316 static $copy = [
317 'type' => true,
318 'value' => true,
319 ];
320
322 $retFields = [];
323
324 foreach ( $fields as $name => $field ) {
325 $ret = array_intersect_key( $field, $copy );
326
327 if ( isset( $field['options'] ) ) {
328 $ret['options'] = array_map( function ( $msg ) use ( $module ) {
329 return $msg->setContext( $module )->plain();
330 }, $field['options'] );
331 ApiResult::setArrayType( $ret['options'], 'assoc' );
332 }
333 $this->formatMessage( $ret, 'label', $field['label'] );
334 $this->formatMessage( $ret, 'help', $field['help'] );
335 $ret['optional'] = !empty( $field['optional'] );
336 $ret['sensitive'] = !empty( $field['sensitive'] );
337
338 $retFields[$name] = $ret;
339 }
340
341 ApiResult::setArrayType( $retFields, 'assoc' );
342
343 return $retFields;
344 }
345
352 public static function getStandardParams( $action, ...$wantedParams ) {
353 $params = [
354 'requests' => [
355 ApiBase::PARAM_TYPE => 'string',
357 ApiBase::PARAM_HELP_MSG => [ 'api-help-authmanagerhelper-requests', $action ],
358 ],
359 'request' => [
360 ApiBase::PARAM_TYPE => 'string',
362 ApiBase::PARAM_HELP_MSG => [ 'api-help-authmanagerhelper-request', $action ],
363 ],
364 'messageformat' => [
365 ApiBase::PARAM_DFLT => 'wikitext',
366 ApiBase::PARAM_TYPE => [ 'html', 'wikitext', 'raw', 'none' ],
367 ApiBase::PARAM_HELP_MSG => 'api-help-authmanagerhelper-messageformat',
368 ],
369 'mergerequestfields' => [
370 ApiBase::PARAM_DFLT => false,
371 ApiBase::PARAM_HELP_MSG => 'api-help-authmanagerhelper-mergerequestfields',
372 ],
373 'preservestate' => [
374 ApiBase::PARAM_DFLT => false,
375 ApiBase::PARAM_HELP_MSG => 'api-help-authmanagerhelper-preservestate',
376 ],
377 'returnurl' => [
378 ApiBase::PARAM_TYPE => 'string',
379 ApiBase::PARAM_HELP_MSG => 'api-help-authmanagerhelper-returnurl',
380 ],
381 'continue' => [
382 ApiBase::PARAM_DFLT => false,
383 ApiBase::PARAM_HELP_MSG => 'api-help-authmanagerhelper-continue',
384 ],
385 ];
386
387 $ret = [];
388 foreach ( $wantedParams as $name ) {
389 if ( isset( $params[$name] ) ) {
390 $ret[$name] = $params[$name];
391 }
392 }
393 return $ret;
394 }
395}
Helper class for AuthManager-using API modules.
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.
formatAuthenticationResponse(AuthenticationResponse $res)
Format an AuthenticationResponse for return.
static newForModule(ApiBase $module)
Static version of the constructor, for chaining.
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()
static blacklistAuthenticationRequests(array $reqs, array $blacklist)
Filter out authentication requests by class name.
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:42
const PARAM_REQUIRED
(boolean) Is the parameter required?
Definition ApiBase.php:118
const PARAM_TYPE
(string|string[]) Either an array of allowed value strings, or a string type as described below.
Definition ApiBase.php:94
const PARAM_DFLT
(null|boolean|integer|string) Default value of the parameter.
Definition ApiBase.php:55
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:761
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter.
Definition ApiBase.php:131
getModuleName()
Get the name of the module being executed by this instance.
Definition ApiBase.php:520
const PARAM_ISMULTI
(boolean) Accept multiple pipe-separated values for this parameter (e.g.
Definition ApiBase.php:58
static create( $msg, $code=null, array $data=null)
Create an IApiMessage for the message.
const META_TYPE
Key for the 'type' metadata item.
static setArrayType(array &$arr, $type, $kvpKeyName=null)
Set the array data type.
static setIndexedTagName(array &$arr, $tag)
Set the tag name for numeric-keyed values in XML format.
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.
This transfers state between the login and account creation flows.
PSR-3 logger instance factory.
The Message class provides methods which fulfil two basic services:
Definition Message.php:162
getParams()
Returns the message parameters.
Definition Message.php:369
getKey()
Returns the message key.
Definition Message.php:358
setContext(IContextSource $context)
Set the language and the title from a context object.
Definition Message.php:713
return true
Definition router.php:94