Page MenuHomeWickedGov Phorge

CentralAuthSessionManager.php
No OneTemporary

Size
7 KB
Referenced Files
None
Subscribers
None

CentralAuthSessionManager.php

<?php
/**
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
*/
namespace MediaWiki\Extension\CentralAuth;
use MediaWiki\Config\ServiceOptions;
use MediaWiki\MediaWikiServices;
use MediaWiki\Session\Session;
use MediaWiki\Session\SessionManager;
use MWCryptRand;
use Wikimedia\ObjectCache\BagOStuff;
use Wikimedia\ObjectCache\CachedBagOStuff;
use Wikimedia\Stats\IBufferingStatsdDataFactory;
use Wikimedia\Stats\StatsFactory;
class CentralAuthSessionManager {
/**
* @internal Only public for service wiring use
*/
public const CONSTRUCTOR_OPTIONS = [
'CentralAuthSessionCacheType',
'SessionCacheType',
];
/** @var BagOStuff|null Session cache */
private $sessionStore = null;
private ServiceOptions $options;
private IBufferingStatsdDataFactory $statsdDataFactory;
private StatsFactory $statsFactory;
public function __construct(
ServiceOptions $options,
IBufferingStatsdDataFactory $statsdDataFactory,
StatsFactory $statsFactory
) {
$options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
$this->options = $options;
$this->statsdDataFactory = $statsdDataFactory;
$this->statsFactory = $statsFactory;
}
/**
* @return string db name, for session key creation
* Note that if there is more than one CentralAuth database
* in use for the same session key store, the database names
* MUST be unique.
*/
private function getCentralAuthDBForSessionKey() {
return MediaWikiServices::getInstance()
->getDBLoadBalancerFactory()->getPrimaryDatabase( 'virtual-centralauth' )->getDomainID();
}
/**
* @param string $keygroup
* @param string ...$components
* @return string The global session key (with proper escaping)
*/
public function makeSessionKey( string $keygroup, ...$components ): string {
return $this->getSessionStore()->makeGlobalKey(
$keygroup, $this->getCentralAuthDBForSessionKey(), ...$components
);
}
/**
* Get a cache for storage of central sessions
* @return BagOStuff
*/
public function getSessionStore(): BagOStuff {
if ( !$this->sessionStore ) {
$sessionCacheType = $this->options->get( 'CentralAuthSessionCacheType' )
?? $this->options->get( 'SessionCacheType' );
$cache = MediaWikiServices::getInstance()->getObjectCacheFactory()->getInstance( $sessionCacheType );
$this->sessionStore = $cache instanceof CachedBagOStuff
? $cache : new CachedBagOStuff( $cache );
}
return $this->sessionStore;
}
/**
* Get the central session data associated with the given local session.
*
* When the session is not centrally logged in, an empty array is returned.
*
* @param Session|null $session The local session. If omitted, uses the global session.
* @return array
*/
public function getCentralSession( $session = null ) {
if ( !$session ) {
$session = SessionManager::getGlobalSession();
}
$id = $session->get( 'CentralAuth::centralSessionId' );
if ( $id !== null ) {
return $this->getCentralSessionById( $id );
} else {
return [];
}
}
/**
* Get the central session data.
*
* The shape of the data is not enforced by this class, but in practice it will contain these keys:
* - sessionId: string, the central session ID
* - expiry: int, timestamp when the session expires
* - user: string, the username
* - token: string, the central token (gu_auth_token)
* - remember: bool, the "keep me logged in" flag.
*
* When $id is not found in the central session store, an empty array is returned.
*
* During central login, the session is a provisional "stub session" (which will be seen
* by the session provider as an anonymous session) with the following keys:
* - pending_name: string, the username
* - pending_guid: string, the central user ID
* - sessionId, expiry: as above
* @param string $id
* @return array
*/
public function getCentralSessionById( $id ) {
$key = $this->makeSessionKey( 'session', $id );
$stime = microtime( true );
$data = $this->getSessionStore()->get( $key ) ?: [];
$real = microtime( true ) - $stime;
// Stay backward compatible with the dashboard feeding on
// this data. NOTE: $real is in second with microsecond-level
// precision. This is reconciled on the grafana dashboard.
$this->statsdDataFactory->timing( 'centralauth.session.read', $real );
$this->statsFactory->withComponent( 'CentralAuth' )
->getTiming( 'session_read_seconds' )
->observe( $real * 1000 );
return $data;
}
/**
* Set data in the central session. Uses the central session ID stored in the local session
* to find the data; if not present (or $reset is used), creates a new object under a new ID
* and stores the ID in the local session.
*
* When not overridden in $data, the following keys in the central session data are preserved:
* expiry, user, token. (Expiry will be extended if the session is beyond half its lifetime.)
* sessionId will be updated as needed. Other data (ie. the remember flag or the stub session
* fields) will be lost if not explicitly included in $data. This is true regardless of whether
* $reset is used.
*
* @param array $data New session data.
* @param bool|string $reset Reset the session ID. If a string, this is the new ID.
* @param Session|null $session Local session. When omitted, uses the global session.
* @return string|null Session ID
*/
public function setCentralSession( array $data, $reset = false, $session = null ) {
$keepKeys = [ 'user' => true, 'token' => true, 'expiry' => true ];
$session ??= SessionManager::getGlobalSession();
$id = $session->get( 'CentralAuth::centralSessionId' );
if ( $reset || $id === null ) {
$id = is_string( $reset ) ? $reset : MWCryptRand::generateHex( 32 );
}
$data['sessionId'] = $id;
$sessionStore = $this->getSessionStore();
$key = $this->makeSessionKey( 'session', $id );
// Copy certain keys from the existing session, if any (T124821)
$existing = $sessionStore->get( $key );
if ( is_array( $existing ) ) {
$data += array_intersect_key( $existing, $keepKeys );
}
$isDirty = ( $data !== $existing );
if ( $isDirty || !isset( $data['expiry'] ) || $data['expiry'] < time() + 32100 ) {
$data['expiry'] = time() + $sessionStore::TTL_DAY;
$stime = microtime( true );
$sessionStore->set(
$key,
$data,
$sessionStore::TTL_DAY
);
$real = microtime( true ) - $stime;
// Stay backward compatible with the dashboard feeding on
// this data. NOTE: $real is in second with microsecond-level
// precision. This is reconciled on the grafana dashboard.
$this->statsdDataFactory->timing( 'centralauth.session.write', $real );
$this->statsFactory->withComponent( 'CentralAuth' )
->getTiming( 'session_write_seconds' )
->observe( $real * 1000 );
}
if ( $session ) {
$session->set( 'CentralAuth::centralSessionId', $id );
}
return $id;
}
}

File Metadata

Mime Type
text/x-php
Expires
Sat, May 16, 13:24 (1 d, 18 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
b4/86/8210309c66f8888d4b82a2ec275e
Default Alt Text
CentralAuthSessionManager.php (7 KB)

Event Timeline