Page MenuHomeWickedGov Phorge

CentralAuthTokenSessionProvider.php
No OneTemporary

Size
6 KB
Referenced Files
None
Subscribers
None

CentralAuthTokenSessionProvider.php

<?php
use MediaWiki\Extension\CentralAuth\CentralAuthSessionManager;
use MediaWiki\Extension\CentralAuth\CentralAuthTokenManager;
use MediaWiki\Extension\CentralAuth\User\CentralAuthUser;
use MediaWiki\Request\WebRequest;
use MediaWiki\Session\SessionBackend;
use MediaWiki\Session\SessionInfo;
use MediaWiki\Session\SessionProvider;
use MediaWiki\Session\UserInfo;
use MediaWiki\User\User;
use MediaWiki\User\UserIdentityLookup;
use MediaWiki\User\UserNameUtils;
use Wikimedia\LightweightObjectStore\ExpirationAwareness;
/**
* Base class for token based CentralAuth SessionProviders for use with different kinds of APIs.
*
* This base class provides functionality for looking at a token from the request,
* and checking that it corresponds to an existing token generated by
* CentralAuthTokenProvider.
* If the token is present but invalid, a CentralAuthTokenSessionProvider returns a
* bogus SessionInfo to prevent other SessionProviders from establishing a session.
*/
abstract class CentralAuthTokenSessionProvider extends SessionProvider {
private UserIdentityLookup $userIdentityLookup;
private CentralAuthSessionManager $sessionManager;
protected CentralAuthTokenManager $tokenManager;
/**
* @param UserIdentityLookup $userIdentityLookup
* @param CentralAuthSessionManager $sessionManager
* @param CentralAuthTokenManager $tokenManager
*/
public function __construct(
UserIdentityLookup $userIdentityLookup,
CentralAuthSessionManager $sessionManager,
CentralAuthTokenManager $tokenManager
) {
parent::__construct();
$this->userIdentityLookup = $userIdentityLookup;
$this->sessionManager = $sessionManager;
$this->tokenManager = $tokenManager;
}
/**
* Returns a bogus session, which can be used to prevent other SessionProviders
* from attemption to establish a session.
*
* May be overridden by subclasses to somehow cause error handling ot be triggered later.
*
* Per default, it just returns null.
*
* @param string $code Error code
* @param string|array $error Error message key, or key+parameters
* @return SessionInfo|null
*/
protected function makeBogusSessionInfo( $code, $error ) {
// Then return an appropriate SessionInfo
$id = $this->hashToSessionId( 'bogus' );
return new SessionInfo( SessionInfo::MAX_PRIORITY, [
'provider' => $this,
'id' => $id,
'userInfo' => UserInfo::newAnonymous(),
'persisted' => false,
'metadata' => [
'error-code' => $code,
'error' => $error,
],
] );
}
/**
* @param WebRequest $request
* @return mixed `null` indicates that no token was provided, `false` indicates an invalid token,
* any other value is detokenized data
*/
abstract protected function getTokenDataFromRequest( WebRequest $request );
/** @inheritDoc */
public function provideSessionInfo( WebRequest $request ) {
$data = $this->getTokenDataFromRequest( $request );
if ( $data === null ) {
return null;
}
$this->logger->debug( __METHOD__ . ': Found a token!' );
if ( !is_array( $data ) ||
!isset( $data['userName'] ) ||
!isset( $data['token'] ) ||
!isset( $data['origin'] ) ||
!isset( $data['originSessionId'] )
) {
$this->logger->info( __METHOD__ . ': centralauthtoken is invalid' );
return $this->makeBogusSessionInfo( 'badtoken', 'apierror-centralauth-badtoken' );
}
$userName = $data['userName'];
$authToken = $data['token'];
// Clean up username
$userName = $this->userNameUtils->getCanonical( $userName );
if ( !$userName ) {
$this->logger->info( __METHOD__ . ': invalid username' );
return $this->makeBogusSessionInfo( 'badtoken', 'apierror-centralauth-badtoken' );
}
if ( !$this->userNameUtils->isUsable( $userName ) ) {
$this->logger->info( __METHOD__ . ': unusable username' );
return $this->makeBogusSessionInfo( 'badusername',
[ 'apierror-centralauth-badusername', wfEscapeWikiText( $userName ) ] );
}
// Try the central user
$centralUser = CentralAuthUser::getInstanceByName( $userName );
// Skip if they're being renamed
if ( $centralUser->renameInProgress() ) {
$this->logger->info( __METHOD__ . ': rename in progress' );
return $this->makeBogusSessionInfo(
'renameinprogress', 'apierror-centralauth-renameinprogress'
);
}
if ( !$centralUser->exists() ) {
$this->logger->info( __METHOD__ . ': global account doesn\'t exist' );
return $this->makeBogusSessionInfo( 'badtoken', 'apierror-centralauth-badtoken' );
}
if ( !$centralUser->isAttached() ) {
$userIdentity = $this->userIdentityLookup->getUserIdentityByName( $userName );
if ( $userIdentity && $userIdentity->isRegistered() ) {
$this->logger->info( __METHOD__ . ': not attached and local account exists' );
return $this->makeBogusSessionInfo( 'badtoken', 'apierror-centralauth-badtoken' );
}
}
$key = $this->sessionManager->makeSessionKey( 'api-token-blacklist', (string)$centralUser->getId() );
$sessionStore = $this->sessionManager->getSessionStore();
if ( $sessionStore->get( $key ) ) {
$this->logger->info( __METHOD__ . ': user is blacklisted' );
return $this->makeBogusSessionInfo( 'badtoken', 'apierror-centralauth-badtoken' );
}
if ( $centralUser->authenticateWithToken( $authToken ) != 'ok' ) {
$this->logger->info( __METHOD__ . ': token mismatch' );
return $this->makeBogusSessionInfo( 'badtoken', 'apierror-centralauth-badtoken' );
}
$this->logger->debug( __METHOD__ . ': logged in from session' );
$info = [
'userInfo' => UserInfo::newFromName( $userName, true ),
'provider' => $this,
'id' => $this->hashToSessionId( implode( "\n", $data ) ),
'persisted' => true,
'forceUse' => true,
];
return new SessionInfo( SessionInfo::MAX_PRIORITY, $info );
}
public function persistsSessionId() {
return false;
}
public function canChangeUser() {
return false;
}
/** @inheritDoc */
public function persistSession(
SessionBackend $session, WebRequest $request
) {
// Nothing to do
}
/** @inheritDoc */
public function unpersistSession( WebRequest $request ) {
// Nothing to do
}
/** @inheritDoc */
public function invalidateSessionsForUser( User $user ) {
$centralUser = CentralAuthUser::getPrimaryInstance( $user );
if ( $centralUser->exists() && ( $centralUser->isAttached() || !$user->isRegistered() ) ) {
$centralUser->resetAuthToken();
}
}
/** @inheritDoc */
public function preventSessionsForUser( $username ) {
$username = $this->userNameUtils->getCanonical( $username, UserNameUtils::RIGOR_VALID );
if ( !$username ) {
return;
}
$centralUser = CentralAuthUser::getInstanceByName( $username );
if ( !$centralUser->exists() ) {
return;
}
// Assume blacklisting for a day will be enough because we assume by
// then CentralAuth itself will have been instructed to more
// permanently block the user.
$sessionStore = $this->sessionManager->getSessionStore();
$key = $this->sessionManager->makeSessionKey( 'api-token-blacklist', (string)$centralUser->getId() );
$sessionStore->set( $key, true, ExpirationAwareness::TTL_DAY );
}
}

File Metadata

Mime Type
text/x-php
Expires
Sat, Oct 3, 12:28 (2 d, 23 h ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
79/62/df84f8657ccb44e1f527b898662f
Default Alt Text
CentralAuthTokenSessionProvider.php (6 KB)

Event Timeline