MediaWiki  1.34.0
ApiAuthManagerHelper.php
Go to the documentation of this file.
1 <?php
29 
37 
39  private $module;
40 
42  private $messageFormat;
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 ) {
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',
356  ApiBase::PARAM_ISMULTI => true,
357  ApiBase::PARAM_HELP_MSG => [ 'api-help-authmanagerhelper-requests', $action ],
358  ],
359  'request' => [
360  ApiBase::PARAM_TYPE => 'string',
361  ApiBase::PARAM_REQUIRED => true,
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 }
StatusValue\newFatal
static newFatal( $message,... $parameters)
Factory function for fatal errors.
Definition: StatusValue.php:69
ApiBase\PARAM_REQUIRED
const PARAM_REQUIRED
(boolean) Is the parameter required?
Definition: ApiBase.php:118
ApiAuthManagerHelper\formatAuthenticationResponse
formatAuthenticationResponse(AuthenticationResponse $res)
Format an AuthenticationResponse for return.
Definition: ApiAuthManagerHelper.php:184
ApiResult\META_TYPE
const META_TYPE
Key for the 'type' metadata item.
Definition: ApiResult.php:110
ApiBase\PARAM_HELP_MSG
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter.
Definition: ApiBase.php:131
true
return true
Definition: router.php:92
ApiAuthManagerHelper\securitySensitiveOperation
securitySensitiveOperation( $operation)
Call $manager->securitySensitiveOperationStatus()
Definition: ApiAuthManagerHelper.php:98
ApiBase\PARAM_TYPE
const PARAM_TYPE
(string|string[]) Either an array of allowed value strings, or a string type as described below.
Definition: ApiBase.php:94
Message
ApiAuthManagerHelper\getStandardParams
static getStandardParams( $action,... $wantedParams)
Fetch the standard parameters this helper recognizes.
Definition: ApiAuthManagerHelper.php:352
$res
$res
Definition: testCompression.php:52
ApiAuthManagerHelper\formatRequests
formatRequests(array $reqs)
Format an array of AuthenticationRequests for return.
Definition: ApiAuthManagerHelper.php:269
ApiBase
This abstract class implements many basic API functions, and is the base of all API classes.
Definition: ApiBase.php:42
ApiAuthManagerHelper\loadAuthenticationRequests
loadAuthenticationRequests( $action)
Fetch and load the AuthenticationRequests for an action.
Definition: ApiAuthManagerHelper.php:136
ApiAuthManagerHelper\__construct
__construct(ApiBase $module)
Definition: ApiAuthManagerHelper.php:47
ApiResult\setArrayType
static setArrayType(array &$arr, $type, $kvpKeyName=null)
Set the array data type.
Definition: ApiResult.php:728
MediaWiki\Logger\LoggerFactory
PSR-3 logger instance factory.
Definition: LoggerFactory.php:45
MediaWiki\Auth\AuthenticationResponse
This is a value object to hold authentication response data.
Definition: AuthenticationResponse.php:37
MediaWiki\Auth\CreateFromLoginAuthenticationRequest
This transfers state between the login and account creation flows.
Definition: CreateFromLoginAuthenticationRequest.php:34
ApiAuthManagerHelper
Helper class for AuthManager-using API modules.
Definition: ApiAuthManagerHelper.php:36
ApiBase\extractRequestParams
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition: ApiBase.php:761
ApiMessage\create
static create( $msg, $code=null, array $data=null)
Create an IApiMessage for the message.
Definition: ApiMessage.php:40
ApiAuthManagerHelper\$messageFormat
string $messageFormat
Message output format.
Definition: ApiAuthManagerHelper.php:42
ApiResult\setIndexedTagName
static setIndexedTagName(array &$arr, $tag)
Set the tag name for numeric-keyed values in XML format.
Definition: ApiResult.php:616
StatusValue\newGood
static newGood( $value=null)
Factory function for good results.
Definition: StatusValue.php:81
ApiAuthManagerHelper\logAuthenticationResult
logAuthenticationResult( $event, $result)
Logs successful or failed authentication.
Definition: ApiAuthManagerHelper.php:235
ApiAuthManagerHelper\blacklistAuthenticationRequests
static blacklistAuthenticationRequests(array $reqs, array $blacklist)
Filter out authentication requests by class name.
Definition: ApiAuthManagerHelper.php:121
ApiAuthManagerHelper\$module
ApiBase $module
API module, for context and parameters.
Definition: ApiAuthManagerHelper.php:39
ApiAuthManagerHelper\formatMessage
formatMessage(array &$res, $key, Message $message)
Format a message for output.
Definition: ApiAuthManagerHelper.php:69
MediaWiki\Auth\AuthManager
This serves as the entry point to the authentication system.
Definition: AuthManager.php:85
$status
return $status
Definition: SyntaxHighlight.php:347
ApiBase\PARAM_DFLT
const PARAM_DFLT
(null|boolean|integer|string) Default value of the parameter.
Definition: ApiBase.php:55
ApiBase\getModuleName
getModuleName()
Get the name of the module being executed by this instance.
Definition: ApiBase.php:520
ApiBase\PARAM_ISMULTI
const PARAM_ISMULTI
(boolean) Accept multiple pipe-separated values for this parameter (e.g.
Definition: ApiBase.php:58
ApiAuthManagerHelper\newForModule
static newForModule(ApiBase $module)
Static version of the constructor, for chaining.
Definition: ApiAuthManagerHelper.php:59
ApiAuthManagerHelper\getPreservedRequest
getPreservedRequest()
Fetch the preserved CreateFromLoginAuthenticationRequest, if any.
Definition: ApiAuthManagerHelper.php:258
ApiAuthManagerHelper\formatFields
formatFields(array $fields)
Clean up a field array for output.
Definition: ApiAuthManagerHelper.php:315
MediaWiki\Auth\AuthenticationRequest
This is a value object for authentication requests.
Definition: AuthenticationRequest.php:37