MediaWiki REL1_37
AuthManagerSpecialPage.php
Go to the documentation of this file.
1<?php
2
8
18abstract class AuthManagerSpecialPage extends SpecialPage {
22 protected static $allowedActions = [
23 AuthManager::ACTION_LOGIN, AuthManager::ACTION_LOGIN_CONTINUE,
24 AuthManager::ACTION_CREATE, AuthManager::ACTION_CREATE_CONTINUE,
25 AuthManager::ACTION_LINK, AuthManager::ACTION_LINK_CONTINUE,
26 AuthManager::ACTION_CHANGE, AuthManager::ACTION_REMOVE, AuthManager::ACTION_UNLINK,
27 ];
28
30 protected static $messages = [];
31
33 protected $authAction;
34
36 protected $authRequests;
37
39 protected $subPage;
40
42 protected $isReturn;
43
45 protected $savedRequest;
46
59 public function onAuthChangeFormFields(
60 array $requests, array $fieldInfo, array &$formDescriptor, $action
61 ) {
62 }
63
68 protected function getLoginSecurityLevel() {
69 return $this->getName();
70 }
71
72 public function getRequest() {
73 return $this->savedRequest ?: $this->getContext()->getRequest();
74 }
75
86 protected function setRequest( array $data, $wasPosted = null ) {
87 $request = $this->getContext()->getRequest();
88 if ( $wasPosted === null ) {
89 $wasPosted = $request->wasPosted();
90 }
91 $this->savedRequest = new DerivativeRequest( $request, $data + $request->getQueryValues(),
92 $wasPosted );
93 }
94
101 protected function beforeExecute( $subPage ) {
102 $this->getOutput()->disallowUserJs();
103
104 return $this->handleReturnBeforeExecute( $subPage )
106 }
107
124 protected function handleReturnBeforeExecute( $subPage ) {
125 $authManager = $this->getAuthManager();
126 $key = 'AuthManagerSpecialPage:return:' . $this->getName();
127
128 if ( $subPage === 'return' ) {
129 $this->loadAuth( $subPage );
130 $preservedParams = $this->getPreservedParams( false );
131
132 // FIXME save POST values only from request
133 $authData = array_diff_key( $this->getRequest()->getValues(),
134 $preservedParams, [ 'title' => 1 ] );
135 $authManager->setAuthenticationSessionData( $key, $authData );
136
137 $url = $this->getPageTitle()->getFullURL( $preservedParams, false, PROTO_HTTPS );
138 $this->getOutput()->redirect( $url );
139 return false;
140 }
141
142 $authData = $authManager->getAuthenticationSessionData( $key );
143 if ( $authData ) {
145 $this->isReturn = true;
146 $this->setRequest( $authData, true );
147 }
148
149 return true;
150 }
151
162 protected function handleReauthBeforeExecute( $subPage ) {
163 $authManager = $this->getAuthManager();
164 $request = $this->getRequest();
165 $key = 'AuthManagerSpecialPage:reauth:' . $this->getName();
166
167 $securityLevel = $this->getLoginSecurityLevel();
168 if ( $securityLevel ) {
169 $securityStatus = $authManager->securitySensitiveOperationStatus( $securityLevel );
170 if ( $securityStatus === AuthManager::SEC_REAUTH ) {
171 $queryParams = array_diff_key( $request->getQueryValues(), [ 'title' => true ] );
172
173 if ( $request->wasPosted() ) {
174 // unique ID in case the same special page is open in multiple browser tabs
175 $uniqueId = MWCryptRand::generateHex( 6 );
176 $key .= ':' . $uniqueId;
177
178 $queryParams = [ 'authUniqueId' => $uniqueId ] + $queryParams;
179 $authData = array_diff_key( $request->getValues(),
180 $this->getPreservedParams( false ), [ 'title' => 1 ] );
181 $authManager->setAuthenticationSessionData( $key, $authData );
182 }
183
184 $title = SpecialPage::getTitleFor( 'Userlogin' );
185 $url = $title->getFullURL( [
186 'returnto' => $this->getFullTitle()->getPrefixedDBkey(),
187 'returntoquery' => wfArrayToCgi( $queryParams ),
188 'force' => $securityLevel,
189 ], false, PROTO_HTTPS );
190
191 $this->getOutput()->redirect( $url );
192 return false;
193 } elseif ( $securityStatus !== AuthManager::SEC_OK ) {
194 throw new ErrorPageError( 'cannotauth-not-allowed-title', 'cannotauth-not-allowed' );
195 }
196 }
197
198 $uniqueId = $request->getVal( 'authUniqueId' );
199 if ( $uniqueId ) {
200 $key .= ':' . $uniqueId;
201 $authData = $authManager->getAuthenticationSessionData( $key );
202 if ( $authData ) {
204 $this->setRequest( $authData, true );
205 }
206 }
207
208 return true;
209 }
210
218 abstract protected function getDefaultAction( $subPage );
219
226 protected function messageKey( $defaultKey ) {
227 return array_key_exists( $defaultKey, static::$messages )
228 ? static::$messages[$defaultKey] : $defaultKey;
229 }
230
236 protected function getRequestBlacklist() {
237 return [];
238 }
239
250 protected function loadAuth( $subPage, $authAction = null, $reset = false ) {
251 // Do not load if already loaded, to cut down on the number of getAuthenticationRequests
252 // calls. This is important for requests which have hidden information so any
253 // getAuthenticationRequests call would mean putting data into some cache.
254 if (
255 !$reset && $this->subPage === $subPage && $this->authAction
256 && ( !$authAction || $authAction === $this->authAction )
257 ) {
258 return;
259 }
260
261 $request = $this->getRequest();
262 $this->subPage = $subPage;
263 $this->authAction = $authAction ?: $request->getText( 'authAction' );
264 if ( !in_array( $this->authAction, static::$allowedActions, true ) ) {
265 $this->authAction = $this->getDefaultAction( $subPage );
266 if ( $request->wasPosted() ) {
267 $continueAction = $this->getContinueAction( $this->authAction );
268 if ( in_array( $continueAction, static::$allowedActions, true ) ) {
269 $this->authAction = $continueAction;
270 }
271 }
272 }
273
274 $allReqs = $this->getAuthManager()->getAuthenticationRequests(
275 $this->authAction, $this->getUser() );
276 $this->authRequests = array_filter( $allReqs, function ( $req ) {
277 return !in_array( get_class( $req ), $this->getRequestBlacklist(), true );
278 } );
279 }
280
285 protected function isContinued() {
286 return in_array( $this->authAction, [
287 AuthManager::ACTION_LOGIN_CONTINUE,
288 AuthManager::ACTION_CREATE_CONTINUE,
289 AuthManager::ACTION_LINK_CONTINUE,
290 ], true );
291 }
292
298 protected function getContinueAction( $action ) {
299 switch ( $action ) {
300 case AuthManager::ACTION_LOGIN:
301 $action = AuthManager::ACTION_LOGIN_CONTINUE;
302 break;
303 case AuthManager::ACTION_CREATE:
304 $action = AuthManager::ACTION_CREATE_CONTINUE;
305 break;
306 case AuthManager::ACTION_LINK:
307 $action = AuthManager::ACTION_LINK_CONTINUE;
308 break;
309 }
310 return $action;
311 }
312
321 protected function isActionAllowed( $action ) {
322 $authManager = $this->getAuthManager();
323 if ( !in_array( $action, static::$allowedActions, true ) ) {
324 throw new InvalidArgumentException( 'invalid action: ' . $action );
325 }
326
327 // calling getAuthenticationRequests can be expensive, avoid if possible
328 $requests = ( $action === $this->authAction ) ? $this->authRequests
330 if ( !$requests ) {
331 // no provider supports this action in the current state
332 return false;
333 }
334
335 switch ( $action ) {
336 case AuthManager::ACTION_LOGIN:
337 case AuthManager::ACTION_LOGIN_CONTINUE:
339 case AuthManager::ACTION_CREATE:
340 case AuthManager::ACTION_CREATE_CONTINUE:
342 case AuthManager::ACTION_LINK:
343 case AuthManager::ACTION_LINK_CONTINUE:
345 case AuthManager::ACTION_CHANGE:
346 case AuthManager::ACTION_REMOVE:
347 case AuthManager::ACTION_UNLINK:
348 return true;
349 default:
350 // should never reach here but makes static code analyzers happy
351 throw new InvalidArgumentException( 'invalid action: ' . $action );
352 }
353 }
354
361 protected function performAuthenticationStep( $action, array $requests ) {
362 if ( !in_array( $action, static::$allowedActions, true ) ) {
363 throw new InvalidArgumentException( 'invalid action: ' . $action );
364 }
365
366 $authManager = $this->getAuthManager();
367 $returnToUrl = $this->getPageTitle( 'return' )
368 ->getFullURL( $this->getPreservedParams( true ), false, PROTO_HTTPS );
369
370 switch ( $action ) {
371 case AuthManager::ACTION_LOGIN:
372 return $authManager->beginAuthentication( $requests, $returnToUrl );
373 case AuthManager::ACTION_LOGIN_CONTINUE:
374 return $authManager->continueAuthentication( $requests );
375 case AuthManager::ACTION_CREATE:
376 return $authManager->beginAccountCreation( $this->getAuthority(), $requests,
377 $returnToUrl );
378 case AuthManager::ACTION_CREATE_CONTINUE:
379 return $authManager->continueAccountCreation( $requests );
380 case AuthManager::ACTION_LINK:
381 return $authManager->beginAccountLink( $this->getUser(), $requests, $returnToUrl );
382 case AuthManager::ACTION_LINK_CONTINUE:
383 return $authManager->continueAccountLink( $requests );
384 case AuthManager::ACTION_CHANGE:
385 case AuthManager::ACTION_REMOVE:
386 case AuthManager::ACTION_UNLINK:
387 if ( count( $requests ) > 1 ) {
388 throw new InvalidArgumentException( 'only one auth request can be changed at a time' );
389 } elseif ( !$requests ) {
390 throw new InvalidArgumentException( 'no auth request' );
391 }
392 $req = reset( $requests );
394 $this->getHookRunner()->onChangeAuthenticationDataAudit( $req, $status );
395 if ( !$status->isGood() ) {
396 return AuthenticationResponse::newFail( $status->getMessage() );
397 }
399 return AuthenticationResponse::newPass();
400 default:
401 // should never reach here but makes static code analyzers happy
402 throw new InvalidArgumentException( 'invalid action: ' . $action );
403 }
404 }
405
416 protected function trySubmit() {
417 $status = false;
418
419 $form = $this->getAuthForm( $this->authRequests, $this->authAction );
420 $form->setSubmitCallback( [ $this, 'handleFormSubmit' ] );
421
422 if ( $this->getRequest()->wasPosted() ) {
423 // handle tokens manually; $form->tryAuthorizedSubmit only works for logged-in users
424 $requestTokenValue = $this->getRequest()->getVal( $this->getTokenName() );
425 $sessionToken = $this->getToken();
426 if ( $sessionToken->wasNew() ) {
427 return Status::newFatal( $this->messageKey( 'authform-newtoken' ) );
428 } elseif ( !$requestTokenValue ) {
429 return Status::newFatal( $this->messageKey( 'authform-notoken' ) );
430 } elseif ( !$sessionToken->match( $requestTokenValue ) ) {
431 return Status::newFatal( $this->messageKey( 'authform-wrongtoken' ) );
432 }
433
434 $form->prepareForm();
435 $status = $form->trySubmit();
436
437 // HTMLForm submit return values are a mess; let's ensure it is false or a Status
438 // FIXME this probably should be in HTMLForm
439 if ( $status === true ) {
440 // not supposed to happen since our submit handler should always return a Status
441 throw new UnexpectedValueException( 'HTMLForm::trySubmit() returned true' );
442 } elseif ( $status === false ) {
443 // form was not submitted; nothing to do
444 } elseif ( $status instanceof Status ) {
445 // already handled by the form; nothing to do
446 } elseif ( $status instanceof StatusValue ) {
447 // in theory not an allowed return type but nothing stops the submit handler from
448 // accidentally returning it so best check and fix
449 $status = Status::wrap( $status );
450 } elseif ( is_string( $status ) ) {
451 $status = Status::newFatal( new RawMessage( '$1', [ $status ] ) );
452 } elseif ( is_array( $status ) ) {
453 if ( is_string( reset( $status ) ) ) {
454 $status = Status::newFatal( ...$status );
455 } elseif ( is_array( reset( $status ) ) ) {
456 $ret = Status::newGood();
457 foreach ( $status as $message ) {
458 $ret->fatal( ...$message );
459 }
460 $status = $ret;
461 } else {
462 throw new UnexpectedValueException( 'invalid HTMLForm::trySubmit() return value: '
463 . 'first element of array is ' . gettype( reset( $status ) ) );
464 }
465 } else {
466 // not supposed to happen but HTMLForm does not actually verify the return type
467 // from the submit callback; better safe then sorry
468 throw new UnexpectedValueException( 'invalid HTMLForm::trySubmit() return type: '
469 . gettype( $status ) );
470 }
471
472 if ( ( !$status || !$status->isOK() ) && $this->isReturn ) {
473 // This is awkward. There was a form validation error, which means the data was not
474 // passed to AuthManager. Normally we would display the form with an error message,
475 // but for the data we received via the redirect flow that would not be helpful at all.
476 // Let's just submit the data to AuthManager directly instead.
477 LoggerFactory::getInstance( 'authentication' )
478 ->warning( 'Validation error on return', [ 'data' => $form->mFieldData,
479 'status' => $status->getWikiText( false, false, 'en' ) ] );
480 $status = $this->handleFormSubmit( $form->mFieldData );
481 }
482 }
483
484 $changeActions = [
485 AuthManager::ACTION_CHANGE, AuthManager::ACTION_REMOVE, AuthManager::ACTION_UNLINK
486 ];
487 if ( in_array( $this->authAction, $changeActions, true ) && $status && !$status->isOK() ) {
488 $this->getHookRunner()->onChangeAuthenticationDataAudit( reset( $this->authRequests ), $status );
489 }
490
491 return $status;
492 }
493
500 public function handleFormSubmit( $data ) {
501 $requests = AuthenticationRequest::loadRequestsFromSubmission( $this->authRequests, $data );
502 $response = $this->performAuthenticationStep( $this->authAction, $requests );
503
504 // we can't handle FAIL or similar as failure here since it might require changing the form
505 return Status::newGood( $response );
506 }
507
516 protected function getPreservedParams( $withToken = false ) {
517 $params = [];
518 if ( $this->authAction !== $this->getDefaultAction( $this->subPage ) ) {
519 $params['authAction'] = $this->getContinueAction( $this->authAction );
520 }
521 if ( $withToken ) {
522 $params[$this->getTokenName()] = $this->getToken()->toString();
523 }
524 return $params;
525 }
526
534 protected function getAuthFormDescriptor( $requests, $action ) {
535 $fieldInfo = AuthenticationRequest::mergeFieldInfo( $requests );
536 $formDescriptor = $this->fieldInfoToFormDescriptor( $requests, $fieldInfo, $action );
537
538 $this->addTabIndex( $formDescriptor );
539
540 return $formDescriptor;
541 }
542
549 protected function getAuthForm( array $requests, $action ) {
550 $formDescriptor = $this->getAuthFormDescriptor( $requests, $action );
551 $context = $this->getContext();
552 if ( $context->getRequest() !== $this->getRequest() ) {
553 // We have overridden the request, need to make sure the form uses that too.
554 $context = new DerivativeContext( $this->getContext() );
555 $context->setRequest( $this->getRequest() );
556 }
557 $form = HTMLForm::factory( 'ooui', $formDescriptor, $context );
558 $form->setAction( $this->getFullTitle()->getFullURL( $this->getPreservedParams() ) );
559 $form->addHiddenField( $this->getTokenName(), $this->getToken()->toString() );
560 $form->addHiddenField( 'authAction', $this->authAction );
561 $form->suppressDefaultSubmit( !$this->needsSubmitButton( $requests ) );
562
563 return $form;
564 }
565
570 protected function displayForm( $status ) {
571 if ( $status instanceof StatusValue ) {
572 $status = Status::wrap( $status );
573 }
574 $form = $this->getAuthForm( $this->authRequests, $this->authAction );
575 $form->prepareForm()->displayForm( $status );
576 }
577
589 protected function needsSubmitButton( array $requests ) {
590 $customSubmitButtonPresent = false;
591
592 // Secondary and preauth providers always need their data; they will not care what button
593 // is used, so they can be ignored. So can OPTIONAL buttons createdby primary providers;
594 // that's the point in being optional. Se we need to check whether all primary providers
595 // have their own buttons and whether there is at least one button present.
596 foreach ( $requests as $req ) {
597 if ( $req->required === AuthenticationRequest::PRIMARY_REQUIRED ) {
598 if ( $this->hasOwnSubmitButton( $req ) ) {
599 $customSubmitButtonPresent = true;
600 } else {
601 return true;
602 }
603 }
604 }
605 return !$customSubmitButtonPresent;
606 }
607
613 protected function hasOwnSubmitButton( AuthenticationRequest $req ) {
614 foreach ( $req->getFieldInfo() as $field => $info ) {
615 if ( $info['type'] === 'button' ) {
616 return true;
617 }
618 }
619 return false;
620 }
621
627 protected function addTabIndex( &$formDescriptor ) {
628 $i = 1;
629 foreach ( $formDescriptor as $field => &$definition ) {
630 $class = false;
631 if ( array_key_exists( 'class', $definition ) ) {
632 $class = $definition['class'];
633 } elseif ( array_key_exists( 'type', $definition ) ) {
634 $class = HTMLForm::$typeMappings[$definition['type']];
635 }
636 if ( $class !== HTMLInfoField::class ) {
637 $definition['tabindex'] = $i;
638 $i++;
639 }
640 }
641 }
642
648 protected function getToken() {
649 return $this->getRequest()->getSession()->getToken( 'AuthManagerSpecialPage:'
650 . $this->getName() );
651 }
652
658 protected function getTokenName() {
659 return 'wpAuthToken';
660 }
661
671 protected function fieldInfoToFormDescriptor( array $requests, array $fieldInfo, $action ) {
672 $formDescriptor = [];
673 foreach ( $fieldInfo as $fieldName => $singleFieldInfo ) {
674 $formDescriptor[$fieldName] = self::mapSingleFieldInfo( $singleFieldInfo, $fieldName );
675 }
676
677 $requestSnapshot = serialize( $requests );
678 $this->onAuthChangeFormFields( $requests, $fieldInfo, $formDescriptor, $action );
679 $this->getHookRunner()->onAuthChangeFormFields( $requests, $fieldInfo,
680 $formDescriptor, $action );
681 if ( $requestSnapshot !== serialize( $requests ) ) {
682 LoggerFactory::getInstance( 'authentication' )->warning(
683 'AuthChangeFormFields hook changed auth requests' );
684 }
685
686 // Process the special 'weight' property, which is a way for AuthChangeFormFields hook
687 // subscribers (who only see one field at a time) to influence ordering.
688 self::sortFormDescriptorFields( $formDescriptor );
689
690 return $formDescriptor;
691 }
692
700 protected static function mapSingleFieldInfo( $singleFieldInfo, $fieldName ) {
701 $type = self::mapFieldInfoTypeToFormDescriptorType( $singleFieldInfo['type'] );
702 $descriptor = [
703 'type' => $type,
704 // Do not prefix input name with 'wp'. This is important for the redirect flow.
705 'name' => $fieldName,
706 ];
707
708 if ( $type === 'submit' && isset( $singleFieldInfo['label'] ) ) {
709 $descriptor['default'] = $singleFieldInfo['label']->plain();
710 } elseif ( $type !== 'submit' ) {
711 $descriptor += array_filter( [
712 // help-message is omitted as it is usually not really useful for a web interface
713 'label-message' => self::getField( $singleFieldInfo, 'label' ),
714 ] );
715
716 if ( isset( $singleFieldInfo['options'] ) ) {
717 $descriptor['options'] = array_flip( array_map( static function ( $message ) {
719 return $message->parse();
720 }, $singleFieldInfo['options'] ) );
721 }
722
723 if ( isset( $singleFieldInfo['value'] ) ) {
724 $descriptor['default'] = $singleFieldInfo['value'];
725 }
726
727 if ( empty( $singleFieldInfo['optional'] ) ) {
728 $descriptor['required'] = true;
729 }
730 }
731
732 return $descriptor;
733 }
734
741 protected static function sortFormDescriptorFields( array &$formDescriptor ) {
742 $i = 0;
743 foreach ( $formDescriptor as &$field ) {
744 $field['__index'] = $i++;
745 }
746 uasort( $formDescriptor, function ( $first, $second ) {
747 return self::getField( $first, 'weight', 0 ) <=> self::getField( $second, 'weight', 0 )
748 ?: $first['__index'] <=> $second['__index'];
749 } );
750 foreach ( $formDescriptor as &$field ) {
751 unset( $field['__index'] );
752 }
753 }
754
762 protected static function getField( array $array, $fieldName, $default = null ) {
763 if ( array_key_exists( $fieldName, $array ) ) {
764 return $array[$fieldName];
765 } else {
766 return $default;
767 }
768 }
769
776 protected static function mapFieldInfoTypeToFormDescriptorType( $type ) {
777 $map = [
778 'string' => 'text',
779 'password' => 'password',
780 'select' => 'select',
781 'checkbox' => 'check',
782 'multiselect' => 'multiselect',
783 'button' => 'submit',
784 'hidden' => 'hidden',
785 'null' => 'info',
786 ];
787 if ( !array_key_exists( $type, $map ) ) {
788 throw new \LogicException( 'invalid field type: ' . $type );
789 }
790 return $map[$type];
791 }
792
805 protected static function mergeDefaultFormDescriptor(
806 array $fieldInfo, array $formDescriptor, array $defaultFormDescriptor
807 ) {
808 // keep the ordering from $defaultFormDescriptor where there is no explicit weight
809 foreach ( $defaultFormDescriptor as $fieldName => $defaultField ) {
810 // remove everything that is not in the fieldinfo, is not marked as a supplemental field
811 // to something in the fieldinfo, and is not an info field or a submit button
812 if (
813 !isset( $fieldInfo[$fieldName] )
814 && (
815 !isset( $defaultField['baseField'] )
816 || !isset( $fieldInfo[$defaultField['baseField']] )
817 )
818 && (
819 !isset( $defaultField['type'] )
820 || !in_array( $defaultField['type'], [ 'submit', 'info' ], true )
821 )
822 ) {
823 $defaultFormDescriptor[$fieldName] = null;
824 continue;
825 }
826
827 // default message labels should always take priority
828 $requestField = $formDescriptor[$fieldName] ?? [];
829 if (
830 isset( $defaultField['label'] )
831 || isset( $defaultField['label-message'] )
832 || isset( $defaultField['label-raw'] )
833 ) {
834 unset( $requestField['label'], $requestField['label-message'], $defaultField['label-raw'] );
835 }
836
837 $defaultFormDescriptor[$fieldName] += $requestField;
838 }
839
840 return array_filter( $defaultFormDescriptor + $formDescriptor );
841 }
842}
serialize()
const PROTO_HTTPS
Definition Defines.php:193
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.
handleReauthBeforeExecute( $subPage)
Handle redirection when the user needs to (re)authenticate.
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.
static mergeDefaultFormDescriptor(array $fieldInfo, array $formDescriptor, array $defaultFormDescriptor)
Apply defaults to a form descriptor, without creating non-existend fields.
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.
This serves as the entry point to the authentication system.
canLinkAccounts()
Determine whether accounts can be linked.
setAuthenticationSessionData( $key, $data)
Store authentication in the current session.
securitySensitiveOperationStatus( $operation)
Whether security-sensitive operations should proceed.
beginAccountLink(User $user, array $reqs, $returnToUrl)
Start an account linking flow.
continueAccountLink(array $reqs)
Continue an account linking flow.
allowsAuthenticationDataChange(AuthenticationRequest $req, $checkData=true)
Validate a change of authentication data (e.g.
beginAuthentication(array $reqs, $returnToUrl)
Start an authentication flow.
getAuthenticationSessionData( $key, $default=null)
Fetch authentication data from the current session.
beginAccountCreation(Authority $creator, array $reqs, $returnToUrl)
Start an account creation flow.
canCreateAccounts()
Determine whether accounts can be created.
changeAuthenticationData(AuthenticationRequest $req, $isAddition=false)
Change authentication data (e.g.
removeAuthenticationSessionData( $key)
Remove authentication data.
getAuthenticationRequests( $action, UserIdentity $user=null)
Return the applicable list of AuthenticationRequests.
continueAuthentication(array $reqs)
Continue an authentication flow.
canAuthenticateNow()
Indicate whether user authentication is possible.
continueAccountCreation(array $reqs)
Continue an account creation flow.
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,...
AuthManager null $authManager
getContext()
Gets the context this SpecialPage is executed in.
getAuthority()
Shortcut to get the Authority executing this instance.
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:44
The WebRequest class encapsulates getting at data passed in the URL or via a POSTed form stripping il...