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,
55 array $requests, array $fieldInfo, array &$formDescriptor, $action
65 return $this->savedRequest ?: $this->
getContext()->getRequest();
76 protected function setRequest( array $data, $wasPosted =
null ) {
78 if ( $wasPosted ===
null ) {
79 $wasPosted = $request->wasPosted();
81 $this->savedRequest =
new DerivativeRequest( $request, $data + $request->getQueryValues(),
109 $authManager = AuthManager::singleton();
110 $key =
'AuthManagerSpecialPage:return:' . $this->
getName();
117 $authData = array_diff_key( $this->
getRequest()->getValues(),
118 $preservedParams, [
'title' => 1 ] );
119 $authManager->setAuthenticationSessionData( $key, $authData );
126 $authData = $authManager->getAuthenticationSessionData( $key );
128 $authManager->removeAuthenticationSessionData( $key );
129 $this->isReturn =
true;
147 $authManager = AuthManager::singleton();
149 $key =
'AuthManagerSpecialPage:reauth:' . $this->
getName();
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 ] );
158 if ( $request->wasPosted() ) {
161 $key .=
':' . $uniqueId;
163 $queryParams = [
'authUniqueId' => $uniqueId ] + $queryParams;
164 $authData = array_diff_key( $request->getValues(),
166 $authManager->setAuthenticationSessionData( $key, $authData );
170 $url =
$title->getFullURL( [
171 'returnto' => $this->
getFullTitle()->getPrefixedDBkey(),
173 'force' => $securityLevel,
178 } elseif ( $securityStatus !== AuthManager::SEC_OK ) {
179 throw new ErrorPageError(
'cannotauth-not-allowed-title',
'cannotauth-not-allowed' );
183 $uniqueId = $request->getVal(
'authUniqueId' );
185 $key .=
':' . $uniqueId;
186 $authData = $authManager->getAuthenticationSessionData( $key );
188 $authManager->removeAuthenticationSessionData( $key );
211 return array_key_exists( $defaultKey, static::$messages )
212 ? static::$messages[$defaultKey] : $defaultKey;
237 !$reset && $this->subPage ===
$subPage && $this->authAction
245 $this->authAction =
$authAction ?: $request->getText(
'authAction' );
246 if ( !in_array( $this->authAction, static::$allowedActions,
true ) ) {
248 if ( $request->wasPosted() ) {
250 if ( in_array( $continueAction, static::$allowedActions,
true ) ) {
251 $this->authAction = $continueAction;
256 $allReqs = AuthManager::singleton()->getAuthenticationRequests(
257 $this->authAction, $this->
getUser() );
258 $this->authRequests = array_filter( $allReqs,
function ( $req ) {
268 return in_array( $this->authAction, [
269 AuthManager::ACTION_LOGIN_CONTINUE,
270 AuthManager::ACTION_CREATE_CONTINUE,
271 AuthManager::ACTION_LINK_CONTINUE,
282 case AuthManager::ACTION_LOGIN:
283 $action = AuthManager::ACTION_LOGIN_CONTINUE;
285 case AuthManager::ACTION_CREATE:
286 $action = AuthManager::ACTION_CREATE_CONTINUE;
288 case AuthManager::ACTION_LINK:
289 $action = AuthManager::ACTION_LINK_CONTINUE;
304 $authManager = AuthManager::singleton();
305 if ( !in_array( $action, static::$allowedActions,
true ) ) {
306 throw new InvalidArgumentException(
'invalid action: ' . $action );
311 : $authManager->getAuthenticationRequests( $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:
333 throw new InvalidArgumentException(
'invalid action: ' . $action );
344 if ( !in_array( $action, static::$allowedActions,
true ) ) {
345 throw new InvalidArgumentException(
'invalid action: ' . $action );
348 $authManager = AuthManager::singleton();
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,
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' );
374 $req = reset( $requests );
375 $status = $authManager->allowsAuthenticationDataChange( $req );
378 return AuthenticationResponse::newFail(
$status->getMessage() );
380 $authManager->changeAuthenticationData( $req );
381 return AuthenticationResponse::newPass();
384 throw new InvalidArgumentException(
'invalid action: ' . $action );
401 $form = $this->
getAuthForm( $this->authRequests, $this->authAction );
402 $form->setSubmitCallback( [ $this,
'handleFormSubmit' ] );
408 if ( $sessionToken->wasNew() ) {
410 } elseif ( !$requestTokenValue ) {
412 } elseif ( !$sessionToken->match( $requestTokenValue ) ) {
416 $form->prepareForm();
423 throw new UnexpectedValueException(
'HTMLForm::trySubmit() returned true' );
424 } elseif (
$status ===
false ) {
432 } elseif ( is_string(
$status ) ) {
434 } elseif ( is_array(
$status ) ) {
435 if ( is_string( reset(
$status ) ) ) {
437 } elseif ( is_array( reset(
$status ) ) ) {
439 foreach (
$status as $message ) {
440 $ret->fatal( ...$message );
444 throw new UnexpectedValueException(
'invalid HTMLForm::trySubmit() return value: '
445 .
'first element of array is ' . gettype( reset(
$status ) ) );
450 throw new UnexpectedValueException(
'invalid HTMLForm::trySubmit() return type: '
459 LoggerFactory::getInstance(
'authentication' )
460 ->warning(
'Validation error on return', [
'data' => $form->mFieldData,
461 'status' =>
$status->getWikiText(
false,
false,
'en' ) ] );
467 AuthManager::ACTION_CHANGE, AuthManager::ACTION_REMOVE, AuthManager::ACTION_UNLINK
469 if ( in_array( $this->authAction, $changeActions,
true ) &&
$status && !
$status->isOK() ) {
470 Hooks::run(
'ChangeAuthenticationDataAudit', [ reset( $this->authRequests ),
$status ] );
483 $requests = AuthenticationRequest::loadRequestsFromSubmission( $this->authRequests, $data );
515 $fieldInfo = AuthenticationRequest::mergeFieldInfo( $requests );
520 return $formDescriptor;
539 $form->addHiddenField(
'authAction', $this->authAction );
553 $form = $this->
getAuthForm( $this->authRequests, $this->authAction );
554 $form->prepareForm()->displayForm(
$status );
568 $customSubmitButtonPresent =
false;
574 foreach ( $requests as $req ) {
575 if ( $req->required === AuthenticationRequest::PRIMARY_REQUIRED ) {
577 $customSubmitButtonPresent =
true;
583 return !$customSubmitButtonPresent;
593 if ( $info[
'type'] ===
'button' ) {
607 foreach ( $formDescriptor as $field => &$definition ) {
609 if ( array_key_exists(
'class', $definition ) ) {
610 $class = $definition[
'class'];
611 } elseif ( array_key_exists(
'type', $definition ) ) {
614 if ( $class !== HTMLInfoField::class ) {
615 $definition[
'tabindex'] = $i;
626 return $this->
getRequest()->getSession()->getToken(
'AuthManagerSpecialPage:'
635 return 'wpAuthToken';
648 $formDescriptor = [];
649 foreach ( $fieldInfo as $fieldName => $singleFieldInfo ) {
653 $requestSnapshot =
serialize( $requests );
655 \Hooks::run(
'AuthChangeFormFields', [ $requests, $fieldInfo, &$formDescriptor, $action ] );
656 if ( $requestSnapshot !==
serialize( $requests ) ) {
657 LoggerFactory::getInstance(
'authentication' )->warning(
658 'AuthChangeFormFields hook changed auth requests' );
665 return $formDescriptor;
680 'name' => $fieldName,
683 if (
$type ===
'submit' && isset( $singleFieldInfo[
'label'] ) ) {
684 $descriptor[
'default'] = $singleFieldInfo[
'label']->plain();
685 } elseif (
$type !==
'submit' ) {
686 $descriptor += array_filter( [
688 'label-message' => self::getField( $singleFieldInfo,
'label' ),
691 if ( isset( $singleFieldInfo[
'options'] ) ) {
692 $descriptor[
'options'] = array_flip( array_map(
function ( $message ) {
694 return $message->parse();
695 }, $singleFieldInfo[
'options'] ) );
698 if ( isset( $singleFieldInfo[
'value'] ) ) {
699 $descriptor[
'default'] = $singleFieldInfo[
'value'];
702 if ( empty( $singleFieldInfo[
'optional'] ) ) {
703 $descriptor[
'required'] =
true;
718 foreach ( $formDescriptor as &$field ) {
719 $field[
'__index'] = $i++;
721 uasort( $formDescriptor,
function ( $first, $second ) {
723 ?: $first[
'__index'] <=> $second[
'__index'];
725 foreach ( $formDescriptor as &$field ) {
726 unset( $field[
'__index'] );
737 protected static function getField( array $array, $fieldName, $default =
null ) {
738 if ( array_key_exists( $fieldName, $array ) ) {
739 return $array[$fieldName];
754 'password' =>
'password',
755 'select' =>
'select',
756 'checkbox' =>
'check',
757 'multiselect' =>
'multiselect',
758 'button' =>
'submit',
759 'hidden' =>
'hidden',
762 if ( !array_key_exists(
$type, $map ) ) {
763 throw new \LogicException(
'invalid field type: ' .
$type );