2016-02-01 20:44:03 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace MediaWiki\Session;
|
|
|
|
|
|
2018-01-10 13:32:56 +00:00
|
|
|
use Config;
|
Hooks::run() call site migration
Migrate all callers of Hooks::run() to use the new
HookContainer/HookRunner system.
General principles:
* Use DI if it is already used. We're not changing the way state is
managed in this patch.
* HookContainer is always injected, not HookRunner. HookContainer
is a service, it's a more generic interface, it is the only
thing that provides isRegistered() which is needed in some cases,
and a HookRunner can be efficiently constructed from it
(confirmed by benchmark). Because HookContainer is needed
for object construction, it is also needed by all factories.
* "Ask your friendly local base class". Big hierarchies like
SpecialPage and ApiBase have getHookContainer() and getHookRunner()
methods in the base class, and classes that extend that base class
are not expected to know or care where the base class gets its
HookContainer from.
* ProtectedHookAccessorTrait provides protected getHookContainer() and
getHookRunner() methods, getting them from the global service
container. The point of this is to ease migration to DI by ensuring
that call sites ask their local friendly base class rather than
getting a HookRunner from the service container directly.
* Private $this->hookRunner. In some smaller classes where accessor
methods did not seem warranted, there is a private HookRunner property
which is accessed directly. Very rarely (two cases), there is a
protected property, for consistency with code that conventionally
assumes protected=private, but in cases where the class might actually
be overridden, a protected accessor is preferred over a protected
property.
* The last resort: Hooks::runner(). Mostly for static, file-scope and
global code. In a few cases it was used for objects with broken
construction schemes, out of horror or laziness.
Constructors with new required arguments:
* AuthManager
* BadFileLookup
* BlockManager
* ClassicInterwikiLookup
* ContentHandlerFactory
* ContentSecurityPolicy
* DefaultOptionsManager
* DerivedPageDataUpdater
* FullSearchResultWidget
* HtmlCacheUpdater
* LanguageFactory
* LanguageNameUtils
* LinkRenderer
* LinkRendererFactory
* LocalisationCache
* MagicWordFactory
* MessageCache
* NamespaceInfo
* PageEditStash
* PageHandlerFactory
* PageUpdater
* ParserFactory
* PermissionManager
* RevisionStore
* RevisionStoreFactory
* SearchEngineConfig
* SearchEngineFactory
* SearchFormWidget
* SearchNearMatcher
* SessionBackend
* SpecialPageFactory
* UserNameUtils
* UserOptionsManager
* WatchedItemQueryService
* WatchedItemStore
Constructors with new optional arguments:
* DefaultPreferencesFactory
* Language
* LinkHolderArray
* MovePage
* Parser
* ParserCache
* PasswordReset
* Router
setHookContainer() now required after construction:
* AuthenticationProvider
* ResourceLoaderModule
* SearchEngine
Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
|
|
|
use MediaWiki\HookContainer\HookContainer;
|
|
|
|
|
use MediaWiki\MediaWikiServices;
|
2020-06-30 15:09:24 +00:00
|
|
|
use MediaWikiIntegrationTestCase;
|
2016-02-01 20:44:03 +00:00
|
|
|
use User;
|
2020-01-10 00:00:51 +00:00
|
|
|
use Wikimedia\AtEase\AtEase;
|
2017-04-19 19:37:35 +00:00
|
|
|
use Wikimedia\TestingAccessWrapper;
|
2016-02-01 20:44:03 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @group Session
|
|
|
|
|
* @group Database
|
|
|
|
|
* @covers MediaWiki\Session\SessionBackend
|
|
|
|
|
*/
|
2020-06-30 15:09:24 +00:00
|
|
|
class SessionBackendTest extends MediaWikiIntegrationTestCase {
|
2021-05-26 15:50:28 +00:00
|
|
|
use SessionProviderTestTrait;
|
|
|
|
|
|
2020-05-16 00:27:13 +00:00
|
|
|
private const SESSIONID = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
|
2016-02-01 20:44:03 +00:00
|
|
|
|
2018-01-10 13:32:56 +00:00
|
|
|
/** @var SessionManager */
|
2016-02-01 20:44:03 +00:00
|
|
|
protected $manager;
|
2018-01-10 13:32:56 +00:00
|
|
|
|
|
|
|
|
/** @var Config */
|
2016-02-01 20:44:03 +00:00
|
|
|
protected $config;
|
2018-01-10 13:32:56 +00:00
|
|
|
|
|
|
|
|
/** @var SessionProvider */
|
2016-02-01 20:44:03 +00:00
|
|
|
protected $provider;
|
2018-01-10 13:32:56 +00:00
|
|
|
|
|
|
|
|
/** @var TestBagOStuff */
|
2016-02-01 20:44:03 +00:00
|
|
|
protected $store;
|
|
|
|
|
|
|
|
|
|
protected $onSessionMetadataCalled = false;
|
|
|
|
|
|
Hooks::run() call site migration
Migrate all callers of Hooks::run() to use the new
HookContainer/HookRunner system.
General principles:
* Use DI if it is already used. We're not changing the way state is
managed in this patch.
* HookContainer is always injected, not HookRunner. HookContainer
is a service, it's a more generic interface, it is the only
thing that provides isRegistered() which is needed in some cases,
and a HookRunner can be efficiently constructed from it
(confirmed by benchmark). Because HookContainer is needed
for object construction, it is also needed by all factories.
* "Ask your friendly local base class". Big hierarchies like
SpecialPage and ApiBase have getHookContainer() and getHookRunner()
methods in the base class, and classes that extend that base class
are not expected to know or care where the base class gets its
HookContainer from.
* ProtectedHookAccessorTrait provides protected getHookContainer() and
getHookRunner() methods, getting them from the global service
container. The point of this is to ease migration to DI by ensuring
that call sites ask their local friendly base class rather than
getting a HookRunner from the service container directly.
* Private $this->hookRunner. In some smaller classes where accessor
methods did not seem warranted, there is a private HookRunner property
which is accessed directly. Very rarely (two cases), there is a
protected property, for consistency with code that conventionally
assumes protected=private, but in cases where the class might actually
be overridden, a protected accessor is preferred over a protected
property.
* The last resort: Hooks::runner(). Mostly for static, file-scope and
global code. In a few cases it was used for objects with broken
construction schemes, out of horror or laziness.
Constructors with new required arguments:
* AuthManager
* BadFileLookup
* BlockManager
* ClassicInterwikiLookup
* ContentHandlerFactory
* ContentSecurityPolicy
* DefaultOptionsManager
* DerivedPageDataUpdater
* FullSearchResultWidget
* HtmlCacheUpdater
* LanguageFactory
* LanguageNameUtils
* LinkRenderer
* LinkRendererFactory
* LocalisationCache
* MagicWordFactory
* MessageCache
* NamespaceInfo
* PageEditStash
* PageHandlerFactory
* PageUpdater
* ParserFactory
* PermissionManager
* RevisionStore
* RevisionStoreFactory
* SearchEngineConfig
* SearchEngineFactory
* SearchFormWidget
* SearchNearMatcher
* SessionBackend
* SpecialPageFactory
* UserNameUtils
* UserOptionsManager
* WatchedItemQueryService
* WatchedItemStore
Constructors with new optional arguments:
* DefaultPreferencesFactory
* Language
* LinkHolderArray
* MovePage
* Parser
* ParserCache
* PasswordReset
* Router
setHookContainer() now required after construction:
* AuthenticationProvider
* ResourceLoaderModule
* SearchEngine
Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
|
|
|
/**
|
|
|
|
|
* @return HookContainer
|
|
|
|
|
*/
|
|
|
|
|
private function getHookContainer() {
|
|
|
|
|
// Need a real HookContainer to support modification of $wgHooks in the test
|
|
|
|
|
return MediaWikiServices::getInstance()->getHookContainer();
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-01 20:44:03 +00:00
|
|
|
/**
|
|
|
|
|
* Returns a non-persistent backend that thinks it has at least one session active
|
|
|
|
|
* @param User|null $user
|
2021-01-14 08:20:36 +00:00
|
|
|
* @param string|null $id
|
2018-01-10 13:32:56 +00:00
|
|
|
* @return SessionBackend
|
2016-02-01 20:44:03 +00:00
|
|
|
*/
|
2016-03-22 21:50:32 +00:00
|
|
|
protected function getBackend( User $user = null, $id = null ) {
|
2016-02-01 20:44:03 +00:00
|
|
|
if ( !$this->config ) {
|
|
|
|
|
$this->config = new \HashConfig();
|
|
|
|
|
$this->manager = null;
|
|
|
|
|
}
|
|
|
|
|
if ( !$this->store ) {
|
|
|
|
|
$this->store = new TestBagOStuff();
|
|
|
|
|
$this->manager = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$logger = new \Psr\Log\NullLogger();
|
|
|
|
|
if ( !$this->manager ) {
|
2016-02-17 09:09:32 +00:00
|
|
|
$this->manager = new SessionManager( [
|
2016-02-01 20:44:03 +00:00
|
|
|
'store' => $this->store,
|
|
|
|
|
'logger' => $logger,
|
|
|
|
|
'config' => $this->config,
|
2016-02-17 09:09:32 +00:00
|
|
|
] );
|
2016-02-01 20:44:03 +00:00
|
|
|
}
|
|
|
|
|
|
Hooks::run() call site migration
Migrate all callers of Hooks::run() to use the new
HookContainer/HookRunner system.
General principles:
* Use DI if it is already used. We're not changing the way state is
managed in this patch.
* HookContainer is always injected, not HookRunner. HookContainer
is a service, it's a more generic interface, it is the only
thing that provides isRegistered() which is needed in some cases,
and a HookRunner can be efficiently constructed from it
(confirmed by benchmark). Because HookContainer is needed
for object construction, it is also needed by all factories.
* "Ask your friendly local base class". Big hierarchies like
SpecialPage and ApiBase have getHookContainer() and getHookRunner()
methods in the base class, and classes that extend that base class
are not expected to know or care where the base class gets its
HookContainer from.
* ProtectedHookAccessorTrait provides protected getHookContainer() and
getHookRunner() methods, getting them from the global service
container. The point of this is to ease migration to DI by ensuring
that call sites ask their local friendly base class rather than
getting a HookRunner from the service container directly.
* Private $this->hookRunner. In some smaller classes where accessor
methods did not seem warranted, there is a private HookRunner property
which is accessed directly. Very rarely (two cases), there is a
protected property, for consistency with code that conventionally
assumes protected=private, but in cases where the class might actually
be overridden, a protected accessor is preferred over a protected
property.
* The last resort: Hooks::runner(). Mostly for static, file-scope and
global code. In a few cases it was used for objects with broken
construction schemes, out of horror or laziness.
Constructors with new required arguments:
* AuthManager
* BadFileLookup
* BlockManager
* ClassicInterwikiLookup
* ContentHandlerFactory
* ContentSecurityPolicy
* DefaultOptionsManager
* DerivedPageDataUpdater
* FullSearchResultWidget
* HtmlCacheUpdater
* LanguageFactory
* LanguageNameUtils
* LinkRenderer
* LinkRendererFactory
* LocalisationCache
* MagicWordFactory
* MessageCache
* NamespaceInfo
* PageEditStash
* PageHandlerFactory
* PageUpdater
* ParserFactory
* PermissionManager
* RevisionStore
* RevisionStoreFactory
* SearchEngineConfig
* SearchEngineFactory
* SearchFormWidget
* SearchNearMatcher
* SessionBackend
* SpecialPageFactory
* UserNameUtils
* UserOptionsManager
* WatchedItemQueryService
* WatchedItemStore
Constructors with new optional arguments:
* DefaultPreferencesFactory
* Language
* LinkHolderArray
* MovePage
* Parser
* ParserCache
* PasswordReset
* Router
setHookContainer() now required after construction:
* AuthenticationProvider
* ResourceLoaderModule
* SearchEngine
Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
|
|
|
$hookContainer = $this->getHookContainer();
|
|
|
|
|
|
2016-02-01 20:44:03 +00:00
|
|
|
if ( !$this->provider ) {
|
|
|
|
|
$this->provider = new \DummySessionProvider();
|
|
|
|
|
}
|
2021-05-26 15:50:28 +00:00
|
|
|
$this->initProvider( $this->provider, null, $this->config, $this->manager, $hookContainer );
|
2016-02-01 20:44:03 +00:00
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
$info = new SessionInfo( SessionInfo::MIN_PRIORITY, [
|
2016-02-01 20:44:03 +00:00
|
|
|
'provider' => $this->provider,
|
2016-03-22 21:50:32 +00:00
|
|
|
'id' => $id ?: self::SESSIONID,
|
2016-02-01 20:44:03 +00:00
|
|
|
'persisted' => true,
|
|
|
|
|
'userInfo' => UserInfo::newFromUser( $user ?: new User, true ),
|
|
|
|
|
'idIsSafe' => true,
|
2016-02-17 09:09:32 +00:00
|
|
|
] );
|
2016-02-01 20:44:03 +00:00
|
|
|
$id = new SessionId( $info->getId() );
|
|
|
|
|
|
Hooks::run() call site migration
Migrate all callers of Hooks::run() to use the new
HookContainer/HookRunner system.
General principles:
* Use DI if it is already used. We're not changing the way state is
managed in this patch.
* HookContainer is always injected, not HookRunner. HookContainer
is a service, it's a more generic interface, it is the only
thing that provides isRegistered() which is needed in some cases,
and a HookRunner can be efficiently constructed from it
(confirmed by benchmark). Because HookContainer is needed
for object construction, it is also needed by all factories.
* "Ask your friendly local base class". Big hierarchies like
SpecialPage and ApiBase have getHookContainer() and getHookRunner()
methods in the base class, and classes that extend that base class
are not expected to know or care where the base class gets its
HookContainer from.
* ProtectedHookAccessorTrait provides protected getHookContainer() and
getHookRunner() methods, getting them from the global service
container. The point of this is to ease migration to DI by ensuring
that call sites ask their local friendly base class rather than
getting a HookRunner from the service container directly.
* Private $this->hookRunner. In some smaller classes where accessor
methods did not seem warranted, there is a private HookRunner property
which is accessed directly. Very rarely (two cases), there is a
protected property, for consistency with code that conventionally
assumes protected=private, but in cases where the class might actually
be overridden, a protected accessor is preferred over a protected
property.
* The last resort: Hooks::runner(). Mostly for static, file-scope and
global code. In a few cases it was used for objects with broken
construction schemes, out of horror or laziness.
Constructors with new required arguments:
* AuthManager
* BadFileLookup
* BlockManager
* ClassicInterwikiLookup
* ContentHandlerFactory
* ContentSecurityPolicy
* DefaultOptionsManager
* DerivedPageDataUpdater
* FullSearchResultWidget
* HtmlCacheUpdater
* LanguageFactory
* LanguageNameUtils
* LinkRenderer
* LinkRendererFactory
* LocalisationCache
* MagicWordFactory
* MessageCache
* NamespaceInfo
* PageEditStash
* PageHandlerFactory
* PageUpdater
* ParserFactory
* PermissionManager
* RevisionStore
* RevisionStoreFactory
* SearchEngineConfig
* SearchEngineFactory
* SearchFormWidget
* SearchNearMatcher
* SessionBackend
* SpecialPageFactory
* UserNameUtils
* UserOptionsManager
* WatchedItemQueryService
* WatchedItemStore
Constructors with new optional arguments:
* DefaultPreferencesFactory
* Language
* LinkHolderArray
* MovePage
* Parser
* ParserCache
* PasswordReset
* Router
setHookContainer() now required after construction:
* AuthenticationProvider
* ResourceLoaderModule
* SearchEngine
Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
|
|
|
$backend = new SessionBackend( $id, $info, $this->store, $logger, $hookContainer, 10 );
|
2017-04-19 19:37:35 +00:00
|
|
|
$priv = TestingAccessWrapper::newFromObject( $backend );
|
2016-02-01 20:44:03 +00:00
|
|
|
$priv->persist = false;
|
2016-02-17 09:09:32 +00:00
|
|
|
$priv->requests = [ 100 => new \FauxRequest() ];
|
2016-02-26 21:17:37 +00:00
|
|
|
$priv->requests[100]->setSessionId( $id );
|
2016-02-01 20:44:03 +00:00
|
|
|
$priv->usePhpSessionHandling = false;
|
|
|
|
|
|
2017-04-19 19:37:35 +00:00
|
|
|
$manager = TestingAccessWrapper::newFromObject( $this->manager );
|
2016-03-22 21:50:32 +00:00
|
|
|
$manager->allSessionBackends = [ $backend->getId() => $backend ] + $manager->allSessionBackends;
|
|
|
|
|
$manager->allSessionIds = [ $backend->getId() => $id ] + $manager->allSessionIds;
|
2016-02-17 09:09:32 +00:00
|
|
|
$manager->sessionProviders = [ (string)$this->provider => $this->provider ];
|
2016-02-01 20:44:03 +00:00
|
|
|
|
|
|
|
|
return $backend;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testConstructor() {
|
|
|
|
|
// Set variables
|
|
|
|
|
$this->getBackend();
|
|
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
$info = new SessionInfo( SessionInfo::MIN_PRIORITY, [
|
2016-02-01 20:44:03 +00:00
|
|
|
'provider' => $this->provider,
|
|
|
|
|
'id' => self::SESSIONID,
|
|
|
|
|
'persisted' => true,
|
|
|
|
|
'userInfo' => UserInfo::newFromName( 'UTSysop', false ),
|
|
|
|
|
'idIsSafe' => true,
|
2016-02-17 09:09:32 +00:00
|
|
|
] );
|
2016-02-01 20:44:03 +00:00
|
|
|
$id = new SessionId( $info->getId() );
|
|
|
|
|
$logger = new \Psr\Log\NullLogger();
|
Hooks::run() call site migration
Migrate all callers of Hooks::run() to use the new
HookContainer/HookRunner system.
General principles:
* Use DI if it is already used. We're not changing the way state is
managed in this patch.
* HookContainer is always injected, not HookRunner. HookContainer
is a service, it's a more generic interface, it is the only
thing that provides isRegistered() which is needed in some cases,
and a HookRunner can be efficiently constructed from it
(confirmed by benchmark). Because HookContainer is needed
for object construction, it is also needed by all factories.
* "Ask your friendly local base class". Big hierarchies like
SpecialPage and ApiBase have getHookContainer() and getHookRunner()
methods in the base class, and classes that extend that base class
are not expected to know or care where the base class gets its
HookContainer from.
* ProtectedHookAccessorTrait provides protected getHookContainer() and
getHookRunner() methods, getting them from the global service
container. The point of this is to ease migration to DI by ensuring
that call sites ask their local friendly base class rather than
getting a HookRunner from the service container directly.
* Private $this->hookRunner. In some smaller classes where accessor
methods did not seem warranted, there is a private HookRunner property
which is accessed directly. Very rarely (two cases), there is a
protected property, for consistency with code that conventionally
assumes protected=private, but in cases where the class might actually
be overridden, a protected accessor is preferred over a protected
property.
* The last resort: Hooks::runner(). Mostly for static, file-scope and
global code. In a few cases it was used for objects with broken
construction schemes, out of horror or laziness.
Constructors with new required arguments:
* AuthManager
* BadFileLookup
* BlockManager
* ClassicInterwikiLookup
* ContentHandlerFactory
* ContentSecurityPolicy
* DefaultOptionsManager
* DerivedPageDataUpdater
* FullSearchResultWidget
* HtmlCacheUpdater
* LanguageFactory
* LanguageNameUtils
* LinkRenderer
* LinkRendererFactory
* LocalisationCache
* MagicWordFactory
* MessageCache
* NamespaceInfo
* PageEditStash
* PageHandlerFactory
* PageUpdater
* ParserFactory
* PermissionManager
* RevisionStore
* RevisionStoreFactory
* SearchEngineConfig
* SearchEngineFactory
* SearchFormWidget
* SearchNearMatcher
* SessionBackend
* SpecialPageFactory
* UserNameUtils
* UserOptionsManager
* WatchedItemQueryService
* WatchedItemStore
Constructors with new optional arguments:
* DefaultPreferencesFactory
* Language
* LinkHolderArray
* MovePage
* Parser
* ParserCache
* PasswordReset
* Router
setHookContainer() now required after construction:
* AuthenticationProvider
* ResourceLoaderModule
* SearchEngine
Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
|
|
|
$hookContainer = $this->getHookContainer();
|
2016-02-01 20:44:03 +00:00
|
|
|
try {
|
Hooks::run() call site migration
Migrate all callers of Hooks::run() to use the new
HookContainer/HookRunner system.
General principles:
* Use DI if it is already used. We're not changing the way state is
managed in this patch.
* HookContainer is always injected, not HookRunner. HookContainer
is a service, it's a more generic interface, it is the only
thing that provides isRegistered() which is needed in some cases,
and a HookRunner can be efficiently constructed from it
(confirmed by benchmark). Because HookContainer is needed
for object construction, it is also needed by all factories.
* "Ask your friendly local base class". Big hierarchies like
SpecialPage and ApiBase have getHookContainer() and getHookRunner()
methods in the base class, and classes that extend that base class
are not expected to know or care where the base class gets its
HookContainer from.
* ProtectedHookAccessorTrait provides protected getHookContainer() and
getHookRunner() methods, getting them from the global service
container. The point of this is to ease migration to DI by ensuring
that call sites ask their local friendly base class rather than
getting a HookRunner from the service container directly.
* Private $this->hookRunner. In some smaller classes where accessor
methods did not seem warranted, there is a private HookRunner property
which is accessed directly. Very rarely (two cases), there is a
protected property, for consistency with code that conventionally
assumes protected=private, but in cases where the class might actually
be overridden, a protected accessor is preferred over a protected
property.
* The last resort: Hooks::runner(). Mostly for static, file-scope and
global code. In a few cases it was used for objects with broken
construction schemes, out of horror or laziness.
Constructors with new required arguments:
* AuthManager
* BadFileLookup
* BlockManager
* ClassicInterwikiLookup
* ContentHandlerFactory
* ContentSecurityPolicy
* DefaultOptionsManager
* DerivedPageDataUpdater
* FullSearchResultWidget
* HtmlCacheUpdater
* LanguageFactory
* LanguageNameUtils
* LinkRenderer
* LinkRendererFactory
* LocalisationCache
* MagicWordFactory
* MessageCache
* NamespaceInfo
* PageEditStash
* PageHandlerFactory
* PageUpdater
* ParserFactory
* PermissionManager
* RevisionStore
* RevisionStoreFactory
* SearchEngineConfig
* SearchEngineFactory
* SearchFormWidget
* SearchNearMatcher
* SessionBackend
* SpecialPageFactory
* UserNameUtils
* UserOptionsManager
* WatchedItemQueryService
* WatchedItemStore
Constructors with new optional arguments:
* DefaultPreferencesFactory
* Language
* LinkHolderArray
* MovePage
* Parser
* ParserCache
* PasswordReset
* Router
setHookContainer() now required after construction:
* AuthenticationProvider
* ResourceLoaderModule
* SearchEngine
Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
|
|
|
new SessionBackend( $id, $info, $this->store, $logger, $hookContainer, 10 );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->fail( 'Expected exception not thrown' );
|
|
|
|
|
} catch ( \InvalidArgumentException $ex ) {
|
|
|
|
|
$this->assertSame(
|
|
|
|
|
"Refusing to create session for unverified user {$info->getUserInfo()}",
|
|
|
|
|
$ex->getMessage()
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
$info = new SessionInfo( SessionInfo::MIN_PRIORITY, [
|
2016-02-01 20:44:03 +00:00
|
|
|
'id' => self::SESSIONID,
|
|
|
|
|
'userInfo' => UserInfo::newFromName( 'UTSysop', true ),
|
|
|
|
|
'idIsSafe' => true,
|
2016-02-17 09:09:32 +00:00
|
|
|
] );
|
2016-02-01 20:44:03 +00:00
|
|
|
$id = new SessionId( $info->getId() );
|
|
|
|
|
try {
|
Hooks::run() call site migration
Migrate all callers of Hooks::run() to use the new
HookContainer/HookRunner system.
General principles:
* Use DI if it is already used. We're not changing the way state is
managed in this patch.
* HookContainer is always injected, not HookRunner. HookContainer
is a service, it's a more generic interface, it is the only
thing that provides isRegistered() which is needed in some cases,
and a HookRunner can be efficiently constructed from it
(confirmed by benchmark). Because HookContainer is needed
for object construction, it is also needed by all factories.
* "Ask your friendly local base class". Big hierarchies like
SpecialPage and ApiBase have getHookContainer() and getHookRunner()
methods in the base class, and classes that extend that base class
are not expected to know or care where the base class gets its
HookContainer from.
* ProtectedHookAccessorTrait provides protected getHookContainer() and
getHookRunner() methods, getting them from the global service
container. The point of this is to ease migration to DI by ensuring
that call sites ask their local friendly base class rather than
getting a HookRunner from the service container directly.
* Private $this->hookRunner. In some smaller classes where accessor
methods did not seem warranted, there is a private HookRunner property
which is accessed directly. Very rarely (two cases), there is a
protected property, for consistency with code that conventionally
assumes protected=private, but in cases where the class might actually
be overridden, a protected accessor is preferred over a protected
property.
* The last resort: Hooks::runner(). Mostly for static, file-scope and
global code. In a few cases it was used for objects with broken
construction schemes, out of horror or laziness.
Constructors with new required arguments:
* AuthManager
* BadFileLookup
* BlockManager
* ClassicInterwikiLookup
* ContentHandlerFactory
* ContentSecurityPolicy
* DefaultOptionsManager
* DerivedPageDataUpdater
* FullSearchResultWidget
* HtmlCacheUpdater
* LanguageFactory
* LanguageNameUtils
* LinkRenderer
* LinkRendererFactory
* LocalisationCache
* MagicWordFactory
* MessageCache
* NamespaceInfo
* PageEditStash
* PageHandlerFactory
* PageUpdater
* ParserFactory
* PermissionManager
* RevisionStore
* RevisionStoreFactory
* SearchEngineConfig
* SearchEngineFactory
* SearchFormWidget
* SearchNearMatcher
* SessionBackend
* SpecialPageFactory
* UserNameUtils
* UserOptionsManager
* WatchedItemQueryService
* WatchedItemStore
Constructors with new optional arguments:
* DefaultPreferencesFactory
* Language
* LinkHolderArray
* MovePage
* Parser
* ParserCache
* PasswordReset
* Router
setHookContainer() now required after construction:
* AuthenticationProvider
* ResourceLoaderModule
* SearchEngine
Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
|
|
|
new SessionBackend( $id, $info, $this->store, $logger, $hookContainer, 10 );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->fail( 'Expected exception not thrown' );
|
|
|
|
|
} catch ( \InvalidArgumentException $ex ) {
|
|
|
|
|
$this->assertSame( 'Cannot create session without a provider', $ex->getMessage() );
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
$info = new SessionInfo( SessionInfo::MIN_PRIORITY, [
|
2016-02-01 20:44:03 +00:00
|
|
|
'provider' => $this->provider,
|
|
|
|
|
'id' => self::SESSIONID,
|
|
|
|
|
'persisted' => true,
|
|
|
|
|
'userInfo' => UserInfo::newFromName( 'UTSysop', true ),
|
|
|
|
|
'idIsSafe' => true,
|
2016-02-17 09:09:32 +00:00
|
|
|
] );
|
2016-02-01 20:44:03 +00:00
|
|
|
$id = new SessionId( '!' . $info->getId() );
|
|
|
|
|
try {
|
Hooks::run() call site migration
Migrate all callers of Hooks::run() to use the new
HookContainer/HookRunner system.
General principles:
* Use DI if it is already used. We're not changing the way state is
managed in this patch.
* HookContainer is always injected, not HookRunner. HookContainer
is a service, it's a more generic interface, it is the only
thing that provides isRegistered() which is needed in some cases,
and a HookRunner can be efficiently constructed from it
(confirmed by benchmark). Because HookContainer is needed
for object construction, it is also needed by all factories.
* "Ask your friendly local base class". Big hierarchies like
SpecialPage and ApiBase have getHookContainer() and getHookRunner()
methods in the base class, and classes that extend that base class
are not expected to know or care where the base class gets its
HookContainer from.
* ProtectedHookAccessorTrait provides protected getHookContainer() and
getHookRunner() methods, getting them from the global service
container. The point of this is to ease migration to DI by ensuring
that call sites ask their local friendly base class rather than
getting a HookRunner from the service container directly.
* Private $this->hookRunner. In some smaller classes where accessor
methods did not seem warranted, there is a private HookRunner property
which is accessed directly. Very rarely (two cases), there is a
protected property, for consistency with code that conventionally
assumes protected=private, but in cases where the class might actually
be overridden, a protected accessor is preferred over a protected
property.
* The last resort: Hooks::runner(). Mostly for static, file-scope and
global code. In a few cases it was used for objects with broken
construction schemes, out of horror or laziness.
Constructors with new required arguments:
* AuthManager
* BadFileLookup
* BlockManager
* ClassicInterwikiLookup
* ContentHandlerFactory
* ContentSecurityPolicy
* DefaultOptionsManager
* DerivedPageDataUpdater
* FullSearchResultWidget
* HtmlCacheUpdater
* LanguageFactory
* LanguageNameUtils
* LinkRenderer
* LinkRendererFactory
* LocalisationCache
* MagicWordFactory
* MessageCache
* NamespaceInfo
* PageEditStash
* PageHandlerFactory
* PageUpdater
* ParserFactory
* PermissionManager
* RevisionStore
* RevisionStoreFactory
* SearchEngineConfig
* SearchEngineFactory
* SearchFormWidget
* SearchNearMatcher
* SessionBackend
* SpecialPageFactory
* UserNameUtils
* UserOptionsManager
* WatchedItemQueryService
* WatchedItemStore
Constructors with new optional arguments:
* DefaultPreferencesFactory
* Language
* LinkHolderArray
* MovePage
* Parser
* ParserCache
* PasswordReset
* Router
setHookContainer() now required after construction:
* AuthenticationProvider
* ResourceLoaderModule
* SearchEngine
Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
|
|
|
new SessionBackend( $id, $info, $this->store, $logger, $hookContainer, 10 );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->fail( 'Expected exception not thrown' );
|
|
|
|
|
} catch ( \InvalidArgumentException $ex ) {
|
|
|
|
|
$this->assertSame(
|
|
|
|
|
'SessionId and SessionInfo don\'t match',
|
|
|
|
|
$ex->getMessage()
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
$info = new SessionInfo( SessionInfo::MIN_PRIORITY, [
|
2016-02-01 20:44:03 +00:00
|
|
|
'provider' => $this->provider,
|
|
|
|
|
'id' => self::SESSIONID,
|
|
|
|
|
'persisted' => true,
|
|
|
|
|
'userInfo' => UserInfo::newFromName( 'UTSysop', true ),
|
|
|
|
|
'idIsSafe' => true,
|
2016-02-17 09:09:32 +00:00
|
|
|
] );
|
2016-02-01 20:44:03 +00:00
|
|
|
$id = new SessionId( $info->getId() );
|
Hooks::run() call site migration
Migrate all callers of Hooks::run() to use the new
HookContainer/HookRunner system.
General principles:
* Use DI if it is already used. We're not changing the way state is
managed in this patch.
* HookContainer is always injected, not HookRunner. HookContainer
is a service, it's a more generic interface, it is the only
thing that provides isRegistered() which is needed in some cases,
and a HookRunner can be efficiently constructed from it
(confirmed by benchmark). Because HookContainer is needed
for object construction, it is also needed by all factories.
* "Ask your friendly local base class". Big hierarchies like
SpecialPage and ApiBase have getHookContainer() and getHookRunner()
methods in the base class, and classes that extend that base class
are not expected to know or care where the base class gets its
HookContainer from.
* ProtectedHookAccessorTrait provides protected getHookContainer() and
getHookRunner() methods, getting them from the global service
container. The point of this is to ease migration to DI by ensuring
that call sites ask their local friendly base class rather than
getting a HookRunner from the service container directly.
* Private $this->hookRunner. In some smaller classes where accessor
methods did not seem warranted, there is a private HookRunner property
which is accessed directly. Very rarely (two cases), there is a
protected property, for consistency with code that conventionally
assumes protected=private, but in cases where the class might actually
be overridden, a protected accessor is preferred over a protected
property.
* The last resort: Hooks::runner(). Mostly for static, file-scope and
global code. In a few cases it was used for objects with broken
construction schemes, out of horror or laziness.
Constructors with new required arguments:
* AuthManager
* BadFileLookup
* BlockManager
* ClassicInterwikiLookup
* ContentHandlerFactory
* ContentSecurityPolicy
* DefaultOptionsManager
* DerivedPageDataUpdater
* FullSearchResultWidget
* HtmlCacheUpdater
* LanguageFactory
* LanguageNameUtils
* LinkRenderer
* LinkRendererFactory
* LocalisationCache
* MagicWordFactory
* MessageCache
* NamespaceInfo
* PageEditStash
* PageHandlerFactory
* PageUpdater
* ParserFactory
* PermissionManager
* RevisionStore
* RevisionStoreFactory
* SearchEngineConfig
* SearchEngineFactory
* SearchFormWidget
* SearchNearMatcher
* SessionBackend
* SpecialPageFactory
* UserNameUtils
* UserOptionsManager
* WatchedItemQueryService
* WatchedItemStore
Constructors with new optional arguments:
* DefaultPreferencesFactory
* Language
* LinkHolderArray
* MovePage
* Parser
* ParserCache
* PasswordReset
* Router
setHookContainer() now required after construction:
* AuthenticationProvider
* ResourceLoaderModule
* SearchEngine
Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
|
|
|
$backend = new SessionBackend( $id, $info, $this->store, $logger, $hookContainer, 10 );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->assertSame( self::SESSIONID, $backend->getId() );
|
|
|
|
|
$this->assertSame( $id, $backend->getSessionId() );
|
|
|
|
|
$this->assertSame( $this->provider, $backend->getProvider() );
|
2018-01-13 00:02:09 +00:00
|
|
|
$this->assertInstanceOf( User::class, $backend->getUser() );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->assertSame( 'UTSysop', $backend->getUser()->getName() );
|
|
|
|
|
$this->assertSame( $info->wasPersisted(), $backend->isPersistent() );
|
|
|
|
|
$this->assertSame( $info->wasRemembered(), $backend->shouldRememberUser() );
|
|
|
|
|
$this->assertSame( $info->forceHTTPS(), $backend->shouldForceHTTPS() );
|
|
|
|
|
|
|
|
|
|
$expire = time() + 100;
|
2018-01-10 13:32:56 +00:00
|
|
|
$this->store->setSessionMeta( self::SESSIONID, [ 'expires' => $expire ] );
|
2016-02-01 20:44:03 +00:00
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
$info = new SessionInfo( SessionInfo::MIN_PRIORITY, [
|
2016-02-01 20:44:03 +00:00
|
|
|
'provider' => $this->provider,
|
|
|
|
|
'id' => self::SESSIONID,
|
|
|
|
|
'persisted' => true,
|
|
|
|
|
'forceHTTPS' => true,
|
2016-02-17 09:09:32 +00:00
|
|
|
'metadata' => [ 'foo' ],
|
2016-02-01 20:44:03 +00:00
|
|
|
'idIsSafe' => true,
|
2016-02-17 09:09:32 +00:00
|
|
|
] );
|
2016-02-01 20:44:03 +00:00
|
|
|
$id = new SessionId( $info->getId() );
|
Hooks::run() call site migration
Migrate all callers of Hooks::run() to use the new
HookContainer/HookRunner system.
General principles:
* Use DI if it is already used. We're not changing the way state is
managed in this patch.
* HookContainer is always injected, not HookRunner. HookContainer
is a service, it's a more generic interface, it is the only
thing that provides isRegistered() which is needed in some cases,
and a HookRunner can be efficiently constructed from it
(confirmed by benchmark). Because HookContainer is needed
for object construction, it is also needed by all factories.
* "Ask your friendly local base class". Big hierarchies like
SpecialPage and ApiBase have getHookContainer() and getHookRunner()
methods in the base class, and classes that extend that base class
are not expected to know or care where the base class gets its
HookContainer from.
* ProtectedHookAccessorTrait provides protected getHookContainer() and
getHookRunner() methods, getting them from the global service
container. The point of this is to ease migration to DI by ensuring
that call sites ask their local friendly base class rather than
getting a HookRunner from the service container directly.
* Private $this->hookRunner. In some smaller classes where accessor
methods did not seem warranted, there is a private HookRunner property
which is accessed directly. Very rarely (two cases), there is a
protected property, for consistency with code that conventionally
assumes protected=private, but in cases where the class might actually
be overridden, a protected accessor is preferred over a protected
property.
* The last resort: Hooks::runner(). Mostly for static, file-scope and
global code. In a few cases it was used for objects with broken
construction schemes, out of horror or laziness.
Constructors with new required arguments:
* AuthManager
* BadFileLookup
* BlockManager
* ClassicInterwikiLookup
* ContentHandlerFactory
* ContentSecurityPolicy
* DefaultOptionsManager
* DerivedPageDataUpdater
* FullSearchResultWidget
* HtmlCacheUpdater
* LanguageFactory
* LanguageNameUtils
* LinkRenderer
* LinkRendererFactory
* LocalisationCache
* MagicWordFactory
* MessageCache
* NamespaceInfo
* PageEditStash
* PageHandlerFactory
* PageUpdater
* ParserFactory
* PermissionManager
* RevisionStore
* RevisionStoreFactory
* SearchEngineConfig
* SearchEngineFactory
* SearchFormWidget
* SearchNearMatcher
* SessionBackend
* SpecialPageFactory
* UserNameUtils
* UserOptionsManager
* WatchedItemQueryService
* WatchedItemStore
Constructors with new optional arguments:
* DefaultPreferencesFactory
* Language
* LinkHolderArray
* MovePage
* Parser
* ParserCache
* PasswordReset
* Router
setHookContainer() now required after construction:
* AuthenticationProvider
* ResourceLoaderModule
* SearchEngine
Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
|
|
|
$backend = new SessionBackend( $id, $info, $this->store, $logger, $hookContainer, 10 );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->assertSame( self::SESSIONID, $backend->getId() );
|
|
|
|
|
$this->assertSame( $id, $backend->getSessionId() );
|
|
|
|
|
$this->assertSame( $this->provider, $backend->getProvider() );
|
2018-01-13 00:02:09 +00:00
|
|
|
$this->assertInstanceOf( User::class, $backend->getUser() );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->assertTrue( $backend->getUser()->isAnon() );
|
|
|
|
|
$this->assertSame( $info->wasPersisted(), $backend->isPersistent() );
|
|
|
|
|
$this->assertSame( $info->wasRemembered(), $backend->shouldRememberUser() );
|
|
|
|
|
$this->assertSame( $info->forceHTTPS(), $backend->shouldForceHTTPS() );
|
2017-04-19 19:37:35 +00:00
|
|
|
$this->assertSame( $expire, TestingAccessWrapper::newFromObject( $backend )->expires );
|
2016-02-17 09:09:32 +00:00
|
|
|
$this->assertSame( [ 'foo' ], $backend->getProviderMetadata() );
|
2016-02-01 20:44:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testSessionStuff() {
|
|
|
|
|
$backend = $this->getBackend();
|
2017-04-19 19:37:35 +00:00
|
|
|
$priv = TestingAccessWrapper::newFromObject( $backend );
|
2016-02-17 09:09:32 +00:00
|
|
|
$priv->requests = []; // Remove dummy session
|
2016-02-01 20:44:03 +00:00
|
|
|
|
2017-04-19 19:37:35 +00:00
|
|
|
$manager = TestingAccessWrapper::newFromObject( $this->manager );
|
2016-02-01 20:44:03 +00:00
|
|
|
|
|
|
|
|
$request1 = new \FauxRequest();
|
|
|
|
|
$session1 = $backend->getSession( $request1 );
|
|
|
|
|
$request2 = new \FauxRequest();
|
|
|
|
|
$session2 = $backend->getSession( $request2 );
|
|
|
|
|
|
2016-03-28 18:53:04 +00:00
|
|
|
$this->assertInstanceOf( Session::class, $session1 );
|
|
|
|
|
$this->assertInstanceOf( Session::class, $session2 );
|
2020-02-28 15:45:22 +00:00
|
|
|
$this->assertCount( 2, $priv->requests );
|
2016-02-01 20:44:03 +00:00
|
|
|
|
2017-04-19 19:37:35 +00:00
|
|
|
$index = TestingAccessWrapper::newFromObject( $session1 )->index;
|
2016-02-01 20:44:03 +00:00
|
|
|
|
|
|
|
|
$this->assertSame( $request1, $backend->getRequest( $index ) );
|
|
|
|
|
$this->assertSame( null, $backend->suggestLoginUsername( $index ) );
|
|
|
|
|
$request1->setCookie( 'UserName', 'Example' );
|
|
|
|
|
$this->assertSame( 'Example', $backend->suggestLoginUsername( $index ) );
|
|
|
|
|
|
|
|
|
|
$session1 = null;
|
2020-02-28 15:45:22 +00:00
|
|
|
$this->assertCount( 1, $priv->requests );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->assertArrayHasKey( $backend->getId(), $manager->allSessionBackends );
|
|
|
|
|
$this->assertSame( $backend, $manager->allSessionBackends[$backend->getId()] );
|
|
|
|
|
try {
|
|
|
|
|
$backend->getRequest( $index );
|
|
|
|
|
$this->fail( 'Expected exception not thrown' );
|
|
|
|
|
} catch ( \InvalidArgumentException $ex ) {
|
|
|
|
|
$this->assertSame( 'Invalid session index', $ex->getMessage() );
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
$backend->suggestLoginUsername( $index );
|
|
|
|
|
$this->fail( 'Expected exception not thrown' );
|
|
|
|
|
} catch ( \InvalidArgumentException $ex ) {
|
|
|
|
|
$this->assertSame( 'Invalid session index', $ex->getMessage() );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$session2 = null;
|
2019-09-30 14:20:34 +00:00
|
|
|
$this->assertSame( [], $priv->requests );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->assertArrayNotHasKey( $backend->getId(), $manager->allSessionBackends );
|
|
|
|
|
$this->assertArrayHasKey( $backend->getId(), $manager->allSessionIds );
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-26 20:02:56 +00:00
|
|
|
public function testSetProviderMetadata() {
|
|
|
|
|
$backend = $this->getBackend();
|
2017-04-19 19:37:35 +00:00
|
|
|
$priv = TestingAccessWrapper::newFromObject( $backend );
|
2016-02-26 20:02:56 +00:00
|
|
|
$priv->providerMetadata = [ 'dummy' ];
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
$backend->setProviderMetadata( 'foo' );
|
|
|
|
|
$this->fail( 'Expected exception not thrown' );
|
|
|
|
|
} catch ( \InvalidArgumentException $ex ) {
|
|
|
|
|
$this->assertSame( '$metadata must be an array or null', $ex->getMessage() );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
$backend->setProviderMetadata( (object)[] );
|
|
|
|
|
$this->fail( 'Expected exception not thrown' );
|
|
|
|
|
} catch ( \InvalidArgumentException $ex ) {
|
|
|
|
|
$this->assertSame( '$metadata must be an array or null', $ex->getMessage() );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->assertFalse( $this->store->getSession( self::SESSIONID ), 'sanity check' );
|
|
|
|
|
$backend->setProviderMetadata( [ 'dummy' ] );
|
|
|
|
|
$this->assertFalse( $this->store->getSession( self::SESSIONID ) );
|
|
|
|
|
|
|
|
|
|
$this->assertFalse( $this->store->getSession( self::SESSIONID ), 'sanity check' );
|
|
|
|
|
$backend->setProviderMetadata( [ 'test' ] );
|
|
|
|
|
$this->assertNotFalse( $this->store->getSession( self::SESSIONID ) );
|
|
|
|
|
$this->assertSame( [ 'test' ], $backend->getProviderMetadata() );
|
|
|
|
|
$this->store->deleteSession( self::SESSIONID );
|
|
|
|
|
|
|
|
|
|
$this->assertFalse( $this->store->getSession( self::SESSIONID ), 'sanity check' );
|
|
|
|
|
$backend->setProviderMetadata( null );
|
|
|
|
|
$this->assertNotFalse( $this->store->getSession( self::SESSIONID ) );
|
|
|
|
|
$this->assertSame( null, $backend->getProviderMetadata() );
|
|
|
|
|
$this->store->deleteSession( self::SESSIONID );
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-01 20:44:03 +00:00
|
|
|
public function testResetId() {
|
|
|
|
|
$id = session_id();
|
|
|
|
|
|
2018-01-13 00:02:09 +00:00
|
|
|
$builder = $this->getMockBuilder( \DummySessionProvider::class )
|
2021-03-20 15:18:58 +00:00
|
|
|
->onlyMethods( [ 'persistsSessionId', 'sessionIdWasReset' ] );
|
2016-02-01 20:44:03 +00:00
|
|
|
|
|
|
|
|
$this->provider = $builder->getMock();
|
2021-04-22 08:28:11 +00:00
|
|
|
$this->provider->method( 'persistsSessionId' )
|
|
|
|
|
->willReturn( false );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->provider->expects( $this->never() )->method( 'sessionIdWasReset' );
|
|
|
|
|
$backend = $this->getBackend( User::newFromName( 'UTSysop' ) );
|
2017-04-19 19:37:35 +00:00
|
|
|
$manager = TestingAccessWrapper::newFromObject( $this->manager );
|
2016-02-01 20:44:03 +00:00
|
|
|
$sessionId = $backend->getSessionId();
|
|
|
|
|
$backend->resetId();
|
|
|
|
|
$this->assertSame( self::SESSIONID, $backend->getId() );
|
|
|
|
|
$this->assertSame( $backend->getId(), $sessionId->getId() );
|
|
|
|
|
$this->assertSame( $id, session_id() );
|
|
|
|
|
$this->assertSame( $backend, $manager->allSessionBackends[self::SESSIONID] );
|
|
|
|
|
|
|
|
|
|
$this->provider = $builder->getMock();
|
2021-04-22 08:28:11 +00:00
|
|
|
$this->provider->method( 'persistsSessionId' )
|
|
|
|
|
->willReturn( true );
|
2016-02-01 20:44:03 +00:00
|
|
|
$backend = $this->getBackend();
|
|
|
|
|
$this->provider->expects( $this->once() )->method( 'sessionIdWasReset' )
|
|
|
|
|
->with( $this->identicalTo( $backend ), $this->identicalTo( self::SESSIONID ) );
|
2017-04-19 19:37:35 +00:00
|
|
|
$manager = TestingAccessWrapper::newFromObject( $this->manager );
|
2016-02-01 20:44:03 +00:00
|
|
|
$sessionId = $backend->getSessionId();
|
|
|
|
|
$backend->resetId();
|
|
|
|
|
$this->assertNotEquals( self::SESSIONID, $backend->getId() );
|
|
|
|
|
$this->assertSame( $backend->getId(), $sessionId->getId() );
|
2019-12-13 14:29:10 +00:00
|
|
|
$this->assertIsArray( $this->store->getSession( $backend->getId() ) );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->assertFalse( $this->store->getSession( self::SESSIONID ) );
|
|
|
|
|
$this->assertSame( $id, session_id() );
|
|
|
|
|
$this->assertArrayNotHasKey( self::SESSIONID, $manager->allSessionBackends );
|
|
|
|
|
$this->assertArrayHasKey( $backend->getId(), $manager->allSessionBackends );
|
|
|
|
|
$this->assertSame( $backend, $manager->allSessionBackends[$backend->getId()] );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testPersist() {
|
2018-01-13 00:02:09 +00:00
|
|
|
$this->provider = $this->getMockBuilder( \DummySessionProvider::class )
|
2021-03-20 15:18:58 +00:00
|
|
|
->onlyMethods( [ 'persistSession' ] )->getMock();
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->provider->expects( $this->once() )->method( 'persistSession' );
|
|
|
|
|
$backend = $this->getBackend();
|
|
|
|
|
$this->assertFalse( $backend->isPersistent(), 'sanity check' );
|
|
|
|
|
$backend->save(); // This one shouldn't call $provider->persistSession()
|
|
|
|
|
|
|
|
|
|
$backend->persist();
|
|
|
|
|
$this->assertTrue( $backend->isPersistent(), 'sanity check' );
|
|
|
|
|
|
|
|
|
|
$this->provider = null;
|
|
|
|
|
$backend = $this->getBackend();
|
2017-04-19 19:37:35 +00:00
|
|
|
$wrap = TestingAccessWrapper::newFromObject( $backend );
|
2016-02-01 20:44:03 +00:00
|
|
|
$wrap->persist = true;
|
|
|
|
|
$wrap->expires = 0;
|
|
|
|
|
$backend->persist();
|
|
|
|
|
$this->assertNotEquals( 0, $wrap->expires );
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-26 21:17:37 +00:00
|
|
|
public function testUnpersist() {
|
2018-01-13 00:02:09 +00:00
|
|
|
$this->provider = $this->getMockBuilder( \DummySessionProvider::class )
|
2021-03-20 15:18:58 +00:00
|
|
|
->onlyMethods( [ 'unpersistSession' ] )->getMock();
|
2016-02-26 21:17:37 +00:00
|
|
|
$this->provider->expects( $this->once() )->method( 'unpersistSession' );
|
|
|
|
|
$backend = $this->getBackend();
|
2017-04-19 19:37:35 +00:00
|
|
|
$wrap = TestingAccessWrapper::newFromObject( $backend );
|
2016-02-26 21:17:37 +00:00
|
|
|
$wrap->store = new \CachedBagOStuff( $this->store );
|
|
|
|
|
$wrap->persist = true;
|
|
|
|
|
$wrap->dataDirty = true;
|
|
|
|
|
|
|
|
|
|
$backend->save(); // This one shouldn't call $provider->persistSession(), but should save
|
|
|
|
|
$this->assertTrue( $backend->isPersistent(), 'sanity check' );
|
|
|
|
|
$this->assertNotFalse( $this->store->getSession( self::SESSIONID ), 'sanity check' );
|
|
|
|
|
|
|
|
|
|
$backend->unpersist();
|
|
|
|
|
$this->assertFalse( $backend->isPersistent() );
|
|
|
|
|
$this->assertFalse( $this->store->getSession( self::SESSIONID ) );
|
2017-05-24 03:36:28 +00:00
|
|
|
$this->assertNotFalse(
|
|
|
|
|
$wrap->store->get( $wrap->store->makeKey( 'MWSession', self::SESSIONID ) )
|
|
|
|
|
);
|
2016-02-26 21:17:37 +00:00
|
|
|
}
|
|
|
|
|
|
2016-02-01 20:44:03 +00:00
|
|
|
public function testRememberUser() {
|
|
|
|
|
$backend = $this->getBackend();
|
|
|
|
|
|
|
|
|
|
$remembered = $backend->shouldRememberUser();
|
|
|
|
|
$backend->setRememberUser( !$remembered );
|
|
|
|
|
$this->assertNotEquals( $remembered, $backend->shouldRememberUser() );
|
|
|
|
|
$backend->setRememberUser( $remembered );
|
|
|
|
|
$this->assertEquals( $remembered, $backend->shouldRememberUser() );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testForceHTTPS() {
|
|
|
|
|
$backend = $this->getBackend();
|
|
|
|
|
|
|
|
|
|
$force = $backend->shouldForceHTTPS();
|
|
|
|
|
$backend->setForceHTTPS( !$force );
|
|
|
|
|
$this->assertNotEquals( $force, $backend->shouldForceHTTPS() );
|
|
|
|
|
$backend->setForceHTTPS( $force );
|
|
|
|
|
$this->assertEquals( $force, $backend->shouldForceHTTPS() );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testLoggedOutTimestamp() {
|
|
|
|
|
$backend = $this->getBackend();
|
|
|
|
|
|
|
|
|
|
$backend->setLoggedOutTimestamp( 42 );
|
|
|
|
|
$this->assertSame( 42, $backend->getLoggedOutTimestamp() );
|
|
|
|
|
$backend->setLoggedOutTimestamp( '123' );
|
|
|
|
|
$this->assertSame( 123, $backend->getLoggedOutTimestamp() );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testSetUser() {
|
2016-05-18 09:19:20 +00:00
|
|
|
$user = static::getTestSysop()->getUser();
|
2016-02-01 20:44:03 +00:00
|
|
|
|
2018-01-13 00:02:09 +00:00
|
|
|
$this->provider = $this->getMockBuilder( \DummySessionProvider::class )
|
2021-03-20 15:18:58 +00:00
|
|
|
->onlyMethods( [ 'canChangeUser' ] )->getMock();
|
2021-04-22 08:28:11 +00:00
|
|
|
$this->provider->method( 'canChangeUser' )
|
|
|
|
|
->willReturn( false );
|
2016-02-01 20:44:03 +00:00
|
|
|
$backend = $this->getBackend();
|
|
|
|
|
$this->assertFalse( $backend->canSetUser() );
|
|
|
|
|
try {
|
|
|
|
|
$backend->setUser( $user );
|
|
|
|
|
$this->fail( 'Expected exception not thrown' );
|
|
|
|
|
} catch ( \BadMethodCallException $ex ) {
|
|
|
|
|
$this->assertSame(
|
|
|
|
|
'Cannot set user on this session; check $session->canSetUser() first',
|
|
|
|
|
$ex->getMessage()
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
$this->assertNotSame( $user, $backend->getUser() );
|
|
|
|
|
|
|
|
|
|
$this->provider = null;
|
|
|
|
|
$backend = $this->getBackend();
|
|
|
|
|
$this->assertTrue( $backend->canSetUser() );
|
|
|
|
|
$this->assertNotSame( $user, $backend->getUser(), 'sanity check' );
|
|
|
|
|
$backend->setUser( $user );
|
|
|
|
|
$this->assertSame( $user, $backend->getUser() );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testDirty() {
|
|
|
|
|
$backend = $this->getBackend();
|
2017-04-19 19:37:35 +00:00
|
|
|
$priv = TestingAccessWrapper::newFromObject( $backend );
|
2016-02-01 20:44:03 +00:00
|
|
|
$priv->dataDirty = false;
|
|
|
|
|
$backend->dirty();
|
|
|
|
|
$this->assertTrue( $priv->dataDirty );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testGetData() {
|
|
|
|
|
$backend = $this->getBackend();
|
|
|
|
|
$data = $backend->getData();
|
2016-02-17 09:09:32 +00:00
|
|
|
$this->assertSame( [], $data );
|
2017-04-19 19:37:35 +00:00
|
|
|
$this->assertTrue( TestingAccessWrapper::newFromObject( $backend )->dataDirty );
|
2016-02-01 20:44:03 +00:00
|
|
|
$data['???'] = '!!!';
|
2016-02-17 09:09:32 +00:00
|
|
|
$this->assertSame( [ '???' => '!!!' ], $data );
|
2016-02-01 20:44:03 +00:00
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
$testData = [ 'foo' => 'foo!', 'bar', [ 'baz', null ] ];
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->store->setSessionData( self::SESSIONID, $testData );
|
|
|
|
|
$backend = $this->getBackend();
|
|
|
|
|
$this->assertSame( $testData, $backend->getData() );
|
2017-04-19 19:37:35 +00:00
|
|
|
$this->assertFalse( TestingAccessWrapper::newFromObject( $backend )->dataDirty );
|
2016-02-01 20:44:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testAddData() {
|
|
|
|
|
$backend = $this->getBackend();
|
2017-04-19 19:37:35 +00:00
|
|
|
$priv = TestingAccessWrapper::newFromObject( $backend );
|
2016-02-01 20:44:03 +00:00
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
$priv->data = [ 'foo' => 1 ];
|
2016-02-01 20:44:03 +00:00
|
|
|
$priv->dataDirty = false;
|
2016-02-17 09:09:32 +00:00
|
|
|
$backend->addData( [ 'foo' => 1 ] );
|
|
|
|
|
$this->assertSame( [ 'foo' => 1 ], $priv->data );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->assertFalse( $priv->dataDirty );
|
|
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
$priv->data = [ 'foo' => 1 ];
|
2016-02-01 20:44:03 +00:00
|
|
|
$priv->dataDirty = false;
|
2016-02-17 09:09:32 +00:00
|
|
|
$backend->addData( [ 'foo' => '1' ] );
|
|
|
|
|
$this->assertSame( [ 'foo' => '1' ], $priv->data );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->assertTrue( $priv->dataDirty );
|
|
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
$priv->data = [ 'foo' => 1 ];
|
2016-02-01 20:44:03 +00:00
|
|
|
$priv->dataDirty = false;
|
2016-02-17 09:09:32 +00:00
|
|
|
$backend->addData( [ 'bar' => 2 ] );
|
|
|
|
|
$this->assertSame( [ 'foo' => 1, 'bar' => 2 ], $priv->data );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->assertTrue( $priv->dataDirty );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testDelaySave() {
|
2016-02-17 09:09:32 +00:00
|
|
|
$this->mergeMwGlobalArrayValue( 'wgHooks', [ 'SessionMetadata' => [ $this ] ] );
|
2016-02-01 20:44:03 +00:00
|
|
|
$backend = $this->getBackend();
|
2017-04-19 19:37:35 +00:00
|
|
|
$priv = TestingAccessWrapper::newFromObject( $backend );
|
2016-02-01 20:44:03 +00:00
|
|
|
$priv->persist = true;
|
|
|
|
|
|
|
|
|
|
// Saves happen normally when no delay is in effect
|
|
|
|
|
$this->onSessionMetadataCalled = false;
|
|
|
|
|
$priv->metaDirty = true;
|
|
|
|
|
$backend->save();
|
|
|
|
|
$this->assertTrue( $this->onSessionMetadataCalled, 'sanity check' );
|
|
|
|
|
|
|
|
|
|
$this->onSessionMetadataCalled = false;
|
|
|
|
|
$priv->metaDirty = true;
|
|
|
|
|
$priv->autosave();
|
|
|
|
|
$this->assertTrue( $this->onSessionMetadataCalled, 'sanity check' );
|
|
|
|
|
|
|
|
|
|
$delay = $backend->delaySave();
|
|
|
|
|
|
|
|
|
|
// Autosave doesn't happen when no delay is in effect
|
|
|
|
|
$this->onSessionMetadataCalled = false;
|
|
|
|
|
$priv->metaDirty = true;
|
|
|
|
|
$priv->autosave();
|
|
|
|
|
$this->assertFalse( $this->onSessionMetadataCalled );
|
|
|
|
|
|
|
|
|
|
// Save still does happen when no delay is in effect
|
|
|
|
|
$priv->save();
|
|
|
|
|
$this->assertTrue( $this->onSessionMetadataCalled );
|
|
|
|
|
|
|
|
|
|
// Save happens when delay is consumed
|
|
|
|
|
$this->onSessionMetadataCalled = false;
|
|
|
|
|
$priv->metaDirty = true;
|
2016-10-12 05:36:03 +00:00
|
|
|
\Wikimedia\ScopedCallback::consume( $delay );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->assertTrue( $this->onSessionMetadataCalled );
|
|
|
|
|
|
|
|
|
|
// Test multiple delays
|
|
|
|
|
$delay1 = $backend->delaySave();
|
|
|
|
|
$delay2 = $backend->delaySave();
|
|
|
|
|
$delay3 = $backend->delaySave();
|
|
|
|
|
$this->onSessionMetadataCalled = false;
|
|
|
|
|
$priv->metaDirty = true;
|
|
|
|
|
$priv->autosave();
|
|
|
|
|
$this->assertFalse( $this->onSessionMetadataCalled );
|
2016-10-12 05:36:03 +00:00
|
|
|
\Wikimedia\ScopedCallback::consume( $delay3 );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->assertFalse( $this->onSessionMetadataCalled );
|
2016-10-12 05:36:03 +00:00
|
|
|
\Wikimedia\ScopedCallback::consume( $delay1 );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->assertFalse( $this->onSessionMetadataCalled );
|
2016-10-12 05:36:03 +00:00
|
|
|
\Wikimedia\ScopedCallback::consume( $delay2 );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->assertTrue( $this->onSessionMetadataCalled );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testSave() {
|
2016-05-18 09:19:20 +00:00
|
|
|
$user = static::getTestSysop()->getUser();
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->store = new TestBagOStuff();
|
2016-02-17 09:09:32 +00:00
|
|
|
$testData = [ 'foo' => 'foo!', 'bar', [ 'baz', null ] ];
|
2016-02-01 20:44:03 +00:00
|
|
|
|
2017-04-05 23:39:50 +00:00
|
|
|
$neverHook = $this->getMockBuilder( __CLASS__ )
|
2021-03-20 15:18:58 +00:00
|
|
|
->onlyMethods( [ 'onSessionMetadata' ] )->getMock();
|
2016-02-01 20:44:03 +00:00
|
|
|
$neverHook->expects( $this->never() )->method( 'onSessionMetadata' );
|
|
|
|
|
|
2018-01-13 00:02:09 +00:00
|
|
|
$builder = $this->getMockBuilder( \DummySessionProvider::class )
|
2021-03-20 15:18:58 +00:00
|
|
|
->onlyMethods( [ 'persistSession', 'unpersistSession' ] );
|
2016-02-26 21:17:37 +00:00
|
|
|
|
|
|
|
|
$neverProvider = $builder->getMock();
|
2016-02-01 20:44:03 +00:00
|
|
|
$neverProvider->expects( $this->never() )->method( 'persistSession' );
|
2016-02-26 21:17:37 +00:00
|
|
|
$neverProvider->expects( $this->never() )->method( 'unpersistSession' );
|
2016-02-01 20:44:03 +00:00
|
|
|
|
|
|
|
|
// Not persistent or dirty
|
|
|
|
|
$this->provider = $neverProvider;
|
2016-02-17 09:09:32 +00:00
|
|
|
$this->mergeMwGlobalArrayValue( 'wgHooks', [ 'SessionMetadata' => [ $neverHook ] ] );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->store->setSessionData( self::SESSIONID, $testData );
|
|
|
|
|
$backend = $this->getBackend( $user );
|
|
|
|
|
$this->store->deleteSession( self::SESSIONID );
|
|
|
|
|
$this->assertFalse( $backend->isPersistent(), 'sanity check' );
|
2017-04-19 19:37:35 +00:00
|
|
|
TestingAccessWrapper::newFromObject( $backend )->metaDirty = false;
|
|
|
|
|
TestingAccessWrapper::newFromObject( $backend )->dataDirty = false;
|
2016-02-01 20:44:03 +00:00
|
|
|
$backend->save();
|
|
|
|
|
$this->assertFalse( $this->store->getSession( self::SESSIONID ), 'making sure it didn\'t save' );
|
|
|
|
|
|
2016-02-26 21:17:37 +00:00
|
|
|
// (but does unpersist if forced)
|
|
|
|
|
$this->provider = $builder->getMock();
|
|
|
|
|
$this->provider->expects( $this->never() )->method( 'persistSession' );
|
|
|
|
|
$this->provider->expects( $this->atLeastOnce() )->method( 'unpersistSession' );
|
|
|
|
|
$this->mergeMwGlobalArrayValue( 'wgHooks', [ 'SessionMetadata' => [ $neverHook ] ] );
|
|
|
|
|
$this->store->setSessionData( self::SESSIONID, $testData );
|
|
|
|
|
$backend = $this->getBackend( $user );
|
|
|
|
|
$this->store->deleteSession( self::SESSIONID );
|
2017-04-19 19:37:35 +00:00
|
|
|
TestingAccessWrapper::newFromObject( $backend )->persist = false;
|
|
|
|
|
TestingAccessWrapper::newFromObject( $backend )->forcePersist = true;
|
2016-02-26 21:17:37 +00:00
|
|
|
$this->assertFalse( $backend->isPersistent(), 'sanity check' );
|
2017-04-19 19:37:35 +00:00
|
|
|
TestingAccessWrapper::newFromObject( $backend )->metaDirty = false;
|
|
|
|
|
TestingAccessWrapper::newFromObject( $backend )->dataDirty = false;
|
2016-02-26 21:17:37 +00:00
|
|
|
$backend->save();
|
|
|
|
|
$this->assertFalse( $this->store->getSession( self::SESSIONID ), 'making sure it didn\'t save' );
|
|
|
|
|
|
|
|
|
|
// (but not to a WebRequest associated with a different session)
|
|
|
|
|
$this->provider = $neverProvider;
|
|
|
|
|
$this->mergeMwGlobalArrayValue( 'wgHooks', [ 'SessionMetadata' => [ $neverHook ] ] );
|
|
|
|
|
$this->store->setSessionData( self::SESSIONID, $testData );
|
|
|
|
|
$backend = $this->getBackend( $user );
|
2017-04-19 19:37:35 +00:00
|
|
|
TestingAccessWrapper::newFromObject( $backend )->requests[100]
|
2016-02-26 21:17:37 +00:00
|
|
|
->setSessionId( new SessionId( 'x' ) );
|
|
|
|
|
$this->store->deleteSession( self::SESSIONID );
|
2017-04-19 19:37:35 +00:00
|
|
|
TestingAccessWrapper::newFromObject( $backend )->persist = false;
|
|
|
|
|
TestingAccessWrapper::newFromObject( $backend )->forcePersist = true;
|
2016-02-26 21:17:37 +00:00
|
|
|
$this->assertFalse( $backend->isPersistent(), 'sanity check' );
|
2017-04-19 19:37:35 +00:00
|
|
|
TestingAccessWrapper::newFromObject( $backend )->metaDirty = false;
|
|
|
|
|
TestingAccessWrapper::newFromObject( $backend )->dataDirty = false;
|
2016-02-26 21:17:37 +00:00
|
|
|
$backend->save();
|
|
|
|
|
$this->assertFalse( $this->store->getSession( self::SESSIONID ), 'making sure it didn\'t save' );
|
|
|
|
|
|
2016-02-01 20:44:03 +00:00
|
|
|
// Not persistent, but dirty
|
|
|
|
|
$this->provider = $neverProvider;
|
|
|
|
|
$this->onSessionMetadataCalled = false;
|
2016-02-17 09:09:32 +00:00
|
|
|
$this->mergeMwGlobalArrayValue( 'wgHooks', [ 'SessionMetadata' => [ $this ] ] );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->store->setSessionData( self::SESSIONID, $testData );
|
|
|
|
|
$backend = $this->getBackend( $user );
|
|
|
|
|
$this->store->deleteSession( self::SESSIONID );
|
|
|
|
|
$this->assertFalse( $backend->isPersistent(), 'sanity check' );
|
2017-04-19 19:37:35 +00:00
|
|
|
TestingAccessWrapper::newFromObject( $backend )->metaDirty = false;
|
|
|
|
|
TestingAccessWrapper::newFromObject( $backend )->dataDirty = true;
|
2016-02-01 20:44:03 +00:00
|
|
|
$backend->save();
|
|
|
|
|
$this->assertTrue( $this->onSessionMetadataCalled );
|
|
|
|
|
$blob = $this->store->getSession( self::SESSIONID );
|
2019-12-13 14:29:10 +00:00
|
|
|
$this->assertIsArray( $blob );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->assertArrayHasKey( 'metadata', $blob );
|
|
|
|
|
$metadata = $blob['metadata'];
|
2019-12-13 14:29:10 +00:00
|
|
|
$this->assertIsArray( $metadata );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->assertArrayHasKey( '???', $metadata );
|
|
|
|
|
$this->assertSame( '!!!', $metadata['???'] );
|
2016-01-30 01:09:57 +00:00
|
|
|
$this->assertFalse( $this->store->getSessionFromBackend( self::SESSIONID ),
|
|
|
|
|
'making sure it didn\'t save to backend' );
|
2016-02-01 20:44:03 +00:00
|
|
|
|
|
|
|
|
// Persistent, not dirty
|
|
|
|
|
$this->provider = $neverProvider;
|
2016-02-17 09:09:32 +00:00
|
|
|
$this->mergeMwGlobalArrayValue( 'wgHooks', [ 'SessionMetadata' => [ $neverHook ] ] );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->store->setSessionData( self::SESSIONID, $testData );
|
|
|
|
|
$backend = $this->getBackend( $user );
|
|
|
|
|
$this->store->deleteSession( self::SESSIONID );
|
2017-04-19 19:37:35 +00:00
|
|
|
TestingAccessWrapper::newFromObject( $backend )->persist = true;
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->assertTrue( $backend->isPersistent(), 'sanity check' );
|
2017-04-19 19:37:35 +00:00
|
|
|
TestingAccessWrapper::newFromObject( $backend )->metaDirty = false;
|
|
|
|
|
TestingAccessWrapper::newFromObject( $backend )->dataDirty = false;
|
2016-02-01 20:44:03 +00:00
|
|
|
$backend->save();
|
|
|
|
|
$this->assertFalse( $this->store->getSession( self::SESSIONID ), 'making sure it didn\'t save' );
|
|
|
|
|
|
2016-02-26 21:17:37 +00:00
|
|
|
// (but will persist if forced)
|
|
|
|
|
$this->provider = $builder->getMock();
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->provider->expects( $this->atLeastOnce() )->method( 'persistSession' );
|
2016-02-26 21:17:37 +00:00
|
|
|
$this->provider->expects( $this->never() )->method( 'unpersistSession' );
|
2016-02-17 09:09:32 +00:00
|
|
|
$this->mergeMwGlobalArrayValue( 'wgHooks', [ 'SessionMetadata' => [ $neverHook ] ] );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->store->setSessionData( self::SESSIONID, $testData );
|
|
|
|
|
$backend = $this->getBackend( $user );
|
|
|
|
|
$this->store->deleteSession( self::SESSIONID );
|
2017-04-19 19:37:35 +00:00
|
|
|
TestingAccessWrapper::newFromObject( $backend )->persist = true;
|
|
|
|
|
TestingAccessWrapper::newFromObject( $backend )->forcePersist = true;
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->assertTrue( $backend->isPersistent(), 'sanity check' );
|
2017-04-19 19:37:35 +00:00
|
|
|
TestingAccessWrapper::newFromObject( $backend )->metaDirty = false;
|
|
|
|
|
TestingAccessWrapper::newFromObject( $backend )->dataDirty = false;
|
2016-02-01 20:44:03 +00:00
|
|
|
$backend->save();
|
|
|
|
|
$this->assertFalse( $this->store->getSession( self::SESSIONID ), 'making sure it didn\'t save' );
|
|
|
|
|
|
|
|
|
|
// Persistent and dirty
|
|
|
|
|
$this->provider = $neverProvider;
|
|
|
|
|
$this->onSessionMetadataCalled = false;
|
2016-02-17 09:09:32 +00:00
|
|
|
$this->mergeMwGlobalArrayValue( 'wgHooks', [ 'SessionMetadata' => [ $this ] ] );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->store->setSessionData( self::SESSIONID, $testData );
|
|
|
|
|
$backend = $this->getBackend( $user );
|
|
|
|
|
$this->store->deleteSession( self::SESSIONID );
|
2017-04-19 19:37:35 +00:00
|
|
|
TestingAccessWrapper::newFromObject( $backend )->persist = true;
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->assertTrue( $backend->isPersistent(), 'sanity check' );
|
2017-04-19 19:37:35 +00:00
|
|
|
TestingAccessWrapper::newFromObject( $backend )->metaDirty = false;
|
|
|
|
|
TestingAccessWrapper::newFromObject( $backend )->dataDirty = true;
|
2016-02-01 20:44:03 +00:00
|
|
|
$backend->save();
|
|
|
|
|
$this->assertTrue( $this->onSessionMetadataCalled );
|
|
|
|
|
$blob = $this->store->getSession( self::SESSIONID );
|
2019-12-13 14:29:10 +00:00
|
|
|
$this->assertIsArray( $blob );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->assertArrayHasKey( 'metadata', $blob );
|
|
|
|
|
$metadata = $blob['metadata'];
|
2019-12-13 14:29:10 +00:00
|
|
|
$this->assertIsArray( $metadata );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->assertArrayHasKey( '???', $metadata );
|
|
|
|
|
$this->assertSame( '!!!', $metadata['???'] );
|
2020-10-28 19:57:14 +00:00
|
|
|
$this->assertIsArray( $this->store->getSessionFromBackend( self::SESSIONID ),
|
2016-01-30 01:09:57 +00:00
|
|
|
'making sure it did save to backend' );
|
2016-02-01 20:44:03 +00:00
|
|
|
|
2016-02-26 21:17:37 +00:00
|
|
|
// (also persists if forced)
|
|
|
|
|
$this->provider = $builder->getMock();
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->provider->expects( $this->atLeastOnce() )->method( 'persistSession' );
|
2016-02-26 21:17:37 +00:00
|
|
|
$this->provider->expects( $this->never() )->method( 'unpersistSession' );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->onSessionMetadataCalled = false;
|
2016-02-17 09:09:32 +00:00
|
|
|
$this->mergeMwGlobalArrayValue( 'wgHooks', [ 'SessionMetadata' => [ $this ] ] );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->store->setSessionData( self::SESSIONID, $testData );
|
|
|
|
|
$backend = $this->getBackend( $user );
|
|
|
|
|
$this->store->deleteSession( self::SESSIONID );
|
2017-04-19 19:37:35 +00:00
|
|
|
TestingAccessWrapper::newFromObject( $backend )->persist = true;
|
|
|
|
|
TestingAccessWrapper::newFromObject( $backend )->forcePersist = true;
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->assertTrue( $backend->isPersistent(), 'sanity check' );
|
2017-04-19 19:37:35 +00:00
|
|
|
TestingAccessWrapper::newFromObject( $backend )->metaDirty = false;
|
|
|
|
|
TestingAccessWrapper::newFromObject( $backend )->dataDirty = true;
|
2016-02-01 20:44:03 +00:00
|
|
|
$backend->save();
|
|
|
|
|
$this->assertTrue( $this->onSessionMetadataCalled );
|
|
|
|
|
$blob = $this->store->getSession( self::SESSIONID );
|
2019-12-13 14:29:10 +00:00
|
|
|
$this->assertIsArray( $blob );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->assertArrayHasKey( 'metadata', $blob );
|
|
|
|
|
$metadata = $blob['metadata'];
|
2019-12-13 14:29:10 +00:00
|
|
|
$this->assertIsArray( $metadata );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->assertArrayHasKey( '???', $metadata );
|
|
|
|
|
$this->assertSame( '!!!', $metadata['???'] );
|
2020-10-28 19:57:14 +00:00
|
|
|
$this->assertIsArray( $this->store->getSessionFromBackend( self::SESSIONID ),
|
2016-01-30 01:09:57 +00:00
|
|
|
'making sure it did save to backend' );
|
2016-02-01 20:44:03 +00:00
|
|
|
|
2016-02-26 21:17:37 +00:00
|
|
|
// (also persists if metadata dirty)
|
|
|
|
|
$this->provider = $builder->getMock();
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->provider->expects( $this->atLeastOnce() )->method( 'persistSession' );
|
2016-02-26 21:17:37 +00:00
|
|
|
$this->provider->expects( $this->never() )->method( 'unpersistSession' );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->onSessionMetadataCalled = false;
|
2016-02-17 09:09:32 +00:00
|
|
|
$this->mergeMwGlobalArrayValue( 'wgHooks', [ 'SessionMetadata' => [ $this ] ] );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->store->setSessionData( self::SESSIONID, $testData );
|
|
|
|
|
$backend = $this->getBackend( $user );
|
|
|
|
|
$this->store->deleteSession( self::SESSIONID );
|
2017-04-19 19:37:35 +00:00
|
|
|
TestingAccessWrapper::newFromObject( $backend )->persist = true;
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->assertTrue( $backend->isPersistent(), 'sanity check' );
|
2017-04-19 19:37:35 +00:00
|
|
|
TestingAccessWrapper::newFromObject( $backend )->metaDirty = true;
|
|
|
|
|
TestingAccessWrapper::newFromObject( $backend )->dataDirty = false;
|
2016-02-01 20:44:03 +00:00
|
|
|
$backend->save();
|
|
|
|
|
$this->assertTrue( $this->onSessionMetadataCalled );
|
|
|
|
|
$blob = $this->store->getSession( self::SESSIONID );
|
2019-12-13 14:29:10 +00:00
|
|
|
$this->assertIsArray( $blob );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->assertArrayHasKey( 'metadata', $blob );
|
|
|
|
|
$metadata = $blob['metadata'];
|
2019-12-13 14:29:10 +00:00
|
|
|
$this->assertIsArray( $metadata );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->assertArrayHasKey( '???', $metadata );
|
|
|
|
|
$this->assertSame( '!!!', $metadata['???'] );
|
2020-10-28 19:57:14 +00:00
|
|
|
$this->assertIsArray( $this->store->getSessionFromBackend( self::SESSIONID ),
|
2016-01-30 01:09:57 +00:00
|
|
|
'making sure it did save to backend' );
|
2016-02-01 20:44:03 +00:00
|
|
|
|
|
|
|
|
// Not marked dirty, but dirty data
|
2016-02-10 16:43:23 +00:00
|
|
|
// (e.g. indirect modification from ArrayAccess::offsetGet)
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->provider = $neverProvider;
|
|
|
|
|
$this->onSessionMetadataCalled = false;
|
2016-02-17 09:09:32 +00:00
|
|
|
$this->mergeMwGlobalArrayValue( 'wgHooks', [ 'SessionMetadata' => [ $this ] ] );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->store->setSessionData( self::SESSIONID, $testData );
|
|
|
|
|
$backend = $this->getBackend( $user );
|
|
|
|
|
$this->store->deleteSession( self::SESSIONID );
|
2017-04-19 19:37:35 +00:00
|
|
|
TestingAccessWrapper::newFromObject( $backend )->persist = true;
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->assertTrue( $backend->isPersistent(), 'sanity check' );
|
2017-04-19 19:37:35 +00:00
|
|
|
TestingAccessWrapper::newFromObject( $backend )->metaDirty = false;
|
|
|
|
|
TestingAccessWrapper::newFromObject( $backend )->dataDirty = false;
|
|
|
|
|
TestingAccessWrapper::newFromObject( $backend )->dataHash = 'Doesn\'t match';
|
2016-02-01 20:44:03 +00:00
|
|
|
$backend->save();
|
|
|
|
|
$this->assertTrue( $this->onSessionMetadataCalled );
|
|
|
|
|
$blob = $this->store->getSession( self::SESSIONID );
|
2019-12-13 14:29:10 +00:00
|
|
|
$this->assertIsArray( $blob );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->assertArrayHasKey( 'metadata', $blob );
|
|
|
|
|
$metadata = $blob['metadata'];
|
2019-12-13 14:29:10 +00:00
|
|
|
$this->assertIsArray( $metadata );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->assertArrayHasKey( '???', $metadata );
|
|
|
|
|
$this->assertSame( '!!!', $metadata['???'] );
|
2020-10-28 19:57:14 +00:00
|
|
|
$this->assertIsArray( $this->store->getSessionFromBackend( self::SESSIONID ),
|
2016-01-30 01:09:57 +00:00
|
|
|
'making sure it did save to backend' );
|
2016-02-01 20:44:03 +00:00
|
|
|
|
|
|
|
|
// Bad hook
|
|
|
|
|
$this->provider = null;
|
2017-04-05 23:39:50 +00:00
|
|
|
$mockHook = $this->getMockBuilder( __CLASS__ )
|
2021-03-20 15:18:58 +00:00
|
|
|
->onlyMethods( [ 'onSessionMetadata' ] )->getMock();
|
2021-04-22 08:40:46 +00:00
|
|
|
$mockHook->method( 'onSessionMetadata' )
|
2016-02-01 20:44:03 +00:00
|
|
|
->will( $this->returnCallback(
|
2021-02-07 13:10:36 +00:00
|
|
|
static function ( SessionBackend $backend, array &$metadata, array $requests ) {
|
2016-02-01 20:44:03 +00:00
|
|
|
$metadata['userId']++;
|
|
|
|
|
}
|
|
|
|
|
) );
|
2016-02-17 09:09:32 +00:00
|
|
|
$this->mergeMwGlobalArrayValue( 'wgHooks', [ 'SessionMetadata' => [ $mockHook ] ] );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->store->setSessionData( self::SESSIONID, $testData );
|
|
|
|
|
$backend = $this->getBackend( $user );
|
|
|
|
|
$backend->dirty();
|
|
|
|
|
try {
|
|
|
|
|
$backend->save();
|
|
|
|
|
$this->fail( 'Expected exception not thrown' );
|
|
|
|
|
} catch ( \UnexpectedValueException $ex ) {
|
|
|
|
|
$this->assertSame(
|
|
|
|
|
'SessionMetadata hook changed metadata key "userId"',
|
|
|
|
|
$ex->getMessage()
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SessionManager::preventSessionsForUser
|
2017-04-19 19:37:35 +00:00
|
|
|
TestingAccessWrapper::newFromObject( $this->manager )->preventUsers = [
|
2016-02-01 20:44:03 +00:00
|
|
|
$user->getName() => true,
|
2016-02-17 09:09:32 +00:00
|
|
|
];
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->provider = $neverProvider;
|
2016-02-17 09:09:32 +00:00
|
|
|
$this->mergeMwGlobalArrayValue( 'wgHooks', [ 'SessionMetadata' => [ $neverHook ] ] );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->store->setSessionData( self::SESSIONID, $testData );
|
|
|
|
|
$backend = $this->getBackend( $user );
|
|
|
|
|
$this->store->deleteSession( self::SESSIONID );
|
2017-04-19 19:37:35 +00:00
|
|
|
TestingAccessWrapper::newFromObject( $backend )->persist = true;
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->assertTrue( $backend->isPersistent(), 'sanity check' );
|
2017-04-19 19:37:35 +00:00
|
|
|
TestingAccessWrapper::newFromObject( $backend )->metaDirty = true;
|
|
|
|
|
TestingAccessWrapper::newFromObject( $backend )->dataDirty = true;
|
2016-02-01 20:44:03 +00:00
|
|
|
$backend->save();
|
|
|
|
|
$this->assertFalse( $this->store->getSession( self::SESSIONID ), 'making sure it didn\'t save' );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testRenew() {
|
2016-05-18 09:19:20 +00:00
|
|
|
$user = static::getTestSysop()->getUser();
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->store = new TestBagOStuff();
|
2016-02-17 09:09:32 +00:00
|
|
|
$testData = [ 'foo' => 'foo!', 'bar', [ 'baz', null ] ];
|
2016-02-01 20:44:03 +00:00
|
|
|
|
|
|
|
|
// Not persistent
|
2018-01-13 00:02:09 +00:00
|
|
|
$this->provider = $this->getMockBuilder( \DummySessionProvider::class )
|
2021-03-20 15:18:58 +00:00
|
|
|
->onlyMethods( [ 'persistSession' ] )->getMock();
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->provider->expects( $this->never() )->method( 'persistSession' );
|
|
|
|
|
$this->onSessionMetadataCalled = false;
|
2016-02-17 09:09:32 +00:00
|
|
|
$this->mergeMwGlobalArrayValue( 'wgHooks', [ 'SessionMetadata' => [ $this ] ] );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->store->setSessionData( self::SESSIONID, $testData );
|
|
|
|
|
$backend = $this->getBackend( $user );
|
|
|
|
|
$this->store->deleteSession( self::SESSIONID );
|
2017-04-19 19:37:35 +00:00
|
|
|
$wrap = TestingAccessWrapper::newFromObject( $backend );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->assertFalse( $backend->isPersistent(), 'sanity check' );
|
|
|
|
|
$wrap->metaDirty = false;
|
|
|
|
|
$wrap->dataDirty = false;
|
|
|
|
|
$wrap->forcePersist = false;
|
|
|
|
|
$wrap->expires = 0;
|
|
|
|
|
$backend->renew();
|
|
|
|
|
$this->assertTrue( $this->onSessionMetadataCalled );
|
|
|
|
|
$blob = $this->store->getSession( self::SESSIONID );
|
2019-12-13 14:29:10 +00:00
|
|
|
$this->assertIsArray( $blob );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->assertArrayHasKey( 'metadata', $blob );
|
|
|
|
|
$metadata = $blob['metadata'];
|
2019-12-13 14:29:10 +00:00
|
|
|
$this->assertIsArray( $metadata );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->assertArrayHasKey( '???', $metadata );
|
|
|
|
|
$this->assertSame( '!!!', $metadata['???'] );
|
|
|
|
|
$this->assertNotEquals( 0, $wrap->expires );
|
|
|
|
|
|
|
|
|
|
// Persistent
|
2018-01-13 00:02:09 +00:00
|
|
|
$this->provider = $this->getMockBuilder( \DummySessionProvider::class )
|
2021-03-20 15:18:58 +00:00
|
|
|
->onlyMethods( [ 'persistSession' ] )->getMock();
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->provider->expects( $this->atLeastOnce() )->method( 'persistSession' );
|
|
|
|
|
$this->onSessionMetadataCalled = false;
|
2016-02-17 09:09:32 +00:00
|
|
|
$this->mergeMwGlobalArrayValue( 'wgHooks', [ 'SessionMetadata' => [ $this ] ] );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->store->setSessionData( self::SESSIONID, $testData );
|
|
|
|
|
$backend = $this->getBackend( $user );
|
|
|
|
|
$this->store->deleteSession( self::SESSIONID );
|
2017-04-19 19:37:35 +00:00
|
|
|
$wrap = TestingAccessWrapper::newFromObject( $backend );
|
2016-02-01 20:44:03 +00:00
|
|
|
$wrap->persist = true;
|
|
|
|
|
$this->assertTrue( $backend->isPersistent(), 'sanity check' );
|
|
|
|
|
$wrap->metaDirty = false;
|
|
|
|
|
$wrap->dataDirty = false;
|
|
|
|
|
$wrap->forcePersist = false;
|
|
|
|
|
$wrap->expires = 0;
|
|
|
|
|
$backend->renew();
|
|
|
|
|
$this->assertTrue( $this->onSessionMetadataCalled );
|
|
|
|
|
$blob = $this->store->getSession( self::SESSIONID );
|
2019-12-13 14:29:10 +00:00
|
|
|
$this->assertIsArray( $blob );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->assertArrayHasKey( 'metadata', $blob );
|
|
|
|
|
$metadata = $blob['metadata'];
|
2019-12-13 14:29:10 +00:00
|
|
|
$this->assertIsArray( $metadata );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->assertArrayHasKey( '???', $metadata );
|
|
|
|
|
$this->assertSame( '!!!', $metadata['???'] );
|
|
|
|
|
$this->assertNotEquals( 0, $wrap->expires );
|
|
|
|
|
|
|
|
|
|
// Not persistent, not expiring
|
2018-01-13 00:02:09 +00:00
|
|
|
$this->provider = $this->getMockBuilder( \DummySessionProvider::class )
|
2021-03-20 15:18:58 +00:00
|
|
|
->onlyMethods( [ 'persistSession' ] )->getMock();
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->provider->expects( $this->never() )->method( 'persistSession' );
|
|
|
|
|
$this->onSessionMetadataCalled = false;
|
2016-02-17 09:09:32 +00:00
|
|
|
$this->mergeMwGlobalArrayValue( 'wgHooks', [ 'SessionMetadata' => [ $this ] ] );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->store->setSessionData( self::SESSIONID, $testData );
|
|
|
|
|
$backend = $this->getBackend( $user );
|
|
|
|
|
$this->store->deleteSession( self::SESSIONID );
|
2017-04-19 19:37:35 +00:00
|
|
|
$wrap = TestingAccessWrapper::newFromObject( $backend );
|
2016-02-01 20:44:03 +00:00
|
|
|
$this->assertFalse( $backend->isPersistent(), 'sanity check' );
|
|
|
|
|
$wrap->metaDirty = false;
|
|
|
|
|
$wrap->dataDirty = false;
|
|
|
|
|
$wrap->forcePersist = false;
|
|
|
|
|
$expires = time() + $wrap->lifetime + 100;
|
|
|
|
|
$wrap->expires = $expires;
|
|
|
|
|
$backend->renew();
|
|
|
|
|
$this->assertFalse( $this->onSessionMetadataCalled );
|
|
|
|
|
$this->assertFalse( $this->store->getSession( self::SESSIONID ), 'making sure it didn\'t save' );
|
|
|
|
|
$this->assertEquals( $expires, $wrap->expires );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function onSessionMetadata( SessionBackend $backend, array &$metadata, array $requests ) {
|
|
|
|
|
$this->onSessionMetadataCalled = true;
|
|
|
|
|
$metadata['???'] = '!!!';
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-22 21:50:32 +00:00
|
|
|
public function testTakeOverGlobalSession() {
|
|
|
|
|
if ( !PHPSessionHandler::isInstalled() ) {
|
|
|
|
|
PHPSessionHandler::install( SessionManager::singleton() );
|
|
|
|
|
}
|
|
|
|
|
if ( !PHPSessionHandler::isEnabled() ) {
|
2016-03-28 18:53:04 +00:00
|
|
|
$rProp = new \ReflectionProperty( PHPSessionHandler::class, 'instance' );
|
2016-03-22 21:50:32 +00:00
|
|
|
$rProp->setAccessible( true );
|
2017-04-19 19:37:35 +00:00
|
|
|
$handler = TestingAccessWrapper::newFromObject( $rProp->getValue() );
|
2021-02-07 13:10:36 +00:00
|
|
|
$resetHandler = new \Wikimedia\ScopedCallback( static function () use ( $handler ) {
|
2016-03-22 21:50:32 +00:00
|
|
|
session_write_close();
|
|
|
|
|
$handler->enable = false;
|
|
|
|
|
} );
|
|
|
|
|
$handler->enable = true;
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-18 09:19:20 +00:00
|
|
|
$backend = $this->getBackend( static::getTestSysop()->getUser() );
|
2017-04-19 19:37:35 +00:00
|
|
|
TestingAccessWrapper::newFromObject( $backend )->usePhpSessionHandling = true;
|
2016-03-22 21:50:32 +00:00
|
|
|
|
|
|
|
|
$resetSingleton = TestUtils::setSessionManagerSingleton( $this->manager );
|
|
|
|
|
|
2017-04-19 19:37:35 +00:00
|
|
|
$manager = TestingAccessWrapper::newFromObject( $this->manager );
|
2016-03-22 21:50:32 +00:00
|
|
|
$request = \RequestContext::getMain()->getRequest();
|
|
|
|
|
$manager->globalSession = $backend->getSession( $request );
|
|
|
|
|
$manager->globalSessionRequest = $request;
|
|
|
|
|
|
|
|
|
|
session_id( '' );
|
2017-04-19 19:37:35 +00:00
|
|
|
TestingAccessWrapper::newFromObject( $backend )->checkPHPSession();
|
2016-03-22 21:50:32 +00:00
|
|
|
$this->assertSame( $backend->getId(), session_id() );
|
|
|
|
|
session_write_close();
|
|
|
|
|
|
|
|
|
|
$backend2 = $this->getBackend(
|
|
|
|
|
User::newFromName( 'UTSysop' ), 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'
|
|
|
|
|
);
|
2017-04-19 19:37:35 +00:00
|
|
|
TestingAccessWrapper::newFromObject( $backend2 )->usePhpSessionHandling = true;
|
2016-03-22 21:50:32 +00:00
|
|
|
|
|
|
|
|
session_id( '' );
|
2017-04-19 19:37:35 +00:00
|
|
|
TestingAccessWrapper::newFromObject( $backend2 )->checkPHPSession();
|
2016-03-22 21:50:32 +00:00
|
|
|
$this->assertSame( '', session_id() );
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-01 20:44:03 +00:00
|
|
|
public function testResetIdOfGlobalSession() {
|
|
|
|
|
if ( !PHPSessionHandler::isInstalled() ) {
|
|
|
|
|
PHPSessionHandler::install( SessionManager::singleton() );
|
|
|
|
|
}
|
|
|
|
|
if ( !PHPSessionHandler::isEnabled() ) {
|
2016-03-28 18:53:04 +00:00
|
|
|
$rProp = new \ReflectionProperty( PHPSessionHandler::class, 'instance' );
|
2016-02-01 20:44:03 +00:00
|
|
|
$rProp->setAccessible( true );
|
2017-04-19 19:37:35 +00:00
|
|
|
$handler = TestingAccessWrapper::newFromObject( $rProp->getValue() );
|
2021-02-07 13:10:36 +00:00
|
|
|
$resetHandler = new \Wikimedia\ScopedCallback( static function () use ( $handler ) {
|
2016-02-01 20:44:03 +00:00
|
|
|
session_write_close();
|
|
|
|
|
$handler->enable = false;
|
|
|
|
|
} );
|
|
|
|
|
$handler->enable = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$backend = $this->getBackend( User::newFromName( 'UTSysop' ) );
|
2017-04-19 19:37:35 +00:00
|
|
|
TestingAccessWrapper::newFromObject( $backend )->usePhpSessionHandling = true;
|
2016-02-01 20:44:03 +00:00
|
|
|
|
2016-03-22 21:50:32 +00:00
|
|
|
$resetSingleton = TestUtils::setSessionManagerSingleton( $this->manager );
|
2016-02-01 20:44:03 +00:00
|
|
|
|
2017-04-19 19:37:35 +00:00
|
|
|
$manager = TestingAccessWrapper::newFromObject( $this->manager );
|
2016-02-01 20:44:03 +00:00
|
|
|
$request = \RequestContext::getMain()->getRequest();
|
|
|
|
|
$manager->globalSession = $backend->getSession( $request );
|
|
|
|
|
$manager->globalSessionRequest = $request;
|
|
|
|
|
|
|
|
|
|
session_id( self::SESSIONID );
|
2019-05-26 21:46:15 +00:00
|
|
|
AtEase::quietCall( 'session_start' );
|
2016-03-22 21:50:32 +00:00
|
|
|
$_SESSION['foo'] = __METHOD__;
|
2016-02-01 20:44:03 +00:00
|
|
|
$backend->resetId();
|
|
|
|
|
$this->assertNotEquals( self::SESSIONID, $backend->getId() );
|
|
|
|
|
$this->assertSame( $backend->getId(), session_id() );
|
2016-03-22 21:50:32 +00:00
|
|
|
$this->assertArrayHasKey( 'foo', $_SESSION );
|
|
|
|
|
$this->assertSame( __METHOD__, $_SESSION['foo'] );
|
2016-02-01 20:44:03 +00:00
|
|
|
session_write_close();
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-26 21:17:37 +00:00
|
|
|
public function testUnpersistOfGlobalSession() {
|
|
|
|
|
if ( !PHPSessionHandler::isInstalled() ) {
|
|
|
|
|
PHPSessionHandler::install( SessionManager::singleton() );
|
|
|
|
|
}
|
|
|
|
|
if ( !PHPSessionHandler::isEnabled() ) {
|
2016-03-28 18:53:04 +00:00
|
|
|
$rProp = new \ReflectionProperty( PHPSessionHandler::class, 'instance' );
|
2016-02-26 21:17:37 +00:00
|
|
|
$rProp->setAccessible( true );
|
2017-04-19 19:37:35 +00:00
|
|
|
$handler = TestingAccessWrapper::newFromObject( $rProp->getValue() );
|
2021-02-07 13:10:36 +00:00
|
|
|
$resetHandler = new \Wikimedia\ScopedCallback( static function () use ( $handler ) {
|
2016-02-26 21:17:37 +00:00
|
|
|
session_write_close();
|
|
|
|
|
$handler->enable = false;
|
|
|
|
|
} );
|
|
|
|
|
$handler->enable = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$backend = $this->getBackend( User::newFromName( 'UTSysop' ) );
|
2017-04-19 19:37:35 +00:00
|
|
|
$wrap = TestingAccessWrapper::newFromObject( $backend );
|
2016-02-26 21:17:37 +00:00
|
|
|
$wrap->usePhpSessionHandling = true;
|
|
|
|
|
$wrap->persist = true;
|
|
|
|
|
|
2016-03-22 21:50:32 +00:00
|
|
|
$resetSingleton = TestUtils::setSessionManagerSingleton( $this->manager );
|
2016-02-26 21:17:37 +00:00
|
|
|
|
2017-04-19 19:37:35 +00:00
|
|
|
$manager = TestingAccessWrapper::newFromObject( $this->manager );
|
2016-02-26 21:17:37 +00:00
|
|
|
$request = \RequestContext::getMain()->getRequest();
|
|
|
|
|
$manager->globalSession = $backend->getSession( $request );
|
|
|
|
|
$manager->globalSessionRequest = $request;
|
|
|
|
|
|
|
|
|
|
session_id( self::SESSIONID . 'x' );
|
2019-05-26 21:46:15 +00:00
|
|
|
AtEase::quietCall( 'session_start' );
|
2016-02-26 21:17:37 +00:00
|
|
|
$backend->unpersist();
|
|
|
|
|
$this->assertSame( self::SESSIONID . 'x', session_id() );
|
2018-10-16 14:22:33 +00:00
|
|
|
session_write_close();
|
2016-02-26 21:17:37 +00:00
|
|
|
|
|
|
|
|
session_id( self::SESSIONID );
|
|
|
|
|
$wrap->persist = true;
|
|
|
|
|
$backend->unpersist();
|
|
|
|
|
$this->assertSame( '', session_id() );
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-01 20:44:03 +00:00
|
|
|
public function testGetAllowedUserRights() {
|
2018-01-13 00:02:09 +00:00
|
|
|
$this->provider = $this->getMockBuilder( \DummySessionProvider::class )
|
2021-03-20 15:18:58 +00:00
|
|
|
->onlyMethods( [ 'getAllowedUserRights' ] )
|
2016-02-01 20:44:03 +00:00
|
|
|
->getMock();
|
2021-04-22 08:28:11 +00:00
|
|
|
$this->provider->method( 'getAllowedUserRights' )
|
|
|
|
|
->willReturn( [ 'foo', 'bar' ] );
|
2016-02-01 20:44:03 +00:00
|
|
|
|
|
|
|
|
$backend = $this->getBackend();
|
2016-02-17 09:09:32 +00:00
|
|
|
$this->assertSame( [ 'foo', 'bar' ], $backend->getAllowedUserRights() );
|
2016-02-01 20:44:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|