wiki.techinc.nl/includes/auth/ConfirmLinkSecondaryAuthenticationProvider.php
Florian 3706dcb5c7 Show warnings in HTMLForm and warnings as warnings on Login/Signup form
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
2016-09-17 16:33:39 +02:00

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'
);
}
}