MediaWiki REL1_34
AuthManagerSpecialPage.php
Go to the documentation of this file.
1<?php
2
8
14abstract class AuthManagerSpecialPage extends SpecialPage {
18 protected static $allowedActions = [
19 AuthManager::ACTION_LOGIN, AuthManager::ACTION_LOGIN_CONTINUE,
20 AuthManager::ACTION_CREATE, AuthManager::ACTION_CREATE_CONTINUE,
21 AuthManager::ACTION_LINK, AuthManager::ACTION_LINK_CONTINUE,
22 AuthManager::ACTION_CHANGE, AuthManager::ACTION_REMOVE, AuthManager::ACTION_UNLINK,
23 ];
24
26 protected static $messages = [];
27
29 protected $authAction;
30
32 protected $authRequests;
33
35 protected $subPage;
36
38 protected $isReturn;
39
41 protected $savedRequest;
42
54 public function onAuthChangeFormFields(
55 array $requests, array $fieldInfo, array &$formDescriptor, $action
56 ) {
57 return true;
58 }
59
60 protected function getLoginSecurityLevel() {
61 return $this->getName();
62 }
63
64 public function getRequest() {
65 return $this->savedRequest ?: $this->getContext()->getRequest();
66 }
67
76 protected function setRequest( array $data, $wasPosted = null ) {
77 $request = $this->getContext()->getRequest();
78 if ( $wasPosted === null ) {
79 $wasPosted = $request->wasPosted();
80 }
81 $this->savedRequest = new DerivativeRequest( $request, $data + $request->getQueryValues(),
82 $wasPosted );
83 }
84
85 protected function beforeExecute( $subPage ) {
86 $this->getOutput()->disallowUserJs();
87
88 return $this->handleReturnBeforeExecute( $subPage )
90 }
91
108 protected function handleReturnBeforeExecute( $subPage ) {
109 $authManager = AuthManager::singleton();
110 $key = 'AuthManagerSpecialPage:return:' . $this->getName();
111
112 if ( $subPage === 'return' ) {
113 $this->loadAuth( $subPage );
114 $preservedParams = $this->getPreservedParams( false );
115
116 // FIXME save POST values only from request
117 $authData = array_diff_key( $this->getRequest()->getValues(),
118 $preservedParams, [ 'title' => 1 ] );
119 $authManager->setAuthenticationSessionData( $key, $authData );
120
121 $url = $this->getPageTitle()->getFullURL( $preservedParams, false, PROTO_HTTPS );
122 $this->getOutput()->redirect( $url );
123 return false;
124 }
125
126 $authData = $authManager->getAuthenticationSessionData( $key );
127 if ( $authData ) {
128 $authManager->removeAuthenticationSessionData( $key );
129 $this->isReturn = true;
130 $this->setRequest( $authData, true );
131 }
132
133 return true;
134 }
135
146 protected function handleReauthBeforeExecute( $subPage ) {
147 $authManager = AuthManager::singleton();
148 $request = $this->getRequest();
149 $key = 'AuthManagerSpecialPage:reauth:' . $this->getName();
150
151 $securityLevel = $this->getLoginSecurityLevel();
152 if ( $securityLevel ) {
153 $securityStatus = AuthManager::singleton()
154 ->securitySensitiveOperationStatus( $securityLevel );
155 if ( $securityStatus === AuthManager::SEC_REAUTH ) {
156 $queryParams = array_diff_key( $request->getQueryValues(), [ 'title' => true ] );
157
158 if ( $request->wasPosted() ) {
159 // unique ID in case the same special page is open in multiple browser tabs
160 $uniqueId = MWCryptRand::generateHex( 6 );
161 $key .= ':' . $uniqueId;
162
163 $queryParams = [ 'authUniqueId' => $uniqueId ] + $queryParams;
164 $authData = array_diff_key( $request->getValues(),
165 $this->getPreservedParams( false ), [ 'title' => 1 ] );
166 $authManager->setAuthenticationSessionData( $key, $authData );
167 }
168
169 $title = SpecialPage::getTitleFor( 'Userlogin' );
170 $url = $title->getFullURL( [
171 'returnto' => $this->getFullTitle()->getPrefixedDBkey(),
172 'returntoquery' => wfArrayToCgi( $queryParams ),
173 'force' => $securityLevel,
174 ], false, PROTO_HTTPS );
175
176 $this->getOutput()->redirect( $url );
177 return false;
178 } elseif ( $securityStatus !== AuthManager::SEC_OK ) {
179 throw new ErrorPageError( 'cannotauth-not-allowed-title', 'cannotauth-not-allowed' );
180 }
181 }
182
183 $uniqueId = $request->getVal( 'authUniqueId' );
184 if ( $uniqueId ) {
185 $key .= ':' . $uniqueId;
186 $authData = $authManager->getAuthenticationSessionData( $key );
187 if ( $authData ) {
188 $authManager->removeAuthenticationSessionData( $key );
189 $this->setRequest( $authData, true );
190 }
191 }
192
193 return true;
194 }
195
202 abstract protected function getDefaultAction( $subPage );
203
210 protected function messageKey( $defaultKey ) {
211 return array_key_exists( $defaultKey, static::$messages )
212 ? static::$messages[$defaultKey] : $defaultKey;
213 }
214
219 protected function getRequestBlacklist() {
220 return [];
221 }
222
232 protected function loadAuth( $subPage, $authAction = null, $reset = false ) {
233 // Do not load if already loaded, to cut down on the number of getAuthenticationRequests
234 // calls. This is important for requests which have hidden information so any
235 // getAuthenticationRequests call would mean putting data into some cache.
236 if (
237 !$reset && $this->subPage === $subPage && $this->authAction
238 && ( !$authAction || $authAction === $this->authAction )
239 ) {
240 return;
241 }
242
243 $request = $this->getRequest();
244 $this->subPage = $subPage;
245 $this->authAction = $authAction ?: $request->getText( 'authAction' );
246 if ( !in_array( $this->authAction, static::$allowedActions, true ) ) {
247 $this->authAction = $this->getDefaultAction( $subPage );
248 if ( $request->wasPosted() ) {
249 $continueAction = $this->getContinueAction( $this->authAction );
250 if ( in_array( $continueAction, static::$allowedActions, true ) ) {
251 $this->authAction = $continueAction;
252 }
253 }
254 }
255
256 $allReqs = AuthManager::singleton()->getAuthenticationRequests(
257 $this->authAction, $this->getUser() );
258 $this->authRequests = array_filter( $allReqs, function ( $req ) {
259 return !in_array( get_class( $req ), $this->getRequestBlacklist(), true );
260 } );
261 }
262
267 protected function isContinued() {
268 return in_array( $this->authAction, [
269 AuthManager::ACTION_LOGIN_CONTINUE,
270 AuthManager::ACTION_CREATE_CONTINUE,
271 AuthManager::ACTION_LINK_CONTINUE,
272 ], true );
273 }
274
280 protected function getContinueAction( $action ) {
281 switch ( $action ) {
282 case AuthManager::ACTION_LOGIN:
283 $action = AuthManager::ACTION_LOGIN_CONTINUE;
284 break;
285 case AuthManager::ACTION_CREATE:
286 $action = AuthManager::ACTION_CREATE_CONTINUE;
287 break;
288 case AuthManager::ACTION_LINK:
289 $action = AuthManager::ACTION_LINK_CONTINUE;
290 break;
291 }
292 return $action;
293 }
294
303 protected function isActionAllowed( $action ) {
304 $authManager = AuthManager::singleton();
305 if ( !in_array( $action, static::$allowedActions, true ) ) {
306 throw new InvalidArgumentException( 'invalid action: ' . $action );
307 }
308
309 // calling getAuthenticationRequests can be expensive, avoid if possible
310 $requests = ( $action === $this->authAction ) ? $this->authRequests
311 : $authManager->getAuthenticationRequests( $action );
312 if ( !$requests ) {
313 // no provider supports this action in the current state
314 return false;
315 }
316
317 switch ( $action ) {
318 case AuthManager::ACTION_LOGIN:
319 case AuthManager::ACTION_LOGIN_CONTINUE:
320 return $authManager->canAuthenticateNow();
321 case AuthManager::ACTION_CREATE:
322 case AuthManager::ACTION_CREATE_CONTINUE:
323 return $authManager->canCreateAccounts();
324 case AuthManager::ACTION_LINK:
325 case AuthManager::ACTION_LINK_CONTINUE:
326 return $authManager->canLinkAccounts();
327 case AuthManager::ACTION_CHANGE:
328 case AuthManager::ACTION_REMOVE:
329 case AuthManager::ACTION_UNLINK:
330 return true;
331 default:
332 // should never reach here but makes static code analyzers happy
333 throw new InvalidArgumentException( 'invalid action: ' . $action );
334 }
335 }
336
343 protected function performAuthenticationStep( $action, array $requests ) {
344 if ( !in_array( $action, static::$allowedActions, true ) ) {
345 throw new InvalidArgumentException( 'invalid action: ' . $action );
346 }
347
348 $authManager = AuthManager::singleton();
349 $returnToUrl = $this->getPageTitle( 'return' )
350 ->getFullURL( $this->getPreservedParams( true ), false, PROTO_HTTPS );
351
352 switch ( $action ) {
353 case AuthManager::ACTION_LOGIN:
354 return $authManager->beginAuthentication( $requests, $returnToUrl );
355 case AuthManager::ACTION_LOGIN_CONTINUE:
356 return $authManager->continueAuthentication( $requests );
357 case AuthManager::ACTION_CREATE:
358 return $authManager->beginAccountCreation( $this->getUser(), $requests,
359 $returnToUrl );
360 case AuthManager::ACTION_CREATE_CONTINUE:
361 return $authManager->continueAccountCreation( $requests );
362 case AuthManager::ACTION_LINK:
363 return $authManager->beginAccountLink( $this->getUser(), $requests, $returnToUrl );
364 case AuthManager::ACTION_LINK_CONTINUE:
365 return $authManager->continueAccountLink( $requests );
366 case AuthManager::ACTION_CHANGE:
367 case AuthManager::ACTION_REMOVE:
368 case AuthManager::ACTION_UNLINK:
369 if ( count( $requests ) > 1 ) {
370 throw new InvalidArgumentException( 'only one auth request can be changed at a time' );
371 } elseif ( !$requests ) {
372 throw new InvalidArgumentException( 'no auth request' );
373 }
374 $req = reset( $requests );
375 $status = $authManager->allowsAuthenticationDataChange( $req );
376 Hooks::run( 'ChangeAuthenticationDataAudit', [ $req, $status ] );
377 if ( !$status->isGood() ) {
378 return AuthenticationResponse::newFail( $status->getMessage() );
379 }
380 $authManager->changeAuthenticationData( $req );
381 return AuthenticationResponse::newPass();
382 default:
383 // should never reach here but makes static code analyzers happy
384 throw new InvalidArgumentException( 'invalid action: ' . $action );
385 }
386 }
387
398 protected function trySubmit() {
399 $status = false;
400
401 $form = $this->getAuthForm( $this->authRequests, $this->authAction );
402 $form->setSubmitCallback( [ $this, 'handleFormSubmit' ] );
403
404 if ( $this->getRequest()->wasPosted() ) {
405 // handle tokens manually; $form->tryAuthorizedSubmit only works for logged-in users
406 $requestTokenValue = $this->getRequest()->getVal( $this->getTokenName() );
407 $sessionToken = $this->getToken();
408 if ( $sessionToken->wasNew() ) {
409 return Status::newFatal( $this->messageKey( 'authform-newtoken' ) );
410 } elseif ( !$requestTokenValue ) {
411 return Status::newFatal( $this->messageKey( 'authform-notoken' ) );
412 } elseif ( !$sessionToken->match( $requestTokenValue ) ) {
413 return Status::newFatal( $this->messageKey( 'authform-wrongtoken' ) );
414 }
415
416 $form->prepareForm();
417 $status = $form->trySubmit();
418
419 // HTMLForm submit return values are a mess; let's ensure it is false or a Status
420 // FIXME this probably should be in HTMLForm
421 if ( $status === true ) {
422 // not supposed to happen since our submit handler should always return a Status
423 throw new UnexpectedValueException( 'HTMLForm::trySubmit() returned true' );
424 } elseif ( $status === false ) {
425 // form was not submitted; nothing to do
426 } elseif ( $status instanceof Status ) {
427 // already handled by the form; nothing to do
428 } elseif ( $status instanceof StatusValue ) {
429 // in theory not an allowed return type but nothing stops the submit handler from
430 // accidentally returning it so best check and fix
431 $status = Status::wrap( $status );
432 } elseif ( is_string( $status ) ) {
433 $status = Status::newFatal( new RawMessage( '$1', [ $status ] ) );
434 } elseif ( is_array( $status ) ) {
435 if ( is_string( reset( $status ) ) ) {
436 $status = Status::newFatal( ...$status );
437 } elseif ( is_array( reset( $status ) ) ) {
438 $ret = Status::newGood();
439 foreach ( $status as $message ) {
440 $ret->fatal( ...$message );
441 }
442 $status = $ret;
443 } else {
444 throw new UnexpectedValueException( 'invalid HTMLForm::trySubmit() return value: '
445 . 'first element of array is ' . gettype( reset( $status ) ) );
446 }
447 } else {
448 // not supposed to happen but HTMLForm does not actually verify the return type
449 // from the submit callback; better safe then sorry
450 throw new UnexpectedValueException( 'invalid HTMLForm::trySubmit() return type: '
451 . gettype( $status ) );
452 }
453
454 if ( ( !$status || !$status->isOK() ) && $this->isReturn ) {
455 // This is awkward. There was a form validation error, which means the data was not
456 // passed to AuthManager. Normally we would display the form with an error message,
457 // but for the data we received via the redirect flow that would not be helpful at all.
458 // Let's just submit the data to AuthManager directly instead.
459 LoggerFactory::getInstance( 'authentication' )
460 ->warning( 'Validation error on return', [ 'data' => $form->mFieldData,
461 'status' => $status->getWikiText( false, false, 'en' ) ] );
462 $status = $this->handleFormSubmit( $form->mFieldData );
463 }
464 }
465
466 $changeActions = [
467 AuthManager::ACTION_CHANGE, AuthManager::ACTION_REMOVE, AuthManager::ACTION_UNLINK
468 ];
469 if ( in_array( $this->authAction, $changeActions, true ) && $status && !$status->isOK() ) {
470 Hooks::run( 'ChangeAuthenticationDataAudit', [ reset( $this->authRequests ), $status ] );
471 }
472
473 return $status;
474 }
475
482 public function handleFormSubmit( $data ) {
483 $requests = AuthenticationRequest::loadRequestsFromSubmission( $this->authRequests, $data );
484 $response = $this->performAuthenticationStep( $this->authAction, $requests );
485
486 // we can't handle FAIL or similar as failure here since it might require changing the form
487 return Status::newGood( $response );
488 }
489
497 protected function getPreservedParams( $withToken = false ) {
498 $params = [];
499 if ( $this->authAction !== $this->getDefaultAction( $this->subPage ) ) {
500 $params['authAction'] = $this->getContinueAction( $this->authAction );
501 }
502 if ( $withToken ) {
503 $params[$this->getTokenName()] = $this->getToken()->toString();
504 }
505 return $params;
506 }
507
514 protected function getAuthFormDescriptor( $requests, $action ) {
515 $fieldInfo = AuthenticationRequest::mergeFieldInfo( $requests );
516 $formDescriptor = $this->fieldInfoToFormDescriptor( $requests, $fieldInfo, $action );
517
518 $this->addTabIndex( $formDescriptor );
519
520 return $formDescriptor;
521 }
522
528 protected function getAuthForm( array $requests, $action ) {
529 $formDescriptor = $this->getAuthFormDescriptor( $requests, $action );
530 $context = $this->getContext();
531 if ( $context->getRequest() !== $this->getRequest() ) {
532 // We have overridden the request, need to make sure the form uses that too.
533 $context = new DerivativeContext( $this->getContext() );
534 $context->setRequest( $this->getRequest() );
535 }
536 $form = HTMLForm::factory( 'ooui', $formDescriptor, $context );
537 $form->setAction( $this->getFullTitle()->getFullURL( $this->getPreservedParams() ) );
538 $form->addHiddenField( $this->getTokenName(), $this->getToken()->toString() );
539 $form->addHiddenField( 'authAction', $this->authAction );
540 $form->suppressDefaultSubmit( !$this->needsSubmitButton( $requests ) );
541
542 return $form;
543 }
544
549 protected function displayForm( $status ) {
550 if ( $status instanceof StatusValue ) {
551 $status = Status::wrap( $status );
552 }
553 $form = $this->getAuthForm( $this->authRequests, $this->authAction );
554 $form->prepareForm()->displayForm( $status );
555 }
556
567 protected function needsSubmitButton( array $requests ) {
568 $customSubmitButtonPresent = false;
569
570 // Secondary and preauth providers always need their data; they will not care what button
571 // is used, so they can be ignored. So can OPTIONAL buttons createdby primary providers;
572 // that's the point in being optional. Se we need to check whether all primary providers
573 // have their own buttons and whether there is at least one button present.
574 foreach ( $requests as $req ) {
575 if ( $req->required === AuthenticationRequest::PRIMARY_REQUIRED ) {
576 if ( $this->hasOwnSubmitButton( $req ) ) {
577 $customSubmitButtonPresent = true;
578 } else {
579 return true;
580 }
581 }
582 }
583 return !$customSubmitButtonPresent;
584 }
585
591 protected function hasOwnSubmitButton( AuthenticationRequest $req ) {
592 foreach ( $req->getFieldInfo() as $field => $info ) {
593 if ( $info['type'] === 'button' ) {
594 return true;
595 }
596 }
597 return false;
598 }
599
605 protected function addTabIndex( &$formDescriptor ) {
606 $i = 1;
607 foreach ( $formDescriptor as $field => &$definition ) {
608 $class = false;
609 if ( array_key_exists( 'class', $definition ) ) {
610 $class = $definition['class'];
611 } elseif ( array_key_exists( 'type', $definition ) ) {
612 $class = HTMLForm::$typeMappings[$definition['type']];
613 }
614 if ( $class !== HTMLInfoField::class ) {
615 $definition['tabindex'] = $i;
616 $i++;
617 }
618 }
619 }
620
625 protected function getToken() {
626 return $this->getRequest()->getSession()->getToken( 'AuthManagerSpecialPage:'
627 . $this->getName() );
628 }
629
634 protected function getTokenName() {
635 return 'wpAuthToken';
636 }
637
647 protected function fieldInfoToFormDescriptor( array $requests, array $fieldInfo, $action ) {
648 $formDescriptor = [];
649 foreach ( $fieldInfo as $fieldName => $singleFieldInfo ) {
650 $formDescriptor[$fieldName] = self::mapSingleFieldInfo( $singleFieldInfo, $fieldName );
651 }
652
653 $requestSnapshot = serialize( $requests );
654 $this->onAuthChangeFormFields( $requests, $fieldInfo, $formDescriptor, $action );
655 \Hooks::run( 'AuthChangeFormFields', [ $requests, $fieldInfo, &$formDescriptor, $action ] );
656 if ( $requestSnapshot !== serialize( $requests ) ) {
657 LoggerFactory::getInstance( 'authentication' )->warning(
658 'AuthChangeFormFields hook changed auth requests' );
659 }
660
661 // Process the special 'weight' property, which is a way for AuthChangeFormFields hook
662 // subscribers (who only see one field at a time) to influence ordering.
663 self::sortFormDescriptorFields( $formDescriptor );
664
665 return $formDescriptor;
666 }
667
675 protected static function mapSingleFieldInfo( $singleFieldInfo, $fieldName ) {
676 $type = self::mapFieldInfoTypeToFormDescriptorType( $singleFieldInfo['type'] );
677 $descriptor = [
678 'type' => $type,
679 // Do not prefix input name with 'wp'. This is important for the redirect flow.
680 'name' => $fieldName,
681 ];
682
683 if ( $type === 'submit' && isset( $singleFieldInfo['label'] ) ) {
684 $descriptor['default'] = $singleFieldInfo['label']->plain();
685 } elseif ( $type !== 'submit' ) {
686 $descriptor += array_filter( [
687 // help-message is omitted as it is usually not really useful for a web interface
688 'label-message' => self::getField( $singleFieldInfo, 'label' ),
689 ] );
690
691 if ( isset( $singleFieldInfo['options'] ) ) {
692 $descriptor['options'] = array_flip( array_map( function ( $message ) {
694 return $message->parse();
695 }, $singleFieldInfo['options'] ) );
696 }
697
698 if ( isset( $singleFieldInfo['value'] ) ) {
699 $descriptor['default'] = $singleFieldInfo['value'];
700 }
701
702 if ( empty( $singleFieldInfo['optional'] ) ) {
703 $descriptor['required'] = true;
704 }
705 }
706
707 return $descriptor;
708 }
709
716 protected static function sortFormDescriptorFields( array &$formDescriptor ) {
717 $i = 0;
718 foreach ( $formDescriptor as &$field ) {
719 $field['__index'] = $i++;
720 }
721 uasort( $formDescriptor, function ( $first, $second ) {
722 return self::getField( $first, 'weight', 0 ) <=> self::getField( $second, 'weight', 0 )
723 ?: $first['__index'] <=> $second['__index'];
724 } );
725 foreach ( $formDescriptor as &$field ) {
726 unset( $field['__index'] );
727 }
728 }
729
737 protected static function getField( array $array, $fieldName, $default = null ) {
738 if ( array_key_exists( $fieldName, $array ) ) {
739 return $array[$fieldName];
740 } else {
741 return $default;
742 }
743 }
744
751 protected static function mapFieldInfoTypeToFormDescriptorType( $type ) {
752 $map = [
753 'string' => 'text',
754 'password' => 'password',
755 'select' => 'select',
756 'checkbox' => 'check',
757 'multiselect' => 'multiselect',
758 'button' => 'submit',
759 'hidden' => 'hidden',
760 'null' => 'info',
761 ];
762 if ( !array_key_exists( $type, $map ) ) {
763 throw new \LogicException( 'invalid field type: ' . $type );
764 }
765 return $map[$type];
766 }
767}
serialize()
wfArrayToCgi( $array1, $array2=null, $prefix='')
This function takes one or two arrays as input, and returns a CGI-style string, e....
A special page subclass for authentication-related special pages.
getContinueAction( $action)
Gets the _CONTINUE version of an action.
handleReturnBeforeExecute( $subPage)
Handle redirection from the /return subpage.
getLoginSecurityLevel()
Tells if the special page does something security-sensitive and needs extra defense against a stolen ...
handleReauthBeforeExecute( $subPage)
Handle redirection when the user needs to (re)authenticate.
beforeExecute( $subPage)
Gets called before.
isActionAllowed( $action)
Checks whether AuthManager is ready to perform the action.
WebRequest null $savedRequest
If set, will be used instead of the real request.
string $authAction
one of the AuthManager::ACTION_* constants.
performAuthenticationStep( $action, array $requests)
getAuthForm(array $requests, $action)
displayForm( $status)
Display the form.
loadAuth( $subPage, $authAction=null, $reset=false)
Load or initialize $authAction, $authRequests and $subPage.
bool $isReturn
True if the current request is a result of returning from a redirect flow.
static mapFieldInfoTypeToFormDescriptorType( $type)
Maps AuthenticationRequest::getFieldInfo() types to HTMLForm types.
fieldInfoToFormDescriptor(array $requests, array $fieldInfo, $action)
Turns a field info array into a form descriptor.
getDefaultAction( $subPage)
Get the default action for this special page, if none is given via URL/POST data.
handleFormSubmit( $data)
Submit handler callback for HTMLForm.
static array $messages
Customized messages.
AuthenticationRequest[] $authRequests
needsSubmitButton(array $requests)
Returns true if the form built from the given AuthenticationRequests needs a submit button.
string $subPage
Subpage of the special page.
isContinued()
Returns true if this is not the first step of the authentication.
hasOwnSubmitButton(AuthenticationRequest $req)
Checks whether the given AuthenticationRequest has its own submit button.
getRequestBlacklist()
Allows blacklisting certain request types.
static mapSingleFieldInfo( $singleFieldInfo, $fieldName)
Maps an authentication field configuration for a single field (as returned by AuthenticationRequest::...
static getField(array $array, $fieldName, $default=null)
Get an array value, or a default if it does not exist.
getRequest()
Get the WebRequest being used for this instance.
messageKey( $defaultKey)
Return custom message key.
setRequest(array $data, $wasPosted=null)
Override the POST data, GET data from the real request is preserved.
trySubmit()
Attempts to do an authentication step with the submitted data.
getToken()
Returns the CSRF token.
static sortFormDescriptorFields(array &$formDescriptor)
Sort the fields of a form descriptor by their 'weight' property.
addTabIndex(&$formDescriptor)
Adds a sequential tabindex starting from 1 to all form elements.
getAuthFormDescriptor( $requests, $action)
Generates a HTMLForm descriptor array from a set of authentication requests.
onAuthChangeFormFields(array $requests, array $fieldInfo, array &$formDescriptor, $action)
Change the form descriptor that determines how a field will look in the authentication form.
getPreservedParams( $withToken=false)
Returns URL query parameters which can be used to reload the page (or leave and return) while preserv...
getTokenName()
Returns the name of the CSRF token (under which it should be found in the POST or GET data).
static string[] $allowedActions
The list of actions this special page deals with.
An IContextSource implementation which will inherit context from another source but allow individual ...
Similar to FauxRequest, but only fakes URL parameters and method (POST or GET) and use the base reque...
An error page which can definitely be safely rendered using the OutputPage.
static generateHex( $chars)
Generate a run of cryptographically random data and return it in hexadecimal string format.
This serves as the entry point to the authentication system.
This is a value object for authentication requests.
getFieldInfo()
Fetch input field info.
This is a value object to hold authentication response data.
PSR-3 logger instance factory.
Value object representing a CSRF token.
Definition Token.php:32
Variant of the Message class.
Parent class for all special pages.
getName()
Get the name of this Special Page.
getOutput()
Get the OutputPage being used for this instance.
getUser()
Shortcut to get the User executing this instance.
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name If you don't need a full Title object,...
getContext()
Gets the context this SpecialPage is executed in.
getPageTitle( $subpage=false)
Get a self-referential title object.
getFullTitle()
Return the full title, including $par.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition Status.php:40
The WebRequest class encapsulates getting at data passed in the URL or via a POSTed form stripping il...
const PROTO_HTTPS
Definition Defines.php:209
$context
Definition load.php:45