This commit changes the way how HTMLForm handles a Status object when executed from a request. It now handles, beside the errors, also the warnings of a Status object and prints them out, wrapped in a warning box. The LoginSignupPage uses this feature to show informative warnings actually as warnings and not as more disturbing error messages. Error messages should be reserved for errors and only for erros. An AuthenticationProvider, which returns an UI AuthenticationResponse can choose, if the given message is an error or a warning message. This commit also addds a new function to Status, which allows a developer to split the object into two new Status objects, where one only contains the errors and the other only the warnings of the origin Status object (splitByErrorType). StatusValue also has a new function, splitByErrorType(), to support this. Bug: T139179 Change-Id: I9a27911613e62b5c4cb86bea40696cb37c4f49c2
158 lines
4.8 KiB
PHP
158 lines
4.8 KiB
PHP
<?php
|
|
|
|
namespace MediaWiki\Auth;
|
|
|
|
use User;
|
|
|
|
/**
|
|
* Links third-party authentication to the user's account
|
|
*
|
|
* If the user logged into linking provider accounts that aren't linked to a
|
|
* local user, this provider will prompt the user to link them after a
|
|
* successful login or account creation.
|
|
*
|
|
* To avoid confusing behavior, this provider should be later in the
|
|
* configuration list than any provider that can abort the authentication
|
|
* process, so that it is only invoked for successful authentication.
|
|
*/
|
|
class ConfirmLinkSecondaryAuthenticationProvider extends AbstractSecondaryAuthenticationProvider {
|
|
|
|
public function getAuthenticationRequests( $action, array $options ) {
|
|
return [];
|
|
}
|
|
|
|
public function beginSecondaryAuthentication( $user, array $reqs ) {
|
|
return $this->beginLinkAttempt( $user, 'AuthManager::authnState' );
|
|
}
|
|
|
|
public function continueSecondaryAuthentication( $user, array $reqs ) {
|
|
return $this->continueLinkAttempt( $user, 'AuthManager::authnState', $reqs );
|
|
}
|
|
|
|
public function beginSecondaryAccountCreation( $user, $creator, array $reqs ) {
|
|
return $this->beginLinkAttempt( $user, 'AuthManager::accountCreationState' );
|
|
}
|
|
|
|
public function continueSecondaryAccountCreation( $user, $creator, array $reqs ) {
|
|
return $this->continueLinkAttempt( $user, 'AuthManager::accountCreationState', $reqs );
|
|
}
|
|
|
|
/**
|
|
* Begin the link attempt
|
|
* @param User $user
|
|
* @param string $key Session key to look in
|
|
* @return AuthenticationResponse
|
|
*/
|
|
protected function beginLinkAttempt( $user, $key ) {
|
|
$session = $this->manager->getRequest()->getSession();
|
|
$state = $session->getSecret( $key );
|
|
if ( !is_array( $state ) ) {
|
|
return AuthenticationResponse::newAbstain();
|
|
}
|
|
|
|
$maybeLink = array_filter( $state['maybeLink'], function ( $req ) use ( $user ) {
|
|
if ( !$req->action ) {
|
|
$req->action = AuthManager::ACTION_CHANGE;
|
|
}
|
|
$req->username = $user->getName();
|
|
return $this->manager->allowsAuthenticationDataChange( $req )->isGood();
|
|
} );
|
|
if ( !$maybeLink ) {
|
|
return AuthenticationResponse::newAbstain();
|
|
}
|
|
|
|
$req = new ConfirmLinkAuthenticationRequest( $maybeLink );
|
|
return AuthenticationResponse::newUI(
|
|
[ $req ],
|
|
wfMessage( 'authprovider-confirmlink-message' ),
|
|
'warning'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Continue the link attempt
|
|
* @param User $user
|
|
* @param string $key Session key to look in
|
|
* @param AuthenticationRequest[] $reqs
|
|
* @return AuthenticationResponse
|
|
*/
|
|
protected function continueLinkAttempt( $user, $key, array $reqs ) {
|
|
$req = ButtonAuthenticationRequest::getRequestByName( $reqs, 'linkOk' );
|
|
if ( $req ) {
|
|
return AuthenticationResponse::newPass();
|
|
}
|
|
|
|
$req = AuthenticationRequest::getRequestByClass( $reqs, ConfirmLinkAuthenticationRequest::class );
|
|
if ( !$req ) {
|
|
// WTF? Retry.
|
|
return $this->beginLinkAttempt( $user, $key );
|
|
}
|
|
|
|
$session = $this->manager->getRequest()->getSession();
|
|
$state = $session->getSecret( $key );
|
|
if ( !is_array( $state ) ) {
|
|
return AuthenticationResponse::newAbstain();
|
|
}
|
|
|
|
$maybeLink = [];
|
|
foreach ( $state['maybeLink'] as $linkReq ) {
|
|
$maybeLink[$linkReq->getUniqueId()] = $linkReq;
|
|
}
|
|
if ( !$maybeLink ) {
|
|
return AuthenticationResponse::newAbstain();
|
|
}
|
|
|
|
$state['maybeLink'] = [];
|
|
$session->setSecret( $key, $state );
|
|
|
|
$statuses = [];
|
|
$anyFailed = false;
|
|
foreach ( $req->confirmedLinkIDs as $id ) {
|
|
if ( isset( $maybeLink[$id] ) ) {
|
|
$req = $maybeLink[$id];
|
|
$req->username = $user->getName();
|
|
if ( !$req->action ) {
|
|
// Make sure the action is set, but don't override it if
|
|
// the provider filled it in.
|
|
$req->action = AuthManager::ACTION_CHANGE;
|
|
}
|
|
$status = $this->manager->allowsAuthenticationDataChange( $req );
|
|
$statuses[] = [ $req, $status ];
|
|
if ( $status->isGood() ) {
|
|
$this->manager->changeAuthenticationData( $req );
|
|
} else {
|
|
$anyFailed = true;
|
|
}
|
|
}
|
|
}
|
|
if ( !$anyFailed ) {
|
|
return AuthenticationResponse::newPass();
|
|
}
|
|
|
|
$combinedStatus = \Status::newGood();
|
|
foreach ( $statuses as $data ) {
|
|
list( $req, $status ) = $data;
|
|
$descriptionInfo = $req->describeCredentials();
|
|
$description = wfMessage(
|
|
'authprovider-confirmlink-option',
|
|
$descriptionInfo['provider']->text(), $descriptionInfo['account']->text()
|
|
)->text();
|
|
if ( $status->isGood() ) {
|
|
$combinedStatus->error( wfMessage( 'authprovider-confirmlink-success-line', $description ) );
|
|
} else {
|
|
$combinedStatus->error( wfMessage(
|
|
'authprovider-confirmlink-failed-line', $description, $status->getMessage()->text()
|
|
) );
|
|
}
|
|
}
|
|
return AuthenticationResponse::newUI(
|
|
[
|
|
new ButtonAuthenticationRequest(
|
|
'linkOk', wfMessage( 'ok' ), wfMessage( 'authprovider-confirmlink-ok-help' )
|
|
)
|
|
],
|
|
$combinedStatus->getMessage( 'authprovider-confirmlink-failed' ),
|
|
'error'
|
|
);
|
|
}
|
|
}
|