diff options
Diffstat (limited to 'www/wiki/includes/auth/ConfirmLinkSecondaryAuthenticationProvider.php')
-rw-r--r-- | www/wiki/includes/auth/ConfirmLinkSecondaryAuthenticationProvider.php | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/www/wiki/includes/auth/ConfirmLinkSecondaryAuthenticationProvider.php b/www/wiki/includes/auth/ConfirmLinkSecondaryAuthenticationProvider.php new file mode 100644 index 00000000..7f121cde --- /dev/null +++ b/www/wiki/includes/auth/ConfirmLinkSecondaryAuthenticationProvider.php @@ -0,0 +1,158 @@ +<?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' + ); + } +} |