MediaWiki  master
ApiAuthManagerHelper.php
Go to the documentation of this file.
1 <?php
30 
38 
40  private $module;
41 
43  private $messageFormat;
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 }
ApiAuthManagerHelper\newForModule
static newForModule(ApiBase $module, AuthManager $authManager=null)
Static version of the constructor, for chaining.
Definition: ApiAuthManagerHelper.php:66
Message\getParams
getParams()
Returns the message parameters.
Definition: Message.php:352
StatusValue\newFatal
static newFatal( $message,... $parameters)
Factory function for fatal errors.
Definition: StatusValue.php:70
ApiBase\PARAM_REQUIRED
const PARAM_REQUIRED
Definition: ApiBase.php:78
ApiAuthManagerHelper\formatAuthenticationResponse
formatAuthenticationResponse(AuthenticationResponse $res)
Format an AuthenticationResponse for return.
Definition: ApiAuthManagerHelper.php:196
ApiAuthManagerHelper\blacklistAuthenticationRequests
static blacklistAuthenticationRequests(array $reqs, array $remove)
Filter out authentication requests by class name.
Definition: ApiAuthManagerHelper.php:131
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:193
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:105
true
return true
Definition: router.php:90
ApiAuthManagerHelper\securitySensitiveOperation
securitySensitiveOperation( $operation)
Call $manager->securitySensitiveOperationStatus()
Definition: ApiAuthManagerHelper.php:106
ApiBase\PARAM_TYPE
const PARAM_TYPE
Definition: ApiBase.php:72
ApiAuthManagerHelper\getStandardParams
static getStandardParams( $action,... $wantedParams)
Fetch the standard parameters this helper recognizes.
Definition: ApiAuthManagerHelper.php:363
$res
$res
Definition: testCompression.php:57
ApiAuthManagerHelper\formatRequests
formatRequests(array $reqs)
Format an array of AuthenticationRequests for return.
Definition: ApiAuthManagerHelper.php:281
ApiBase
This abstract class implements many basic API functions, and is the base of all API classes.
Definition: ApiBase.php:55
Message\getKey
getKey()
Returns the message key.
Definition: Message.php:341
ApiAuthManagerHelper\loadAuthenticationRequests
loadAuthenticationRequests( $action)
Fetch and load the AuthenticationRequests for an action.
Definition: ApiAuthManagerHelper.php:146
ApiResult\setArrayType
static setArrayType(array &$arr, $type, $kvpKeyName=null)
Set the array data type.
Definition: ApiResult.php:715
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:35
ApiAuthManagerHelper
Helper class for AuthManager-using API modules.
Definition: ApiAuthManagerHelper.php:37
MediaWiki\Auth\AuthenticationRequest\getUniqueId
getUniqueId()
Supply a unique key for deduplication.
Definition: AuthenticationRequest.php:90
ApiBase\extractRequestParams
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition: ApiBase.php:707
ApiMessage\create
static create( $msg, $code=null, array $data=null)
Create an IApiMessage for the message.
Definition: ApiMessage.php:43
ApiAuthManagerHelper\$messageFormat
string $messageFormat
Message output format.
Definition: ApiAuthManagerHelper.php:43
ApiAuthManagerHelper\__construct
__construct(ApiBase $module, AuthManager $authManager=null)
Definition: ApiAuthManagerHelper.php:52
ApiResult\setIndexedTagName
static setIndexedTagName(array &$arr, $tag)
Set the tag name for numeric-keyed values in XML format.
Definition: ApiResult.php:603
StatusValue\newGood
static newGood( $value=null)
Factory function for good results.
Definition: StatusValue.php:82
ApiAuthManagerHelper\logAuthenticationResult
logAuthenticationResult( $event, $result)
Logs successful or failed authentication.
Definition: ApiAuthManagerHelper.php:247
ApiAuthManagerHelper\$module
ApiBase $module
API module, for context and parameters.
Definition: ApiAuthManagerHelper.php:40
Message\setContext
setContext(IContextSource $context)
Set the language and the title from a context object.
Definition: Message.php:748
ApiAuthManagerHelper\formatMessage
formatMessage(array &$res, $key, Message $message)
Format a message for output.
Definition: ApiAuthManagerHelper.php:76
MediaWiki\Auth\AuthManager
This serves as the entry point to the authentication system.
Definition: AuthManager.php:102
Parser\stripOuterParagraph
static stripOuterParagraph( $html)
Strip outer.
Definition: Parser.php:6311
ApiBase\PARAM_DFLT
const PARAM_DFLT
Definition: ApiBase.php:70
ApiBase\getModuleName
getModuleName()
Get the name of the module being executed by this instance.
Definition: ApiBase.php:440
ApiBase\PARAM_ISMULTI
const PARAM_ISMULTI
Definition: ApiBase.php:71
Message
The Message class deals with fetching and processing of interface message into a variety of formats.
Definition: Message.php:138
ApiAuthManagerHelper\getPreservedRequest
getPreservedRequest()
Fetch the preserved CreateFromLoginAuthenticationRequest, if any.
Definition: ApiAuthManagerHelper.php:270
ApiAuthManagerHelper\formatFields
formatFields(array $fields)
Clean up a field array for output.
Definition: ApiAuthManagerHelper.php:326
ApiAuthManagerHelper\$authManager
AuthManager $authManager
Definition: ApiAuthManagerHelper.php:46
MediaWiki\Auth\AuthenticationRequest
This is a value object for authentication requests.
Definition: AuthenticationRequest.php:38